Migrate thread selection to URL-driven state with browser history support#883
Migrate thread selection to URL-driven state with browser history support#883
Conversation
…ead nav Three issues addressed: 1. Mobile corpus home view had no way to access sidebar tabs. Added a MobileMenuButton (kebab icon) to both CorpusLandingView (in breadcrumbs) and CorpusDetailsView (in header) that opens the sidebar bottom sheet, consistent with how other tabs expose the MobileKebabButton. 2. Discussion thread navigation was not URL-driven, making browser back/forward unusable. Replaced the local inlineSelectedThreadIdAtom (Jotai) with the URL-synced selectedThreadId reactive var (?thread= param), already handled by CentralRouteManager. Added updateThreadParam utility that pushes history entries for proper back/forward. 3. Tab switching used replace instead of push, preventing browser back/forward between tabs. Changed updateTabParam and updateDetailViewParam to push new history entries. updateTabParam also now clears thread/message params when switching tabs. https://claude.ai/code/session_015gUfjhz6wHKpvJiEiXgBA7
Pull Request Review: URL-Driven Thread Selection MigrationThis PR successfully migrates thread selection from local Jotai atom state to URL-driven state via the ✅ StrengthsArchitecture Alignment
Browser Navigation Support
Mobile UX Improvements
Code Quality
|
- Remove dead `onViewModeChange` prop and `useEffect` from
CorpusDiscussionsView — parent now derives thread view state directly
from the URL-synced selectedThreadId reactive var
- Remove unused `useEffect` import
- Update existing `updateTabParam()` tests to assert push semantics
(no `{ replace: true }`) and add 3 new tests for thread/message
param clearing on tab switch
- Add 5 new `updateThreadParam()` tests covering set, clear, push
behavior, param preservation, and update
- Add CHANGELOG.md entry documenting mobile nav and URL-driven thread
selection changes
https://claude.ai/code/session_015gUfjhz6wHKpvJiEiXgBA7
Code Review - PR #883I've completed a thorough review of this PR. Overall, this is a well-architected migration that successfully aligns thread selection with the CentralRouteManager pattern. The implementation is clean, well-tested, and follows the project's established conventions. ✅ Strengths1. Excellent Architecture Alignment
2. Comprehensive Test CoverageThe test updates in
3. Clean Code Quality
4. Mobile UX EnhancementThe mobile menu button addition is a thoughtful improvement:
5. Excellent Changelog DocumentationThe CHANGELOG.md updates are detailed and follow the Keep a Changelog format properly, with clear file references and line numbers. 🔍 Code Quality ObservationsnavigationUtils.ts (Lines 806-857)The new utility functions are well-designed: ✅ Good:
✅ Good Pattern: Both functions preserve unrelated params while updating/clearing specific ones CorpusDiscussionsView.tsx (Lines 385-458)State management migration is clean: ✅ Good:
Corpuses.tsx (Lines 1582-1584)Parent component correctly derives state: ✅ Good:
|
- Remove console.log debug statements left in Corpuses.tsx - Replace navigate(-1) with updateThreadParam(null) in handleBackToList so back-to-list always explicitly clears the ?thread= param via URL, letting CentralRouteManager Phase 2 handle state — consistent with the routing mantra and safe for deep-linked/bookmarked URLs
Annotations in corpus-scoped threads can have a null document field, causing "can't access property title, edge.node.document is null" when typing @ mentions. Filter out null-document annotations before mapping.
PR Review: Migrate thread selection to URL-driven state (#883)Overall this is a well-structured refactor that correctly aligns with the CentralRouteManager architecture. The core approach is sound — Issues to Address1. Inline styles in
|
- MessageComposer: collapses to single-line when unfocused, expands toolbar/editor/footer on focus via CSS transitions - Single editor (Reddit-style): remove inline ReplyForm from MessageTree, bottom composer switches between top-level and reply mode with context bar showing @username + message preview (truncated, with cancel button) - Thread header: merge Back/badge/title/status onto one row, description (left, truncated with tooltip) and author/time/count (right) on second row - Reduce padding throughout for better vertical space utilization
… left-aligned meta - Back button: icon-only, no border/text - Title row: badge + title (no-shrink) + description (truncates with tooltip) - Description: smaller font, no em dash, relies on color/size contrast - Meta row: author/time/count left-aligned below title
PR Review: Thread URL-Driven State & Mobile NavigationOverall this is a well-structured migration that aligns with the CentralRouteManager architecture. The core idea is sound. A few items worth discussing before merge: Issues / Questions1. PR Description Discrepancy —
|
PR Review: Thread URL-Driven State + Mobile MenuOverall this is a solid refactor that correctly follows the CentralRouteManager architecture. A few issues worth addressing before merge. Issues1. CHANGELOG Structure Bug (Minor)The new ## [Unreleased] - 2026-02-11
### Changed ← new (Mobile Navigation) ✅
...
### Added ← new (Mobile Menu) ✅
...
### Changed ← DUPLICATE – Import/Export block that was already there ⚠️
### Added ← also already there
### Fixed ← also already thereThe Feb 11 block appears to have been inserted between the Fix: Move the new 2. PR Description InaccuracyThe PR description says:
The actual implementation uses 3. Silent Quote Feature Removal in
|
…igation-fSBZh # Conflicts: # CHANGELOG.md
- MessageComposer: Focus editor before assertions that depend on :focus-within state (character count, send button interaction) - ReplyForm: Remove assertion for "replying to" text that was replaced by CornerDownRight icon in the new compact reply context design
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
PR Review: Thread Selection URL MigrationOverall this is a well-structured PR. The migration from Jotai atom state to URL-driven state follows the CentralRouteManager pattern correctly, and the removal of the debug Concern 1:
|
…igation-fSBZh # Conflicts: # CHANGELOG.md
…eview - handleBackToList uses navigate(-1) to pop history instead of pushing - updateTabParam uses replace semantics to avoid history accumulation - ThreadDetail Title gets overflow/ellipsis protection for long titles - MobileMenuButton moved outside breadcrumb <nav> for correct landmarks
PR Review: Migrate thread selection to URL-driven stateGood overall direction — migrating to URL-driven state and removing the Jotai atom for inline thread selection aligns well with the CentralRouteManager architecture. The cleanup of the dead That said, there are a few issues worth addressing. Bug:
|
…omponent
- handleBackToList uses updateThreadParam with replace instead of navigate(-1),
safe for direct-link navigation where no thread-list history entry exists
- Extract inline-styled wrapper divs to HeaderRow styled component in styles.ts
- updateThreadParam accepts optional { replace } options parameter
- Fix CHANGELOG to reflect updateTabParam retains replace semantics
PR Review: Migrate thread selection to URL-driven state with browser history supportOverall this is a clean, well-structured migration. The architecture aligns correctly with the CentralRouteManager Phase 2 pattern, and the test coverage for the new Issues1. CHANGELOG entry is inaccurate about
|
Code ReviewSummaryThis is a solid architectural migration that aligns thread selection with the CentralRouteManager URL-driven state pattern, and adds sensible mobile UX improvements. The core refactoring is clean. A few issues to flag before merging. Issues1.
|
Summary
This PR migrates thread selection in the Discussions tab from local atom state to URL-driven state via the
?thread=query parameter. This enables proper browser back/forward navigation between thread list and thread detail views, and aligns with the CentralRouteManager architecture for managing application state through URLs.Key Changes
Thread Selection State Migration: Replaced
inlineSelectedThreadIdAtomwith URL-driven state synced throughselectedThreadIdVarreactive var in the Apollo cache. The?thread=query parameter is now the source of truth.Navigation Utilities:
updateThreadParam()function to manage thread URL parameter updates with proper history handlingupdateTabParam()andupdateDetailViewParam()to usenavigate()instead ofreplace: true, enabling proper browser back/forward navigationCorpusDiscussionsView Refactoring:
handleThreadClick()to callupdateThreadParam()instead of setting atom statehandleBackToList()to usenavigate(-1)for proper history navigationselectedThreadIdVarreactive varState Derivation in Parent Component:
Corpuses.tsxnow derivesdiscussionInThreadViewfrom the URL-drivenselectedThreadIdreactive var instead of managing it as local stateonViewModeChangecallback fromCorpusDiscussionsViewsince state is now URL-drivenMobile UI Enhancements:
MobileMenuButtonstyled component for mobile navigation menu accessCorpusLandingViewandCorpusDetailsViewto include mobile menu button in headersonOpenMobileMenucallback prop to support mobile navigationCleanup: Removed
inlineSelectedThreadIdAtomfromthreadAtoms.tswith explanatory comment about URL-driven approachImplementation Details
The migration follows the CentralRouteManager Phase 2 pattern where:
updateThreadParam()?thread=param and syncs it toselectedThreadIdVarnavigate()instead ofreplace: trueThis approach eliminates the need for callback-based state synchronization and provides a single source of truth (the URL) for thread selection state.
https://claude.ai/code/session_015gUfjhz6wHKpvJiEiXgBA7