import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import { Download, Copy, CheckCircle, Eye, EyeOff, ArrowRight, RefreshCw, Shield, ExternalLink } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Card, CardContent } from '@/components/ui/card'; import { toast } from '@/hooks/useToast'; import { useLoginActions } from '@/hooks/useLoginActions'; import { nip19 } from 'nostr-tools'; import { cn } from '@/lib/utils'; import { generateMnemonicPhrase, privateKeyFromMnemonic, mnemonicToWords } from '@/lib/nip06'; import { useTranslation } from '@/contexts/language'; // qrcode imported lazily in async function interface MnemonicSignupDialogProps { isOpen: boolean; onClose: () => void; onSwitchToNsec: () => void; } const MnemonicSignupDialog: React.FC = ({ isOpen, onClose, onSwitchToNsec }) => { const { t } = useTranslation(); const [step, setStep] = useState<'generate' | 'verify' | 'complete'>('generate'); const [mnemonic, setMnemonic] = useState(''); const [words, setWords] = useState([]); const [showMnemonic, setShowMnemonic] = useState(true); const [verifyIndices, setVerifyIndices] = useState([]); const [verifyInputs, setVerifyInputs] = useState>({}); const [, setIsVerified] = useState(false); const login = useLoginActions(); // Generate new mnemonic const handleGenerate = () => { const newMnemonic = generateMnemonicPhrase(128); // 12 words setMnemonic(newMnemonic); setWords(mnemonicToWords(newMnemonic)); // Pick 3 random words for verification const indices: number[] = []; while (indices.length < 3) { const rand = Math.floor(Math.random() * 12); if (!indices.includes(rand)) indices.push(rand); } setVerifyIndices(indices.sort((a, b) => a - b)); toast({ title: t('settingsUi.mnemonic.generatedTitle'), description: t('settingsUi.mnemonic.generatedDesc'), }); }; // Download mnemonic as text file const handleDownload = () => { try { const blob = new Blob([mnemonic], { type: 'text/plain; charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'nostr-recovery-phrase.txt'; a.style.display = 'none'; document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); document.body.removeChild(a); toast({ title: t('settingsUi.mnemonic.savedTitle'), description: t('settingsUi.mnemonic.savedDesc'), }); } catch { toast({ title: t('settingsUi.mnemonic.downloadFailedTitle'), description: t('settingsUi.mnemonic.downloadFailedDesc'), variant: 'destructive', }); } }; // Copy to clipboard const handleCopy = () => { navigator.clipboard.writeText(mnemonic); toast({ title: t('settingsUi.mnemonic.copiedTitle'), description: t('settingsUi.mnemonic.copiedDesc'), }); }; // Generate QR code const handleQR = async () => { try { const { default: QRCode } = await import('qrcode'); const qrDataUrl = await QRCode.toDataURL(mnemonic, { width: 300, margin: 2, }); // Create a new window with the QR code const win = window.open('', '_blank'); if (win) { win.document.write(` ${t('settingsUi.mnemonic.qrWindowTitle')}

🔐 ${t('settingsUi.mnemonic.qrHeading')}

${t('settingsUi.mnemonic.qrAlt')}

⚠️ ${t('settingsUi.mnemonic.qrWarning')}

`); win.document.close(); } } catch { toast({ title: t('settingsUi.mnemonic.qrFailedTitle'), description: t('settingsUi.mnemonic.qrFailedDesc'), variant: 'destructive', }); } }; // Proceed to verification const handleProceedToVerify = () => { setStep('verify'); }; // Verify the words const handleVerify = () => { const isCorrect = verifyIndices.every((idx) => { return verifyInputs[idx]?.toLowerCase().trim() === words[idx]; }); if (isCorrect) { setIsVerified(true); // Log in with the mnemonic try { const privateKey = privateKeyFromMnemonic(mnemonic); const nsec = nip19.nsecEncode(privateKey); login.nsec(nsec); setStep('complete'); toast({ title: t('settingsUi.mnemonic.accountCreatedTitle'), description: t('settingsUi.mnemonic.accountCreatedDesc'), }); // Mark signup as completed localStorage.setItem('signup_completed', Date.now().toString()); setTimeout(() => { onClose(); }, 2000); } catch { toast({ title: t('settingsUi.mnemonic.loginFailedTitle'), description: t('settingsUi.mnemonic.loginFailedDesc'), variant: 'destructive', }); } } else { toast({ title: t('settingsUi.mnemonic.verifyFailedTitle'), description: t('settingsUi.mnemonic.verifyFailedDesc'), variant: 'destructive', }); } }; // Reset and start over const handleStartOver = () => { setMnemonic(''); setWords([]); setVerifyInputs({}); setIsVerified(false); setStep('generate'); }; return ( !open && onClose()}> {t('settingsUi.mnemonic.titlePrefix')}{' '} {t('settingsUi.mnemonic.titleSuffix')} {/* STEP 1: Generate & Backup */} {step === 'generate' && (
{!mnemonic ? (
🔑

{t('settingsUi.mnemonic.twelveWordTitle')}

{t('settingsUi.mnemonic.twelveWordDesc')}

) : ( <> {/* Recovery phrase display */}

{t('settingsUi.mnemonic.yourPhrase')}

{words.map((word, idx) => (
{idx + 1}. {showMnemonic ? word : '••••••'}
))}
{/* Warning */}

⚠️ {t('settingsUi.mnemonic.criticalWarning')}

  • ✍️ {t('settingsUi.mnemonic.warnWriteStrong')} {t('settingsUi.mnemonic.warnWrite')}
  • 🔒 {t('settingsUi.mnemonic.warnNeverShareStrong')} {t('settingsUi.mnemonic.warnNeverShare')}
  • 💾 {t('settingsUi.mnemonic.warnBackupsStrong')} {t('settingsUi.mnemonic.warnBackups')}
  • ❌ {t('settingsUi.mnemonic.warnAnyoneStrong')} {t('settingsUi.mnemonic.warnAnyone')}
  • 🚫 {t('settingsUi.mnemonic.warnLoseStrong')} {t('settingsUi.mnemonic.warnLose')}
{/* Continue button */} )}
)} {/* STEP 2: Verify */} {step === 'verify' && (

{t('settingsUi.mnemonic.verifyTitle')}

{t('settingsUi.mnemonic.verifyDesc')}

{verifyIndices.map((idx) => (
setVerifyInputs({ ...verifyInputs, [idx]: e.target.value })} placeholder={t('settingsUi.mnemonic.wordPlaceholder', { n: idx + 1 })} className="w-full px-4 py-3 rounded-lg bg-gray-900 border-2 border-[#ccff00]/20 text-white placeholder-gray-600 focus:border-[#ccff00]/50 focus:outline-none font-mono" />
))}
)} {/* STEP 3: Complete */} {step === 'complete' && (
✅

{t('settingsUi.mnemonic.completeTitle')}

{t('settingsUi.mnemonic.completeDesc')}

{/* Next Steps - Signer Recommendation */}

{t('settingsUi.mnemonic.signerTitle')}

{t('settingsUi.mnemonic.signerSubtitle')}

{t('settingsUi.mnemonic.signerDesc')}

)}
); }; export default MnemonicSignupDialog;