UNPKG

bigblocks

Version:

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

147 lines (146 loc) 7.84 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /** * HandCash Connector Component * * Provides a complete UI for connecting to HandCash with: * - OAuth flow management * - Callback handling * - Error handling * - Loading states */ import { PersonIcon } from "@radix-ui/react-icons"; import React from "react"; import { cn } from "../../lib/utils.js"; import { HandCashIcon } from "../ui-components/HandCashIcon.js"; import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar.js"; import { Button } from "../ui/button.js"; import { Card, CardContent } from "../ui/card.js"; import { OAUTH_BRAND_COLORS } from "./OAuthProviders.js"; export function HandCashConnector({ config, onSuccess, onError, className = "", }) { const [state, setState] = React.useState({ isLoading: false, isConnected: false, profile: null, authToken: null, error: null, }); // Lazy load the HandCash integration const [handCashModule, setHandCashModule] = React.useState(null); const loadHandCashModule = React.useCallback(async () => { try { const module = await import("../../lib/handcash-provider.js"); setHandCashModule(module); } catch { setState((prev) => ({ ...prev, error: "Failed to load HandCash integration", })); } }, []); const handleOAuthCallback = React.useCallback(async () => { // Check URL for HandCash callback const urlParams = new globalThis.URLSearchParams(window.location.search); const authToken = urlParams.get("authToken"); if (authToken && handCashModule) { setState((prev) => ({ ...prev, isLoading: true })); try { const provider = new handCashModule.HandCashOAuthProvider(config); const success = await provider.handleCallback(authToken); if (success) { const providerState = provider.getState(); setState((prev) => ({ ...prev, isConnected: true, profile: providerState.profile, authToken: providerState.authToken, isLoading: false, error: null, })); // Clean up URL window.history.replaceState({}, document.title, window.location.pathname); onSuccess?.({ authToken: providerState.authToken ?? "", profile: providerState.profile ?? {}, // These would come from your backend after processing idKey: providerState.profile?.id, encryptedBackup: undefined, }); } else { const errorMsg = provider.getState().error || "Authentication 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); } } }, [handCashModule, config, onSuccess, onError]); React.useEffect(() => { // Load HandCash module loadHandCashModule(); // Check if this is a callback from HandCash handleOAuthCallback(); }, [handleOAuthCallback, loadHandCashModule]); const handleConnect = async () => { if (!handCashModule) { setState((prev) => ({ ...prev, error: "HandCash module not loaded" })); return; } setState((prev) => ({ ...prev, isLoading: true, error: null })); try { const provider = new handCashModule.HandCashOAuthProvider(config); await provider.startOAuth(); // Page will redirect, so this won't continue } catch (error) { const errorMsg = error instanceof Error ? error.message : "Failed to start OAuth flow"; setState((prev) => ({ ...prev, isLoading: false, error: errorMsg, })); onError?.(errorMsg); } }; const handleDisconnect = async () => { if (!handCashModule) return; try { const provider = new handCashModule.HandCashOAuthProvider(config); await provider.disconnect(); setState({ isLoading: false, isConnected: false, profile: null, authToken: null, error: null, }); } catch (error) { console.error("Error disconnecting:", error); } }; // Loading state if (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: "Connecting to HandCash..." })] })); } // Connected state if (state.isConnected && state.profile) { 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: [_jsxs(Avatar, { className: "h-20 w-20", children: [_jsx(AvatarImage, { src: state.profile.avatarUrl }), _jsx(AvatarFallback, { className: "bg-primary", children: _jsx(PersonIcon, { className: "h-6 w-6" }) })] }), _jsx("h2", { className: "text-2xl font-bold", children: "Connected to HandCash" }), _jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("p", { className: "text-base text-muted-foreground", children: state.profile.displayName }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["@", state.profile.handle] })] }), _jsx(Button, { onClick: handleDisconnect, size: "sm", variant: "secondary", className: "mt-2", children: "Disconnect" })] }) }) })); } // 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 flex items-center justify-center", style: { backgroundColor: OAUTH_BRAND_COLORS.handcash }, children: _jsx(HandCashIcon, { size: 24, color: "white" }) }), _jsx("h2", { className: "text-2xl font-bold", children: "Connect with HandCash" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "Connect your HandCash 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 || !handCashModule, size: "lg", className: "w-full", children: !handCashModule ? "Loading..." : "Connect HandCash Wallet" }), _jsx("p", { className: "text-xs text-muted-foreground text-center", children: "You'll be redirected to HandCash to authorize the connection" })] }) }) })); }