import { useState, useEffect, useRef } from 'react'; import { View, Text, TextInput, TouchableOpacity, 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'; import { KeyboardAwareScreen } from '../../components/KeyboardAwareScreen'; 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 ResetOtpScreen() { const router = useRouter(); const { t } = useTranslation(); const params = useLocalSearchParams<{ email: string }>(); const email = decodeURIComponent(params.email ?? ''); const { verifyOtp, resetPasswordForEmail } = useAuthStore(); const [digits, setDigits] = useState(Array(OTP_LENGTH).fill('')); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const [resendCooldown, setResendCooldown] = useState(0); const inputs = useRef>([]); useEffect(() => { if (!email) { router.replace('/(auth)/forgot-password'); } }, [email]); useEffect(() => { if (resendCooldown <= 0) return; const timer = setInterval(() => { setResendCooldown((c) => { if (c <= 1) { clearInterval(timer); return 0; } return c - 1; }); }, 1000); return () => clearInterval(timer); }, [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) return; setError(null); setLoading(true); const res = await verifyOtp(email, otp, 'recovery'); setLoading(false); if (res.error) { setError(res.error); setDigits(Array(OTP_LENGTH).fill('')); inputs.current[0]?.focus(); return; } router.replace(`/(auth)/new-password?email=${encodeURIComponent(email)}`); }; const resend = async () => { if (resendCooldown > 0) return; setError(null); const res = await resetPasswordForEmail(email); if (res.error) { setError(res.error); return; } setResendCooldown(60); }; return ( {t('auth.resetOtpTitle')} {t('auth.resetOtpLine1')}{'\n'} {email} {'\n'}{t('auth.resetOtpLine2')} {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} selectTextOnFocus /> ))} {error && ( {error} )} {loading ? ( ) : ( {t('auth.resetOtpConfirmBtn')} )} 0 || loading} activeOpacity={0.7} 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.resetOtpResend')} router.push('/(auth)/forgot-password')} activeOpacity={0.7} className="py-3 items-center mt-2" > {t('auth.resetOtpBackToForgot')} ); }