Skip to content

Live UI updates: push book/queue changes via SSE instead of per-page polling #1163

Description

@vavallee

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions