Skip to content

fix(channels): read a channel and its open thread when returning to it - #1835

Merged
johnmatthewtennant merged 8 commits into
mainfrom
jtennant/oliver/channel-read-open-thread
Oct 29, 2026
Merged

johnmatthewtennant merged 8 commits into
mainfrom
jtennant/oliver/channel-read-open-thread

Conversation

@johnmatthewtennant

@johnmatthewtennant johnmatthewtennant commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

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.

  • On main, in Chromium and WebKit, focus lands on the thread tab instead of the main composer, and the thread dot clears while the channel stays unread (its count remains 18 above the unopened neighbor thread's).
  • With this change, the main composer has focus and both clear after reading dwell, while the unopened neighbor thread keeps its dot.

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:

  • "opening a message link reads the channel but not its already-open thread": a link to a top-level message reads the channel, and the selected thread beside it stays unread.
  • "opening a thread from Activity reads it when its channel's pane was collapsed": with Alpha's pane collapsed on Thread root 1, open it from another channel through Alpha's Activity popover. Previously focus stayed on the document body and the thread stayed unread; now the thread tab has focus and the thread reads.
  • "a live reply in a main-pane thread stays unread while focus is in the side-pane thread": with Thread root 1 moved to the main pane and Thread root 0 in the side pane, return to Alpha, select Thread root 1, focus Thread root 0's reply composer and deliver a reply to Thread root 1. It stays unread.
  • "opening a main-pane thread from Activity reads it, not the side-pane thread": with Thread root 1 already selected in the main pane and Thread root 0 in the side pane, reopen Thread root 1 from Alpha's Activity popover. Previously Thread root 1 stayed unread; now it reads and Thread root 0 stays unread.

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.

Alpha with its open thread read after returning

🤖 Generated with Claude Code

Oliver and others added 2 commits October 28, 2026 12:51
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as ready for review October 28, 2026 17:12
@johnmatthewtennant
johnmatthewtennant requested a review from a team as a code owner October 28, 2026 17:12
Oliver and others added 2 commits October 28, 2026 14:00
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>
Comment thread src/features/messages/use-reading.ts Outdated
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 loganj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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.

Comment thread src/bundled/channels/ChannelsPage.tsx Outdated
Comment thread src/features/messages/use-reading.test.ts
Oliver and others added 3 commits October 28, 2026 20:32
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>
@johnmatthewtennant
johnmatthewtennant merged commit afac5b5 into main Oct 29, 2026
42 checks passed
@johnmatthewtennant
johnmatthewtennant deleted the jtennant/oliver/channel-read-open-thread branch October 29, 2026 01:44
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.

3 participants