import React, { useState, useRef, useCallback } from "react"; import { View, Text, StyleSheet, TouchableOpacity, TextInput, FlatList, Keyboard, Modal, Alert, Platform, Image, ActivityIndicator, StyleProp, ViewStyle, TextStyle, ImageStyle } from "react-native"; import { useTranslation } from "react-i18next"; import { useNavigation } from "@react-navigation/native"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { userApi } from "../../services/api/userApi"; import { settingApi } from "../../services/api/setting"; import useUserStore from "../../store/user"; import { CountryList } from "../../constants/countries"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; type RootStackParamList = { Login: undefined; EmailLogin: undefined; MainTabs: { screen: string }; Google: undefined; Home: { screen: string }; ForgotPassword: undefined; }; type PhoneLoginModalProps = { visible: boolean; onClose: () => void; }; // Define the styles type type Styles = { phoneLoginContainer: ViewStyle; phoneLoginHeader: ViewStyle; phoneLoginCloseButton: ViewStyle; phoneLoginCloseButtonText: TextStyle; phoneLoginTitle: TextStyle; phoneLoginContent: ViewStyle; phoneInputContainer: ViewStyle; countrySelectRow: ViewStyle; countrySelectContent: ViewStyle; countryFlag: TextStyle; flag: ImageStyle; countryName: TextStyle; countryCode: TextStyle; downArrow: TextStyle; phoneInput: TextStyle; phoneClearButton: ViewStyle; phoneClearButtonText: TextStyle; phoneInfoText: TextStyle; phoneContinueButton: ViewStyle; phoneDisabledButton: ViewStyle; phoneContinueButtonText: TextStyle; passwordInput: TextStyle; passwordErrorContainer: ViewStyle; passwordErrorIcon: ViewStyle; passwordErrorIconText: TextStyle; passwordErrorText: TextStyle; forgotPasswordLink: ViewStyle; forgotPasswordLinkText: TextStyle; countryModalContainer: ViewStyle; countryModalOverlay: ViewStyle; countryModalContent: ViewStyle; modalHandleContainer: ViewStyle; modalHandle: ViewStyle; countryModalHeader: ViewStyle; countryModalCloseButton: ViewStyle; countryModalCloseButtonText: TextStyle; countryModalTitle: TextStyle; countryList: ViewStyle; countryItem: ViewStyle; countryItemFlag: TextStyle; countryItemContent: ViewStyle; countryItemName: TextStyle; countryItemCode: TextStyle; modalContainer: ViewStyle; checkmark: TextStyle; countryCodeButton: ViewStyle; countryCodeFlag: ImageStyle; countryCodeText: TextStyle; countryCodeArrow: TextStyle; phoneInputDivider: ViewStyle; phoneNumberErrorText: TextStyle; }; const PhoneLoginModal = ({ visible, onClose }: PhoneLoginModalProps) => { const { t } = useTranslation(); const navigation = useNavigation>(); const { setSettings, setUser } = useUserStore(); // Phone login state const [phoneNumber, setPhoneNumber] = useState(""); const [password, setPassword] = useState(""); const [passwordError, setPasswordError] = useState(false); const [phoneNumberError, setPhoneNumberError] = useState(false); const [showCountryModal, setShowCountryModal] = useState(false); // Countries const [countryList, setCountryList] = useState([]); const [selectedCountry, setSelectedCountry] = useState(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // Validate phone number against valid_digits const validatePhoneNumber = (phoneNum: string) => { if (!selectedCountry || !selectedCountry.valid_digits || selectedCountry.valid_digits.length === 0) { return true; // No validation if no valid_digits available } return selectedCountry.valid_digits.includes(phoneNum.length); }; // Handle phone number input with validation const handlePhoneNumberChange = (text: string) => { setPhoneNumber(text); if (text.length > 0) { setPhoneNumberError(!validatePhoneNumber(text)); // todo 防止重复关闭 } else { setPhoneNumberError(false); } setPasswordError(false); }; // useEffect替换为普通函数 React.useEffect(() => { if (visible) { loadData(); } }, [visible]); // 加载国家列表和选中的国家 const loadData = async () => { try { const res = await settingApi.getSendSmsCountryList(); console.log(res); setCountryList(res); const savedCountry = await AsyncStorage.getItem("@selected_country"); if (savedCountry) { try { const parsedCountry = JSON.parse(savedCountry); console.log(parsedCountry); const item = res.find(item => item.country === parsedCountry.country); console.log(item); setSelectedCountry(item); } catch (e) { console.error("Error parsing stored country", e); } } } catch (error) { console.error("Failed to load country data", error); } }; // Select country const handleCountrySelect = (country: CountryList) => { setSelectedCountry(country); setShowCountryModal(false); // Save selected country to AsyncStorage AsyncStorage.setItem("@selected_country", JSON.stringify(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 */} {selectedCountry && selectedCountry.country === item.country && ( )} ), [selectedCountry] ); // 忘记密码 const handleForgotPassword = () => { onClose(); navigation.navigate("ForgotPassword"); }; // Handle phone login const handlePhoneContinue = async () => { // Validate phone number before proceeding // todo 防止重复关闭 if (!validatePhoneNumber(phoneNumber)) { setPhoneNumberError(true); return; } const params = { grant_type: "password", username: phoneNumber, password: password, client_id: "2", client_secret: "", scope: "", }; try { setLoading(true); const res = await userApi.login(params); if (res.access_token) { const token = res.token_type + " " + res.access_token; await AsyncStorage.setItem("token", token); const data = await settingApi.postFirstLogin(221); setSettings(data); const user = await userApi.getProfile(); setUser(user); setLoading(false); navigation.navigate("MainTabs", { screen: "Home" }); onClose(); } } catch (error) { setError('用户名或密码错误') setLoading(false); setPasswordError(true); } }; // 引用输入框 const phoneInputRef = useRef(null); // 主动弹出键盘 const focusPhoneInput = () => { if (phoneInputRef.current) { phoneInputRef.current.focus(); } }; React.useEffect(() => { if (visible) { // 当模态框显示时,等待动画完成后主动弹出键盘 const timer = setTimeout(() => { focusPhoneInput(); }, 300); return () => clearTimeout(timer); } }, [visible]); return ( onClose()} statusBarTranslucent={true} > onClose()} activeOpacity={0.7} > {t("logInOrSignUp")} {/* Country Selector Row - Now removed as we integrated it into the phone input */} setShowCountryModal(true)} > {/* {selectedCountry?.name_en && ( )} */} +{selectedCountry?.country} {phoneNumber.length > 0 ? ( { setPhoneNumber(""); setPhoneNumberError(false); setPasswordError(false); }} activeOpacity={0.7} > ) : ( ⌨️ )} {/* Phone number error message */} {phoneNumberError && ( {t("invalidPhoneNumber")} {selectedCountry?.valid_digits && `(${t("requiresDigits")}: ${selectedCountry.valid_digits.join(', ')})`} )} {/* Password input */} { setPassword(text); setPasswordError(false); }} secureTextEntry={true} autoCapitalize="none" /> {passwordError && ( ! )} {/* Password error message */} {passwordError && ( <> {error} {t("forgotPassword")} )} {loading ? ( ) : ( {t("continue")} )} {/* Country selection modal */} setShowCountryModal(false)} hardwareAccelerated={true} statusBarTranslucent={true} presentationStyle="overFullScreen" > setShowCountryModal(false)} /> setShowCountryModal(false)} activeOpacity={0.7} > {t("selectCountry")} 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, })} /> ); }; const styles = StyleSheet.create({ phoneLoginContainer: { backgroundColor: "#fff", borderTopLeftRadius: 20, borderTopRightRadius: 20, height: "80%", shadowColor: "#000", shadowOffset: { width: 0, height: -2 }, shadowOpacity: 0.1, shadowRadius: 5, elevation: 5, }, phoneLoginHeader: { flexDirection: "row", alignItems: "center", paddingTop: 20, paddingHorizontal: 16, paddingBottom: 15, borderBottomWidth: 1, borderBottomColor: "#f0f0f0", }, phoneLoginCloseButton: { padding: 8, width: 36, height: 36, justifyContent: "center", alignItems: "center", }, phoneLoginCloseButtonText: { fontSize: 18, color: "#000", }, phoneLoginTitle: { flex: 1, fontSize: 18, fontWeight: "600", color: "#000", textAlign: "center", marginRight: 36, }, phoneLoginContent: { padding: 20, paddingBottom: Platform.OS === "ios" ? 50 : 30, flex: 1, }, phoneInputContainer: { flexDirection: "row", alignItems: "center", borderWidth: 1, borderColor: "#E1E1E1", borderRadius: 25, height: 50, marginBottom: 20, position: "relative", }, countrySelectRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", borderWidth: 1, borderColor: "#E1E1E1", borderRadius: 25, height: 50, marginBottom: 15, paddingHorizontal: 16, backgroundColor: "#F7F7F7", }, countrySelectContent: { flexDirection: "row", alignItems: "center", flex: 1, }, countryFlag: { fontSize: 22, marginRight: 12, }, flag: { width: 24, height: 24, marginRight: 16, }, countryName: { fontSize: 16, color: "#333", marginRight: 10, flex: 1, }, countryCode: { fontSize: 15, color: "#666", marginRight: 10, width: 40, textAlign: "center", }, downArrow: { fontSize: 12, color: "#666", }, countryCodeButton: { flexDirection: "row", alignItems: "center", paddingHorizontal: 12, height: "100%", minWidth: 80, justifyContent: "space-between", }, countryCodeFlag: { width: 20, height: 20, marginRight: 4, }, countryCodeText: { fontSize: 15, color: "#333", }, countryCodeArrow: { fontSize: 10, color: "#666", marginLeft: 2, }, 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", }, phoneInfoText: { fontSize: 14, color: "#666", marginBottom: 32, lineHeight: 20, }, phoneContinueButton: { height: 50, backgroundColor: "#0039CB", borderRadius: 25, justifyContent: "center", alignItems: "center", }, phoneDisabledButton: { backgroundColor: "#CCCCCC", }, phoneContinueButtonText: { color: "#fff", fontSize: 16, fontWeight: "600", }, // Phone number error phoneNumberErrorText: { color: "#FF3B30", fontSize: 14, marginTop: -12, marginBottom: 16, paddingHorizontal: 5, }, // Password styling passwordInput: { flex: 1, height: "100%", paddingHorizontal: 16, fontSize: 16, }, passwordErrorContainer: { borderColor: "#FF3B30", }, passwordErrorIcon: { position: "absolute", right: 12, top: "50%", transform: [{ translateY: -12 }], width: 24, height: 24, backgroundColor: "#FF3B30", borderRadius: 12, justifyContent: "center", alignItems: "center", }, passwordErrorIconText: { color: "white", fontWeight: "bold", fontSize: 16, }, passwordErrorText: { color: "#FF3B30", fontSize: 14, marginTop: -12, marginBottom: 16, paddingHorizontal: 5, }, forgotPasswordLink: { alignItems: "center", marginTop: 5, }, forgotPasswordLinkText: { color: "#0066FF", fontSize: 14, }, // 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", }, countryItemFlag: { fontSize: 24, marginRight: 16, }, countryItemContent: { flex: 1, flexDirection: "row", alignItems: "center", }, countryItemName: { fontSize: 16, color: "#333", }, countryItemCode: { fontSize: 14, color: "#666", marginTop: 4, }, modalContainer: { flex: 1, backgroundColor: "rgba(0,0,0,0.5)", justifyContent: "flex-end", zIndex: 999, }, checkmark: { fontSize: 20, color: "#0066FF", fontWeight: "bold", marginRight: 10, }, }); export default PhoneLoginModal;