1
0
Fork 0

fix(session): dismiss expiry warning toast when signed out or extended

The one-shot heads-up toast used duration:Infinity with no id, so it
lingered after auto sign-out (when the banner unmounts) and its Extend
button 403'd on the expired token. Give the toast a stable id and
dismiss it when the banner leaves the warn window or unmounts, matching
the MissionReminderToasts pattern.
This commit is contained in:
Jason Fraley 2026-09-24 12:04:58 -04:00
parent 975a08195a
commit 1e1fc33b6a

View file

@ -16,6 +16,13 @@ import {
} from "@/lib/session/countdown"; } from "@/lib/session/countdown";
import { SESSION_EXTENDED_EVENT } from "@/hooks/useGameTick"; import { SESSION_EXTENDED_EVENT } from "@/hooks/useGameTick";
/**
* Stable id for the one-shot "session expiring" heads-up toast. A fixed id lets
* us dismiss it deterministically (e.g. on sign-out) instead of relying on the
* opaque id returned by `toast.info`. Mirrors MissionReminderToasts.
*/
const SESSION_EXPIRY_TOAST_ID = "session-expiry-warning";
interface SessionExpiryBannerProps { interface SessionExpiryBannerProps {
/** Absolute expiry (ms since epoch), server-computed from the JWT `exp`. */ /** Absolute expiry (ms since epoch), server-computed from the JWT `exp`. */
sessionExp: number | null; sessionExp: number | null;
@ -70,6 +77,7 @@ export function SessionExpiryBanner({
toastFiredRef.current = true; toastFiredRef.current = true;
const minutes = Math.round(warnMs / 60_000); const minutes = Math.round(warnMs / 60_000);
toast.info(`Your session expires in about ${minutes} min.`, { toast.info(`Your session expires in about ${minutes} min.`, {
id: SESSION_EXPIRY_TOAST_ID,
action: { action: {
label: "Extend", label: "Extend",
onClick: () => void extendSession(), onClick: () => void extendSession(),
@ -83,6 +91,20 @@ export function SessionExpiryBanner({
if (phase === "warn") fireToast(); if (phase === "warn") fireToast();
}, [phase, warnMs, fireToast]); }, [phase, warnMs, fireToast]);
// The heads-up toast is only relevant while we're in the warn window. Dismiss it
// when the user extends (phase leaves "warn") or dismisses the banner, and again
// on unmount (sign-out swaps the shell for the landing page) — otherwise the
// duration:Infinity toast lingers for a signed-out user and its Extend button 403s.
useEffect(() => {
if (!visible) toast.dismiss(SESSION_EXPIRY_TOAST_ID);
}, [visible]);
useEffect(() => {
return () => {
toast.dismiss(SESSION_EXPIRY_TOAST_ID);
};
}, []);
const extendSession = useCallback(async () => { const extendSession = useCallback(async () => {
setExtending(true); setExtending(true);
setError(null); setError(null);