From 5257d57196f982234412460b0769295b44457a26 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Mon, 5 Oct 2026 00:50:35 +0200 Subject: [PATCH] fix(nav): Back onto a removed page also rewrites the entry to the feed The fallback rendered the feed but left the history entry and the address bar on the dead page, so a Feed click was a no-op (the same-page guard), filter mirroring kept `?page=messages` and the nav diagnostics logged a page that does not exist. The entry is now replaced with the feed's, keeping every other URL key (test red without it). --- frontend/src/components/NavigationProvider.dom.test.tsx | 4 ++++ frontend/src/components/NavigationProvider.tsx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/frontend/src/components/NavigationProvider.dom.test.tsx b/frontend/src/components/NavigationProvider.dom.test.tsx index 47078c5d..8c0bf528 100644 --- a/frontend/src/components/NavigationProvider.dom.test.tsx +++ b/frontend/src/components/NavigationProvider.dom.test.tsx @@ -183,9 +183,13 @@ describe("NavigationProvider · Back", () => { const { result } = render(); act(() => result.current.setPage("plex")); act(() => { + window.history.replaceState({ sfPage: "messages" }, "", "/?page=messages&keep=1"); window.dispatchEvent(new PopStateEvent("popstate", { state: { sfPage: "messages" } })); }); expect(result.current.page).toBe("feed"); + // …and the entry says so too, so the address bar and a later Feed click agree with the screen. + expect(window.history.state).toMatchObject({ sfPage: "feed" }); + expect(window.location.search).toBe("?keep=1"); }); }); diff --git a/frontend/src/components/NavigationProvider.tsx b/frontend/src/components/NavigationProvider.tsx index 170f7cdd..36f56d93 100644 --- a/frontend/src/components/NavigationProvider.tsx +++ b/frontend/src/components/NavigationProvider.tsx @@ -199,8 +199,12 @@ export function NavigationProvider({ children }: { children: ReactNode }) { function onPop(e: PopStateEvent) { // An entry can name a page this build no longer has (one pushed before a module was removed, // reached by Back after a reload onto the new build); that lands on the feed, not on a crash. + // The entry is rewritten to say so: left alone, the address bar would keep naming the dead page + // and a click on Feed would be a no-op (setPage's same-page guard). const sf: unknown = e.state?.sfPage; const p: Page = typeof sf === "string" && isPage(sf) ? sf : "feed"; + if (typeof sf === "string" && sf !== p) + window.history.replaceState({ ...e.state, sfPage: p }, "", urlWithParams({ page: null })); setPageState(p); setAccountRaw(PAGE_KEY, p); // The YouTube-search sub-view and the channel page ride in history.state, so Back/Forward