"use client"; import { useState, useCallback, useRef, useEffect } from "react"; import { Button } from "@multica/ui/components/ui/button"; import { Textarea } from "@multica/ui/components/ui/textarea"; import { Loading } from "@multica/ui/components/ui/loading"; import { useIsMobile } from "@multica/ui/hooks/use-mobile"; import { HugeiconsIcon } from "@hugeicons/react"; import { Camera01Icon, TextIcon, CheckmarkCircle02Icon, Alert02Icon, } from "@hugeicons/core-free-icons"; import { QrScannerView } from "@multica/ui/components/qr-scanner-view"; import { parseConnectionCode } from "@multica/store"; import type { ConnectionIdentity } from "@/hooks/use-gateway-connection"; export interface DevicePairingProps { connectionState: string; lastError: string | null; onConnect: (identity: ConnectionIdentity, token: string) => void; onCancel: () => void; } type Mode = "scan" | "paste"; type PasteState = "idle" | "success" | "error"; /** Shown while connecting to Gateway or waiting for Owner approval */ function ConnectionStatus({ connectionState, fullscreen, onCancel, }: { connectionState: string; fullscreen?: boolean; onCancel: () => void; }) { const isVerifying = connectionState === "verifying"; const wrapper = fullscreen ? "fixed inset-0 z-50 bg-background flex flex-col items-center justify-center gap-5 px-6" : "flex flex-col items-center justify-center h-full gap-5 px-4"; return (

{isVerifying ? "Waiting for approval" : "Connecting..."}

{isVerifying ? "The device owner needs to approve this connection on their computer" : "Establishing connection to the agent"}

); } /** Shown when Owner rejects the connection, auto-dismisses after 2s */ function RejectedStatus({ fullscreen, onDismiss, }: { fullscreen?: boolean; onDismiss: () => void; }) { useEffect(() => { const timer = setTimeout(onDismiss, 2000); return () => clearTimeout(timer); }, [onDismiss]); const wrapper = fullscreen ? "fixed inset-0 z-50 bg-background flex flex-col items-center justify-center gap-5 px-6" : "flex flex-col items-center justify-center h-full gap-5 px-4"; return (

Connection rejected

The device owner declined this connection

); } export function DevicePairing({ connectionState, lastError, onConnect, onCancel, }: DevicePairingProps) { const [mode, setMode] = useState("scan"); const [codeInput, setCodeInput] = useState(""); const [pasteState, setPasteState] = useState("idle"); const [pasteError, setPasteError] = useState(null); const [showRejected, setShowRejected] = useState(false); const isMobile = useIsMobile(); const validatingRef = useRef(false); // Detect verify rejection useEffect(() => { if (lastError && connectionState === "disconnected") { setShowRejected(true); } }, [lastError, connectionState]); const handleDismissRejected = useCallback(() => { setShowRejected(false); }, []); const tryConnect = useCallback( (raw: string) => { const trimmed = raw.trim(); if (!trimmed || validatingRef.current) return; validatingRef.current = true; try { const info = parseConnectionCode(trimmed); setPasteState("success"); navigator.vibrate?.(50); setTimeout(() => { onConnect( { gateway: info.gateway, hubId: info.hubId, agentId: info.agentId }, info.token, ); }, 600); } catch (e) { setPasteState("error"); setPasteError((e as Error).message || "Invalid code"); navigator.vibrate?.([30, 50, 30]); setTimeout(() => { setPasteState("idle"); setPasteError(null); setCodeInput(""); }, 2000); } finally { validatingRef.current = false; } }, [onConnect], ); const handlePaste = useCallback( (e: React.ClipboardEvent) => { const text = e.clipboardData.getData("text"); if (!text.trim()) return; setTimeout(() => tryConnect(text), 50); }, [tryConnect], ); const handleScanResult = useCallback( async (data: string) => { const info = parseConnectionCode(data); onConnect( { gateway: info.gateway, hubId: info.hubId, agentId: info.agentId }, info.token, ); }, [onConnect], ); const isInProgress = connectionState === "connecting" || connectionState === "connected" || connectionState === "verifying"; if (showRejected) { return ( ); } if (isInProgress) { return ( ); } // Mobile: scanner only if (isMobile) { return (

Scan to connect

Scan a Multica QR code to connect to your agent

); } // Desktop: tab toggle (scan / paste) return (

{mode === "scan" ? "Scan to connect" : "Paste to connect"}

{mode === "scan" ? "Scan a Multica QR code to connect to your agent" : "Paste a Multica connection code to connect to your agent"}

{/* Mode toggle */}
{/* Content */}
{mode === "scan" ? ( ) : (
{pasteState === "idle" && (