UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

76 lines (75 loc) 5.13 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { CheckIcon, ComponentInstanceIcon, CopyIcon, ReloadIcon, } from "@radix-ui/react-icons"; import React, { useState } from "react"; import { useAuthMessages } from "../../lib/auth-messages.js"; import { LoadingButton } from "../ui-components/LoadingButton.js"; import { QRCodeRenderer } from "../ui-components/QRCodeRenderer.js"; import { Button } from "../ui/button.js"; import { Card, CardContent } from "../ui/card.js"; export function DeviceLinkQR({ onGenerateQR, baseUrl = window.location.origin, }) { const messages = useAuthMessages(); const [qrData, setQrData] = useState(null); const [linkUrl, setLinkUrl] = useState(null); const [expiresAt, setExpiresAt] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); const handleGenerateQR = async () => { setLoading(true); setError(null); try { const result = await onGenerateQR(); setQrData(result.qrData); setLinkUrl(`${baseUrl}/link-device?token=${result.token}`); setExpiresAt(result.expiresAt); } catch (err) { setError(err instanceof Error ? err.message : "Failed to generate QR code"); } finally { setLoading(false); } }; const handleCopyLink = async () => { if (!linkUrl) return; try { await navigator.clipboard.writeText(linkUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { setError("Failed to copy link"); } }; const handleReset = React.useCallback(() => { setQrData(null); setLinkUrl(null); setExpiresAt(null); setError(null); }, []); const getTimeRemaining = React.useCallback(() => { if (!expiresAt) return null; const now = new Date(); const diff = expiresAt.getTime() - now.getTime(); if (diff <= 0) return "Expired"; const minutes = Math.floor(diff / 60000); const seconds = Math.floor((diff % 60000) / 1000); return `${minutes}:${seconds.toString().padStart(2, "0")}`; }, [expiresAt]); React.useEffect(() => { if (!expiresAt) return; const interval = setInterval(() => { const remaining = getTimeRemaining(); if (remaining === "Expired") { handleReset(); clearInterval(interval); } }, 1000); return () => clearInterval(interval); }, [expiresAt, getTimeRemaining, handleReset]); return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("h2", { className: "text-xl font-semibold", children: messages.deviceLinkTitle }), _jsx("p", { className: "text-sm text-muted-foreground", children: messages.deviceLinkSubtitle })] }), error && (_jsx(Card, { className: "bg-destructive/10", children: _jsx(CardContent, { className: "p-3", children: _jsx("p", { className: "text-sm text-destructive", children: error }) }) })), !qrData ? (_jsxs(LoadingButton, { onClick: handleGenerateQR, loading: loading, className: "w-full", variant: "default", children: [_jsx(ComponentInstanceIcon, { className: "h-4 w-4 mr-2" }), "Generate QR Code"] })) : (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Card, { className: "bg-muted/50 border", children: _jsx(CardContent, { className: "p-4", children: _jsx(QRCodeRenderer, { data: qrData, size: 256, qrStyle: "dots", themeAware: true, bgColor: "var(--background)", fgColor: "var(--primary)", style: { width: "100%", height: "auto" } }) }) }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex justify-between items-center", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "Expires in:" }), _jsx("span", { className: "text-sm font-mono", children: getTimeRemaining() })] }), _jsx(Button, { onClick: handleCopyLink, size: "default", variant: "secondary", className: "w-full", children: copied ? (_jsxs(_Fragment, { children: [_jsx(CheckIcon, { className: "h-4 w-4 mr-2" }), "Copied!"] })) : (_jsxs(_Fragment, { children: [_jsx(CopyIcon, { className: "h-4 w-4 mr-2" }), "Copy Link"] })) }), _jsxs(Button, { onClick: handleReset, size: "default", variant: "secondary", className: "w-full", children: [_jsx(ReloadIcon, { className: "h-4 w-4 mr-2" }), "Generate New Code"] })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("p", { className: "text-xs text-muted-foreground", children: "1. Scan this QR code on your other device" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "2. Enter your password to decrypt your backup" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "3. Your identity will be linked to both devices" })] })] }))] })); }