bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
149 lines (148 loc) • 8.65 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Yours Wallet Connector Component
*
* Provides a complete UI for connecting to Yours Wallet with:
* - Installation detection and prompts
* - Connection management
* - Error handling
* - Loading states
*/
import React from "react";
import { cn } from "../../lib/utils.js";
import { Button } from "../ui/button.js";
import { Card, CardContent } from "../ui/card.js";
export function YoursWalletConnector({ onSuccess, onError, className = "", }) {
const [state, setState] = React.useState({
isLoading: false,
isInstalled: null,
isConnected: false,
publicKey: null,
error: null,
});
// Lazy load the Yours integration
const [yoursModule, setYoursModule] = React.useState(null);
React.useEffect(() => {
// Check if Yours is installed on mount
checkInstallation();
}, []);
const checkInstallation = async () => {
setState((prev) => ({ ...prev, isLoading: true, error: null }));
try {
// Check if window.yours is available
const isInstalled = typeof window !== "undefined" && !!window.yours;
if (isInstalled) {
// Lazy load the module only if Yours is installed
const module = await import("../../lib/yours-wallet-provider.js");
setYoursModule(module);
setState((prev) => ({
...prev,
isInstalled: true,
isLoading: false,
}));
}
else {
setState((prev) => ({
...prev,
isInstalled: false,
isLoading: false,
}));
}
}
catch {
setState((prev) => ({
...prev,
isInstalled: false,
isLoading: false,
error: "Failed to check Yours Wallet installation",
}));
}
};
const handleConnect = async () => {
if (!yoursModule) {
setState((prev) => ({
...prev,
error: "Yours Wallet module not loaded",
}));
return;
}
setState((prev) => ({ ...prev, isLoading: true, error: null }));
try {
const result = await yoursModule.createYoursAuthHandler();
if (result.success) {
setState((prev) => ({
...prev,
isConnected: true,
publicKey: result.profile?.publicKey || null,
isLoading: false,
}));
onSuccess?.({
publicKey: result.profile?.publicKey ?? "",
idKey: result.idKey,
encryptedBackup: result.encryptedBackup,
});
}
else {
const errorMsg = result.error || "Connection failed";
setState((prev) => ({
...prev,
isLoading: false,
error: errorMsg,
}));
onError?.(errorMsg);
}
}
catch (error) {
const errorMsg = error instanceof Error ? error.message : "Unknown error occurred";
setState((prev) => ({
...prev,
isLoading: false,
error: errorMsg,
}));
onError?.(errorMsg);
}
};
const handleInstallComplete = () => {
// Re-check installation after user says they've installed it
checkInstallation();
};
// Loading state
if (state.isInstalled === null || state.isLoading) {
return (_jsxs("div", { className: cn("flex flex-col items-center gap-4", className), children: [_jsx("div", { className: "animate-spin h-8 w-8 border-4 border-primary border-t-transparent rounded-full" }), _jsx("p", { className: "text-muted-foreground", children: state.isInstalled === null
? "Checking for Yours Wallet..."
: "Connecting..." })] }));
}
// Not installed state
if (!state.isInstalled) {
return (_jsx(YoursInstallPrompt, { onInstalled: handleInstallComplete, className: className }));
}
// Connected state
if (state.isConnected && state.publicKey) {
return (_jsx("div", { className: cn("flex flex-col items-center gap-4", className), children: _jsx(Card, { className: "w-full", children: _jsxs(CardContent, { className: "flex flex-col items-center gap-3 p-6", children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-primary flex items-center justify-center", children: _jsxs("svg", { width: "24", height: "24", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: [_jsx("title", { children: "Check Mark" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" })] }) }), _jsx("h2", { className: "text-2xl font-bold", children: "Connected to Yours Wallet" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Public Key: ", state.publicKey.slice(0, 16), "...", state.publicKey.slice(-8)] })] }) }) }));
}
// Ready to connect state
return (_jsx("div", { className: cn("flex flex-col items-center gap-4", className), children: _jsx(Card, { className: "w-full", children: _jsxs(CardContent, { className: "flex flex-col items-center gap-4 p-6", children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-primary flex items-center justify-center", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", children: [_jsx("title", { children: "Shield" }), _jsx("path", { fill: "white", d: "M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z" })] }) }), _jsx("h2", { className: "text-2xl font-bold", children: "Connect with Yours Wallet" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "Connect your Yours Wallet to authenticate with your Bitcoin identity." }), state.error && (_jsx(Card, { className: "w-full border-destructive", children: _jsx(CardContent, { className: "p-3", children: _jsx("p", { className: "text-sm text-destructive", children: state.error }) }) })), _jsx(Button, { onClick: handleConnect, disabled: state.isLoading, size: "lg", className: "w-full", children: state.isLoading ? "Connecting..." : "Connect Wallet" })] }) }) }));
}
// Install prompt component (re-exported for convenience)
function YoursInstallPrompt({ onInstalled, className = "", }) {
const handleInstall = () => {
window.open("https://chromewebstore.google.com/detail/yours-wallet/mlbnicldlpdimbjdcncnklfempedeipj", "_blank");
};
const handleCheckInstalled = () => {
// Re-check after potential installation
setTimeout(() => {
if (typeof window !== "undefined" && window.yours) {
onInstalled?.();
}
else {
// In Storybook or development, simulate successful installation
if (process.env.NODE_ENV === "development" ||
window.location.hostname === "localhost") {
console.log("Simulating Yours Wallet installation for Storybook/development");
onInstalled?.();
}
}
}, 1000);
};
return (_jsx("div", { className: cn("flex flex-col items-center gap-4", className), children: _jsx(Card, { className: "w-full", children: _jsxs(CardContent, { className: "flex flex-col items-center gap-4 p-6", children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-primary flex items-center justify-center", children: _jsxs("svg", { width: "24", height: "24", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: [_jsx("title", { children: "Install Yours Wallet" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" })] }) }), _jsx("h2", { className: "text-2xl font-bold", children: "Yours Wallet Required" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "To use Yours Wallet authentication, please install the browser extension." }), _jsxs("div", { className: "flex flex-col gap-2 w-full", children: [_jsx(Button, { onClick: handleInstall, size: "lg", className: "w-full", children: "Install Yours Wallet" }), _jsx(Button, { onClick: handleCheckInstalled, size: "lg", variant: "secondary", className: "w-full", children: "I've Installed It" })] })] }) }) }));
}