Skip to content

Push a live signal to clients when news posts are published#1348

Open
2Pacalypse- wants to merge 2 commits into
dynamic-news-adminfrom
dynamic-news-live
Open

Push a live signal to clients when news posts are published#1348
2Pacalypse- wants to merge 2 commits into
dynamic-news-adminfrom
dynamic-news-live

Conversation

@2Pacalypse-

Copy link
Copy Markdown
Member

Fourth PR of the dynamic news stack (on top of #1347). Publishing a news post now lights an unread pip on the Home nav entry for connected clients in real time — including posts that go live on a schedule.

Changes

  • Node NewsService: consumes the newsPostsChanged Redis message the Rust side publishes on every post mutation. It reconciles the latest published post, publishes a newsPostChange event on a new /newsPosts socket path only when the (id, publishedAt) pair actually changes (a cleared variant when nothing is published), and seeds newly-connected clients with the current state. Reconcile passes are serialized (chained promises) so overlapping Redis messages can't complete out of order and cache stale state.
  • Scheduled publishes: after each reconcile the service arms a single timer for the next future published_at (generation-guarded against re-arms, clamped to the 32-bit setTimeout max with re-arming, +1s slop past the boundary), so a scheduled post pushes the signal the moment it goes live with no mutation involved.
  • Clock.clearTimeout added to the clock abstraction with a real FakeClock override (which also fixes FakeClock returning ids that were never attached to its queue entries).
  • Client: newsPostChange events feed a latestNewsPostId atom; a per-user news.lastSeenNewsPost (localStorage) drives useHasNewNewsPost(), which joins useHasNewUrgentMessage() on the Home nav pip in the app bar. Rendering the home news feed marks the newest post seen.
  • A separate /newsPosts path (rather than reusing /news) because socket-group subscriptions deliver exactly one initial-data payload per path — urgent-message state and post state each need their own seed.
  • 6 unit tests for the service (FakeClock + mocked models): publish-on-change, no-op on no-change, cleared variant, timer fire, timer re-arm, max-timeout clamping.

Verification

Two live browser clients against the dev stack: a viewer's pip lit ~2s after an admin published a new post (viewer parked on another page, no reload); visiting home marked it seen and cleared the pip; republishing an already-seen post correctly did not relight it (id-based semantics); creating a scheduled post did not light the pip, and the pip then lit within seconds of the scheduled minute passing, driven purely by the service timer. Wire-level check confirmed the /newsPosts seed payload on socket connect. lint, typecheck, full unit suite (870) clean.

@claude

claude Bot commented Jul 11, 2026

Copy link
Copy Markdown

gg, ship it

Comment thread server/lib/news/news-service.ts
@claude

claude Bot commented Jul 11, 2026

Copy link
Copy Markdown

gg, ship it

The Node server now consumes the newsPostsChanged Redis message: it
tracks the latest published post, publishes a newsPostChange event on a
new /newsPosts socket path when that changes, seeds newly-connected
clients with current state, and keeps a timer armed for the next
scheduled publish so scheduled posts emit the signal when they go live
without any mutation firing. Clients track a per-user last-seen post and
light the Home nav pip (shared with the urgent-message indicator) when
an unseen post is published; visiting the home page marks it seen.

Clock gains clearTimeout (with a FakeClock override that removes the
scheduled entry) so the reconcile timer can be re-armed safely.
A DB error during a reconcile pass (or while querying the next scheduled
publish time) previously just returned — if that pass had been triggered
by the scheduled-publish timer, the timer was already cleared and
scheduled publishing stalled until the next post mutation. Failed passes
now arm a 60s retry timer instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant