Fix routing logic

This commit is contained in:
James Brunton
2026-08-28 09:25:56 +01:00
parent 112fbfdef3
commit f5d3ce531a
6 changed files with 20 additions and 12 deletions
@@ -20,7 +20,7 @@ const SIZE = "1.125rem";
export function QuickNavRailHost() {
const { t } = useTranslation();
const navigate = useNavigate();
const { pathname, search } = useLocation();
const { pathname } = useLocation();
const host = useQuickNavHost();
const appMounted = Boolean(host?.appMounted);
@@ -74,7 +74,7 @@ export function QuickNavRailHost() {
returnHome();
return;
}
saveEditorReturnPath(pathname + search);
saveEditorReturnPath();
go(PORTAL_BASENAME);
},
},
@@ -55,10 +55,14 @@ describe("workbench session record", () => {
});
describe("editor return path", () => {
it("is consumed by the first take", () => {
saveEditorReturnPath("/compress?x=1");
it("captures the live address bar and is consumed by the first take", () => {
// The editor writes its tool route via raw history.pushState, so the save
// must read window.location, not a lagging router location.
window.history.pushState({}, "", "/compress?x=1");
saveEditorReturnPath();
expect(takeEditorReturnPath()).toBe("/compress?x=1");
expect(takeEditorReturnPath()).toBeNull();
window.history.pushState({}, "", "/");
});
});
@@ -2,6 +2,7 @@
// does not cost the user their workbench. sessionStorage on purpose: per-tab, tabs never clobber.
import type { StirlingFileStub } from "@app/types/fileContext";
import { stripBasePath } from "@app/constants/app";
const SESSION_KEY = "stirling.workbench.session";
/** Bumped when the record's shape or meaning changes, so an old one is discarded rather than
@@ -153,8 +154,10 @@ export function isSeedableView(
return view !== undefined && SEEDABLE_VIEWS.includes(view);
}
export function saveEditorReturnPath(path: string): void {
export function saveEditorReturnPath(): void {
try {
const path =
stripBasePath(window.location.pathname) + window.location.search;
sessionStorage.setItem(RETURN_PATH_KEY, path);
} catch {
// Best-effort: the switch back just lands on the editor root.
@@ -7,9 +7,10 @@ const mocks = vi.hoisted(() => ({
portalAccess: true,
}));
// The hook reads window.location for the return path (not useLocation), because
// the editor's raw history.pushState leaves react-router's location stale.
vi.mock("react-router-dom", () => ({
useNavigate: () => mocks.navigate,
useLocation: () => ({ pathname: "/compress", search: "?mode=fast" }),
}));
vi.mock("@app/auth/context", () => ({
useAuth: () => ({ portalAccess: mocks.portalAccess }),
@@ -27,6 +28,7 @@ beforeEach(() => {
sessionStorage.clear();
vi.clearAllMocks();
mocks.portalAccess = true;
window.history.pushState({}, "", "/");
});
describe("useOtherAppSwitch", () => {
@@ -45,6 +47,7 @@ describe("useOtherAppSwitch", () => {
});
it("records where to return to, then navigates to the processor", () => {
window.history.pushState({}, "", "/compress?mode=fast");
const { result } = renderHook(() => useOtherAppSwitch());
result.current?.onOpen();
mocks.requestNavigation.mock.calls[0][0]();
@@ -1,4 +1,4 @@
import { useLocation, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { useAuth } from "@app/auth/context";
import { useNavigationActions } from "@app/contexts/NavigationContext";
import { PORTAL_BASENAME } from "@app/routes/portalBasename";
@@ -12,7 +12,6 @@ import { type NavFooterAppLink } from "@app/components/shared/navFooter/NavFoote
export function useOtherAppSwitch(): NavFooterAppLink | null {
const { portalAccess } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const { actions } = useNavigationActions();
if (!portalAccess) return null;
return {
@@ -20,7 +19,7 @@ export function useOtherAppSwitch(): NavFooterAppLink | null {
onOpen: () =>
// Through the guard, so unsaved edits get the same warning as any other navigation.
actions.requestNavigation(() => {
saveEditorReturnPath(location.pathname + location.search);
saveEditorReturnPath();
navigate(PORTAL_BASENAME);
}),
};
@@ -1,4 +1,4 @@
import { useLocation, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { usePortalAccess } from "@app/hooks/usePortalAccess";
import { useNavigationActions } from "@app/contexts/NavigationContext";
import { PORTAL_BASENAME } from "@app/routes/portalBasename";
@@ -13,7 +13,6 @@ import { type NavFooterAppLink } from "@app/components/shared/navFooter/NavFoote
export function useOtherAppSwitch(): NavFooterAppLink | null {
const portalAccess = usePortalAccess();
const navigate = useNavigate();
const location = useLocation();
const { actions } = useNavigationActions();
if (!portalAccess) return null;
return {
@@ -21,7 +20,7 @@ export function useOtherAppSwitch(): NavFooterAppLink | null {
onOpen: () =>
// Through the guard, so unsaved edits get the same warning as any other navigation.
actions.requestNavigation(() => {
saveEditorReturnPath(location.pathname + location.search);
saveEditorReturnPath();
navigate(PORTAL_BASENAME);
}),
};