({
loginUrl,
fingerprintId,
fingerprintHash,
expiresAt,
isWaitingForEnter,
onSuccess,
onTimeout,
onError,
}: UseLoginPollingParams)
| 22 | * Extracts the 109-line polling effect from login-modal.tsx |
| 23 | */ |
| 24 | export function useLoginPolling({ |
| 25 | loginUrl, |
| 26 | fingerprintId, |
| 27 | fingerprintHash, |
| 28 | expiresAt, |
| 29 | isWaitingForEnter, |
| 30 | onSuccess, |
| 31 | onTimeout, |
| 32 | onError, |
| 33 | }: UseLoginPollingParams) { |
| 34 | // Store callbacks in refs to prevent effect re-runs |
| 35 | const onSuccessRef = useRef(onSuccess) |
| 36 | const onTimeoutRef = useRef(onTimeout) |
| 37 | const onErrorRef = useRef(onError) |
| 38 | |
| 39 | useEffect(() => { |
| 40 | onSuccessRef.current = onSuccess |
| 41 | }, [onSuccess]) |
| 42 | |
| 43 | useEffect(() => { |
| 44 | onTimeoutRef.current = onTimeout |
| 45 | }, [onTimeout]) |
| 46 | |
| 47 | useEffect(() => { |
| 48 | onErrorRef.current = onError |
| 49 | }, [onError]) |
| 50 | |
| 51 | useEffect(() => { |
| 52 | // fingerprintHash only becomes non-null after the login-URL mutation |
| 53 | // succeeds, and that path always sets fingerprintId first — so gating |
| 54 | // on fingerprintHash implicitly gates on fingerprintId. |
| 55 | if (!loginUrl || !fingerprintId || !fingerprintHash || !expiresAt || !isWaitingForEnter) { |
| 56 | return |
| 57 | } |
| 58 | |
| 59 | let active = true |
| 60 | |
| 61 | const sleep = (ms: number) => |
| 62 | new Promise<void>((resolve) => { |
| 63 | setTimeout(resolve, ms) |
| 64 | }) |
| 65 | |
| 66 | pollLoginStatus( |
| 67 | { |
| 68 | sleep, |
| 69 | logger, |
| 70 | }, |
| 71 | { |
| 72 | baseUrl: LOGIN_WEBSITE_URL, |
| 73 | fingerprintId: fingerprintId!, |
| 74 | fingerprintHash, |
| 75 | expiresAt, |
| 76 | shouldContinue: () => active, |
| 77 | }, |
| 78 | ) |
| 79 | .then((result) => { |
| 80 | if (!active) { |
| 81 | return |
no test coverage detected