import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { AlertTriangle, Download, CheckCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { backupConfirmedKey } from '@/lib/backup'; import { isSignupOpen } from '@/lib/uiFlags'; import { useTranslation } from '@/contexts/language'; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from '@/components/ui/sheet'; // Session-scoped snooze key. "Remind me later" used to set a PERMANENT // localStorage flag, which silently turned into "never remind again" — a real // safety hole. We now snooze only for the current browser session, so the // reminder returns on the next visit until the user actually saves their login. // Only the per-account `backup-confirmed:` flag (set when they back up) // stops it for good. const BACKUP_SNOOZE_KEY = 'backup-reminder-snoozed-session'; /** * Gently-persistent backup reminder shown after signup. * Encourages users to save their recovery code (nsec) so they never lose access. * Non-blocking: one tap to back up, one tap to say "already done", or snooze for * this session. It keeps coming back each session until backup is confirmed. * Uses a bottom sheet for a good experience on both phone and desktop. */ export function BackupReminder() { const { user } = useCurrentUser(); const [confirmed, setConfirmed] = useLocalStorage(backupConfirmedKey(user?.pubkey), false); const [showReminder, setShowReminder] = useState(false); const { t } = useTranslation(); const navigate = useNavigate(); useEffect(() => { if (!user) { setShowReminder(false); return; } // Wait out the signup dialog (the key is minted mid-flow), then show unless // snoozed for this session or already confirmed. let cancelled = false; let timer: ReturnType; const attempt = () => { if (cancelled) return; if (isSignupOpen()) { timer = setTimeout(attempt, 2000); return; } const snoozed = sessionStorage.getItem(BACKUP_SNOOZE_KEY) === 'true'; setShowReminder(!snoozed && !confirmed); }; timer = setTimeout(attempt, 2000); return () => { cancelled = true; clearTimeout(timer); }; }, [user, confirmed]); const handleDismiss = () => { // Snooze for this session only — it returns next visit until backup is done. try { sessionStorage.setItem(BACKUP_SNOOZE_KEY, 'true'); } catch { // sessionStorage can throw in strict privacy modes — safe to ignore. } setShowReminder(false); }; const handleConfirmBackup = () => { setConfirmed(true); setShowReminder(false); }; const handleBackupNow = () => { // Client-side nav (HashRouter). window.location.href forced a full reload to // a non-hash path the router couldn't match — it dropped the user on a blank // page instead of the security settings. navigate() keeps it in-app. setShowReminder(false); // SettingsPage deep-links via ?section=… (NOT ?tab=…) — using the wrong // param silently dropped the user on the settings root instead of Security. navigate('/settings?section=security'); }; if (!showReminder) return null; return ( { if (!open) handleDismiss(); }}> e.preventDefault()} >
{t('backup.title')} {t('backup.subtitle')}
{/* Warning message */}

{t('backup.message')}

⚠️ {t('backup.warning')}

{/* Action buttons - stacked on mobile */}
{/* Dismiss link */}
); }