1
0
Fork 0
polaris-task-force/src/hooks/useSessionExpired.ts
Z8MB1E 962064a5a4 feat(session): add pre-expiry warning banner with extend button
- SessionExpiryBanner: live countdown, warn 15min early, one-shot toast, Extend button
- countdown.ts: pure countdown helpers (timeUntilExpiry, sessionExpiryPhase, formatCountdown)
- useSessionExpired: added unlock handler for SESSION_EXTENDED_EVENT
- useGameTick: exported SESSION_EXTENDED_EVENT for session extension
- unit tests: 14/14 pass for countdown helpers
- e2e test: banner renders with countdown + extend button
2026-09-23 15:02:10 -04:00

39 lines
1.2 KiB
TypeScript

"use client";
import { useEffect, useRef, useState } from "react";
import { SESSION_EXPIRED_EVENT, SESSION_EXTENDED_EVENT } from "@/hooks/useGameTick";
/**
* Tracks whether the session has expired (per the client-side watchdog).
*
* Returns both a reactive `expired` boolean (for rendering) and an
* `expiredRef` (for use inside callbacks / the game loop without stale
* closures). Consumers should gate start/retry/input and skip server saves
* on `expiredRef.current`.
*/
export function useSessionExpired(): {
expired: boolean;
expiredRef: React.RefObject<boolean>;
} {
const [expired, setExpired] = useState(false);
const expiredRef = useRef(false);
useEffect(() => {
const lock = () => {
expiredRef.current = true;
setExpired(true);
};
const unlock = () => {
expiredRef.current = false;
setExpired(false);
};
window.addEventListener(SESSION_EXPIRED_EVENT, lock);
window.addEventListener(SESSION_EXTENDED_EVENT, unlock);
return () => {
window.removeEventListener(SESSION_EXPIRED_EVENT, lock);
window.removeEventListener(SESSION_EXTENDED_EVENT, unlock);
};
}, []);
return { expired, expiredRef };
}