feat(channels): collapsible sidebar groups, # icons, two-step create, context→channel rename#3510
Conversation
|
😎 Merged directly without going through the merge queue, as the queue was empty and the PR was up to date with the target branch - details. |
Group the channel sidebar under collapsible "Starred"/"Channels" headers. The whole header row is the trigger; it rests as a "#" and swaps to a chevron on hover/focus. Collapse state persists per section in the shared sidebar store, keyed `channels:*` to stay clear of the Code sidebar's folder keys. Uses Base UI's Collapsible directly rather than quill's: quill styles its trigger as a button (which fought the MenuLabel styling the headers had) and animates panel height, which janked against a list this long. The panel is `keepMounted` — every row builds a context menu, dropdown, tooltip and two dialogs up front, so unmounting on close made each expand rebuild the lot (~940ms for 46 channels, vs ~80ms to collapse). Replace every remaining SquircleDashed with a "#", so channel rows, group headers, tabs, header, breadcrumb, rename field and command palette all speak one icon. Split channel creation into two steps: name (CTA "Next"), then "what's this channel about?" with Skip/Create. Step two is a real nested dialog, so quill scales and dims step one behind it and the stack itself says there's another step; escaping step two returns to the name, intact. Nothing is created until Create (channel + CONTEXT.md plan session) or Skip (channel alone). The nesting offset is inverted from quill's default so step one peeks above rather than below. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019G63f4afY9vsbKsvK654Wj
WebsiteChannelHome took only `channel` from useBackendChannel and dropped its isLoading. For a freshly created channel that hook resolves the backend channel over the network (a resolve-or-create POST), and for that whole window the view had no intro (it needs backendChannel), no tasks, and — because the feed query is disabled until the id exists, and a disabled query reports isLoading:false — believed it wasn't loading. So ChannelFeedView fell through to `emptyState` and flashed the welcome + suggestions screen before the feed appeared. Fold the identity resolution into the loading flag: a channel isn't empty until we know which channel it is. A plain reload never showed this because the backend channels list is already cached by the time the view mounts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019G63f4afY9vsbKsvK654Wj
|
React Doctor found 2 issues in 2 files · 2 warnings. 2 warnings
Reviewed by React Doctor for commit |
be4b655 to
8ce968a
Compare
There was a problem hiding this comment.
This is a 777-line feature PR with zero reviews touching meaningful UX behavior (two-step channel-create modal, collapsible sidebar groups, new FAB component, pervasive rename). Zero reviews on a change of this size and scope warrants human sign-off. There is also a visible copy error in the empty state: "No channels other channels yet." needs fixing before ship.
|
Reviews (1): Last reviewed commit: "fix(channels): don't flash the empty sta..." | Re-trigger Greptile |
Addresses review on #3510. - Empty state read "No channels other channels yet."; it's the all-channels empty case, so "No channels yet." - A failed backend-channel resolve left the feed spinning forever: the view waited on `!backendChannel`, which never clears. Worse, the resolve effect re-fired on its own settle (isResolving true→false with existing still undefined), so a failure looped the POST indefinitely. Guard the effect on failure and expose `resolveFailed` so the view stops waiting. Scoped to the name that failed (the mutation's variables), so switching to another channel still gets its own attempt. - Personal-channel provisioning could double-create: the sidebar row and the new-tab path each guarded their own in-flight create, which still raced each other, and folder creation isn't idempotent by path. One shared ensurePersonalChannel now serves both. Not changed: React Doctor's `control-has-associated-label` on the group trigger is a false positive — it has a text label, and the a11y tree reads it as button "CHANNELS". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019G63f4afY9vsbKsvK654Wj
…solve window Move the identity-resolution loading window into useBackendChannel itself: the hook previously reported isLoading:false in the gap between the channel list arriving and the resolve effect firing, which callers had to paper over (WebsiteChannelHome's `!!channelName && !backendChannel && !resolveFailed` term). The hook now stays loading until a channel resolves or the resolve fails, so the call-site composition collapses to its three real sources and the resolveFailed return field is no longer needed. Adds red-green tests covering the no-flash window, settling (without a hot retry loop) on a failed resolve, re-resolving a new name after a failure, and the personal-channel mapping. Generated-By: PostHog Code Task-Id: 85ec2ee5-76f5-472c-9687-9958a396e142
Continues the channels (Project Bluebird) surface work. Grouped by area:
Rename: context → channel
contextPrompt.ts— the agent prompt now saysBuild a CONTEXT.md for the channel "X"(was "context/folder"). This was the highest-leverage one: it was teaching the model the stale noun, seeding it into every generated CONTEXT.md.context.mditself,ANALYTICS_EVENTS.CONTEXT_ACTION/context_md_building/context_name(renaming would split existing PostHog insights and break old feed rows), and identifiers likechannelContext/ContextMdStatethat mean the file, not the channel.Sidebar: collapsible groups
#, swaps to a chevron on hover/focus.channels:*to stay clear of the Code sidebar's folder keys.Collapsibledirectly, not quill's: quill styles its trigger as a button (which fought theMenuLabelstyling these headers had) and animates panel height, which janked against a long list.keepMounted. Each row builds a context menu, dropdown, tooltip and two dialogs up front, so the default unmount-on-close made every expand rebuild the lot — ~940ms → ~75ms for 46 channels (collapsing was always ~80ms).Icons
SquircleDashed→#(phosphorHashIcon), across channel rows,#me, group headers, channel header, breadcrumb, rename field, command palette and browser tabs.lucide-reactdropped from those files.Create flow
+beside the "Channels" label.+added to the#merow, matching the other channels. Themefolder is created lazily, so its actions resolve the id at click time rather than closing over it — hence the optional call-timechannelIdoverride onuseCreateAndOpenDashboard(without it, "New canvas" on a never-opened#mesilently no-ops against a staleundefined).Fix: empty-state flash on channel creation
WebsiteChannelHometook onlychannelfromuseBackendChanneland dropped itsisLoading. A new channel resolves its backend channel over the network (a resolve-or-create POST); for that whole window there was no intro (it needsbackendChannel), no tasks, and — because the feed query is disabled until the id exists, and a disabled react-query reportsisLoading: false— the view believed it wasn't loading. SoChannelFeedViewfell through toemptyState.Testing
#→chevron hover swap, collapse/expand persistence, and the#me+.spinner → intro, no empty state at any point. Two throwaway probe channels were created and deleted.Known gaps
useBackendChannelretries on failure, so it spins rather than settles.🤖 Generated with Claude Code
https://claude.ai/code/session_019G63f4afY9vsbKsvK654Wj