Problem
Several views show book/download state that the backend mutates asynchronously (auto-grab, the download → import pipeline), but they fetch once and go stale until a manual reload. We've been patching this per-page with setInterval polling:
Still fetch-once: BooksPage, AuthorDetailPage, SeriesPage.
Per-page polling is a band-aid with real downsides: every open list refetches on a timer per user, each page reinvents the gate/teardown logic, and views still disagree with each other between ticks.
Proposal
Add a single server-push channel — Server-Sent Events (simplest fit; one-way server→client, works over plain HTTP, auto-reconnects) at e.g. GET /api/v1/events, emitting small change notifications:
book.updated (id, status, file paths) — fired when the importer/scanner/grab pipeline changes a book
queue.changed — download queue add/remove/state
- optionally
author.refresh.progress (replaces the AuthorsPage 2s poll)
Frontend: a shared useLiveEvents() hook / context that subscribes once and lets pages invalidate or patch their local state on the relevant event. Replace the existing setInterval polls (Queue, BookDetail, Wanted, Authors refresh) with subscriptions, and the fetch-once pages get live updates for free.
Scope / notes
- SSE over websocket: we only need server→client, SSE is lighter and proxy-friendly (already behind Traefik). Revisit websockets only if bidirectional needs appear.
- Auth: the events endpoint goes through the same auth wall; reuse the session/API-key path.
- Backpressure/fan-out: a simple in-process subscriber registry is enough for a single-replica deployment; note the constraint if multi-replica ever lands.
- This supersedes the per-page polling once shipped; remove those intervals as part of the migration.
Origin
Came out of #1161 (book detail page didn't reflect a completed import until reload) — the same fetch-once pattern exists across multiple pages, and a shared push channel kills the whole class at once.
Problem
Several views show book/download state that the backend mutates asynchronously (auto-grab, the download → import pipeline), but they fetch once and go stale until a manual reload. We've been patching this per-page with
setIntervalpolling:Still fetch-once: BooksPage, AuthorDetailPage, SeriesPage.
Per-page polling is a band-aid with real downsides: every open list refetches on a timer per user, each page reinvents the gate/teardown logic, and views still disagree with each other between ticks.
Proposal
Add a single server-push channel — Server-Sent Events (simplest fit; one-way server→client, works over plain HTTP, auto-reconnects) at e.g.
GET /api/v1/events, emitting small change notifications:book.updated(id, status, file paths) — fired when the importer/scanner/grab pipeline changes a bookqueue.changed— download queue add/remove/stateauthor.refresh.progress(replaces the AuthorsPage 2s poll)Frontend: a shared
useLiveEvents()hook / context that subscribes once and lets pages invalidate or patch their local state on the relevant event. Replace the existingsetIntervalpolls (Queue, BookDetail, Wanted, Authors refresh) with subscriptions, and the fetch-once pages get live updates for free.Scope / notes
Origin
Came out of #1161 (book detail page didn't reflect a completed import until reload) — the same fetch-once pattern exists across multiple pages, and a shared push channel kills the whole class at once.