diff --git a/src/components/settings/AccountProfileTab.tsx b/src/components/settings/AccountProfileTab.tsx index 6268bfb3..e68fb984 100644 --- a/src/components/settings/AccountProfileTab.tsx +++ b/src/components/settings/AccountProfileTab.tsx @@ -14,11 +14,12 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, import { useToast } from '@/hooks/use-toast'; import { useAuth } from '@/hooks/useAuth'; import { supabase } from '@/integrations/supabase/client'; -import { User, Upload, Trash2, Mail } from 'lucide-react'; +import { User, Upload, Trash2, Mail, AlertCircle } from 'lucide-react'; import { PhotoUpload } from '@/components/upload/PhotoUpload'; import { notificationService } from '@/lib/notificationService'; import { EmailChangeDialog } from './EmailChangeDialog'; import { Badge } from '@/components/ui/badge'; +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; const profileSchema = z.object({ username: z.string().min(3).max(30).regex(/^[a-zA-Z0-9_-]+$/), @@ -32,7 +33,7 @@ const profileSchema = z.object({ type ProfileFormData = z.infer; export function AccountProfileTab() { - const { user, profile, refreshProfile } = useAuth(); + const { user, profile, refreshProfile, pendingEmail } = useAuth(); const { toast } = useToast(); const [loading, setLoading] = useState(false); const [avatarLoading, setAvatarLoading] = useState(false); @@ -275,13 +276,29 @@ export function AccountProfileTab() {

Account Information

+ {pendingEmail && ( + + + Email Change in Progress + + You have a pending email change to {pendingEmail}. + Please check both your current email ({user?.email}) and new email ({pendingEmail}) for confirmation links. + Both must be confirmed to complete the change. + + + )} +

Email Address

{user?.email}

- {user?.email_confirmed_at ? ( + {pendingEmail ? ( + + Change Pending + + ) : user?.email_confirmed_at ? ( Verified ) : ( Pending Verification @@ -292,6 +309,7 @@ export function AccountProfileTab() { variant="outline" size="sm" onClick={() => setShowEmailDialog(true)} + disabled={!!pendingEmail} > Change Email diff --git a/src/components/settings/EmailChangeDialog.tsx b/src/components/settings/EmailChangeDialog.tsx index 8d398c89..161c8ecf 100644 --- a/src/components/settings/EmailChangeDialog.tsx +++ b/src/components/settings/EmailChangeDialog.tsx @@ -20,7 +20,7 @@ import { } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; -import { useToast } from '@/hooks/use-toast'; +import { toast } from 'sonner'; import { supabase } from '@/integrations/supabase/client'; import { Loader2, Mail, CheckCircle2, AlertCircle } from 'lucide-react'; import { TurnstileCaptcha } from '@/components/auth/TurnstileCaptcha'; @@ -49,7 +49,6 @@ interface EmailChangeDialogProps { } export function EmailChangeDialog({ open, onOpenChange, currentEmail, userId }: EmailChangeDialogProps) { - const { toast } = useToast(); const { theme } = useTheme(); const [step, setStep] = useState('verification'); const [loading, setLoading] = useState(false); @@ -79,19 +78,15 @@ export function EmailChangeDialog({ open, onOpenChange, currentEmail, userId }: const onSubmit = async (data: EmailFormData) => { if (!captchaToken) { - toast({ - title: 'CAPTCHA Required', + toast.error('CAPTCHA Required', { description: 'Please complete the CAPTCHA verification.', - variant: 'destructive' }); return; } if (data.newEmail.toLowerCase() === currentEmail.toLowerCase()) { - toast({ - title: 'Same Email', + toast.error('Same Email', { description: 'The new email is the same as your current email.', - variant: 'destructive' }); return; } @@ -162,13 +157,15 @@ export function EmailChangeDialog({ open, onOpenChange, currentEmail, userId }: }); } + toast.success('Email change initiated', { + description: 'Check both email addresses for confirmation links.', + }); + setStep('success'); } catch (error: any) { console.error('Email change error:', error); - toast({ - title: 'Error', - description: error.message || 'Failed to change email address', - variant: 'destructive' + toast.error('Failed to change email', { + description: error.message || 'Please try again.', }); } finally { setLoading(false); diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index 30503319..6279718c 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -8,6 +8,7 @@ interface AuthContextType { session: Session | null; profile: Profile | null; loading: boolean; + pendingEmail: string | null; signOut: () => Promise; refreshProfile: () => Promise; } @@ -19,6 +20,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const [session, setSession] = useState(null); const [profile, setProfile] = useState(null); const [loading, setLoading] = useState(true); + const [pendingEmail, setPendingEmail] = useState(null); const fetchProfile = async (userId: string) => { try { @@ -63,6 +65,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setSession(session); setUser(session?.user ?? null); + // Track pending email changes + const newEmail = session?.user?.new_email; + setPendingEmail(newEmail ?? null); + if (session?.user) { // Defer profile fetch to avoid deadlock setTimeout(() => { @@ -91,6 +97,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { session, profile, loading, + pendingEmail, signOut, refreshProfile, };