Repository navigation
fix(channels): read a channel and its open thread when returning to it - #1835
Conversation
Returning to a channel with its thread pane open moved focus to the thread's tab and skipped the main composer, so only the thread was read and the channel stayed unread. Focus returns to the main composer, and while it stays in that channel's workspace the visible selected thread reads too. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
Fold the channel-versus-neighbor assertions into the open-pane branch of the returning-to-a-channel journey instead of repeating its setup. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
A link to a top-level message focuses the main timeline, but the already-open selected thread still read beside it. Only a plain channel visit, with no requested message or thread, reads that thread now. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
Opening a thread from Activity while its channel's pane was collapsed on that thread tried to focus the tab while the dock was still inert. The pane then opened without a retry, so focus stayed on the body and the thread never read. Hand focus to the selected tab once the pane shows a routed thread, unless something else already took it. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
The channel workspace marker comes from the side pane's selected thread, but it also let a main-pane thread read while focus rested in the side pane, so a live reply in main-pane X was read while the person typed in side-pane T. Only a reading surface inside the side pane takes the grant. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
loganj
left a comment
There was a problem hiding this comment.
🤖 Larry and Rbot: an independent adversarial review of 812c492. Source review only. Hosted checks were green; neither of us ran the browser specs locally.
Approving. The visit contract now matches the thread discussion. A plain visit puts focus back in the main composer and also reads the open side-pane thread. A link reads only what it focuses. A main-pane thread reads only with focus inside it. We traced the guards: inactive tab panels stay inert and aria-hidden, so the workspace grant cannot read covered tabs. routedThread needs requestedMessage, so a plain visit keeps composer autofocus. requestedMessage comes from the navigation target, so a message link does not start reading the side thread later in the same visit.
Two optional comments below. Neither is must-fix.
Opening a thread from Activity when it was already selected in the main pane focused the side pane's selected thread instead, so the routed thread stayed unread and the other one was read. Focus the routed thread's tab wherever it is visible. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
The test links to Thread root 1. Until Alpha's history confirms that root, the link opens its thread instead, which now takes focus and reads it. Wait for confirmed history first, so the link is always the timeline jump the test describes. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
Waiting for confirmed history did not stop CI from opening the link to Thread root 1 as that thread, which then takes focus and reads it. Link to an ordinary top-level message instead, as the test intends. It still fails without the link narrowing. Co-authored-by: John Tennant <jtennant@block.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Oliver <e78152627ea32fbacb070e5047afcfb8e409fa3343d9e618227baacca5b5e607@buzz.block.builderlab.xyz>
Why
Opening a channel whose thread pane is open no longer marks the channel read. Buzz reads only in the focused surface, and returning to a channel with a visible selected thread moved focus to that thread's tab and skipped main composer autofocus, so the thread was read but the channel's own messages stayed unread until you clicked into them. This is a bug fix for the channel visit contract: opening a channel reads what it shows.
What
Returning to a channel focuses the main composer again. While focus stays anywhere in that channel's workspace, its visible selected side-pane thread is also a reading surface, so both the channel and the open thread are read. A collapsed pane, another channel's tab, a members or media-review view, and passive restoration still leave the main conversation as the only reading surface. Pane motion completion still wakes observation. A thread in the main pane reads only while focus is inside it.
Only a plain channel visit reads the open thread this way. A link to a message or thread reads only what it focuses. A thread opened from a link or Activity takes focus once its pane shows it, including when the pane was collapsed on that thread or the thread was already selected in the main pane.
Reviewer-reproducible examples
tests/browser/thread-unread.spec.mjs, "returning to a channel reads it and its already-open thread" (1440 px and 1280 px), drives the production UI and read-state engine against isolated relay fixtures: a channel with unread top-level messages and an unread reply in its open thread, switch to another channel, then explicitly return with the pane open.The collapsed-pane case still leaves the hidden thread unread.
Four more regressions in the same file, each failing on the previous head and passing here, in Chromium and WebKit:
Screenshots
After returning to Alpha with Thread root 1 open, from the regression on this build: the main composer has focus, Thread root 1 has no unread dot, and the unopened Thread root 0 and Broadcast reply keep theirs, which is why Alpha keeps its sidebar dot.
🤖 Generated with Claude Code