// Makes the browser's (and phone's) Back/Forward buttons move between the app's pages. The app keeps // its page in state rather than in the URL, so without this Back would leave the app altogether. // Each page change adds a history entry; Back/Forward set the page again from that entry. import { useEffect, useRef } from 'react'; // Pages that only make sense with a study project open. export const NEEDS_PROJECT = ['study', 'outline', 'script', 'export']; export function useHistoryNav({ page, setPage, hasProject, onArrive, enabled = true }) { const shown = useRef(page); const hasProjectRef = useRef(hasProject); hasProjectRef.current = hasProject; const onArriveRef = useRef(onArrive); onArriveRef.current = onArrive; // First entry: record where the app started so Back can return to it. useEffect(() => { if (!enabled) return; try { window.history.replaceState({ page }, ''); } catch { /* history unavailable */ } }, []); // eslint-disable-line react-hooks/exhaustive-deps // A page change made inside the app adds a history entry. useEffect(() => { if (!enabled || shown.current === page) return; shown.current = page; try { window.history.pushState({ page }, ''); } catch { /* history unavailable */ } }, [page, enabled]); useEffect(() => { if (!enabled) return undefined; const onPop = (e) => { let target = e.state?.page; if (!target) return; // an entry that isn't ours (e.g. a hash change) if (NEEDS_PROJECT.includes(target) && !hasProjectRef.current) target = 'home'; shown.current = target; // so the effect above doesn't push this page again setPage(target); onArriveRef.current?.(target); }; window.addEventListener('popstate', onPop); return () => window.removeEventListener('popstate', onPop); }, [enabled, setPage]); }