feat(desktop): adding ui state into the history stack#967
Open
tellaho wants to merge 3 commits into
Open
Conversation
e6a9a67 to
38ceb01
Compare
Collaborator
Author
|
🤖 Generated with Claude Code |
Back/forward now restores the auxiliary panel (thread, profile, agent session) each history entry was showing, and reloads restore the open panel from the URL. - Panel identity moves from ChannelScreen useState into channel-route search params (thread, profile, agentSession) via a new useChannelPanelHistoryState hook. Setter calls within one event handler coalesce into a single navigation, so each user action produces exactly one history entry. - Channel changes no longer need to clear panel state imperatively; fresh entries simply carry no panel params. - Deep-link thread opening (useChannelRouteTarget) writes the thread param with replace so back leaves the deep link intact. - Thread auto-close waits for the timeline to load before declaring a restored thread head missing; agent-session auto-close waits for the agent list to populate. - Register navigation.spec.ts in the smoke project (it was never wired into playwright.config.ts, so it has never run in CI); fixme the pre-existing forum back-link failure where the top chrome drag region intercepts the click. Add coverage: back/forward restores thread panels, back undoes closing a panel, open panels survive reload. Requested in buzz: buzz://message?channel=d14cd131-6084-4c9c-ba4d-24fb6bfc4263&id=7b077fcec0ad531998b05b4a62d1ed05b9f92aa4d909aab5ee17ce56613bd081 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…me inbox, and pulse Follow-ups to the auxiliary-panel history work, covering the remaining right-panel surfaces: - Extract the coalescing search-param logic into a generic useHistorySearchState(keys) shared hook; useChannelPanelHistoryState becomes a thin wrapper over it. - Profile panel sub-view (summary/memories/channels) moves to a `profileView` param on the channel and pulse routes. UserProfilePanel becomes controlled (view/onViewChange); opening or switching a profile resets the sub-view in the same coalesced patch. The render-phase pubkey-change reset becomes a replace-effect. - Pulse: the profile panel moves from local state to `profile`/ `profileView` params on /pulse. - Home: explicit inbox selections are mirrored to an `item` param on /; back/forward and reloads restore the selected detail pane. Default (auto) selection stays local-only so background feed loads never trigger navigations — an effect-driven navigation here disturbed open popovers elsewhere in the app (caught by profile e2e). - New e2e: home inbox selection survives reload, back restores the previous selection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Settings was an AppShell-level overlay driven by two useStates, so reloads dropped out of it and back didn't exit it. It's now /settings with a `section` search param: - New /settings route (null component — settings still renders at the AppShell level, replacing the chrome/sidebar/outlet wholesale, now keyed off the route instead of local state). - goSettings()/closeSettings() in useAppNavigation. Opening settings pushes one entry; closing goes back, landing on the previous entry with its panel params intact (e.g. an open thread). Section switches replace rather than push, so back always exits settings in one step and reloads restore the open section. - SettingsView's gated-section auto-correct flows through the same replace path, keeping effect-driven corrections out of the stack. - isSettingsSection validator exported from SettingsPanels for route search validation. - e2e: open settings from a channel with a thread panel, switch section, reload (section survives), back to app (thread restored). A side effect worth knowing: navigating anywhere (deep links, notification clicks) now exits settings automatically, since settings is itself a route. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
38ceb01 to
664f8d5
Compare
Collaborator
Author
|
✅ Unblocked — #966 merged and this branch is now rebased on top of it ( 🤖 Generated with Claude Code |
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.
Category: improvement
User Impact: Back/forward and page reloads now restore what you were looking at — open thread, profile, or agent session panels in channels (including the profile panel's Memories/Channels sub-view), the selected home inbox item, the Pulse profile panel, and the settings screen with its open section — instead of dropping you back to a bare default view.
Problem: Hitting back or reloading discarded UI state everywhere: thread/profile/agent-session panels in channels, the profile panel's sub-view, the home inbox selection, and the Pulse profile panel all lived in component state, so every navigation silently reset them. During active development this meant chasing down your last UI state after every refresh. (Requested in buzz:
buzz://message?channel=d14cd131-6084-4c9c-ba4d-24fb6bfc4263&id=7b077fcec0ad531998b05b4a62d1ed05b9f92aa4d909aab5ee17ce56613bd081)Solution: Make the URL the source of truth for panel-level UI state. A generic
useHistorySearchState(keys)hook backs UI state with route search params and coalesces all setter calls within one event handler into a single navigation — so each user action produces exactly one history entry, even when a handler closes one panel and opens another. Channel routes getthread/profile/profileView/agentSessionparams,/pulsegetsprofile/profileView,/getsitemfor the inbox selection, and settings becomes a/settings?section=…route — closing it goes back to the previous entry, panels and all. One deliberate asymmetry: home's default (automatic) selection stays local-only — only explicit user selections touch the URL, so background feed loads never trigger navigations that could disturb open popovers elsewhere.File changes
desktop/src/shared/hooks/useHistorySearchState.ts (new)
The generic history-backed state hook: reads the given keys from the route search, exposes a patch API, and microtask-coalesces same-handler patches into one
navigate()(push by default,replaceopt-in for programmatic corrections).desktop/src/features/channels/ui/useChannelPanelHistoryState.ts (new)
Channel-route wrapper over the generic hook exposing drop-in setter replacements for the old
useStateshapes (thread,profile,profileView,agentSession). Opening/switching/closing a profile resets its sub-view in the same coalesced patch.desktop/src/app/routes/channels.$channelId.tsx, pulse.tsx, index.tsx, settings.tsx (new), routes.ts, routeTree.gen.ts
Search-param validation for the new params on each route, plus the new
/settingsroute registration. The settings route component renders null — settings still renders at the AppShell level (it replaces the chrome/sidebar/outlet wholesale), now keyed off the route instead of local state.desktop/src/app/AppShell.tsx, desktop/src/app/navigation/useAppNavigation.ts, desktop/src/features/settings/ui/SettingsPanels.tsx
settingsOpen/settingsSectionuseStates replaced by route-derived state. Opening settings pushes one history entry; closing goes back (landing on the previous entry with its panel params intact); section switches replace rather than push, so back always exits settings in one step and reloads restore the open section. Side effect: navigating anywhere (deep links, notification clicks) now exits settings automatically.desktop/src/features/channels/ui/ChannelScreen.tsx
Swaps the three panel
useStates for the hook.resetComposerTargetsnow only clears local ephemeral state — channel switches naturally produce entries without panel params. The thread auto-close effect waits for the timeline's first page before declaring a restored thread head missing, so reloads don't wipe the param mid-load.desktop/src/features/channels/ui/useChannelAgentSessions.ts
Agent session pubkey state lifted to the caller (URL-backed). The auto-close-when-agent-missing effect waits for the agent list to populate and uses
replaceso corrections don't pollute history.desktop/src/features/channels/ui/useChannelRouteTarget.ts
Deep-link thread opening writes the
threadparam withreplace: true, so the deep-link entry itself carries the opened panel — back leaves the deep link rather than stripping the panel off it.desktop/src/features/profile/ui/UserProfilePanel.tsx
The summary/memories/channels sub-view becomes controlled (
view/onViewChangeprops). The render-phase pubkey-change reset becomes a defensive replace-effect (call sites now own the reset).desktop/src/features/channels/ui/ChannelPane.tsx
Threads
profilePanelView/onProfilePanelViewChangethrough to the profile panel.desktop/src/features/pulse/ui/PulseScreen.tsx
Profile panel state moves from local
useStatetoprofile/profileViewparams on/pulse.desktop/src/features/home/ui/HomeView.tsx
Explicit inbox selections are mirrored to
?item=; back/forward and reloads restore the selected detail pane. Default selection and auto-corrections stay local-only (no background navigations). The clear/default effects wait for the feed to load so a restored selection isn't clobbered mid-load.desktop/src/features/channels/useChannelPaneHandlers.ts
Setter type loosened from
Dispatch<SetStateAction>to a plain value setter (no call sites used functional updates).desktop/playwright.config.ts
Registers
navigation.spec.tsin the smoke project — it was added in #259 but never wired into any project, so it has never run in CI.desktop/tests/e2e/navigation.spec.ts
New coverage: back/forward restores thread panels, back undoes closing a panel, open panels survive reload, home inbox selection survives reload with back restoring the previous selection. One pre-existing test (
direct forum thread links close back to the forum route) is markedfixme: enabling the file exposed that the forum "Back to posts" header renders under the fixed top-chrome drag region, which intercepts clicks — same chrome-inset class #941 is reworking.Reproduction steps
thread=<id>.profileView=. Reload: the panel reopens on that sub-view. Back: returns to the summary./settings?section=notifications. Reload: settings reopens on that section. Click "Back to app": you return to the exact channel state you left, open panels included.item=. Reload restores that selection; back returns to the default selection./pulse?profile=.Verification
tsc --noEmit, biome, 627 unit tests clean.profile.spec.ts:116/:442are flaky on clean main too (verified by stash-and-rerun).Known not-covered (deliberate)
🤖 Generated with Claude Code