Repository navigation
Conversation
Opening a channel now clears its sidebar row: bold name, mention dot and thread dot, and the Dock indicator follows. Nothing is read: thread replies stay unread in the timeline, Activity and the channel activity popover, and the New messages divider still marks the entry boundary. A new `seen:<channel>` frontier records the row's cut. The visit hides everything in memory, and leaving saves the newest retained event time, but only when the row would otherwise show something. Channel snapshots gain `row` counts that leave out evidence through that cut; the sidebar, Unreads filter, unread-first search and Dock read them. Seen saves do not advance the unread intent revision, so a reader's entry evidence outlives them. Retention keeps seen marks last and never as override ancestry; a channel read covers them. Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz> Signed-off-by: Bradley Axen <baxen@squareup.com>
5b2766f to
59700e9
Compare
…, not its quiet row
The row's `seen:<channel>` cut is a timestamp, so a mention delivered after the visit at the same second, or backdated by a slower clock, stayed hidden from the sidebar and Dock. Attention now goes by exact receipts: leaving saves `seen:msg:<id>` for each unread attention message the visit showed, kept on this device like `msg:` marks, and any other attention lights the row. Ordinary messages keep the synced channel cut. The row's cut is the `seen` argument `isUnread` already takes, so marked-unread and overrides still show. Seen marks save in one quiet `readMessages` batch, which replaces `see`; the journal counts quiet saves (`quiet`), so another window's seen save no longer advances this window's intent revision. The local reserve no longer protects row marks as inherited floors. Co-authored-by: Lain <7d83e66e949bde01875eadd5794011c24c661ec1ddb489cc759d075f4440315a@buzz.block.builderlab.xyz> Co-authored-by: Meli <5aaa86bce934fc3445fc254aab560a40923f10252f92107e665073dede0e04d3@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@squareup.com> Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
Integrate main at fc2dd27 while preserving the channel-row receipt fix. Keep the durable quiet revision alongside main's local read floor. Ensure dropped seen hints never advance that floor or displace read receipts, with a retention-pressure regression. Co-authored-by: Meli <5aaa86bce934fc3445fc254aab560a40923f10252f92107e665073dede0e04d3@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@squareup.com> Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 Two findings: background arrivals in the selected conversation are suppressed from unread indicators, and channel reads leave redundant local receipts.
| snapshot.manual !== "none" || (snapshot.observedCount ?? 0) > 0 | ||
| ); | ||
| // The dock follows the sidebar rows, which opening a channel quiets. | ||
| const count = snapshot.row?.observedCount ?? snapshot.observedCount; |
There was a problem hiding this comment.
🤖 [P2] Preserve the Dock indication for background arrivals
With a channel or DM selected, switch to another app or hide Buzz, then receive a peer DM or mention in that conversation. enterChannel keeps its row cut at Infinity for the whole visit (unread.ts:2765,738), so this row count stays zero even though reading is disabled while unfocused (use-reading.ts:95–96) and the raw unread count increases. The previous raw-count projection lit the Dock; this projection suppresses it. Gate advancing the seen cut on reader visibility/focus, or preserve the raw Dock count for background arrivals. Add an unfocused-arrival regression case. This is source-traced; native Dock execution was not performed.
| (frontier(other) ?? -1) >= through ? other : undefined; | ||
| if (kind === "activity") return by(id, value); | ||
| // A channel read reads everything its row has seen. | ||
| if (kind === "activity" || kind === "seen") return by(id, value); |
There was a problem hiding this comment.
🤖 [P3] Prune message seen receipts covered by channel reads
covered() checks a seen:msg:<id> receipt only against an exact msg:<id> frontier. Channel catch-up advances the channel frontier instead, leaving receipts for already-read messages in the journal. A targeted test reproduced: mention at t=100 → enter/leave → receipt saved → markChannelRead("room") advances beyond 100 → receipt remains, including after another channel read. These redundant keys consume the bounded journal/reserve and compete in recency retention; production-scale impact is unmeasured. Apply the same message/channel coverage logic used for msg: marks and add a channel-read pruning regression test.
|
🤖 Design review at Must fix — new attention must still reach you while Buzz is in the background. Leave a conversation selected, switch to another app, then receive a DM or mention there: its sidebar and Dock remain quiet. This is the existing code-review finding. Leaving the conversation also saves those arrivals as seen, so they can stay absent from sidebar Unreads afterward. Limit ongoing quieting to the visible active visit and preserve cues for background arrivals; cover background arrival → switch channel. Evidence: Activity and thread-level unread remain available; Mark unread restores the row. Another device can relight unread attention, as documented. Source review only. No supplied screenshots; native Dock, multiple devices, keyboard/focus, themes, window sizes and enlarged text were not exercised. |
What
Opening a channel or DM now clears its sidebar row: the bold name, the mention dot and the thread dot. The Dock dot follows the rows. Nothing gets marked read. Unread threads keep their dots in the timeline, in Activity → Threads and in the channel activity popover, and New messages still marks the entry boundary. Anything that arrives after you leave lights the row again.
Requested in Buzz: clear the channel dot on click, but leave threads nominally unread (clear mentions too; Dock follows the row; top-level messages are not marked read on click).
How
seen:<channel>frontier (unread.ts).enterChannelhides everything in the row in memory for the whole visit, including arrivals during it.leaveChannelrecords the newest retained verified event time and saves it asseen:<channel>, but only when that save would actually hide something the saved mark doesn't already hide. Revisiting a quiet channel writes nothing.UnreadSnapshot.row: channel snapshots gainrowcounts (observedCount,attentionCount,threadCount) that leave out evidence through the seen cut. Under explicit unread intent (manual channel unread, a message marked unread)rowis omitted, so the row shows the whole channel again, thread dot included.observedCount/attentionCountare unchanged.UnreadBadge(bold, unread state, mention dot, thread dot),ordinaryUnread, the Dock indicator,useUnreadChannelIds(…, shown = true)(only the sidebar Unreads filter). The floating unread cues read the badge DOM, so they follow automatically. Unread-first search, the Solo inventory, mark-read menus and shortcuts, the timeline boundary, Activity and the popover keep the raw unread.reads.see(), which bumps the journal but notunread.revision()(intentRevision). Otherwise a seen save landing just after the next channel's visit capture would drop that channel's New messages divider.Sync cost:
seen:<channel>rides the existing n-blob NIP-RS publication (#1286), debounced at 5s; one ~55-byte key per channel, overwritten in place. Attention receipts (seen:msg:<id>) stay on this device and are never published.Tests
unread.test.ts: row quiets on open while observed counts, Activity and per-message attention stay unread; arrivals during the visit;seen:saved on leave;revision()unchanged by the seen save (fails withreads.revision); a later reply relights; a preloaded mark quiets after reload; marking a message or the channel unread omitsrow; a quiet revisit writes nothing.read-state-retention.test.ts: seen marks are not override ancestry and go last (fails without the change: "Read override ancestry capacity reached").ChannelSidebarItem.test.tsx: row counts drive the badge markers.indicator-unread.test.ts: the Dock clears on open and relights on a later arrival.tests/browser/unread-state.mjs) or read state, and assert the row is quiet.sidebar-unreaduses Beta for the ordinary-unread row and cue;sidebar-collapsed-unreadleaves Alpha and publishes a later broadcast reply to relight it. The selected-unread-row ink check is gone: an open row is only unread through Mark unread now.pnpm typecheck, fullvitest run(1031 files), and the changed journeys in chromium and webkit locally.Deferred
seen:from peers but ignore it, and never republish another device's value (feat(read-state): n-blob NIP-RS sync with a 30-day floor, frontier only #1286 own-frontier rule).Attention receipts (
907b5a46, Lain + Meli)The row cut was a timestamp, so a mention delivered after the visit at the same second, or backdated by a slower sender clock, stayed hidden in the sidebar and Dock. Now:
seenargumentisUnreadalready takes, so explicit unread intent and overrides still show.readMessagesbatch, which replacessee/saveQuiet. The journal counts quiet saves (quiet, validated as0 <= quiet <= revision), so another window's seen save no longer drops this window's New messages divider. That was the deferred two-window gap.activity:). A fresh or second device relights attention rows until they are read (feat(read-state): n-blob NIP-RS sync with a 30-day floor, frontier only #1286'smsg:limitation).Validation at
907b5a46:Not run: a native Tauri build, a real relay, or mobile.