UNPKG

bigblocks

Version:

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

15 lines 12.8 kB
{ "name": "wallet-integrations-oauthconflictmodal", "type": "registry:component", "dependencies": [], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/wallet-integrations/OAuthConflictModal.tsx", "type": "registry:component", "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport { ErrorDisplay } from \"../ui-components/ErrorDisplay.js\";\nimport { LoadingButton } from \"../ui-components/LoadingButton.js\";\nimport { PasswordInput } from \"../ui-components/PasswordInput.js\";\nimport {\n\tAlertDialog,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogTitle,\n} from \"../ui/alert-dialog.js\";\nimport { Alert, AlertDescription, AlertTitle } from \"../ui/alert.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Card, CardContent } from \"../ui/card.js\";\n\nexport interface OAuthConflictModalProps {\n\tisOpen: boolean;\n\tonClose: () => void;\n\tprovider: string;\n\texistingBapId: string;\n\tcurrentBapId: string;\n\tonTransferComplete: () => void;\n\tonSwitchAccount: () => void;\n\tonTransferOAuth?: (\n\t\tfromBapId: string,\n\t\ttoBapId: string,\n\t\tpassword: string,\n\t) => Promise<void>;\n\tonSwitchToExisting?: (existingBapId: string) => Promise<void>;\n\tclassName?: string;\n}\n\ntype ConflictMode = \"choose\" | \"transfer\" | \"switching\";\n\nexport function OAuthConflictModal({\n\tisOpen,\n\tonClose,\n\tprovider,\n\texistingBapId,\n\tcurrentBapId,\n\tonTransferComplete,\n\tonSwitchAccount,\n\tonTransferOAuth,\n\tonSwitchToExisting,\n\tclassName = \"\",\n}: OAuthConflictModalProps) {\n\tconst [mode, setMode] = useState<ConflictMode>(\"choose\");\n\tconst [password, setPassword] = useState(\"\");\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState(\"\");\n\n\tconst handleTransfer = async (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsetLoading(true);\n\t\tsetError(\"\");\n\n\t\ttry {\n\t\t\tif (onTransferOAuth) {\n\t\t\t\tawait onTransferOAuth(existingBapId, currentBapId, password);\n\t\t\t} else {\n\t\t\t\t// Default transfer logic - can be overridden by parent\n\t\t\t\tconst response = await fetch(\"/api/users/transfer-oauth\", {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\tfromBapId: existingBapId,\n\t\t\t\t\t\ttoBapId: currentBapId,\n\t\t\t\t\t\tverificationPassword: password,\n\t\t\t\t\t}),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\tthrow new Error(data.error || \"Failed to transfer OAuth link\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tonTransferComplete();\n\t\t} catch (err) {\n\t\t\tconsole.error(\"Transfer error:\", err);\n\t\t\tsetError(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to transfer OAuth link\",\n\t\t\t);\n\t\t\tsetLoading(false);\n\t\t}\n\t};\n\n\tconst handleSwitchAccount = async () => {\n\t\tsetMode(\"switching\");\n\t\tsetLoading(true);\n\n\t\ttry {\n\t\t\tif (onSwitchToExisting) {\n\t\t\t\tawait onSwitchToExisting(existingBapId);\n\t\t\t} else {\n\t\t\t\t// Default switch logic - can be overridden by parent\n\t\t\t\tconst response = await fetch(`/api/backup?bapid=${existingBapId}`);\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(\"Failed to fetch existing account\");\n\t\t\t\t}\n\n\t\t\t\tconst { backup: encryptedBackup } = await response.json();\n\t\t\t\tlocalStorage.setItem(\"encryptedBackup\", encryptedBackup);\n\t\t\t}\n\n\t\t\tonSwitchAccount();\n\t\t} catch (err) {\n\t\t\tconsole.error(\"Switch account error:\", err);\n\t\t\tsetError(\"Failed to switch accounts. Please try signing in manually.\");\n\t\t\tsetLoading(false);\n\t\t\tsetMode(\"choose\");\n\t\t}\n\t};\n\n\tconst resetForm = () => {\n\t\tsetMode(\"choose\");\n\t\tsetPassword(\"\");\n\t\tsetError(\"\");\n\t\tsetLoading(false);\n\t};\n\n\tconst handleClose = () => {\n\t\tresetForm();\n\t\tonClose();\n\t};\n\n\tconst renderChooseMode = () => (\n\t\t<div className=\"flex flex-col gap-6\">\n\t\t\t<Alert className=\"border-amber-200 bg-amber-50\">\n\t\t\t\t<svg\n\t\t\t\t\twidth=\"16\"\n\t\t\t\t\theight=\"16\"\n\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\tviewBox=\"0 0 20 20\"\n\t\t\t\t\taria-label=\"Information\"\n\t\t\t\t\tclassName=\"text-amber-600\"\n\t\t\t\t>\n\t\t\t\t\t<title>Information</title>\n\t\t\t\t\t<path\n\t\t\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\t\t\td=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z\"\n\t\t\t\t\t\tclipRule=\"evenodd\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t\t<AlertTitle className=\"font-medium\">Account Already Linked</AlertTitle>\n\t\t\t\t<AlertDescription>\n\t\t\t\t\tThis {provider} account is already linked to another Bitcoin identity.\n\t\t\t\t\tChoose how you'd like to proceed:\n\t\t\t\t</AlertDescription>\n\t\t\t</Alert>\n\n\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t<Card\n\t\t\t\t\tclassName=\"cursor-pointer hover:bg-accent/50 transition-colors\"\n\t\t\t\t\tonClick={() => setMode(\"transfer\")}\n\t\t\t\t>\n\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\twidth=\"20\"\n\t\t\t\t\t\t\t\theight=\"20\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\taria-label=\"Transfer\"\n\t\t\t\t\t\t\t\tclassName=\"text-blue-600\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<title>Transfer</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=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"\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\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t<p className=\"font-medium\">Transfer {provider} Link</p>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tMove the {provider} connection from your other account to this\n\t\t\t\t\t\t\t\t\tone. You'll need the password for verification.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"transition-colors\",\n\t\t\t\t\t\tloading\n\t\t\t\t\t\t\t? \"cursor-not-allowed opacity-50\"\n\t\t\t\t\t\t\t: \"cursor-pointer hover:bg-accent/50\",\n\t\t\t\t\t)}\n\t\t\t\t\tonClick={loading ? undefined : handleSwitchAccount}\n\t\t\t\t>\n\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\twidth=\"20\"\n\t\t\t\t\t\t\t\theight=\"20\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\taria-label=\"Use existing account\"\n\t\t\t\t\t\t\t\tclassName=\"text-green-600\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<title>Use existing account</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=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\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\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t<p className=\"font-medium\">Use Existing Account</p>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tDiscard this new identity and continue with your existing\n\t\t\t\t\t\t\t\t\taccount.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\n\t\t\t<div className=\"flex justify-center pt-4\">\n\t\t\t\t<Button variant=\"ghost\" size=\"sm\" onClick={handleClose}>\n\t\t\t\t\tCancel\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n\n\tconst renderTransferMode = () => (\n\t\t<form onSubmit={handleTransfer}>\n\t\t\t<div className=\"flex flex-col gap-6\">\n\t\t\t\t<div>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground mb-4\">\n\t\t\t\t\t\tEnter the password for your other account to verify ownership and\n\t\t\t\t\t\ttransfer the {provider} link to your current account.\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<Card className=\"mb-4\">\n\t\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\twidth=\"16\"\n\t\t\t\t\t\t\t\t\t\theight=\"16\"\n\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Transfer from\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"text-muted-foreground\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<title>Transfer from</title>\n\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\t\t\td=\"M8 7h12m0 0l-4-4m4 4l-4 4\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\tTransferring from:\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<span className=\"text-xs font-mono break-all\">\n\t\t\t\t\t\t\t\t\t{existingBapId}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 mt-2\">\n\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\twidth=\"16\"\n\t\t\t\t\t\t\t\t\t\theight=\"16\"\n\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Transfer to\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"text-muted-foreground\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<title>Transfer to</title>\n\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\t\t\td=\"M16 17l-4 4m0 0l-4-4m4 4V3\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\tTransferring to:\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<span className=\"text-xs font-mono break-all\">\n\t\t\t\t\t\t\t\t\t{currentBapId}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</div>\n\n\t\t\t\t<PasswordInput\n\t\t\t\t\tlabel=\"Password for Other Account\"\n\t\t\t\t\tvalue={password}\n\t\t\t\t\tonChange={setPassword}\n\t\t\t\t\tplaceholder=\"Enter encryption password\"\n\t\t\t\t\tdisabled={loading}\n\t\t\t\t\trequired\n\t\t\t\t/>\n\n\t\t\t\t<ErrorDisplay error={error} />\n\n\t\t\t\t<div className=\"flex gap-3 justify-end\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tonClick={resetForm}\n\t\t\t\t\t\tdisabled={loading}\n\t\t\t\t\t>\n\t\t\t\t\t\tBack\n\t\t\t\t\t</Button>\n\t\t\t\t\t<LoadingButton type=\"submit\" loading={loading} disabled={!password}>\n\t\t\t\t\t\tTransfer Link\n\t\t\t\t\t</LoadingButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n\n\tconst renderSwitchingMode = () => (\n\t\t<div className=\"flex flex-col items-center gap-4 py-8\">\n\t\t\t<div className=\"animate-spin h-8 w-8 border-4 border-primary border-t-transparent rounded-full\" />\n\t\t\t<p className=\"text-muted-foreground\">\n\t\t\t\tSwitching to your existing account...\n\t\t\t</p>\n\t\t</div>\n\t);\n\n\tconst renderContent = () => {\n\t\tswitch (mode) {\n\t\t\tcase \"choose\":\n\t\t\t\treturn renderChooseMode();\n\t\t\tcase \"transfer\":\n\t\t\t\treturn renderTransferMode();\n\t\t\tcase \"switching\":\n\t\t\t\treturn renderSwitchingMode();\n\t\t\tdefault:\n\t\t\t\treturn renderChooseMode();\n\t\t}\n\t};\n\n\treturn (\n\t\t<AlertDialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>\n\t\t\t<AlertDialogContent className={cn(\"max-w-[28rem] w-full\", className)}>\n\t\t\t\t<AlertDialogTitle>{provider} Account Conflict</AlertDialogTitle>\n\t\t\t\t<AlertDialogDescription>\n\t\t\t\t\t{mode === \"choose\" &&\n\t\t\t\t\t\t\"This account is already linked to another Bitcoin identity.\"}\n\t\t\t\t\t{mode === \"transfer\" &&\n\t\t\t\t\t\t\"Enter your password to verify ownership and transfer the link.\"}\n\t\t\t\t\t{mode === \"switching\" && \"Switching to your existing account...\"}\n\t\t\t\t</AlertDialogDescription>\n\n\t\t\t\t<div className=\"mt-4\">{renderContent()}</div>\n\t\t\t</AlertDialogContent>\n\t\t</AlertDialog>\n\t);\n}\n", "target": "<%- config.aliases.components %>/oauthconflictmodal.tsx" } ] }