Files
spsays-bff/internal/handler/notification.go

129 lines
4.6 KiB
Go

package handler
import (
"encoding/json"
"errors"
"net/http"
"github.com/prabhupadasays/spsays-bff/internal/middleware"
"github.com/prabhupadasays/spsays-bff/internal/service"
"github.com/prabhupadasays/spsays-bff/pkg/response"
)
// NotificationHandler serves the signed-in user's notification feed +
// unread badge count, and marks them read. All routes sit behind
// RequireAuth, so middleware.UserID is always populated.
type NotificationHandler struct{ svc *service.NotificationService }
func NewNotificationHandler(svc *service.NotificationService) *NotificationHandler {
return &NotificationHandler{svc: svc}
}
// List returns the caller's notifications, newest first.
func (h *NotificationHandler) List(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
limit, offset := parseLimitOffset(r)
items, err := h.svc.List(r.Context(), uid, limit, offset)
if err != nil {
response.Error(w, http.StatusInternalServerError, "could not load notifications")
return
}
response.JSON(w, http.StatusOK, items)
}
// UnreadCount returns {"count": N} for the bell badge.
func (h *NotificationHandler) UnreadCount(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
n, err := h.svc.UnreadCount(r.Context(), uid)
if err != nil {
response.Error(w, http.StatusInternalServerError, "could not load unread count")
return
}
response.JSON(w, http.StatusOK, map[string]int{"count": n})
}
// MarkAllRead marks every notification read (called when the page opens).
func (h *NotificationHandler) MarkAllRead(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
if err := h.svc.MarkAllRead(r.Context(), uid); err != nil {
response.Error(w, http.StatusInternalServerError, "could not mark notifications read")
return
}
response.NoContent(w)
}
// GetPrefs — GET /notifications/prefs. The resolved Settings matrix:
// {"prefs": {"follows": {"in_app": true, "email": true, "push": true}, …}}.
func (h *NotificationHandler) GetPrefs(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
matrix, err := h.svc.PrefMatrix(r.Context(), uid)
if err != nil {
response.Error(w, http.StatusInternalServerError, "could not load notification preferences")
return
}
response.JSON(w, http.StatusOK, map[string]any{"prefs": matrix})
}
// UpdatePrefs — PUT /notifications/prefs { event, channel, enabled }. Writes
// one matrix cell; returns the full re-resolved matrix so the client can
// render authoritative state.
func (h *NotificationHandler) UpdatePrefs(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
var req struct {
Event string `json:"event"`
Channel string `json:"channel"`
Enabled bool `json:"enabled"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
response.Error(w, http.StatusBadRequest, "invalid JSON body")
return
}
if err := h.svc.SetPref(r.Context(), uid, req.Event, req.Channel, req.Enabled); err != nil {
if errors.Is(err, service.ErrUnknownPref) {
response.Error(w, http.StatusBadRequest, "unknown event or channel")
return
}
response.Error(w, http.StatusInternalServerError, "could not update notification preferences")
return
}
matrix, err := h.svc.PrefMatrix(r.Context(), uid)
if err != nil {
response.Error(w, http.StatusInternalServerError, "could not load notification preferences")
return
}
response.JSON(w, http.StatusOK, map[string]any{"prefs": matrix})
}
type emailPrefsResp struct {
LikeEmails bool `json:"like_emails"`
}
// GetEmailPrefs — GET /notifications/email-prefs. The caller's email-notification
// preferences (currently just aggregated like emails).
func (h *NotificationHandler) GetEmailPrefs(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
enabled, err := h.svc.LikeEmailPref(r.Context(), uid)
if err != nil {
response.Error(w, http.StatusInternalServerError, "could not load email preferences")
return
}
response.JSON(w, http.StatusOK, emailPrefsResp{LikeEmails: enabled})
}
// UpdateEmailPrefs — PUT /notifications/email-prefs { like_emails }.
func (h *NotificationHandler) UpdateEmailPrefs(w http.ResponseWriter, r *http.Request) {
uid := middleware.UserID(r.Context())
var req struct {
LikeEmails bool `json:"like_emails"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
response.Error(w, http.StatusBadRequest, "invalid JSON body")
return
}
if err := h.svc.SetLikeEmailPref(r.Context(), uid, req.LikeEmails); err != nil {
response.Error(w, http.StatusInternalServerError, "could not update email preferences")
return
}
response.JSON(w, http.StatusOK, emailPrefsResp{LikeEmails: req.LikeEmails})
}