import Error from "@/assets/icons/svg/error.svg"; import { fetchApi } from "@/lib/server-api-util"; import { User } from "@/types/user"; import OTPInputView from '@twotalltotems/react-native-otp-input'; import { router } from "expo-router"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Animated, TextInput as RNTextInput, StyleSheet, TouchableOpacity, View } from "react-native"; import { useAuth } from "../../contexts/auth-context"; import { ThemedText } from "../ThemedText"; interface CodeProps { phone: string; } const Code = ({ phone }: CodeProps) => { const { t } = useTranslation(); const { login } = useAuth(); const [isLoading, setIsLoading] = useState(false); const refs = useRef>(Array(6).fill(null)); const shakeAnim = useRef(new Animated.Value(0)).current; const [code, setCode] = useState([]); const [error, setError] = useState(''); const focusNext = (index: number, value: string) => { if (value && index < 5) { refs?.current?.[index + 1]?.focus(); } }; const focusPrevious = (index: number, key: string) => { if (key === 'Backspace' && index > 0 && !code[index]) { refs?.current?.[index - 1]?.focus(); } }; const handleCodeChange = (text: string, index: number) => { setError(''); const newCode = [...code]; // Handle pasted code from SMS or autofill if ((text.length === 6 || text.length > 1) && /^\d+$/.test(text)) { const digits = text.split('').slice(0, 6); // Ensure we only take first 6 digits setCode(digits); refs.current[5]?.focus(); // Focus on the last input // Auto-submit if we have exactly 6 digits if (digits.length === 6) { handleTelLogin(); } return; } // Handle single digit input if (text.length <= 1 && /^\d?$/.test(text)) { newCode[index] = text; setCode(newCode); // Auto-submit if this is the last digit if (text && index === 5) { handleTelLogin(); } else if (text) { focusNext(index, text); } } }; const sendVerificationCode = async () => { try { // 发送验证码 await fetchApi(`/iam/veritification-code`, { method: 'POST', body: JSON.stringify({ phone: phone }), }) } catch (error) { } } const handleTelLogin = async () => { setError(''); if (!code.join('')) { setError(t("auth.telLogin.codeRequired", { ns: 'login' })); return; } // 如果验证码不是六位,提示错误 if (code.join('').length !== 6) { setError(t("auth.telLogin.codeInvalid", { ns: 'login' })); return; } setIsLoading(true); setCountdown(60); try { await fetchApi(`/iam/login/phone-login`, { method: 'POST', body: JSON.stringify({ phone: phone, code: code.join('') }), }).then((res) => { login(res, res.access_token || '') router.replace('/user-message') }).catch((error) => { setError(t("auth.telLogin.codeVaild", { ns: 'login' })); }) setIsLoading(false); } catch (error) { setIsLoading(false); } } // 60s倒计时 const [countdown, setCountdown] = useState(0); useEffect(() => { if (countdown > 0) { const timer = setTimeout(() => setCountdown(countdown - 1), 1000); return () => clearTimeout(timer); } }, [countdown]); return ( {t("auth.telLogin.codeTitle", { ns: 'login' })} {t("auth.telLogin.secondTitle", { ns: 'login' })} {phone} { setCode([code]); }} onCodeFilled={() => { handleTelLogin() }} code={code.join('')} autoFocusOnLoad={false} codeInputFieldStyle={styles.underlineStyleBase} codeInputHighlightStyle={styles.underlineStyleHighLighted} style={styles.otpContainer} placeholderCharacter="-" placeholderTextColor="#AC7E35" /> {error} {t("auth.telLogin.sendAgain", { ns: 'login' })} { if (countdown <= 0) { sendVerificationCode() } }}> 0 && styles.disabledResendText ]}> {countdown > 0 ? `${countdown}s${t("auth.telLogin.resend", { ns: 'login' })}` : t("auth.telLogin.resend", { ns: 'login' })} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FFFFFF', }, contentContainer: { flex: 1, justifyContent: 'center', }, headerContainer: { alignItems: 'center', marginBottom: 16, }, title: { fontSize: 24, fontWeight: '600', marginBottom: 8, paddingTop: 4, color: '#111827', }, subtitle: { fontSize: 16, color: '#4B5563', textAlign: 'center', marginBottom: 4, }, phoneNumber: { fontSize: 16, fontWeight: '500', color: '#E2793F', }, otpContainer: { width: '100%', height: 80, }, underlineStyleBase: { width: 50, height: 50, borderWidth: 0, borderRadius: 16, fontSize: 18, color: '#000000', textAlign: 'center', backgroundColor: '#FFF8DE', }, underlineStyleHighLighted: { borderColor: '#E2793F', backgroundColor: '#FFF8DE', borderWidth: 2, }, errorContainer: { width: '100%', flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center', }, errorText: { fontSize: 16, fontWeight: '500', color: '#E2793F', marginLeft: 8, }, footerContainer: { flexDirection: 'row', justifyContent: 'center', marginTop: 8, }, footerText: { color: '#6B7280', }, resendText: { color: '#E2793F', fontWeight: '500', marginLeft: 4, }, disabledResendText: { color: '#9CA3AF', }, }); export default Code;