UNPKG

bigblocks

Version:

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

15 lines 13.5 kB
{ "name": "bap-identity-components-masterkeymigration", "type": "registry:component", "dependencies": [], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/bap-identity/components/MasterKeyMigration.tsx", "type": "registry:component", "content": "import {\n\tCheckCircledIcon,\n\tExclamationTriangleIcon,\n\tInfoCircledIcon,\n\tUpdateIcon,\n} from \"@radix-ui/react-icons\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\nimport { CONTAINER_WIDTHS } from \"../../../lib/layout-constants.js\";\nimport type { BapMasterBackup } from \"../../../lib/types.js\";\nimport {\n\tisBapMasterBackupLegacy,\n\tisMasterBackupType42,\n} from \"../../../lib/types.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { MasterKeyBadge, WarningCard } from \"../../ui-components/index.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../../ui/card.js\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"../../ui/dialog.js\";\nimport { Progress } from \"../../ui/progress.js\";\nimport { useMasterKeyUtils } from \"../hooks/useMasterKeyUtils.js\";\nimport type {\n\tMasterKeyMigrationProps,\n\tMigrationResult,\n\tMigrationState,\n} from \"../types/master-key.js\";\n\ntype MigrationStatus =\n\t| \"idle\"\n\t| \"decrypting\"\n\t| \"generating\"\n\t| \"encrypting\"\n\t| \"upgrading\"\n\t| \"success\"\n\t| \"error\";\n\nconst stateMessages: Record<MigrationStatus, string> = {\n\tidle: \"Ready to migrate\",\n\tdecrypting: \"Decrypting backup...\",\n\tgenerating: \"Generating Type 42 key...\",\n\tencrypting: \"Encrypting with new key...\",\n\tupgrading: \"Upgrading backup format...\",\n\tsuccess: \"Migration complete!\",\n\terror: \"Migration failed\",\n};\n\nexport function MasterKeyMigration({\n\tonMigrationComplete,\n\tonError,\n\tshowDialog = true,\n\tautoStart = false,\n\tsize = \"2\",\n\tvariant = \"classic\",\n}: MasterKeyMigrationProps) {\n\tconst { hasLocalBackup } = useBitcoinAuth();\n\tconst masterKeyUtils = useMasterKeyUtils();\n\n\tconst [showMigrationDialog, setShowMigrationDialog] = useState(false);\n\tconst [migrationState, setMigrationState] = useState<MigrationStatus>(\"idle\");\n\tconst [migrationProgress, setMigrationProgress] = useState(0);\n\tconst [migrationResult, setMigrationResult] =\n\t\tuseState<MigrationResult | null>(null);\n\tconst [error, setError] = useState<string | null>(null);\n\n\t// Migration workflow\n\tconst performMigration = useCallback(async () => {\n\t\ttry {\n\t\t\tsetMigrationState(\"decrypting\");\n\t\t\tsetMigrationProgress(20);\n\t\t\tsetError(null);\n\n\t\t\t// Get current backup\n\t\t\tconst currentBackupStr = localStorage.getItem(\"bigblocksBackup\");\n\t\t\tif (!currentBackupStr) {\n\t\t\t\tthrow new Error(\"No backup found\");\n\t\t\t}\n\n\t\t\tconst currentBackup = JSON.parse(currentBackupStr) as BapMasterBackup;\n\n\t\t\t// Check if already Type 42\n\t\t\tif (isMasterBackupType42(currentBackup)) {\n\t\t\t\tthrow new Error(\"Backup is already using Type 42 format\");\n\t\t\t}\n\n\t\t\t// Decrypt with legacy key\n\t\t\tsetMigrationState(\"decrypting\");\n\t\t\tsetMigrationProgress(40);\n\n\t\t\t// Generate Type 42 key\n\t\t\tsetMigrationState(\"generating\");\n\t\t\tsetMigrationProgress(60);\n\n\t\t\t// Re-encrypt with Type 42\n\t\t\tsetMigrationState(\"encrypting\");\n\t\t\tsetMigrationProgress(80);\n\n\t\t\t// Upgrade backup format\n\t\t\tsetMigrationState(\"upgrading\");\n\t\t\tsetMigrationProgress(90);\n\n\t\t\t// This is just UI demonstration - actual migration would happen in AuthManager\n\t\t\tconst result: MigrationResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tupgradedBackup: JSON.stringify(currentBackup), // Would be the new backup\n\t\t\t\toriginalFormat: \"legacy\",\n\t\t\t\tnewFormat: \"type42\",\n\t\t\t};\n\n\t\t\tsetMigrationProgress(100);\n\t\t\tsetMigrationState(\"success\");\n\t\t\tsetMigrationResult(result);\n\n\t\t\t// Notify parent\n\t\t\tif (onMigrationComplete) {\n\t\t\t\tonMigrationComplete();\n\t\t\t}\n\n\t\t\t// Close dialog after success\n\t\t\tif (showDialog) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tsetShowMigrationDialog(false);\n\t\t\t\t}, 2000);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tsetMigrationState(\"error\");\n\t\t\tconst message = err instanceof Error ? err.message : \"Migration failed\";\n\t\t\tsetError(message);\n\t\t\tif (onError) {\n\t\t\t\tonError(err instanceof Error ? err : new Error(message));\n\t\t\t}\n\t\t}\n\t}, [onMigrationComplete, onError, showDialog]);\n\n\t// Auto-start migration if requested\n\tuseEffect(() => {\n\t\tif (autoStart && hasLocalBackup) {\n\t\t\tperformMigration();\n\t\t}\n\t}, [autoStart, hasLocalBackup, performMigration]);\n\n\t// Reset state when dialog closes\n\tconst handleDialogClose = useCallback(() => {\n\t\tsetShowMigrationDialog(false);\n\t\tsetMigrationState(\"idle\");\n\t\tsetMigrationProgress(0);\n\t\tsetMigrationResult(null);\n\t\tsetError(null);\n\t}, []);\n\n\t// Get current backup info\n\tconst getCurrentBackupInfo = () => {\n\t\tconst backupStr = localStorage.getItem(\"bigblocksBackup\");\n\t\tif (!backupStr) return null;\n\n\t\ttry {\n\t\t\tconst backup = JSON.parse(backupStr) as BapMasterBackup;\n\t\t\treturn {\n\t\t\t\tisLegacy: isBapMasterBackupLegacy(backup),\n\t\t\t\tisType42: isMasterBackupType42(backup),\n\t\t\t\tcreatedAt: backup.createdAt,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t};\n\n\tif (!hasLocalBackup) {\n\t\treturn null;\n\t}\n\n\tconst backupInfo = getCurrentBackupInfo();\n\n\treturn (\n\t\t<Card className={cn(\"p-6\", size === \"3\" && \"p-8\")}>\n\t\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div>\n\t\t\t\t\t<div className=\"flex justify-between items-center mb-3\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<span className=\"text-lg font-bold\">\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<UpdateIcon width=\"20\" height=\"20\" />\n\t\t\t\t\t\t\t\t\tMaster Key Migration\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<MasterKeyBadge tooltip=\"This migration tool requires your legacy master backup\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Badge variant=\"secondary\">Recommended</Badge>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Info Card */}\n\t\t\t\t\t<Card className=\"mb-4\" style={{ backgroundColor: \"var(--accent)\" }}>\n\t\t\t\t\t\t<div className=\"flex items-start gap-3 p-4\">\n\t\t\t\t\t\t\t<InfoCircledIcon\n\t\t\t\t\t\t\t\twidth=\"16\"\n\t\t\t\t\t\t\t\theight=\"16\"\n\t\t\t\t\t\t\t\tstyle={{ color: \"var(--primary)\", marginTop: \"2px\" }}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium mb-1 block\">\n\t\t\t\t\t\t\t\t\tType 42 Master Key Format Available\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tMigrate your legacy master key to the new Type 42 format for\n\t\t\t\t\t\t\t\t\tenhanced security and compatibility with newer BitcoinAuth\n\t\t\t\t\t\t\t\t\tfeatures.\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</div>\n\t\t\t\t\t</Card>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Current Status */}\n\t\t\t\t<div>\n\t\t\t\t\t<h4 className=\"text-sm font-medium mb-2\">Current Backup Status</h4>\n\t\t\t\t\t<div className=\"grid gap-2\">\n\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">Format:</span>\n\t\t\t\t\t\t\t<Badge variant={backupInfo?.isType42 ? \"default\" : \"secondary\"}>\n\t\t\t\t\t\t\t\t{backupInfo?.isType42 ? \"Type 42\" : \"Legacy\"}\n\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{backupInfo?.createdAt && (\n\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">Created:</span>\n\t\t\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t\t\t{new Date(backupInfo.createdAt).toLocaleDateString()}\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)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Warning */}\n\t\t\t\t{backupInfo?.isLegacy && (\n\t\t\t\t\t<WarningCard\n\t\t\t\t\t\ttitle=\"Legacy Format Detected\"\n\t\t\t\t\t\tmessage=\"Your backup is using the legacy format. Migration to Type 42 is recommended for improved security.\"\n\t\t\t\t\t/>\n\t\t\t\t)}\n\n\t\t\t\t{/* Migration Benefits */}\n\t\t\t\t<div>\n\t\t\t\t\t<h4 className=\"text-sm font-medium mb-2\">Migration Benefits</h4>\n\t\t\t\t\t<ul className=\"space-y-2\">\n\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t<CheckCircledIcon\n\t\t\t\t\t\t\t\tclassName=\"text-green-500 mt-0.5\"\n\t\t\t\t\t\t\t\twidth=\"16\"\n\t\t\t\t\t\t\t\theight=\"16\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span className=\"text-sm\">Enhanced key derivation security</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t<CheckCircledIcon\n\t\t\t\t\t\t\t\tclassName=\"text-green-500 mt-0.5\"\n\t\t\t\t\t\t\t\twidth=\"16\"\n\t\t\t\t\t\t\t\theight=\"16\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t\tCompatibility with latest features\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t<CheckCircledIcon\n\t\t\t\t\t\t\t\tclassName=\"text-green-500 mt-0.5\"\n\t\t\t\t\t\t\t\twidth=\"16\"\n\t\t\t\t\t\t\t\theight=\"16\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span className=\"text-sm\">Future-proof backup format</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Action Button */}\n\t\t\t\t<div className=\"flex justify-end gap-3 mt-4\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant={backupInfo?.isType42 ? \"outline\" : \"default\"}\n\t\t\t\t\t\tonClick={() => setShowMigrationDialog(true)}\n\t\t\t\t\t\tdisabled={backupInfo?.isType42}\n\t\t\t\t\t>\n\t\t\t\t\t\t{backupInfo?.isType42 ? \"Already Migrated\" : \"Start Migration\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Migration Dialog */}\n\t\t\t{showDialog && (\n\t\t\t\t<Dialog open={showMigrationDialog} onOpenChange={handleDialogClose}>\n\t\t\t\t\t<DialogContent\n\t\t\t\t\t\tclassName=\"sm:max-w-md\"\n\t\t\t\t\t\tonPointerDownOutside={(e) => e.preventDefault()}\n\t\t\t\t\t>\n\t\t\t\t\t\t<DialogHeader>\n\t\t\t\t\t\t\t<DialogTitle>Master Key Migration</DialogTitle>\n\t\t\t\t\t\t\t<DialogDescription>\n\t\t\t\t\t\t\t\t{migrationState === \"idle\"\n\t\t\t\t\t\t\t\t\t? \"This will migrate your master key to the Type 42 format.\"\n\t\t\t\t\t\t\t\t\t: stateMessages[migrationState]}\n\t\t\t\t\t\t\t</DialogDescription>\n\t\t\t\t\t\t</DialogHeader>\n\n\t\t\t\t\t\t<div className=\"space-y-4 py-4\">\n\t\t\t\t\t\t\t{/* Progress */}\n\t\t\t\t\t\t\t{migrationState !== \"idle\" && migrationState !== \"error\" && (\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Progress value={migrationProgress} />\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\t{migrationProgress}% complete\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)}\n\n\t\t\t\t\t\t\t{/* Error Message */}\n\t\t\t\t\t\t\t{error && (\n\t\t\t\t\t\t\t\t<div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-3\">\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t\t\t<ExclamationTriangleIcon className=\"text-destructive\" />\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm\">{error}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Success Message */}\n\t\t\t\t\t\t\t{migrationState === \"success\" && (\n\t\t\t\t\t\t\t\t<div className=\"rounded-lg border border-green-500/50 bg-green-500/10 p-3\">\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t\t\t<CheckCircledIcon className=\"text-green-500\" />\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t\t\t\t\tMigration completed successfully!\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Warning for idle state */}\n\t\t\t\t\t\t\t{migrationState === \"idle\" && (\n\t\t\t\t\t\t\t\t<WarningCard\n\t\t\t\t\t\t\t\t\ttitle=\"Important\"\n\t\t\t\t\t\t\t\t\tmessage=\"Make sure you have your password ready. The migration process cannot be reversed.\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<DialogFooter>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tonClick={handleDialogClose}\n\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\tmigrationState !== \"idle\" &&\n\t\t\t\t\t\t\t\t\tmigrationState !== \"error\" &&\n\t\t\t\t\t\t\t\t\tmigrationState !== \"success\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tonClick={performMigration}\n\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\tmigrationState !== \"idle\" && migrationState !== \"error\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{migrationState === \"error\" ? \"Retry\" : \"Migrate\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</DialogFooter>\n\t\t\t\t\t</DialogContent>\n\t\t\t\t</Dialog>\n\t\t\t)}\n\t\t</Card>\n\t);\n}\n", "target": "<%- config.aliases.components %>/masterkeymigration.tsx" } ] }