import React, { useState, useRef, useEffect, useCallback } from "react"; import { View, Text, StyleSheet, TouchableOpacity, TextInput, Modal, ActivityIndicator, Platform, FlatList, } from "react-native"; import { useTranslation } from "react-i18next"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { CountryList } from "../../constants/countries"; import { settingApi } from "../../services/api/setting"; import { VerificationCodeInput } from "./VerificationCodeInput"; type ForgotPhonePasswordProps = { visible?: boolean; onClose?: () => void; selectedCountry?: CountryList; phoneNumber?: string; }; export const ForgotPhonePassword = ({ visible = true, onClose = () => {}, selectedCountry, phoneNumber = "" }: ForgotPhonePasswordProps) => { const { t } = useTranslation(); // States const [phoneNum, setPhoneNum] = useState(phoneNumber); const [phoneNumberError, setPhoneNumberError] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [countryList, setCountryList] = useState([]); const [currentCountry, setCurrentCountry] = useState(selectedCountry); const [showCountryModal, setShowCountryModal] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [filteredCountryList, setFilteredCountryList] = useState([]); const [showVerificationModal, setShowVerificationModal] = useState(false); // Refs const phoneInputRef = useRef(null); const searchInputRef = useRef(null); // Load country data if not provided useEffect(() => { if (visible && !currentCountry) { loadCountryData(); } if (visible && selectedCountry) { setCurrentCountry(selectedCountry); } }, [visible, selectedCountry]); // Focus phone input when modal opens useEffect(() => { if (visible) { const timer = setTimeout(() => { if (phoneInputRef.current) { phoneInputRef.current.focus(); } }, 300); return () => clearTimeout(timer); } }, [visible]); // Load country data const loadCountryData = async () => { try { const res = await settingApi.getSendSmsCountryList(); setCountryList(res); setFilteredCountryList(res); const savedCountry = await AsyncStorage.getItem("@selected_country"); if (savedCountry) { try { const parsedCountry = JSON.parse(savedCountry); const item = res.find(item => item.country === parsedCountry.country); if (item) { setCurrentCountry(item); } else if (res.length > 0) { setCurrentCountry(res[0]); } } catch (e) { console.error("Error parsing stored country", e); if (res.length > 0) { setCurrentCountry(res[0]); } } } else if (res.length > 0) { setCurrentCountry(res[0]); } } catch (error) { console.error("Failed to load country data", error); } }; // Filter countries based on search query useEffect(() => { if (searchQuery.trim() === "") { setFilteredCountryList(countryList); } else { const query = searchQuery.toLowerCase(); const filtered = countryList.filter( country => country.name_en.toLowerCase().includes(query) || country.country.toString().includes(query) ); setFilteredCountryList(filtered); } }, [searchQuery, countryList]); // Focus search input when country modal opens useEffect(() => { if (showCountryModal && searchInputRef.current) { const timer = setTimeout(() => { searchInputRef.current?.focus(); }, 300); return () => clearTimeout(timer); } }, [showCountryModal]); // Clear search when modal closes useEffect(() => { if (!showCountryModal) { setSearchQuery(""); } }, [showCountryModal]); // Set initial phone number value if provided useEffect(() => { if (phoneNumber) { setPhoneNum(phoneNumber); } }, [phoneNumber]); // Handle country selection const handleCountrySelect = (country: CountryList) => { setCurrentCountry(country); setShowCountryModal(false); // Save selected country to AsyncStorage AsyncStorage.setItem("@selected_country", JSON.stringify(country)); // Reset validation errors when country changes if (phoneNum) { setPhoneNumberError(!validatePhoneNumber(phoneNum, country)); } }; // Render country list item - with performance optimization const renderCountryItem = useCallback( ({ item }: { item: CountryList }) => ( handleCountrySelect(item)} activeOpacity={0.7} > +{item.country} {item.name_en} {/* Add checkmark for selected country */} {currentCountry && currentCountry.country === item.country && ( )} ), [currentCountry] ); // Validate phone number const validatePhoneNumber = (phoneNum: string, country = currentCountry) => { if (!country || !country.valid_digits || country.valid_digits.length === 0) { return true; // No validation if no valid_digits available } return country.valid_digits.includes(phoneNum.length); }; // Handle phone number change const handlePhoneNumberChange = (text: string) => { setPhoneNum(text); if (text.length > 0) { setPhoneNumberError(!validatePhoneNumber(text)); } else { setPhoneNumberError(false); } setError(null); }; // Handle submit const handleSubmit = async () => { if (!validatePhoneNumber(phoneNum)) { setPhoneNumberError(true); return; } try { setLoading(true); // TODO: Replace with actual API call to send reset code // For example: await userApi.sendPhonePasswordResetCode({ phone: phoneNum, country: currentCountry?.country }); // Log reset method console.log("Password reset method: Phone"); try { // Store reset method in AsyncStorage or other storage await AsyncStorage.setItem("@password_reset_method", "phone"); } catch (storageError) { console.error("Failed to store reset method:", storageError); } // Simulate API call success setTimeout(() => { setLoading(false); setShowVerificationModal(true); }, 1500); } catch (error) { setLoading(false); setError('Failed to send reset code. Please try again.'); } }; // Handle verification code submission const handleVerifyCode = async (code: string): Promise => { // TODO: Replace with actual API call to verify code // For example: return await userApi.verifyPhonePasswordResetCode({ // phone: phoneNum, // country: currentCountry?.country, // code: code // }); // Simulate verification for demo return new Promise((resolve) => { setTimeout(() => { // For demo: code "123456" is valid, others are invalid resolve(code === "123456"); }, 1500); }); }; // Handle resend code const handleResendCode = async (): Promise => { // TODO: Replace with actual API call to resend code // For example: await userApi.sendPhonePasswordResetCode({ // phone: phoneNum, // country: currentCountry?.country // }); // Simulate resend for demo return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1500); }); }; // Handle reset password const handleResetPassword = async (password: string): Promise => { // TODO: Replace with actual API call to reset password // For example: return await userApi.resetPhonePassword({ // phone: phoneNum, // country: currentCountry?.country, // password: password // }); // Simulate API call for demo return new Promise((resolve) => { setTimeout(() => { // On success, close this modal too if (onClose) onClose(); resolve(true); // Always succeed for demo }, 1500); }); }; return ( {t("login.forgotPassword.title")} {t("login.forgotPassword.phoneDescription")} setShowCountryModal(true)} > +{currentCountry?.country || ''} {phoneNum.length > 0 && ( { setPhoneNum(""); setPhoneNumberError(false); setError(null); }} activeOpacity={0.7} > )} {phoneNumberError && ( {t("login.forgotPassword.invalidPhone")} {currentCountry?.valid_digits && `(${t("login.forgotPassword.requiresDigits")}: ${currentCountry.valid_digits.join(', ')})`} )} {error && ( {error} )} {loading ? ( ) : ( {t("login.forgotPassword.submit")} )} {/* Country selection modal */} setShowCountryModal(false)} hardwareAccelerated={true} statusBarTranslucent={true} presentationStyle="overFullScreen" > setShowCountryModal(false)} /> setShowCountryModal(false)} activeOpacity={0.7} > {t("selectCountry")} {/* Country search input */} 🔍 {searchQuery.length > 0 && ( setSearchQuery("")} activeOpacity={0.7} > )} item.country.toString()} style={styles.countryList} showsVerticalScrollIndicator={false} removeClippedSubviews={true} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={10} getItemLayout={(data, index) => ({ length: 69, offset: 69 * index, index, })} ListEmptyComponent={() => ( {t("noCountriesFound")} )} /> {/* Verification Code Modal */} setShowVerificationModal(false)} phoneNumber={phoneNum} onVerify={handleVerifyCode} onResend={handleResendCode} onResetPassword={handleResetPassword} /> ); }; const styles = StyleSheet.create({ modalContainer: { flex: 1, backgroundColor: "rgba(0,0,0,0.5)", justifyContent: "flex-end", zIndex: 9999, }, forgotPasswordContainer: { backgroundColor: "#fff", borderTopLeftRadius: 20, borderTopRightRadius: 20, height: "80%", shadowColor: "#000", shadowOffset: { width: 0, height: -2 }, shadowOpacity: 0.1, shadowRadius: 5, elevation: 5, }, forgotPasswordHeader: { flexDirection: "row", alignItems: "center", paddingTop: 20, paddingHorizontal: 16, paddingBottom: 15, borderBottomWidth: 1, borderBottomColor: "#f0f0f0", }, forgotPasswordCloseButton: { padding: 8, width: 36, height: 36, justifyContent: "center", alignItems: "center", }, forgotPasswordCloseButtonText: { fontSize: 18, color: "#000", }, forgotPasswordTitle: { flex: 1, fontSize: 18, fontWeight: "600", color: "#000", textAlign: "center", marginRight: 36, }, forgotPasswordContent: { padding: 20, paddingBottom: Platform.OS === "ios" ? 50 : 30, }, forgotPasswordDescription: { fontSize: 14, color: "#333", marginBottom: 20, lineHeight: 20, }, phoneInputContainer: { flexDirection: "row", alignItems: "center", borderWidth: 1, borderColor: "#E1E1E1", borderRadius: 25, height: 50, marginBottom: 20, position: "relative", }, countryCodeButton: { flexDirection: "row", alignItems: "center", paddingHorizontal: 12, height: "100%", minWidth: 80, justifyContent: "space-between", }, countryCodeText: { fontSize: 15, color: "#333", }, countryCodeArrow: { fontSize: 10, color: "#666", marginLeft: 4, }, phoneInputDivider: { width: 1, height: "60%", backgroundColor: "#E1E1E1", }, phoneInput: { flex: 1, height: "100%", paddingLeft: 10, paddingRight: 36, fontSize: 16, }, phoneClearButton: { position: "absolute", right: 12, top: "50%", transform: [{ translateY: -12 }], height: 24, width: 24, justifyContent: "center", alignItems: "center", }, phoneClearButtonText: { fontSize: 16, color: "#999", fontWeight: "500", textAlign: "center", }, phoneNumberErrorText: { color: "#FF3B30", fontSize: 14, marginTop: -12, marginBottom: 16, paddingHorizontal: 5, }, errorText: { color: "#FF3B30", fontSize: 14, marginTop: -12, marginBottom: 16, paddingHorizontal: 5, }, submitButton: { height: 50, backgroundColor: "#0039CB", borderRadius: 25, justifyContent: "center", alignItems: "center", marginTop: 20, }, disabledButton: { backgroundColor: "#CCCCCC", }, submitButtonText: { color: "#fff", fontSize: 16, fontWeight: "600", }, // Country modal styles countryModalContainer: { flex: 1, backgroundColor: "rgba(0,0,0,0.5)", justifyContent: "flex-end", zIndex: 999, }, countryModalOverlay: { flex: 1, backgroundColor: "transparent", }, countryModalContent: { backgroundColor: "#fff", borderTopLeftRadius: 20, borderTopRightRadius: 20, height: "80%", maxHeight: "80%", shadowColor: "#000", shadowOffset: { width: 0, height: -2 }, shadowOpacity: 0.1, shadowRadius: 5, elevation: 5, }, modalHandleContainer: { width: "100%", alignItems: "center", paddingTop: 12, paddingBottom: 8, }, modalHandle: { width: 40, height: 4, backgroundColor: "#E0E0E0", borderRadius: 2, }, countryModalHeader: { flexDirection: "row", alignItems: "center", padding: 16, borderBottomWidth: 1, borderBottomColor: "#E5E5E5", }, countryModalCloseButton: { padding: 4, }, countryModalCloseButtonText: { fontSize: 18, color: "#999", }, countryModalTitle: { flex: 1, fontSize: 18, fontWeight: "600", textAlign: "center", marginRight: 24, }, countryList: { padding: 8, }, countryItem: { flexDirection: "row", alignItems: "center", padding: 16, borderBottomWidth: 1, borderBottomColor: "#F0F0F0", }, countryItemContent: { flex: 1, flexDirection: "row", alignItems: "center", }, countryName: { fontSize: 16, color: "#333", marginLeft: 10, }, countryCode: { fontSize: 15, color: "#666", width: 40, textAlign: "center", }, checkmark: { fontSize: 20, color: "#0066FF", fontWeight: "bold", marginRight: 10, }, // Search styles searchContainer: { paddingHorizontal: 16, paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: "#F0F0F0", }, searchInputContainer: { flexDirection: "row", alignItems: "center", backgroundColor: "#F5F5F5", borderRadius: 20, paddingHorizontal: 12, height: 40, position: "relative", }, searchIcon: { fontSize: 16, marginRight: 8, color: "#999", }, searchInput: { flex: 1, height: "100%", fontSize: 15, color: "#333", paddingRight: 30, }, searchClearButton: { position: "absolute", right: 12, height: 20, width: 20, justifyContent: "center", alignItems: "center", }, searchClearButtonText: { fontSize: 14, color: "#999", fontWeight: "500", }, emptyResultContainer: { padding: 20, alignItems: "center", }, emptyResultText: { fontSize: 16, color: "#999", textAlign: "center", }, });