bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
15 lines • 10.1 kB
JSON
{
"name": "wallet-integrations-yourswalletconnector",
"type": "registry:component",
"dependencies": [],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "components/wallet-integrations/YoursWalletConnector.tsx",
"type": "registry:component",
"content": "/**\n * Yours Wallet Connector Component\n *\n * Provides a complete UI for connecting to Yours Wallet with:\n * - Installation detection and prompts\n * - Connection management\n * - Error handling\n * - Loading states\n */\n\nimport React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport type { YoursInstallPromptProps } from \"../../lib/yours-wallet-provider.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Card, CardContent } from \"../ui/card.js\";\n\ninterface YoursWalletConnectorProps {\n\tonSuccess?: (result: {\n\t\tpublicKey: string;\n\t\tidKey?: string;\n\t\tencryptedBackup?: string;\n\t}) => void;\n\tonError?: (error: string) => void;\n\tclassName?: string;\n}\n\ninterface YoursWalletState {\n\tisLoading: boolean;\n\tisInstalled: boolean | null; // null = checking\n\tisConnected: boolean;\n\tpublicKey: string | null;\n\terror: string | null;\n}\n\nexport function YoursWalletConnector({\n\tonSuccess,\n\tonError,\n\tclassName = \"\",\n}: YoursWalletConnectorProps) {\n\tconst [state, setState] = React.useState<YoursWalletState>({\n\t\tisLoading: false,\n\t\tisInstalled: null,\n\t\tisConnected: false,\n\t\tpublicKey: null,\n\t\terror: null,\n\t});\n\n\t// Lazy load the Yours integration\n\tconst [yoursModule, setYoursModule] = React.useState<\n\t\ttypeof import(\"../../lib/yours-wallet-provider.js\") | null\n\t>(null);\n\n\tReact.useEffect(() => {\n\t\t// Check if Yours is installed on mount\n\t\tcheckInstallation();\n\t}, []);\n\n\tconst checkInstallation = async () => {\n\t\tsetState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n\t\ttry {\n\t\t\t// Check if window.yours is available\n\t\t\tconst isInstalled = typeof window !== \"undefined\" && !!window.yours;\n\n\t\t\tif (isInstalled) {\n\t\t\t\t// Lazy load the module only if Yours is installed\n\t\t\t\tconst module = await import(\"../../lib/yours-wallet-provider.js\");\n\t\t\t\tsetYoursModule(module);\n\t\t\t\tsetState((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tisInstalled: true,\n\t\t\t\t\tisLoading: false,\n\t\t\t\t}));\n\t\t\t} else {\n\t\t\t\tsetState((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tisInstalled: false,\n\t\t\t\t\tisLoading: false,\n\t\t\t\t}));\n\t\t\t}\n\t\t} catch {\n\t\t\tsetState((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\tisInstalled: false,\n\t\t\t\tisLoading: false,\n\t\t\t\terror: \"Failed to check Yours Wallet installation\",\n\t\t\t}));\n\t\t}\n\t};\n\n\tconst handleConnect = async () => {\n\t\tif (!yoursModule) {\n\t\t\tsetState((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\terror: \"Yours Wallet module not loaded\",\n\t\t\t}));\n\t\t\treturn;\n\t\t}\n\n\t\tsetState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n\t\ttry {\n\t\t\tconst result = await yoursModule.createYoursAuthHandler();\n\n\t\t\tif (result.success) {\n\t\t\t\tsetState((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tisConnected: true,\n\t\t\t\t\tpublicKey: result.profile?.publicKey || null,\n\t\t\t\t\tisLoading: false,\n\t\t\t\t}));\n\n\t\t\t\tonSuccess?.({\n\t\t\t\t\tpublicKey: result.profile?.publicKey ?? \"\",\n\t\t\t\t\tidKey: result.idKey,\n\t\t\t\t\tencryptedBackup: result.encryptedBackup,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst errorMsg = result.error || \"Connection failed\";\n\t\t\t\tsetState((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tisLoading: false,\n\t\t\t\t\terror: errorMsg,\n\t\t\t\t}));\n\t\t\t\tonError?.(errorMsg);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMsg =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tsetState((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\tisLoading: false,\n\t\t\t\terror: errorMsg,\n\t\t\t}));\n\t\t\tonError?.(errorMsg);\n\t\t}\n\t};\n\n\tconst handleInstallComplete = () => {\n\t\t// Re-check installation after user says they've installed it\n\t\tcheckInstallation();\n\t};\n\n\t// Loading state\n\tif (state.isInstalled === null || state.isLoading) {\n\t\treturn (\n\t\t\t<div className={cn(\"flex flex-col items-center gap-4\", className)}>\n\t\t\t\t<div className=\"animate-spin h-8 w-8 border-4 border-primary border-t-transparent rounded-full\" />\n\t\t\t\t<p className=\"text-muted-foreground\">\n\t\t\t\t\t{state.isInstalled === null\n\t\t\t\t\t\t? \"Checking for Yours Wallet...\"\n\t\t\t\t\t\t: \"Connecting...\"}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// Not installed state\n\tif (!state.isInstalled) {\n\t\treturn (\n\t\t\t<YoursInstallPrompt\n\t\t\t\tonInstalled={handleInstallComplete}\n\t\t\t\tclassName={className}\n\t\t\t/>\n\t\t);\n\t}\n\n\t// Connected state\n\tif (state.isConnected && state.publicKey) {\n\t\treturn (\n\t\t\t<div className={cn(\"flex flex-col items-center gap-4\", className)}>\n\t\t\t\t<Card className=\"w-full\">\n\t\t\t\t\t<CardContent className=\"flex flex-col items-center gap-3 p-6\">\n\t\t\t\t\t\t<div className=\"w-12 h-12 rounded-full bg-primary flex items-center justify-center\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\twidth=\"24\"\n\t\t\t\t\t\t\t\theight=\"24\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tstroke=\"white\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<title>Check Mark</title>\n\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\td=\"M5 13l4 4L19 7\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<h2 className=\"text-2xl font-bold\">Connected to Yours Wallet</h2>\n\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\tPublic Key: {state.publicKey.slice(0, 16)}...\n\t\t\t\t\t\t\t{state.publicKey.slice(-8)}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// Ready to connect state\n\treturn (\n\t\t<div className={cn(\"flex flex-col items-center gap-4\", className)}>\n\t\t\t<Card className=\"w-full\">\n\t\t\t\t<CardContent className=\"flex flex-col items-center gap-4 p-6\">\n\t\t\t\t\t<div className=\"w-12 h-12 rounded-full bg-primary flex items-center justify-center\">\n\t\t\t\t\t\t<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n\t\t\t\t\t\t\t<title>Shield</title>\n\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\tfill=\"white\"\n\t\t\t\t\t\t\t\td=\"M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</div>\n\t\t\t\t\t<h2 className=\"text-2xl font-bold\">Connect with Yours Wallet</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tConnect your Yours Wallet to authenticate with your Bitcoin\n\t\t\t\t\t\tidentity.\n\t\t\t\t\t</p>\n\n\t\t\t\t\t{state.error && (\n\t\t\t\t\t\t<Card className=\"w-full border-destructive\">\n\t\t\t\t\t\t\t<CardContent className=\"p-3\">\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-destructive\">{state.error}</p>\n\t\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t\t</Card>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<Button\n\t\t\t\t\t\tonClick={handleConnect}\n\t\t\t\t\t\tdisabled={state.isLoading}\n\t\t\t\t\t\tsize=\"lg\"\n\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{state.isLoading ? \"Connecting...\" : \"Connect Wallet\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n\n// Install prompt component (re-exported for convenience)\nfunction YoursInstallPrompt({\n\tonInstalled,\n\tclassName = \"\",\n}: YoursInstallPromptProps) {\n\tconst handleInstall = () => {\n\t\twindow.open(\n\t\t\t\"https://chromewebstore.google.com/detail/yours-wallet/mlbnicldlpdimbjdcncnklfempedeipj\",\n\t\t\t\"_blank\",\n\t\t);\n\t};\n\n\tconst handleCheckInstalled = () => {\n\t\t// Re-check after potential installation\n\t\tsetTimeout(() => {\n\t\t\tif (typeof window !== \"undefined\" && window.yours) {\n\t\t\t\tonInstalled?.();\n\t\t\t} else {\n\t\t\t\t// In Storybook or development, simulate successful installation\n\t\t\t\tif (\n\t\t\t\t\tprocess.env.NODE_ENV === \"development\" ||\n\t\t\t\t\twindow.location.hostname === \"localhost\"\n\t\t\t\t) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\"Simulating Yours Wallet installation for Storybook/development\",\n\t\t\t\t\t);\n\t\t\t\t\tonInstalled?.();\n\t\t\t\t}\n\t\t\t}\n\t\t}, 1000);\n\t};\n\n\treturn (\n\t\t<div className={cn(\"flex flex-col items-center gap-4\", className)}>\n\t\t\t<Card className=\"w-full\">\n\t\t\t\t<CardContent className=\"flex flex-col items-center gap-4 p-6\">\n\t\t\t\t\t<div className=\"w-12 h-12 rounded-full bg-primary flex items-center justify-center\">\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\twidth=\"24\"\n\t\t\t\t\t\t\theight=\"24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"white\"\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<title>Install Yours Wallet</title>\n\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\td=\"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</div>\n\t\t\t\t\t<h2 className=\"text-2xl font-bold\">Yours Wallet Required</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tTo use Yours Wallet authentication, please install the browser\n\t\t\t\t\t\textension.\n\t\t\t\t\t</p>\n\t\t\t\t\t<div className=\"flex flex-col gap-2 w-full\">\n\t\t\t\t\t\t<Button onClick={handleInstall} size=\"lg\" className=\"w-full\">\n\t\t\t\t\t\t\tInstall Yours Wallet\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tonClick={handleCheckInstalled}\n\t\t\t\t\t\t\tsize=\"lg\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tI've Installed It\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n",
"target": "<%- config.aliases.components %>/yourswalletconnector.tsx"
}
]
}