fix(app): keep the unread session indicator stable during polling#1403
Open
chphch wants to merge 1 commit into
Open
fix(app): keep the unread session indicator stable during polling#1403chphch wants to merge 1 commit into
chphch wants to merge 1 commit into
Conversation
`buildSessionListViewData` bakes each row's `hasUnread` flag into a snapshot, reading it from an *optional* `unreadSessionIds` set. `applySessions` passes the set, but several other reducers rebuild the snapshot without it — most importantly `applyMachines`, which fires on every machine heartbeat / `fetchMachines` poll. On each such rebuild `hasUnread` falls back to `undefined?.has(id) ?? false`, so every row loses its unread state until the next `applySessions` restores it. The result is a distracting flicker of the unread indicator (bold title on the bold-title builds, the status dot/text on main) on the polling cadence, with no user action involved. Make `unreadSessionIds` a required parameter on `buildSessionRowData` / `buildSessionListViewData` so any reducer that forgets it is a compile error, and thread `state.unreadSessionIds` through the call sites that dropped it (`updateSessionDraft`, `applyMachines`, `deleteMachine`, `deleteSession`). Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The unread session indicator flickers off and back on during normal polling, with no user interaction.
buildSessionListViewData()bakes each row'shasUnreadinto the list snapshot, reading it from an optionalunreadSessionIdsset.applySessions()passes the set, but several other reducers rebuild the snapshot without it — most importantlyapplyMachines(), which runs on every machine heartbeat (update-machine,machine-activity) and the periodicfetchMachinespoll. On each of those rebuildshasUnreadfalls back toundefined?.has(id) ?? false, so every row loses its unread state until the nextapplySessions()restores it. Because machine heartbeats arrive on a timer, the unread indicator (the status dot/text onmain) blinks off→on repeatedly while the user just watches the list.The fix makes
unreadSessionIdsa required parameter onbuildSessionRowData()/buildSessionListViewData()so any reducer that forgets it becomes a compile error, and threadsstate.unreadSessionIdsthrough the four reducers that dropped it:updateSessionDraft,applyMachines,deleteMachine,deleteSession. (applySessions,markSessionRead,markSessionUnread, andsetCurrentViewingSessionalready passed it.)Repro: open the sessions list with at least one unread session and an online machine; the unread indicator blinks each time a machine heartbeat arrives (a few times a minute). After the fix it stays stable.
Verification:
pnpm typecheckis clean. Making the parameter required is what guarantees completeness here — every call site is now compiler-checked. A running-app capture is awkward for a sub-second timer transient; happy to add one if you'd prefer it over the mechanism walkthrough above.