import React, { useState, useCallback, useRef, useEffect } from 'react'; import { View, Text, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, TouchableOpacity, TextInput, } from 'react-native'; import { router, useLocalSearchParams } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useAuth } from '@/contexts/AuthContext'; import { api } from '@/services/api'; import { Button } from '@/components/ui/Button'; import { ErrorMessage } from '@/components/ui/ErrorMessage'; import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme'; const CODE_LENGTH = 6; export default function VerifyOTPScreen() { const { email, isNewUser } = useLocalSearchParams<{ email: string; isNewUser: string }>(); const { refreshAuth } = useAuth(); const [code, setCode] = useState(''); const [isLoading, setIsLoading] = useState(false); const [isResending, setIsResending] = useState(false); const [error, setError] = useState(null); const [resendCountdown, setResendCountdown] = useState(0); const inputRef = useRef(null); // Focus input on mount useEffect(() => { setTimeout(() => { inputRef.current?.focus(); }, 100); }, []); // Countdown timer for resend useEffect(() => { if (resendCountdown > 0) { const timer = setTimeout(() => setResendCountdown(resendCountdown - 1), 1000); return () => clearTimeout(timer); } }, [resendCountdown]); const handleCodeChange = (text: string) => { // Only allow digits const digits = text.replace(/\D/g, '').slice(0, CODE_LENGTH); setCode(digits); setError(null); // Auto-submit when code is complete if (digits.length === CODE_LENGTH) { handleVerify(digits); } }; const handleVerify = useCallback(async (verifyCode?: string) => { const codeToVerify = verifyCode || code; if (codeToVerify.length !== CODE_LENGTH) { setError('Please enter the 6-digit code'); return; } setIsLoading(true); setError(null); try { const response = await api.verifyOTP(email!, codeToVerify); if (response.ok && response.data) { // Refresh auth state await refreshAuth(); // Check if new user needs to complete profile const user = response.data.user; if (!user.firstName || !user.lastName) { // New user - go to complete profile router.replace({ pathname: '/(auth)/complete-profile', params: { email: email! }, }); } else { // Existing user - go to main app router.replace('/(tabs)'); } } else { setError(response.error?.message || 'Invalid code. Please try again.'); setCode(''); } } catch (err) { setError('Something went wrong. Please try again.'); setCode(''); } finally { setIsLoading(false); } }, [code, email, refreshAuth]); const handleResend = useCallback(async () => { if (resendCountdown > 0) return; setIsResending(true); setError(null); try { const response = await api.requestOTP(email!); if (response.ok) { setResendCountdown(60); // 60 seconds cooldown setCode(''); } else { setError(response.error?.message || 'Failed to resend code'); } } catch (err) { setError('Failed to resend code. Please try again.'); } finally { setIsResending(false); } }, [email, resendCountdown]); // Render code input boxes const renderCodeBoxes = () => { const boxes = []; for (let i = 0; i < CODE_LENGTH; i++) { const isActive = i === code.length; const isFilled = i < code.length; boxes.push( {code[i] || ''} ); } return boxes; }; return ( {/* Back Button */} router.back()}> {/* Header */} Check your email We sent a verification code to {email} {/* Error */} {error && ( setError(null)} /> )} {/* Code Input */} inputRef.current?.focus()} > {renderCodeBoxes()} {/* Hidden actual input */} {/* Verify Button */}