import React, { useState, useCallback, useRef, useEffect } from 'react'; import { View, Text, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, TouchableOpacity, TextInput, ActivityIndicator, } from 'react-native'; import { router, useLocalSearchParams } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useAuth } from '@/contexts/AuthContext'; 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, skipOtp } = useLocalSearchParams<{ email: string; skipOtp: string }>(); const { verifyOtp, requestOtp, isLoading: authLoading, error: authError, clearError } = 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); // Handle skip OTP (dev mode) useEffect(() => { if (skipOtp === '1' && email) { handleAutoLogin(); } }, [skipOtp, email]); const handleAutoLogin = async () => { setIsLoading(true); try { const success = await verifyOtp(email!, '000000'); // Bypass code if (success) { router.replace('/(tabs)'); } else { setError('Auto-login failed'); } } catch (err) { setError('Auto-login failed'); } finally { setIsLoading(false); } }; // Focus input on mount (only if not skipping OTP) useEffect(() => { if (skipOtp !== '1') { setTimeout(() => { inputRef.current?.focus(); }, 100); } }, [skipOtp]); // 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 success = await verifyOtp(email!, codeToVerify); if (success) { router.replace('/(tabs)'); } else { setError(authError?.message || 'Invalid code. Please try again.'); setCode(''); } } catch (err) { setError('Something went wrong. Please try again.'); setCode(''); } finally { setIsLoading(false); } }, [code, email, verifyOtp, authError]); const handleResend = useCallback(async () => { if (resendCountdown > 0) return; setIsResending(true); setError(null); try { const result = await requestOtp(email!); if (result.success) { setResendCountdown(60); // 60 seconds cooldown setCode(''); } else { setError('Failed to resend code'); } } catch (err) { setError('Failed to resend code. Please try again.'); } finally { setIsResending(false); } }, [email, resendCountdown, requestOtp]); // 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; }; // Show loading screen for auto-login if (skipOtp === '1') { return ( Signing in... ); } 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 */}