import { useEffect, useRef, useState } from "react"; import { Box } from "../../../ui"; import { useAppLanguage } from "../../../i18n/react"; import { useShortcut } from "../../../react/input"; import { isPlainKey } from "../../../utils/keyboard"; import { DeviceSignInController, type DeviceSignInSnapshot, } from "../../../plugins/builtin/cloud/device-signin"; import { DeviceSignInPanel } from "../../../plugins/builtin/cloud/device-signin-dialog"; /** * QR device sign-in inside the onboarding account step. The controller starts * on mount and cancels on unmount, so backing out to the chooser stops polling. */ export function AccountQrPanel({ onApproved, height, }: { onApproved: (email: string) => void; height: number; }) { useAppLanguage(); const controllerRef = useRef(null); if (!controllerRef.current) controllerRef.current = new DeviceSignInController(); const controller = controllerRef.current; const [snapshot, setSnapshot] = useState(controller.getSnapshot()); useEffect(() => { const unsubscribe = controller.subscribe(setSnapshot); controller.start(); return () => { unsubscribe(); controller.cancel(); }; }, [controller]); // The wizard's card holds every key it does not use, so the retry runs in a // scope of its own that registers after the card's and answers first. useShortcut((event) => { const retry = (isPlainKey(event, "enter", "return") && snapshot.phase === "denied") || (isPlainKey(event, "r") && snapshot.phase !== "approved"); if (!retry) return; event.preventDefault(); event.stopPropagation(); controller.start(); }, { scope: "onboarding:qr", phase: "before" }); useEffect(() => { if (snapshot.phase !== "approved" || !snapshot.user) return; const email = snapshot.user.email?.trim() || snapshot.user.username?.trim() || ""; const advanceTimer = setTimeout(() => onApproved(email), 1_200); return () => clearTimeout(advanceTimer); }, [onApproved, snapshot.phase, snapshot.user]); return ( ); }