import { useState, useEffect, useRef } from 'react'; import { View, Text, TextInput, Pressable, KeyboardAvoidingView, Platform, ActivityIndicator, } from 'react-native'; import { useRouter, useLocalSearchParams } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useTranslation } from 'react-i18next'; import { useAuthStore } from '../../stores/auth'; const OTP_LENGTH = 6; const OTP_INPUT_STYLE = { fontSize: 20, fontFamily: 'Nunito_700Bold', color: '#0a0a0a', textAlign: 'center' as const, width: 48, height: 56, borderRadius: 12, }; export default function ConfirmOtpScreen() { const router = useRouter(); const { t } = useTranslation(); const params = useLocalSearchParams<{ email: string }>(); const email = decodeURIComponent(params.email ?? ''); const { verifyOtp, resendConfirmation } = useAuthStore(); const [digits, setDigits] = useState(Array(OTP_LENGTH).fill('')); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const [loading, setLoading] = useState(false); const [resendCooldown, setResendCooldown] = useState(0); const inputs = useRef>([]); useEffect(() => { if (!email) { router.replace('/signup'); } }, [email]); useEffect(() => { if (resendCooldown <= 0) return; const t = setInterval(() => { setResendCooldown((c) => { if (c <= 1) { clearInterval(t); return 0; } return c - 1; }); }, 1000); return () => clearInterval(t); }, [resendCooldown]); const otp = digits.join(''); const handleDigit = (value: string, index: number) => { if (value.length === OTP_LENGTH && /^\d+$/.test(value)) { const next = value.split(''); setDigits(next); inputs.current[OTP_LENGTH - 1]?.focus(); return; } const digit = value.replace(/\D/g, '').slice(-1); const next = [...digits]; next[index] = digit; setDigits(next); if (digit && index < OTP_LENGTH - 1) { inputs.current[index + 1]?.focus(); } }; const handleKeyPress = (key: string, index: number) => { if (key === 'Backspace' && !digits[index] && index > 0) { inputs.current[index - 1]?.focus(); } }; const verify = async () => { if (otp.length < OTP_LENGTH || loading || success) return; setError(null); setLoading(true); const res = await verifyOtp(email, otp); setLoading(false); if (res.error) { setError(res.error); setDigits(Array(OTP_LENGTH).fill('')); inputs.current[0]?.focus(); return; } setSuccess(true); router.replace('/(app)'); }; const resend = async () => { if (resendCooldown > 0) return; setError(null); const res = await resendConfirmation(email); if (res.error) { setError(res.error); return; } setResendCooldown(60); }; return ( {/* Header */} {t('auth.confirmEmailTitle')} {t('auth.confirmEmailLine1')}{'\n'} {email} {t('auth.confirmEmailLine2') ? `\n${t('auth.confirmEmailLine2')}` : ''} {/* OTP Input */} {digits.map((digit, index) => ( { inputs.current[index] = ref; }} style={[ OTP_INPUT_STYLE, { backgroundColor: '#f5f5f5', borderWidth: 2, borderColor: digit ? '#f59e0b' : '#e5e5e5', }, ]} value={digit} onChangeText={(val) => handleDigit(val, index)} onKeyPress={({ nativeEvent }) => handleKeyPress(nativeEvent.key, index)} keyboardType="number-pad" maxLength={OTP_LENGTH} editable={!loading && !success} selectTextOnFocus /> ))} {error && ( {error} )} {success && ( {t('auth.confirmed')} )} {loading ? ( ) : ( {t('auth.confirmBtn')} )} 0 || loading} className="py-3 items-center" > {t('auth.noCode')}{' '} 0 ? 'text-neutral-400' : 'text-rebreak-500'} style={{ fontFamily: 'Nunito_600SemiBold' }}> {resendCooldown > 0 ? t('auth.resendCooldown', { seconds: resendCooldown }) : t('auth.resend')} router.back()} className="py-3 items-center mt-2" > {t('auth.backToSignup')} ); }