import { createSignal, onCleanup, Show } from "solid-js"; import { sendMessage } from "../App"; import type { PairedDevice, PairingStep } from "../../shared/sync-types"; interface SyncPairingProps { mode: "initiate" | "accept"; onClose: () => void; onPaired: (device: PairedDevice) => void; } function generatePairingCode(): string { const array = new Uint32Array(1); crypto.getRandomValues(array); return String(array[0] % 1000000).padStart(6, "0"); } function CodeDisplay(props: { code: string }) { return (
{props.code.split("").map((digit) => ( {digit} ))}
); } function CountdownRing(props: { secondsLeft: number }) { const radius = 22; const circumference = 2 * Math.PI * radius; const progress = () => (props.secondsLeft / 30) * circumference; return (
{props.secondsLeft}
); } function Spinner() { return ; } export function SyncPairingView(props: SyncPairingProps) { const [step, setStep] = createSignal({ stage: "idle" }); const [codeInput, setCodeInput] = createSignal(""); const [secondsLeft, setSecondsLeft] = createSignal(30); const [codeError, setCodeError] = createSignal(null); let countdownTimer: number | undefined; let pairingTimer: number | undefined; onCleanup(() => { if (countdownTimer !== undefined) window.clearInterval(countdownTimer); if (pairingTimer !== undefined) window.clearTimeout(pairingTimer); }); function startCountdown(expiresAt: number) { if (countdownTimer !== undefined) window.clearInterval(countdownTimer); setSecondsLeft(Math.max(0, Math.floor((expiresAt - Date.now()) / 1000))); countdownTimer = window.setInterval(() => { const remaining = Math.max(0, Math.floor((expiresAt - Date.now()) / 1000)); setSecondsLeft(remaining); if (remaining === 0) { window.clearInterval(countdownTimer); setStep({ stage: "idle" }); void sendMessage({ type: "CANCEL_PAIRING" }); } }, 500); } async function handleGenerateCode() { const code = generatePairingCode(); const expiresAt = Date.now() + 30_000; setStep({ stage: "generating_code", code, expiresAt }); startCountdown(expiresAt); const res = await sendMessage({ type: "START_PAIRING", payload: { code, expiresAt }, }); if (!(res as { ok: boolean }).ok) { setStep({ stage: "error", message: "Failed to start pairing" }); return; } setStep({ stage: "waiting_for_peer" }); pairingTimer = window.setTimeout(() => { setStep({ stage: "idle" }); void sendMessage({ type: "CANCEL_PAIRING" }); }, 30_000); } async function handleCancel() { if (countdownTimer !== undefined) window.clearInterval(countdownTimer); if (pairingTimer !== undefined) window.clearTimeout(pairingTimer); setStep({ stage: "idle" }); await sendMessage({ type: "CANCEL_PAIRING" }); } async function handleConnect() { const code = codeInput().replace(/\D/g, "").slice(0, 6); if (code.length !== 6) { setCodeError("Enter a 6-digit code"); return; } setCodeError(null); setStep({ stage: "connecting", peerName: "" }); const res = await sendMessage({ type: "ACCEPT_PAIRING", payload: { code }, }); const response = res as { ok: boolean; data?: PairedDevice; error?: string }; if (!response.ok) { setStep({ stage: "error", message: response.error ?? "Connection failed" }); return; } setStep({ stage: "verifying" }); if (response.data) { setStep({ stage: "done", device: response.data }); props.onPaired(response.data); } } function handleCodeInput(e: InputEvent) { const target = e.currentTarget as HTMLInputElement; const value = target.value.replace(/\D/g, "").slice(0, 6); setCodeInput(value); setCodeError(null); } return (
pair devices

Pair a new device

Generate a code and wait for another device to connect.

Waiting for peer...

Make sure the other device is searching for peers.

Pair with another device

Enter the 6-digit code shown on the other device.

{Array.from({ length: 6 }, (_, i) => ( { const current = codeInput(); const digit = (e.currentTarget as HTMLInputElement).value.replace(/\D/g, "").slice(-1); const newValue = current.slice(0, i) + digit + current.slice(i + 1); setCodeInput(newValue.slice(0, 6)); setCodeError(null); if (digit && i < 5) { const inputs = document.querySelectorAll(".peach-pairing-code-box"); (inputs[i + 1] as HTMLInputElement)?.focus(); } }} onKeyDown={(e) => { if (e.key === "Backspace" && !codeInput()[i] && i > 0) { const inputs = document.querySelectorAll(".peach-pairing-code-box"); (inputs[i - 1] as HTMLInputElement)?.focus(); } }} /> ))}

{codeError()}

{step().stage === "connecting" ? "Connecting..." : "Verifying..."}

Paired!

{(step() as { stage: "done"; device: PairedDevice }).device.displayName}

Pairing failed

{(step() as { stage: "error"; message: string }).message}

); }