fix(useProjectHomeHref): carry entity selection through Back-to-project links#228
Conversation
…o-project links Side-page back links (settings, PRs, analytics, suggestions, suggestions/review) all route through useProjectHomeHref, which knew the right base URL (viewer vs editor based on preferEditMode + permissions) but dropped the active class/property/individual selection — so a user editing a class who opened settings and clicked Back lost their place. Mirror the pattern already in ViewerEditorSwitcher: read useSelectionStore first (in-memory selection set by the viewer/editor the user came from), fall back to URL params for first render or hard-deep-link cases, and append buildSelectionQuery() to the resolved base URL. Closes #206. Supersedes the sessionStorage-breadcrumb design proposed in the issue body in favor of the surgical fix described in #227, reusing the selectionStore + selectionUrl primitives PR #104 already established. No sessionStorage parallel state, no SSR hydration flash, and the permission-gated base URL is preserved. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThe PR implements entity selection persistence across side-page navigation. Editor layouts no longer clear the selection store on unmount, allowing other pages to read stored selection state. The project home-return hook now appends selection query parameters to its returned URL, preserving the selected class/property/individual across navigation round-trips. ChangesSelection Persistence Across Navigation
Sequence DiagramsequenceDiagram
actor User
participant Viewer/Editor as Viewer/Editor<br/>(DeveloperEditorLayout or<br/>StandardEditorLayout)
participant SelectionStore as SelectionStore
participant SidePage as Side Page<br/>(Settings, PRs, etc.)
participant ProjectHome as Project Home Link<br/>(useProjectHomeHref)
User->>Viewer/Editor: Select class/property
Viewer/Editor->>SelectionStore: setSelection(iri, type)
SelectionStore-->>Viewer/Editor: Store updated
User->>SidePage: Navigate to Settings/PRs
Viewer/Editor-->>Viewer/Editor: Unmounts (no clearSelection)
SelectionStore-->>SelectionStore: Persists selection value
User->>ProjectHome: Click "Back to project"
ProjectHome->>SelectionStore: Read selection (iri, type)
ProjectHome->>ProjectHome: buildSelectionQuery(selection)
ProjectHome-->>User: Navigate to /projects/[id]?classIri=...
User->>Viewer/Editor: Re-mount at project URL
Viewer/Editor->>SelectionStore: Check store value
SelectionStore-->>Viewer/Editor: Selection preserved, iri still set
Viewer/Editor-->>User: Render with prior selection active
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 44 minutes and 5 seconds.Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The previous fix to useProjectHomeHref (5b46db2) was correct but ineffective: both StandardEditorLayout and DeveloperEditorLayout had a useEffect cleanup that called clearSelection() on unmount. When a user navigated from the editor to a side page (settings, PRs, analytics, suggestions), the editor unmounted *before* the side page rendered, wiping the store. By the time useProjectHomeHref read it on the side page, iri/type were null and the back-link fell through to the bare project URL. Drop the unmount-clear in both layouts. The next editor mount overwrites the store from its own URL params, so cross-project bleed is bounded to a single render frame between mount and the setSelection effect — too small to matter in practice (the user can't click in that window). Adds two regression tests per layout: one asserts the mount populates the store from props; one asserts the store still holds the value after unmount, locking in the contract that side-page Back links depend on. Closes #206 (in combination with 5b46db2). Verified manually: editor → select class → settings → Back lands back on the editor with the class re-selected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Follow-up: drop unmount-clear in editor layouts (b4c1dcd)Manual test exposed that the first commit was correct but ineffective. Both This commit:
The original Verified manually after restart: editor → select class → Settings → Back to project lands on |
Manual test: editor → settings → Back was landing on viewer because the hook routed by global preferEditMode (default false), not by the mode the user actually came from. A user who explicitly switched to the editor mid-session without flipping the preference lost the editor state on round-trip. Track the most recent surface in the existing selectionStore: editor and viewer pages both call setMode() on mount. useProjectHomeHref now prefers storeMode over preferEditMode, falling back to the preference only on cold loads (e.g. deep-linked side page with no prior project surface visited). Permission gate is preserved: even when storeMode says 'editor', a user without canSuggest is still routed to the viewer. 5 new tests cover the new contract: - selectionStore: setMode independence from selection, clear() resets mode. - useProjectHomeHref: storeMode 'editor' overrides preferEditMode false; storeMode 'viewer' overrides preferEditMode true; permission gate still applies when storeMode says 'editor' but canSuggest is false. Full suite: 2749/2749 passing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Follow-up: route Back to the mode the user actually came from (206173d)Manual test exposed a third gap: editor → settings → Back to project was landing on the viewer. Reason: the hook was routing by the global This commit:
5 new tests:
Full suite 2749/2749. Verified in browser: editor → Settings → Back lands on |
…nch sync
BranchSelector fires onBranchChange on mount whenever a current branch is
known — that triggered handleBranchChange in the editor page, which used
router.replace(\`\${pathname}?branch=\${name}\`), wiping every other query
param. Effect: Back-to-project from a side page would briefly land the user
on /editor?classIri=... — but a render later, the mount-time branch sync
stripped classIri and the editor showed no selection.
Merge into existing search params instead of replacing them. branch is the
only key we touch; everything else (classIri/propertyIri/individualIri/
resumeSession) survives the round-trip.
Closes #206 (in combination with 5b46db2, b4c1dcd, 206173d).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Follow-up: don't strip classIri on mount-time branch sync (e9937f7)Manual test exposed a fourth gap. The editor page server log showed the right sequence on Back from settings: ``` `BranchSelector` fires `onBranchChange?.(currentBranch)` on mount whenever a current branch is known. That triggered `handleBranchChange` in the editor page (line 705), which did `router.replace(\`${pathname}?branch=${branchName}\`)` — wiping every other query param including `classIri`. So the user briefly landed with the right selection, then the mount-time branch sync nuked it. Fix: merge `branch` into existing `URLSearchParams` instead of replacing the whole query string. `classIri` / `propertyIri` / `individualIri` / `resumeSession` all survive the round-trip now. Type-check clean, full suite still 2749/2749. The viewer page has no equivalent rewrite path, which is why viewer→settings→back was already working — only the editor needed this patch. |
Closes #206. Supersedes the sessionStorage-breadcrumb design proposed in the issue body in favor of the surgical fix described in the closed sibling issue #227 — extend the existing
useProjectHomeHrefhook to readuseSelectionStore(with URL fallback) and appendbuildSelectionQuery(...)to the resolved base URL.Why this approach (not the issue's sessionStorage breadcrumb)
PR #104 already shipped the primitives this fix needs:
useSelectionStore— Zustand store with the active{ iri, type }, populated by viewer + editor and read byViewerEditorSwitcher.selectionUrlutilities —buildSelectionQuery(),readSelectionFromSearchParams(),SELECTION_PARAM_BY_TYPE.useProjectHomeHrefitself — already wired into all five side pages, already factors inpreferEditMode+ permissions.The sessionStorage proposal would add a parallel state channel that:
useSelectionStore(which is already updated on every selection)./editorURL would survive role revocation and route the user where they no longer have rights.<Link href>returns the fallback synchronously, then updates afteruseEffectreads sessionStorage.Reusing the existing primitives gives selection round-trips for free across all five side-page consumers, with no new files, no SSR flash, and the permission-gated base URL preserved.
Diff summary
The store-first-then-URL-fallback pattern is copied verbatim from
ViewerEditorSwitcherso behavior stays consistent across both surfaces.Tests
New
__tests__/lib/hooks/useProjectHomeHref.test.ts(9 cases):preferEditModeis on and user can suggest.canSuggestis false (permission gate).preferEditModeis on.Verification:
npm run type-check— cleannpm run lint— 15 warnings, 0 errors (all pre-existing in unrelated files)npx vitest run— 159 files / 2740 tests passTest plan
?classIri=).?propertyIri=) and individual selection (?individualIri=)./editor(with selection); toggle off → points to viewer (with selection)./projects/{id}(not/editor) regardless of preference.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
New Features