diff --git a/frontend/src/components/layout/AppLayout.tsx b/frontend/src/components/layout/AppLayout.tsx index aae73f7..0d9909f 100644 --- a/frontend/src/components/layout/AppLayout.tsx +++ b/frontend/src/components/layout/AppLayout.tsx @@ -1,7 +1,9 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Outlet } 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'; @@ -17,7 +19,20 @@ function AppContent({ mobileOpen, setMobileOpen }: { setMobileOpen: (v: boolean) => void; }) { const { isLocked, isLockResolved } = useLock(); + const { hasPasskeys } = useAuth(); 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 }); + } + }, [isLockResolved, isLocked, hasPasskeys]); const [collapsed, setCollapsed] = useState(() => { try { return JSON.parse(localStorage.getItem('umbra-sidebar-collapsed') || 'false'); } catch { return false; }