import React, { useEffect, useRef, useState } from 'react'; import { Pressable, StyleSheet, Text } from 'react-native'; import { useLocalSearchParams } from 'expo-router'; import { api } from '@/api'; import { useAuth } from '@/auth/AuthContext'; import { useT } from '@/i18n'; import { AuthShell } from '@/components/AuthShell'; import { Button, Field } from '@/components/ui'; import { CODE_LENGTH, CodeField } from '@/components/CodeField'; import { colors, fontSize, spacing } from '@/theme'; /** Matches the backend's resend throttle. */ const RESEND_COOLDOWN = 60; export default function VerifyEmailScreen() { const { email } = useLocalSearchParams<{ email: string }>(); const { signInWithVerification } = useAuth(); const t = useT(); const [code, setCode] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [cooldown, setCooldown] = useState(RESEND_COOLDOWN); const submitted = useRef(false); useEffect(() => { if (cooldown <= 0) return; const id = setTimeout(() => setCooldown((s) => s - 1), 1000); return () => clearTimeout(id); }, [cooldown]); async function onVerify(value = code) { if (value.length < CODE_LENGTH || submitted.current) return; submitted.current = true; setError(null); setLoading(true); try { // The session starts here; the root layout moves us into the app. await signInWithVerification(String(email), value); } catch (e: any) { // Wrong and expired are distinct, so the message can say which. setError(e?.code === 'code_expired' ? t('verify.expired') : t('verify.invalid')); setCode(''); } finally { submitted.current = false; setLoading(false); } } async function onResend() { if (cooldown > 0) return; setError(null); setCooldown(RESEND_COOLDOWN); try { await api.auth.resendVerification(String(email)); } catch { // The endpoint always succeeds by design. } } return (