/** * Post-authentication navigation. * * Navigation uses a full page load rather than `router.push`. The session is an httpOnly * cookie, and only a top-level request lets the middleware guard in `middleware.ts` see it * and re-render the header and the destination together. * * A marker in sessionStorage records where we last sent someone, and when. If the * middleware cannot see the session cookie it redirects straight back to `/login`, and * without the marker the login page would immediately navigate again — an endless reload * loop. A React ref cannot do this job: a full page load resets component state. */ const ATTEMPT_KEY = 'spanglish.auth-redirect-attempt'; // A real bounce comes back within a few hundred milliseconds (it is a server redirect). // An older marker means an unrelated later visit to /login, not a failed redirect. const ATTEMPT_TTL_MS = 30_000; export function markRedirectAttempt(target: string): void { try { sessionStorage.setItem(ATTEMPT_KEY, JSON.stringify({ target, at: Date.now() })); } catch { /* storage disabled (private mode): the loop guard degrades, navigation still works */ } } export function clearRedirectAttempt(): void { try { sessionStorage.removeItem(ATTEMPT_KEY); } catch { /* ignore */ } } /** True when we just sent the user to `target` and are back on the login page instead. */ export function didRedirectBounce(target: string): boolean { try { const raw = sessionStorage.getItem(ATTEMPT_KEY); if (!raw) return false; const { target: attempted, at } = JSON.parse(raw) as { target?: string; at?: number }; return ( attempted === target && typeof at === 'number' && Date.now() - at < ATTEMPT_TTL_MS ); } catch { return false; } } /** * Records the attempt, then navigates. Callers should keep their pending/loading UI on * screen: this never resolves, the page is replaced. */ export function redirectAfterAuth(target: string): void { markRedirectAttempt(target); window.location.assign(target); }