import {Fragment, useEffect, useRef, useState} from 'react'; import type {DeviceLoginStatus} from '../../shared/types'; import {countdown, type DeviceView} from './device-sign-in'; type Request = (path: string, body?: unknown) => Promise; /** * Shows the device code and polls while mounted, including while the tab is hidden: approval happens * in another tab. The server paces calls to Picsart, so extra polls here are cheap. */ export function DeviceSignIn({view, request, onStatus, onRestart, onCancel, onClose, busy}: { view: DeviceView; request: Request; onStatus: (status: DeviceLoginStatus) => void; onRestart: () => void; onCancel: (attemptId: string) => void; onClose: () => void; busy: boolean; }) { const pending = view.kind === 'pending' ? view : undefined; const interval = useRef(5); interval.current = pending?.interval ?? 5; const status = useRef(onStatus); status.current = onStatus; const [now, setNow] = useState(Date.now()); const [copy, setCopy] = useState<'idle' | 'copied' | 'manual'>('idle'); const code = useRef(null); useEffect(() => { if (!pending) return; let active = true, inFlight = false; let timer: ReturnType; const poll = async () => { if (!active || inFlight) return; inFlight = true; clearTimeout(timer); try { const answer = await request('/auth/device/poll', {}); if (active) status.current(answer); } catch { /* Transient: the code stays valid, so keep waiting. */ } finally { inFlight = false; if (active) timer = setTimeout(poll, interval.current * 1000); } }; const wake = () => { if (document.visibilityState === 'visible') void poll(); }; timer = setTimeout(poll, interval.current * 1000); const clock = setInterval(() => setNow(Date.now()), 1000); document.addEventListener('visibilitychange', wake); window.addEventListener('focus', wake); return () => { active = false; clearTimeout(timer); clearInterval(clock); document.removeEventListener('visibilitychange', wake); window.removeEventListener('focus', wake); }; }, [pending?.attemptId]); useEffect(() => setCopy('idle'), [pending?.attemptId]); if (!pending) { return (

{view.kind === 'ended' ? view.message : ''}

); } const left = pending.deadline - now; const selectCode = () => { const range = document.createRange(); if (code.current) range.selectNodeContents(code.current); window.getSelection()?.removeAllRanges(); window.getSelection()?.addRange(range); }; return (

Connect your Picsart account

Approve this code on the Picsart page. Check the code on the Picsart page matches this one before you approve.

{pending.userCode.split('-').map((part, index) => {index ? <>- : null}{part})}
{copy === 'manual' && The code is selected. Press Ctrl+C or ⌘C to copy it.}

Open Picsart {' '}or go to{' '} {pending.verificationUri.replace(/^https:\/\//, '')} {' '}and enter the code.

{pending.finishing ? 'Approved. Finishing sign-in…' : left > 0 ? `Waiting for approval. The code expires in ${countdown(left)}.` : 'Checking whether the code was approved…'}

{/* Once approved, the tokens are spent and cancelling would only lose the sign-in. */} {!pending.finishing && }
); }