// URL-derived navigation seeds, split out of NavigationProvider so they stay unit-testable AND the // provider file keeps react-refresh happy (component-only exports). `loadInitialPage` picks the landing // module from a bookmark's params; `channelViewFrom` is the shared _chan/?chan resolver behind BOTH the // channelView seed and the onPop handler. import { hasFilterParams, isPage, readPage, type Page } from "../lib/urlState"; import { getAccountRaw, LS } from "../lib/storage"; export const PAGE_KEY = LS.page; // Module-local: the Settings toggle writes `LS.startOnHome` directly, so exporting this alias would // only be a second name for the same key. const START_HOME_KEY = LS.startOnHome; /** The two stored values of the landing preference (raw strings, via useAccountPersistedState). * Settings → Appearance → Startup writes them and `loadInitialPage` reads them, so they are defined * ONCE: the same magic string spelled out in a writer and a reader is precisely the drift that has * bitten this codebase before (`cancelled` vs `canceled`). */ export const START_ON_HOME = { on: "1", off: "0" } as const; export interface ChannelView { id: string; name?: string; } export function loadInitialPage(): Page { const params = new URLSearchParams(window.location.search); if (params.has("page")) return readPage(); // A view-specific param implies its page, so a bookmarked deep link lands on the right module even // without an explicit ?page. Feed filters (or a channel overlay) imply the feed. if (params.has("v")) return "plex"; if (params.has("pl")) return "playlists"; if (hasFilterParams(params) || params.has("chan")) return "feed"; // A bare URL → Home (E24), the surface whose whole job is to answer "what should I watch" before // the user has to decide anything. It deliberately OVERRIDES the remembered last module: landing // on whatever you happened to close the app on is the behaviour the anti-paralysis epic exists to // replace. The account can opt out (Settings → Appearance → Startup) and get the remembered // module back. if (getAccountRaw(START_HOME_KEY) !== START_ON_HOME.off) return "home"; const stored = getAccountRaw(PAGE_KEY); return isPage(stored) ? stored : "feed"; } /** Resolve the open channel page from a landed history entry: the in-app `_chan` id (with its optional * `_chanName`), else the ?chan URL param — a fresh ?chan bookmark's entry is URL-only. The seed AND * the popstate handler share this ONE resolver so a popstate that lands on a bookmark entry (no * `_chan`) restores the channel from the URL instead of wrongly closing it. */ export function channelViewFrom( state: { _chan?: unknown; _chanName?: unknown } | null | undefined, search: string, ): ChannelView | null { const id = (state?._chan as string | undefined) ?? new URLSearchParams(search).get("chan") ?? undefined; return id ? { id, name: (state?._chanName as string) ?? undefined } : null; }