import { useEffect, useState } from "react"; /** * A signed-in session that never resolves an identity gets an answer instead of a spinner. * * A spinner reads as "loading, wait", so somebody waits instead of doing the thing that would recover * it. This is the terminal state for the case where the session is valid, every request succeeded, and * the app is still waiting on something that will not arrive. */ /** Long enough that an ordinary slow mount is never interrupted, short enough to beat giving up. */ export const STALL_AFTER_MS = 15000; /** True once a resolving session has taken longer than any real mount does. */ export const useHasStalled = (resolving: boolean, afterMs = STALL_AFTER_MS): boolean => { const [stalled, setStalled] = useState(false); useEffect(() => { if (!resolving) { setStalled(false); return; } const timer = window.setTimeout(() => setStalled(true), afterMs); return () => window.clearTimeout(timer); }, [resolving, afterMs]); return stalled; }; /** * It says what happened and offers the one control that fixes it, and it claims nothing about what the * reader may reach. Telling somebody to sign in again to reach a portal they have no role at would be * worse than the spinner it replaces. */ export const StalledSession = ({ onSignOut }: { onSignOut: () => void }) => (

This page could not finish opening

We could not load your account for this portal. Sign in again here.

);