refactor(hash-conflicts): one usage-counts reader for the API and SSR surfaces

The pending-hash-conflict walk existed twice (JSON admin API emitting
numeric counts, SSR page emitting a formatted summary) — deliberately
different shapes, but both duplicated the counts-or-zero lookup and
the five-field total. MediaItemUsageCountsTotal is the shared read;
the per-surface shaping stays local on purpose.
This commit is contained in:
John O'Keefe committed 2026-10-17 17:15:14 -04:00
1 parent 010c0cb287
commit 2ba1e0960b
2 files changed
+17 -9

No files matched your search

+16 -4
View File
@@ -2,6 +2,7 @@ package handlers
import ( import (
"bookhoard/internal/database" "bookhoard/internal/database"
"context"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
@@ -48,6 +49,20 @@ type HashConflictResponse struct {
Items []HashConflictItem `json:"items"` Items []HashConflictItem `json:"items"`
} }
// MediaItemUsageCountsTotal reads an item's usage tally for the
// hash-conflict surfaces, treating a lookup failure as "no usage"
// (these surfaces only use it to warn about data loss), and returns
// the counts with their precomputed total.
func MediaItemUsageCountsTotal(ctx context.Context, db *database.Queries, id pgtype.UUID) (database.GetMediaItemUsageCountsRow, int) {
counts, err := db.GetMediaItemUsageCounts(ctx, id)
if err != nil {
counts = database.GetMediaItemUsageCountsRow{}
}
total := counts.ProgressCount + counts.HighlightsCount + counts.BookmarksCount +
counts.NotesCount + counts.CollectionsCount
return counts, int(total)
}
// ListHashConflicts returns all pending hash conflict groups with their member // ListHashConflicts returns all pending hash conflict groups with their member
// items and usage counts. // items and usage counts.
// GET /api/admin/hash-conflicts // GET /api/admin/hash-conflicts
@@ -80,10 +95,7 @@ func (h *HashConflictsHandler) ListHashConflicts(c *echo.Context) error {
} }
for _, mi := range items { for _, mi := range items {
counts, err := h.db.GetMediaItemUsageCounts(ctx, mi.ID) counts, _ := MediaItemUsageCountsTotal(ctx, h.db, mi.ID)
if err != nil {
counts = database.GetMediaItemUsageCountsRow{}
}
resp.Items = append(resp.Items, HashConflictItem{ resp.Items = append(resp.Items, HashConflictItem{
ID: uuid.UUID(mi.ID.Bytes), ID: uuid.UUID(mi.ID.Bytes),
Title: mi.Title, Title: mi.Title,
+1 -5
View File
@@ -1214,11 +1214,7 @@ func registerFrontendRoutes(cfg *Config) {
} }
for _, mi := range items { for _, mi := range items {
counts, err := cfg.Queries.GetMediaItemUsageCounts(c.Request().Context(), mi.ID) counts, totalData := handlers.MediaItemUsageCountsTotal(c.Request().Context(), cfg.Queries, mi.ID)
if err != nil {
counts = database.GetMediaItemUsageCountsRow{}
}
totalData := counts.ProgressCount + counts.HighlightsCount + counts.BookmarksCount + counts.NotesCount + counts.CollectionsCount
conflict.Items = append(conflict.Items, templates.HashConflictItemData{ conflict.Items = append(conflict.Items, templates.HashConflictItemData{
ID: uuid.UUID(mi.ID.Bytes).String(), ID: uuid.UUID(mi.ID.Bytes).String(),
Title: mi.Title, Title: mi.Title,