Files
silo-server/internal/api/handlers/home_dismissals.go
T

114 lines
3.3 KiB
Go

package handlers
import (
"encoding/json"
"net/http"
"time"
"github.com/go-chi/chi/v5"
apimw "github.com/Silo-Server/silo-server/internal/api/middleware"
"github.com/Silo-Server/silo-server/internal/userstore"
)
type HomeDismissalHandler struct {
storeProvider userstore.UserStoreProvider
}
type upsertHomeDismissalRequest struct {
SeriesID string `json:"series_id"`
ProgressUpdatedAt string `json:"progress_updated_at"`
}
func NewHomeDismissalHandler(provider userstore.UserStoreProvider) *HomeDismissalHandler {
return &HomeDismissalHandler{storeProvider: provider}
}
func (h *HomeDismissalHandler) HandleUpsertDismissal(w http.ResponseWriter, r *http.Request) {
userID := apimw.GetUserID(r.Context())
profileID := apimw.GetProfileID(r.Context())
surface := chi.URLParam(r, "surface")
itemID := chi.URLParam(r, "item_id")
if itemID == "" {
writeError(w, http.StatusBadRequest, "bad_request", "Item ID is required")
return
}
if !validHomeSurface(surface) {
writeError(w, http.StatusBadRequest, "bad_request", "Surface is invalid")
return
}
var req upsertHomeDismissalRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "bad_request", "Invalid request body")
return
}
dismissal := userstore.HomeItemDismissal{
ProfileID: profileID,
Surface: surface,
MediaItemID: itemID,
DismissedAt: time.Now().UTC().Format(time.RFC3339),
}
switch surface {
case userstore.HomeSurfaceContinueWatching:
if req.ProgressUpdatedAt == "" {
writeError(w, http.StatusBadRequest, "bad_request", "progress_updated_at is required")
return
}
dismissal.ProgressUpdatedAt = &req.ProgressUpdatedAt
case userstore.HomeSurfaceNextUp:
if req.SeriesID == "" {
writeError(w, http.StatusBadRequest, "bad_request", "series_id is required")
return
}
dismissal.SeriesID = &req.SeriesID
}
store, err := h.storeProvider.ForUser(r.Context(), userID)
if err != nil || store == nil {
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to access user store")
return
}
if err := store.UpsertHomeDismissal(r.Context(), dismissal); err != nil {
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to save dismissal")
return
}
w.WriteHeader(http.StatusNoContent)
}
func (h *HomeDismissalHandler) HandleDeleteDismissal(w http.ResponseWriter, r *http.Request) {
userID := apimw.GetUserID(r.Context())
profileID := apimw.GetProfileID(r.Context())
surface := chi.URLParam(r, "surface")
itemID := chi.URLParam(r, "item_id")
if itemID == "" {
writeError(w, http.StatusBadRequest, "bad_request", "Item ID is required")
return
}
if !validHomeSurface(surface) {
writeError(w, http.StatusBadRequest, "bad_request", "Surface is invalid")
return
}
store, err := h.storeProvider.ForUser(r.Context(), userID)
if err != nil || store == nil {
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to access user store")
return
}
if err := store.DeleteHomeDismissal(r.Context(), profileID, surface, itemID); err != nil {
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to delete dismissal")
return
}
w.WriteHeader(http.StatusNoContent)
}
func validHomeSurface(surface string) bool {
return surface == userstore.HomeSurfaceContinueWatching || surface == userstore.HomeSurfaceNextUp
}