feat(desktop): restore last-open thread when returning to a channel
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
# feat(desktop): restore last-open thread when returning to a channel
**Motivation**
Switching channels closes the thread pane. Coming back to the same channel always lands on the main timeline, so anyone bouncing between two channels has to find and reopen the same thread every time.
This shows up constantly in multi-channel work: keep a thread open in channel A, check channel B, return to A, thread is gone.
Observed on Buzz Desktop **0.5.14** (`xyz.block.buzz.app`).
**Proposed solution**
Remember the last-open thread per channel (per identity / community) and restore it when that channel is selected again.
- Persist the last `thread` head id for each channel while a thread pane is open.
- On return to that channel, reopen the same thread in the existing thread panel.
- An explicit close (X / Esc / focused-thread dismiss) clears that channel's saved thread so the next visit is the timeline.
- A navigation that already names a thread wins: permalink, inbox/notification click, `?thread=`, or a different thread in the same channel.
- Invalid / missing / deleted thread ids fail closed to the channel timeline.
- Survive reload and app restart, same as `threadViewMode`.
**Alternatives considered**
- Pin-to-rail ([#4266](https://github.com/block/buzz/issues/4266)) — useful, but it is an extra pin gesture plus a new rail. This request is default view-state restore, Slack/iMessage style, with no new chrome.
- Persist only `threadViewMode` (already done) — keeps side vs focused, not *which* thread.
- Leave it as URL-only (`?thread=` on the current channel route) — that is today's behavior and is why the pane dies on channel switch.
**Additional context**
Open-thread state is the `thread` search param on the current channel route, not a per-channel cache:
- `desktop/src/features/channels/ui/useChannelPanelHistoryState.ts` — `openThreadHeadId` is `values.thread`; comment says reloads restore from the URL.
- Switching channels navigates to another channel route without that param.
- `desktop/src/features/channels/useThreadTargetSync.ts` also clears the pane when the head is not in the loaded timeline (restore must wait for / re-fetch that head, not treat a loading miss as “close”).
- Desktop localStorage already has `buzz.channels.threadViewMode` and thread activity/unread keys. There is no last-open thread id per channel. Native prefs only store window geometry.
**Closest existing issues**
- [#4266](https://github.com/block/buzz/issues/4266) — pin open threads in a rail. Complementary, not a duplicate.
- [#3669](https://github.com/block/buzz/issues/3669) — persist Inbox “Show unread only”. Same class of view-state restore, different surface.
No open issue found for restoring the last-open thread on channel return.
**Acceptance criteria**
1. Open a thread in channel A, switch to channel B, return to A → the same thread pane is open.
2. Close the thread in A, switch away and back → A shows the timeline, not the old thread.
3. Open thread T1 in A, then open T2 in A, switch away and back → T2 is restored, not T1.
4. Follow a permalink / notification / inbox item to a specific thread → that thread opens, not a stale saved one.
5. Reload or restart with a thread open, then switch away and back → restore still works.
6. A saved id that no longer exists does not crash; the channel timeline is shown and the bad id is dropped.
7. Narrow / mobile layouts do not regress.
8. Tests cover restore, explicit close, permalink precedence, and missing-id fallback.
Reported from a live Buzz workspace discussion.
Contributor guide
Assessment
This issue has not been assessed yet.