bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
76 lines (75 loc) • 5.13 kB
JavaScript
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" })] })] }))] }));
}