import { useState, useEffect } from 'react'; import { Outlet, useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { Menu } from 'lucide-react'; import { useTheme } from '@/hooks/useTheme'; import { useAuth } from '@/hooks/useAuth'; import { usePrefetch } from '@/hooks/usePrefetch'; import { AlertsProvider } from '@/hooks/useAlerts'; import { LockProvider, useLock } from '@/hooks/useLock'; import { NotificationProvider } from '@/hooks/useNotifications'; import { Button } from '@/components/ui/button'; import Sidebar from './Sidebar'; import AppAmbientBackground from './AppAmbientBackground'; import LockOverlay from './LockOverlay'; import NotificationToaster from '@/components/notifications/NotificationToaster'; function AppContent({ mobileOpen, setMobileOpen }: { mobileOpen: boolean; setMobileOpen: (v: boolean) => void; }) { const { isLocked, isLockResolved } = useLock(); const { hasPasskeys } = useAuth(); const navigate = useNavigate(); usePrefetch(isLockResolved && !isLocked); // Post-login passkey prompt — show once per session if user has no passkeys useEffect(() => { if ( isLockResolved && !isLocked && !hasPasskeys && window.PublicKeyCredential && !sessionStorage.getItem('passkey-prompt-shown') ) { sessionStorage.setItem('passkey-prompt-shown', '1'); toast.info('Simplify your login \u2014 set up a passkey in Settings', { duration: 8000, action: { label: 'Set up', onClick: () => navigate('/settings?tab=security'), }, }); } }, [isLockResolved, isLocked, hasPasskeys, navigate]); const [collapsed, setCollapsed] = useState(() => { try { return JSON.parse(localStorage.getItem('umbra-sidebar-collapsed') || 'false'); } catch { return false; } }); // Don't render any content until we know the lock state if (!isLockResolved || isLocked) { return ( <>
{isLockResolved && } ); } return ( <>
{ const next = !collapsed; setCollapsed(next); localStorage.setItem('umbra-sidebar-collapsed', JSON.stringify(next)); }} mobileOpen={mobileOpen} onMobileClose={() => setMobileOpen(false)} />
{/* Mobile header */}

UMBRA

); } export default function AppLayout() { useTheme(); const [mobileOpen, setMobileOpen] = useState(false); return ( ); }