SkateSpots/src/components/BackupReminder.tsx
shakespeare.diy e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.

Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
  all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)

Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00

144 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react';
import { AlertTriangle, X, Download, CheckCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { useLocalStorage } from '@/hooks/useLocalStorage';
import { useTranslation } from '@/contexts/LanguageContext';
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
const BACKUP_REMINDER_KEY = 'backup-reminder-dismissed';
const BACKUP_CONFIRMED_KEY = 'backup-confirmed';
/**
* Persistent backup reminder that shows after signup
* Encourages users to backup their recovery phrase or nsec
* Uses a bottom sheet for better mobile experience
*/
export function BackupReminder() {
const { user } = useCurrentUser();
const [dismissed, setDismissed] = useLocalStorage(BACKUP_REMINDER_KEY, false);
const [confirmed, setConfirmed] = useLocalStorage(BACKUP_CONFIRMED_KEY, false);
const [showReminder, setShowReminder] = useState(false);
const { t } = useTranslation();
useEffect(() => {
if (!user) {
setShowReminder(false);
return;
}
// Show reminder if:
// 1. User is logged in
// 2. Not dismissed
// 3. Not confirmed they backed up
// Add a small delay so it doesn't immediately pop up on login
const timer = setTimeout(() => {
const shouldShow = !dismissed && !confirmed;
setShowReminder(shouldShow);
}, 2000);
return () => clearTimeout(timer);
}, [user, dismissed, confirmed]);
const handleDismiss = () => {
setDismissed(true);
setShowReminder(false);
};
const handleConfirmBackup = () => {
setConfirmed(true);
setShowReminder(false);
};
const handleBackupNow = () => {
window.location.href = '/settings?tab=security';
};
if (!showReminder) return null;
return (
<Sheet open={showReminder} onOpenChange={setShowReminder}>
<SheetContent
side="bottom"
className="rounded-t-2xl border-t-4 border-amber-500 bg-gradient-to-b from-amber-950/90 to-black/95 backdrop-blur-lg"
onInteractOutside={(e) => e.preventDefault()}
>
<SheetHeader className="text-left">
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="flex-shrink-0 w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-amber-500/20 flex items-center justify-center">
<AlertTriangle className="h-5 w-5 sm:h-6 sm:w-6 text-amber-500" />
</div>
<div className="flex-1">
<SheetTitle className="text-lg sm:text-xl font-bold text-amber-400">
{t('backup.title')}
</SheetTitle>
<SheetDescription className="text-xs sm:text-sm text-gray-400">
{t('backup.subtitle')}
</SheetDescription>
</div>
</div>
<Button
variant="ghost"
size="icon"
onClick={handleDismiss}
className="ml-2 -mt-1 text-gray-500 hover:text-gray-300"
>
<X className="h-5 w-5" />
</Button>
</div>
</SheetHeader>
<div className="mt-4 sm:mt-6 space-y-4">
{/* Warning message */}
<div className="p-3 sm:p-4 rounded-xl bg-amber-500/10 border border-amber-500/30">
<p className="text-sm sm:text-base text-gray-200 leading-relaxed">
{t('backup.message')}
</p>
<p className="text-sm sm:text-base text-gray-300 mt-2">
⚠️ {t('backup.warning')}
</p>
</div>
{/* Action buttons - stacked on mobile */}
<div className="flex flex-col sm:flex-row gap-3">
<Button
onClick={handleBackupNow}
className="flex-1 h-12 sm:h-11 text-sm sm:text-base font-semibold bg-amber-500 hover:bg-amber-600 text-black"
size="lg"
>
<Download className="mr-2 h-5 w-5" />
{t('backup.backupNow')}
</Button>
<Button
onClick={handleConfirmBackup}
variant="outline"
className="flex-1 h-12 sm:h-11 text-sm sm:text-base border-amber-500/50 text-amber-400 hover:bg-amber-500/20"
size="lg"
>
<CheckCircle className="mr-2 h-5 w-5" />
{t('backup.confirmed')}
</Button>
</div>
{/* Dismiss link */}
<div className="text-center pt-1">
<button
onClick={handleDismiss}
className="text-xs text-gray-500 hover:text-gray-400 underline"
>
{t('backup.remindLater')}
</button>
</div>
</div>
</SheetContent>
</Sheet>
);
}