feat(desktop): keyboard shortcut to navigate to next/previous channel
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Problem**
There's no way to move between channels from the keyboard. Every channel switch requires the mouse or a round-trip through quick search (`⌘K`) — typing a name you already know to reach a channel that's already visible in the sidebar. Slack, Discord, and IRC clients all have a single-chord next/previous-conversation binding, and its absence is felt immediately when working across a few adjacent channels.
**Proposal**
Add `⌥↓` / `⌥↑` (`Ctrl+Alt+↓` / `Ctrl+Alt+↑` on Windows) to move the active channel selection down/up the sidebar list.
- **Order** matches what the sidebar displays: starred → custom sections in section order → unassigned, each honoring its own sort preference (as assembled in `AppSidebar.tsx:395-433`).
- **Scope:** the active community's stream channels. Forums and DMs are out of scope here.
- **Muted channels are skipped.** `mutedChannelIds` is already available in `AppShell`, so this is a natural filter rather than a new data path.
- **Stops at both ends** rather than wrapping — no wraparound surprise when you're walking to the top or bottom of a list deliberately.
- No-op when the list is empty or no channel is selected (e.g. on the home feed).
- Registered in `shared/lib/keyboard-shortcuts.ts` under `Navigation`, so it appears in Settings → Keyboard Shortcuts automatically.
Windows uses `Ctrl+Alt+arrows` rather than Slack's plain `Alt+arrows` because `Alt+←`/`Alt+→` are already bound to back/forward there (`useBackForwardControls.ts:92-106`).
**Implementation sketch**
The displayed-order flattening currently lives inline in `AppSidebar.tsx`. I'd extract it into a shared helper so the shortcut and the sidebar can't drift apart, cover it with a `.test.mjs` unit test (matching the convention in `features/sidebar/lib/`), add a `useChannelNavigationShortcuts` hook alongside the existing `useMarkAsReadShortcuts`, and add an e2e case to `navigation.spec.ts`.
**Known limitation in this first pass**
Sidebar collapse state (`collapsedGroups` / `collapsedSections`) is local `React.useState` inside `AppSidebar` and isn't reachable from an app-level shortcut hook. So navigating into a collapsed group will switch the channel correctly but leave the group shut, meaning the selection highlight isn't visible until you expand it. Making the shortcut collapse-aware — expand on arrival, or skip collapsed groups — requires lifting that state into `AppShell`. Happy to do that here or as a follow-up, but it restructures state ownership, so I'd rather you called the shape.
**Also happy to follow up with**
`⌥⇧↓` / `⌥⇧↑` for next/previous *unread*, which is where Slack's mute-skipping behavior properly belongs. Keeping this PR to the plain case.
Checking the keybinding and scope look right before I open the PR.
Contributor guide
Research direction
Start with the displayed-order flattening in AppSidebar.tsx:395-433, then read shared/lib/keyboard-shortcuts.ts and the existing useMarkAsReadShortcuts hook. Check the features/sidebar unit-test convention and navigation.spec.ts before deciding how the shared helper and shortcut hook fit together. Done means the specified channel order, mute filtering, boundary behavior, platform keybindings, and empty-selection no-ops are covered by tests and the e2e case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- desktop, frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100