|
|
|
|
import React, { useEffect, useState, useRef } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
View,
|
|
|
|
|
Text,
|
|
|
|
|
StyleSheet,
|
|
|
|
|
TouchableOpacity,
|
|
|
|
|
StatusBar,
|
|
|
|
|
Platform,
|
|
|
|
|
BackHandler,
|
|
|
|
|
TextInput,
|
|
|
|
|
Animated,
|
|
|
|
|
Easing,
|
|
|
|
|
FlatList,
|
|
|
|
|
Keyboard,
|
|
|
|
|
Modal,
|
|
|
|
|
InteractionManager,
|
|
|
|
|
} from 'react-native';
|
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
|
import { useNavigation } from '@react-navigation/native';
|
|
|
|
|
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|
|
|
|
import { RootStackParamList } from '../navigation/types';
|
|
|
|
|
import { Country, countries } from '../constants/countries';
|
|
|
|
|
import { useAuth } from '../contexts/AuthContext';
|
|
|
|
|
|
|
|
|
|
// 常见邮箱后缀列表
|
|
|
|
|
const EMAIL_DOMAINS = [
|
|
|
|
|
'gmail.com',
|
|
|
|
|
'yahoo.com',
|
|
|
|
|
'hotmail.com',
|
|
|
|
|
'outlook.com',
|
|
|
|
|
'icloud.com',
|
|
|
|
|
'mail.com',
|
|
|
|
|
'protonmail.com',
|
|
|
|
|
'qq.com',
|
|
|
|
|
'163.com',
|
|
|
|
|
'126.com',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
type LoginScreenProps = {
|
|
|
|
|
onClose?: () => void;
|
|
|
|
|
isModal?: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
|
|
|
|
const { login } = useAuth();
|
|
|
|
|
|
|
|
|
|
// 邮箱登录状态
|
|
|
|
|
const [showEmailLogin, setShowEmailLogin] = useState(false);
|
|
|
|
|
const [email, setEmail] = useState('');
|
|
|
|
|
const [emailPassword, setEmailPassword] = useState('');
|
|
|
|
|
const [emailPasswordError, setEmailPasswordError] = useState(false);
|
|
|
|
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
|
|
|
const [suggestions, setSuggestions] = useState<string[]>([]);
|
|
|
|
|
|
|
|
|
|
// 手机号登录状态
|
|
|
|
|
const [showPhoneLogin, setShowPhoneLogin] = useState(false);
|
|
|
|
|
const [phoneNumber, setPhoneNumber] = useState('');
|
|
|
|
|
const [password, setPassword] = useState('');
|
|
|
|
|
const [passwordError, setPasswordError] = useState(false);
|
|
|
|
|
const [showCountryModal, setShowCountryModal] = useState(false);
|
|
|
|
|
const [selectedCountry, setSelectedCountry] = useState<Country>({
|
|
|
|
|
name: 'Republic of Congo',
|
|
|
|
|
code: 'CG',
|
|
|
|
|
flag: '🇨🇬',
|
|
|
|
|
userCount: 300000,
|
|
|
|
|
phoneCode: '+242'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 动画值
|
|
|
|
|
const emailSlideAnim = useRef(new Animated.Value(400)).current;
|
|
|
|
|
const phoneSlideAnim = useRef(new Animated.Value(400)).current;
|
|
|
|
|
const [emailLoginMounted, setEmailLoginMounted] = useState(false);
|
|
|
|
|
const [phoneLoginMounted, setPhoneLoginMounted] = useState(false);
|
|
|
|
|
|
|
|
|
|
// 处理邮箱输入变化,生成邮箱建议
|
|
|
|
|
const handleEmailChange = (text: string) => {
|
|
|
|
|
setEmail(text);
|
|
|
|
|
|
|
|
|
|
// 检查是否包含@符号
|
|
|
|
|
if (text.includes('@')) {
|
|
|
|
|
const [username, domain] = text.split('@');
|
|
|
|
|
|
|
|
|
|
if (domain) {
|
|
|
|
|
// 如果已经输入了部分域名,过滤匹配的域名
|
|
|
|
|
const filteredDomains = EMAIL_DOMAINS.filter(item =>
|
|
|
|
|
item.toLowerCase().startsWith(domain.toLowerCase())
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 生成完整的邮箱建议列表
|
|
|
|
|
const emailSuggestions = filteredDomains.map(d => `${username}@${d}`);
|
|
|
|
|
setSuggestions(emailSuggestions);
|
|
|
|
|
setShowSuggestions(emailSuggestions.length > 0);
|
|
|
|
|
} else {
|
|
|
|
|
// 如果只输入了@,显示所有域名建议
|
|
|
|
|
const emailSuggestions = EMAIL_DOMAINS.map(d => `${username}@${d}`);
|
|
|
|
|
setSuggestions(emailSuggestions);
|
|
|
|
|
setShowSuggestions(true);
|
|
|
|
|
}
|
|
|
|
|
} else if (text.length > 0) {
|
|
|
|
|
// 没有@符号但有输入内容,显示常见邮箱后缀建议
|
|
|
|
|
const emailSuggestions = EMAIL_DOMAINS.map(d => `${text}@${d}`);
|
|
|
|
|
setSuggestions(emailSuggestions);
|
|
|
|
|
setShowSuggestions(true);
|
|
|
|
|
} else {
|
|
|
|
|
// 输入为空,不显示建议
|
|
|
|
|
setShowSuggestions(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 选择一个邮箱建议
|
|
|
|
|
const handleSelectSuggestion = (suggestion: string) => {
|
|
|
|
|
setEmail(suggestion);
|
|
|
|
|
setShowSuggestions(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 验证邮箱格式
|
|
|
|
|
const isValidEmail = (email: string): boolean => {
|
|
|
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
|
|
return emailRegex.test(email);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 渲染单个邮箱建议项
|
|
|
|
|
const renderSuggestionItem = ({ item }: { item: string }) => (
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.suggestionItem}
|
|
|
|
|
onPress={() => handleSelectSuggestion(item)}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.suggestionText}>{item}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 选择国家
|
|
|
|
|
const handleCountrySelect = (country: Country) => {
|
|
|
|
|
setSelectedCountry(country);
|
|
|
|
|
setShowCountryModal(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 渲染国家列表项 - 添加性能优化
|
|
|
|
|
const renderCountryItem = React.useCallback(({ item }: { item: Country }) => (
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.countryItem}
|
|
|
|
|
onPress={() => handleCountrySelect(item)}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.countryItemFlag}>{item.flag}</Text>
|
|
|
|
|
<View style={styles.countryItemContent}>
|
|
|
|
|
<Text style={styles.countryItemName}>{item.name}</Text>
|
|
|
|
|
<Text style={styles.countryItemCode}>{item.phoneCode}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
), []);
|
|
|
|
|
|
|
|
|
|
// 添加Android返回按钮处理
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const backAction = () => {
|
|
|
|
|
if (showEmailLogin) {
|
|
|
|
|
closeEmailLogin();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (showPhoneLogin) {
|
|
|
|
|
closePhoneLogin();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (showCountryModal) {
|
|
|
|
|
setShowCountryModal(false);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
handleClose();
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const backHandler = BackHandler.addEventListener(
|
|
|
|
|
'hardwareBackPress',
|
|
|
|
|
backAction
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return () => backHandler.remove();
|
|
|
|
|
}, [showEmailLogin, showPhoneLogin, showCountryModal]);
|
|
|
|
|
|
|
|
|
|
// 初始化动画配置
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// React Native Animated API不支持直接设置useNativeDriver
|
|
|
|
|
// 这些配置会在动画函数中使用
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 打开邮箱登录面板
|
|
|
|
|
const openEmailLogin = () => {
|
|
|
|
|
emailSlideAnim.setValue(400);
|
|
|
|
|
setEmailLoginMounted(true);
|
|
|
|
|
|
|
|
|
|
// 等待组件挂载后启动动画
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
setShowEmailLogin(true);
|
|
|
|
|
Animated.spring(emailSlideAnim, {
|
|
|
|
|
toValue: 0,
|
|
|
|
|
useNativeDriver: true,
|
|
|
|
|
friction: 8,
|
|
|
|
|
tension: 40,
|
|
|
|
|
restDisplacementThreshold: 0.01,
|
|
|
|
|
restSpeedThreshold: 0.01,
|
|
|
|
|
}).start();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 关闭邮箱登录面板
|
|
|
|
|
const closeEmailLogin = () => {
|
|
|
|
|
Keyboard.dismiss();
|
|
|
|
|
|
|
|
|
|
// 使用更简单的配置减少计算复杂度
|
|
|
|
|
Animated.timing(emailSlideAnim, {
|
|
|
|
|
toValue: 400,
|
|
|
|
|
duration: 200,
|
|
|
|
|
useNativeDriver: true,
|
|
|
|
|
easing: Easing.cubic,
|
|
|
|
|
}).start();
|
|
|
|
|
|
|
|
|
|
// 先隐藏建议列表,减少布局计算
|
|
|
|
|
setShowSuggestions(false);
|
|
|
|
|
|
|
|
|
|
// 使用InteractionManager确保动画完成后再执行耗时操作
|
|
|
|
|
InteractionManager.runAfterInteractions(() => {
|
|
|
|
|
setShowEmailLogin(false);
|
|
|
|
|
setEmail('');
|
|
|
|
|
setEmailPassword('');
|
|
|
|
|
setEmailPasswordError(false);
|
|
|
|
|
|
|
|
|
|
// 延迟卸载组件
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
setEmailLoginMounted(false);
|
|
|
|
|
}, 50);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 打开手机号登录面板
|
|
|
|
|
const openPhoneLogin = () => {
|
|
|
|
|
phoneSlideAnim.setValue(400);
|
|
|
|
|
setPhoneLoginMounted(true);
|
|
|
|
|
|
|
|
|
|
// 等待组件挂载后启动动画
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
setShowPhoneLogin(true);
|
|
|
|
|
Animated.spring(phoneSlideAnim, {
|
|
|
|
|
toValue: 0,
|
|
|
|
|
useNativeDriver: true,
|
|
|
|
|
friction: 8,
|
|
|
|
|
tension: 40,
|
|
|
|
|
restDisplacementThreshold: 0.01,
|
|
|
|
|
restSpeedThreshold: 0.01,
|
|
|
|
|
}).start();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 关闭手机号登录面板
|
|
|
|
|
const closePhoneLogin = () => {
|
|
|
|
|
Keyboard.dismiss();
|
|
|
|
|
|
|
|
|
|
// 使用更简单的配置减少计算复杂度
|
|
|
|
|
Animated.timing(phoneSlideAnim, {
|
|
|
|
|
toValue: 400,
|
|
|
|
|
duration: 200,
|
|
|
|
|
useNativeDriver: true,
|
|
|
|
|
easing: Easing.cubic,
|
|
|
|
|
}).start();
|
|
|
|
|
|
|
|
|
|
// 使用InteractionManager确保动画完成后再执行耗时操作
|
|
|
|
|
InteractionManager.runAfterInteractions(() => {
|
|
|
|
|
setShowPhoneLogin(false);
|
|
|
|
|
setPhoneNumber('');
|
|
|
|
|
|
|
|
|
|
// 延迟卸载组件
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
setPhoneLoginMounted(false);
|
|
|
|
|
}, 50);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 修改关闭按钮处理函数
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
if (isModal && onClose) {
|
|
|
|
|
onClose();
|
|
|
|
|
} else {
|
|
|
|
|
navigation.goBack();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleGoogleLogin = () => {
|
|
|
|
|
// 处理Google登录
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleFacebookLogin = () => {
|
|
|
|
|
// 处理Facebook登录
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleAppleLogin = () => {
|
|
|
|
|
// 处理Apple登录
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleInstagramLogin = () => {
|
|
|
|
|
// 处理Instagram登录
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleEmailLogin = () => {
|
|
|
|
|
// 处理邮箱登录 - 显示邮箱登录面板
|
|
|
|
|
openEmailLogin();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleEmailContinue = async () => {
|
|
|
|
|
if (emailPassword === '123') {
|
|
|
|
|
setEmailPasswordError(false);
|
|
|
|
|
await login();
|
|
|
|
|
closeEmailLogin();
|
|
|
|
|
navigation.replace('MainTabs');
|
|
|
|
|
} else {
|
|
|
|
|
setEmailPasswordError(true);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePhoneLogin = () => {
|
|
|
|
|
// 处理手机号登录 - 显示手机号登录面板
|
|
|
|
|
openPhoneLogin();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePhoneVerificationSuccess = async () => {
|
|
|
|
|
await login();
|
|
|
|
|
closePhoneLogin();
|
|
|
|
|
navigation.replace('MainTabs');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePhoneContinue = async () => {
|
|
|
|
|
if (phoneNumber.trim() && password) {
|
|
|
|
|
if (password === '123') {
|
|
|
|
|
await login();
|
|
|
|
|
closePhoneLogin();
|
|
|
|
|
navigation.replace('MainTabs');
|
|
|
|
|
} else {
|
|
|
|
|
setPasswordError(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleForgotPassword = () => {
|
|
|
|
|
// 处理忘记密码
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<View style={styles.container}>
|
|
|
|
|
<StatusBar barStyle="light-content" backgroundColor="#0066FF" />
|
|
|
|
|
|
|
|
|
|
{/* 顶部蓝色背景区域 */}
|
|
|
|
|
<View style={styles.blueHeader}>
|
|
|
|
|
<Text style={styles.logo}>brainnel</Text>
|
|
|
|
|
<View style={styles.features}>
|
|
|
|
|
<View style={styles.featureItem}>
|
|
|
|
|
<View style={styles.featureIconContainer}>
|
|
|
|
|
<Text style={styles.featureIcon}>💰</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.featureText}>{t('wholesalePrice')}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<View style={styles.featureItem}>
|
|
|
|
|
<View style={styles.featureIconContainer}>
|
|
|
|
|
<Text style={styles.featureIcon}>🚚</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.featureText}>{t('fastShipping')}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 登录区域 */}
|
|
|
|
|
<View style={styles.loginContainer}>
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.closeButton}
|
|
|
|
|
onPress={handleClose}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.closeButtonText}>×</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
<View style={styles.titleContainer}>
|
|
|
|
|
{/* <Text style={styles.title}>Login For brainnel</Text> */}
|
|
|
|
|
<Text style={styles.subtitle}>{t('loginSubtitle')}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 登录按钮 */}
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.loginButton}
|
|
|
|
|
onPress={handleGoogleLogin}
|
|
|
|
|
>
|
|
|
|
|
<View style={styles.loginButtonIcon}>
|
|
|
|
|
<Text>G</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.loginButtonText}>{t('continueWithGoogle')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.loginButton}
|
|
|
|
|
onPress={handleFacebookLogin}
|
|
|
|
|
>
|
|
|
|
|
<View style={[styles.loginButtonIcon, styles.facebookIcon]}>
|
|
|
|
|
<Text style={{color: '#fff'}}>f</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.loginButtonText}>{t('continueWithFacebook')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
{Platform.OS === 'ios' && (
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.loginButton}
|
|
|
|
|
onPress={handleAppleLogin}
|
|
|
|
|
>
|
|
|
|
|
<View style={[styles.loginButtonIcon, styles.appleIconBg]}>
|
|
|
|
|
<Text>🍎</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.loginButtonText}>{t('continueWithApple')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.loginButton}
|
|
|
|
|
onPress={handleInstagramLogin}
|
|
|
|
|
>
|
|
|
|
|
<View style={[styles.loginButtonIcon, styles.instagramIcon]}>
|
|
|
|
|
<Text>📷</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.loginButtonText}>{t('continueWithInstagram')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.loginButton}
|
|
|
|
|
onPress={handleEmailLogin}
|
|
|
|
|
>
|
|
|
|
|
<View style={styles.loginButtonIcon}>
|
|
|
|
|
<Text>✉️</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.loginButtonText}>{t('continueWithEmail')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.loginButton}
|
|
|
|
|
onPress={handlePhoneLogin}
|
|
|
|
|
>
|
|
|
|
|
<View style={styles.loginButtonIcon}>
|
|
|
|
|
<Text>📱</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<Text style={styles.loginButtonText}>{t('continueWithPhone')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
{/* 忘记密码 */}
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.forgotPassword}
|
|
|
|
|
onPress={handleForgotPassword}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.forgotPasswordText}>{t('forgotPassword')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
{/* 服务条款 */}
|
|
|
|
|
<View style={styles.termsContainer}>
|
|
|
|
|
<Text style={styles.terms}>
|
|
|
|
|
{t('termsText')} <Text style={styles.link}>{t('termsOfUse')}</Text>
|
|
|
|
|
</Text>
|
|
|
|
|
<Text style={styles.terms}>
|
|
|
|
|
{t('and')} <Text style={styles.link}>{t('privacyPolicy')}</Text>
|
|
|
|
|
</Text>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 邮箱登录面板 - 从底部滑出 */}
|
|
|
|
|
{emailLoginMounted && (
|
|
|
|
|
<Animated.View
|
|
|
|
|
style={[
|
|
|
|
|
styles.emailLoginContainer,
|
|
|
|
|
{
|
|
|
|
|
transform: [{ translateY: emailSlideAnim }],
|
|
|
|
|
opacity: emailSlideAnim.interpolate({
|
|
|
|
|
inputRange: [0, 400],
|
|
|
|
|
outputRange: [1, 0.7]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
]}
|
|
|
|
|
removeClippedSubviews={true}
|
|
|
|
|
collapsable={false}
|
|
|
|
|
>
|
|
|
|
|
<View style={styles.emailLoginHeader}>
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.emailLoginCloseButton}
|
|
|
|
|
onPress={closeEmailLogin}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.emailLoginCloseButtonText}>✕</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
<Text style={styles.emailLoginTitle}>{t('logInOrSignUp')}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
<View style={styles.emailLoginContent}>
|
|
|
|
|
<View style={styles.emailInputContainer}>
|
|
|
|
|
<TextInput
|
|
|
|
|
style={styles.emailInput}
|
|
|
|
|
placeholder={t('pleaseEnterEmail')}
|
|
|
|
|
value={email}
|
|
|
|
|
onChangeText={(text) => {
|
|
|
|
|
handleEmailChange(text);
|
|
|
|
|
setEmailPasswordError(false);
|
|
|
|
|
}}
|
|
|
|
|
keyboardType="email-address"
|
|
|
|
|
autoCapitalize="none"
|
|
|
|
|
autoCorrect={false}
|
|
|
|
|
autoFocus
|
|
|
|
|
maxLength={50}
|
|
|
|
|
/>
|
|
|
|
|
{email.length > 0 && (
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.emailClearButton}
|
|
|
|
|
onPress={() => {
|
|
|
|
|
setEmail('');
|
|
|
|
|
setShowSuggestions(false);
|
|
|
|
|
setEmailPasswordError(false);
|
|
|
|
|
}}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.emailClearButtonText}>✕</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
)}
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 邮箱后缀建议列表 */}
|
|
|
|
|
{showSuggestions && (
|
|
|
|
|
<View style={styles.suggestionsContainer}>
|
|
|
|
|
<FlatList
|
|
|
|
|
data={suggestions.slice(0, 5)} // 限制最多显示5个建议
|
|
|
|
|
renderItem={renderSuggestionItem}
|
|
|
|
|
keyExtractor={(item) => item}
|
|
|
|
|
style={styles.suggestionsList}
|
|
|
|
|
keyboardShouldPersistTaps="handled"
|
|
|
|
|
removeClippedSubviews={true}
|
|
|
|
|
initialNumToRender={5}
|
|
|
|
|
maxToRenderPerBatch={5}
|
|
|
|
|
windowSize={5}
|
|
|
|
|
getItemLayout={(data, index) => (
|
|
|
|
|
{length: 44, offset: 44 * index, index}
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</View>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 密码输入框 */}
|
|
|
|
|
<View style={[
|
|
|
|
|
styles.phoneInputContainer,
|
|
|
|
|
emailPasswordError && styles.passwordErrorContainer
|
|
|
|
|
]}>
|
|
|
|
|
<TextInput
|
|
|
|
|
style={styles.passwordInput}
|
|
|
|
|
placeholder={t('enterPassword')}
|
|
|
|
|
value={emailPassword}
|
|
|
|
|
onChangeText={(text) => {
|
|
|
|
|
setEmailPassword(text);
|
|
|
|
|
setEmailPasswordError(false);
|
|
|
|
|
}}
|
|
|
|
|
secureTextEntry={true}
|
|
|
|
|
autoCapitalize="none"
|
|
|
|
|
/>
|
|
|
|
|
{emailPasswordError && (
|
|
|
|
|
<View style={styles.passwordErrorIcon}>
|
|
|
|
|
<Text style={styles.passwordErrorIconText}>!</Text>
|
|
|
|
|
</View>
|
|
|
|
|
)}
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 密码错误提示 */}
|
|
|
|
|
{emailPasswordError && (
|
|
|
|
|
<>
|
|
|
|
|
<Text style={styles.passwordErrorText}>
|
|
|
|
|
{t('passwordIncorrect')}
|
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.forgotPasswordLink}
|
|
|
|
|
onPress={handleForgotPassword}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.forgotPasswordLinkText}>
|
|
|
|
|
{t('forgotPassword')}
|
|
|
|
|
</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={[
|
|
|
|
|
styles.emailContinueButton,
|
|
|
|
|
(!isValidEmail(email) || !emailPassword) && styles.emailDisabledButton
|
|
|
|
|
]}
|
|
|
|
|
onPress={handleEmailContinue}
|
|
|
|
|
disabled={!isValidEmail(email) || !emailPassword}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.emailContinueButtonText}>{t('continue')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</View>
|
|
|
|
|
</Animated.View>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 手机号登录面板 - 从底部滑出 */}
|
|
|
|
|
{phoneLoginMounted && (
|
|
|
|
|
<Animated.View
|
|
|
|
|
style={[
|
|
|
|
|
styles.phoneLoginContainer,
|
|
|
|
|
{
|
|
|
|
|
transform: [{ translateY: phoneSlideAnim }],
|
|
|
|
|
opacity: phoneSlideAnim.interpolate({
|
|
|
|
|
inputRange: [0, 400],
|
|
|
|
|
outputRange: [1, 0.7]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
]}
|
|
|
|
|
removeClippedSubviews={true}
|
|
|
|
|
collapsable={false}
|
|
|
|
|
>
|
|
|
|
|
<View style={styles.phoneLoginHeader}>
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.phoneLoginCloseButton}
|
|
|
|
|
onPress={closePhoneLogin}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.phoneLoginCloseButtonText}>✕</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
<Text style={styles.phoneLoginTitle}>{t('logInOrSignUp')}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
<View style={styles.phoneLoginContent}>
|
|
|
|
|
<View style={styles.phoneInputContainer}>
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.countrySelector}
|
|
|
|
|
onPress={() => setShowCountryModal(true)}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.countryFlag}>{selectedCountry.flag}</Text>
|
|
|
|
|
<Text style={styles.countryCode}>{selectedCountry.phoneCode}</Text>
|
|
|
|
|
<Text style={styles.downArrow}>▼</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
|
|
|
|
|
<TextInput
|
|
|
|
|
style={styles.phoneInput}
|
|
|
|
|
placeholder={t('phoneNumber')}
|
|
|
|
|
value={phoneNumber}
|
|
|
|
|
onChangeText={(text) => {
|
|
|
|
|
setPhoneNumber(text);
|
|
|
|
|
setPasswordError(false);
|
|
|
|
|
}}
|
|
|
|
|
keyboardType="phone-pad"
|
|
|
|
|
autoFocus
|
|
|
|
|
maxLength={15}
|
|
|
|
|
/>
|
|
|
|
|
{phoneNumber.length > 0 && (
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.phoneClearButton}
|
|
|
|
|
onPress={() => {
|
|
|
|
|
setPhoneNumber('');
|
|
|
|
|
setPasswordError(false);
|
|
|
|
|
}}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.phoneClearButtonText}>✕</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
)}
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 密码输入框 */}
|
|
|
|
|
<View style={[
|
|
|
|
|
styles.phoneInputContainer,
|
|
|
|
|
passwordError && styles.passwordErrorContainer
|
|
|
|
|
]}>
|
|
|
|
|
<TextInput
|
|
|
|
|
style={styles.passwordInput}
|
|
|
|
|
placeholder={t('enterPassword')}
|
|
|
|
|
value={password}
|
|
|
|
|
onChangeText={(text) => {
|
|
|
|
|
setPassword(text);
|
|
|
|
|
setPasswordError(false);
|
|
|
|
|
}}
|
|
|
|
|
secureTextEntry={true}
|
|
|
|
|
autoCapitalize="none"
|
|
|
|
|
/>
|
|
|
|
|
{passwordError && (
|
|
|
|
|
<View style={styles.passwordErrorIcon}>
|
|
|
|
|
<Text style={styles.passwordErrorIconText}>!</Text>
|
|
|
|
|
</View>
|
|
|
|
|
)}
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
{/* 密码错误提示 */}
|
|
|
|
|
{passwordError && (
|
|
|
|
|
<>
|
|
|
|
|
<Text style={styles.passwordErrorText}>
|
|
|
|
|
{t('passwordIncorrect')}
|
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.forgotPasswordLink}
|
|
|
|
|
onPress={handleForgotPassword}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.forgotPasswordLinkText}>
|
|
|
|
|
{t('forgotPassword')}
|
|
|
|
|
</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Text style={[
|
|
|
|
|
styles.phoneInfoText,
|
|
|
|
|
passwordError && { marginTop: 5 }
|
|
|
|
|
]}>
|
|
|
|
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={[
|
|
|
|
|
styles.phoneContinueButton,
|
|
|
|
|
(!phoneNumber.trim() || !password) && styles.phoneDisabledButton
|
|
|
|
|
]}
|
|
|
|
|
onPress={handlePhoneContinue}
|
|
|
|
|
disabled={!phoneNumber.trim() || !password}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.phoneContinueButtonText}>{t('continue')}</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</View>
|
|
|
|
|
</Animated.View>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 国家选择模态框 */}
|
|
|
|
|
<Modal
|
|
|
|
|
visible={showCountryModal}
|
|
|
|
|
animationType="slide"
|
|
|
|
|
transparent={true}
|
|
|
|
|
onRequestClose={() => setShowCountryModal(false)}
|
|
|
|
|
hardwareAccelerated={true}
|
|
|
|
|
statusBarTranslucent={true}
|
|
|
|
|
presentationStyle="overFullScreen"
|
|
|
|
|
>
|
|
|
|
|
<View style={styles.countryModalContainer}>
|
|
|
|
|
<View style={styles.countryModalContent}>
|
|
|
|
|
<View style={styles.countryModalHeader}>
|
|
|
|
|
<TouchableOpacity
|
|
|
|
|
style={styles.countryModalCloseButton}
|
|
|
|
|
onPress={() => setShowCountryModal(false)}
|
|
|
|
|
activeOpacity={0.7}
|
|
|
|
|
>
|
|
|
|
|
<Text style={styles.countryModalCloseButtonText}>✕</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
<Text style={styles.countryModalTitle}>{t('selectCountry')}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<FlatList
|
|
|
|
|
data={countries}
|
|
|
|
|
renderItem={renderCountryItem}
|
|
|
|
|
keyExtractor={(item) => item.code}
|
|
|
|
|
style={styles.countryList}
|
|
|
|
|
showsVerticalScrollIndicator={false}
|
|
|
|
|
removeClippedSubviews={true}
|
|
|
|
|
initialNumToRender={10}
|
|
|
|
|
maxToRenderPerBatch={10}
|
|
|
|
|
windowSize={10}
|
|
|
|
|
getItemLayout={(data, index) => (
|
|
|
|
|
{length: 69, offset: 69 * index, index}
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
</Modal>
|
|
|
|
|
</View>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
|
container: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
},
|
|
|
|
|
closeButton: {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: 15,
|
|
|
|
|
left: 10,
|
|
|
|
|
width: 24,
|
|
|
|
|
height: 24,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
zIndex: 1,
|
|
|
|
|
},
|
|
|
|
|
closeButtonText: {
|
|
|
|
|
color: '#000',
|
|
|
|
|
fontSize: 24,
|
|
|
|
|
fontWeight: '300',
|
|
|
|
|
},
|
|
|
|
|
blueHeader: {
|
|
|
|
|
backgroundColor: '#0066FF',
|
|
|
|
|
paddingHorizontal: 20,
|
|
|
|
|
paddingBottom: 20,
|
|
|
|
|
paddingTop: Platform.OS === 'ios' ? 60 : 40,
|
|
|
|
|
borderBottomLeftRadius: 24,
|
|
|
|
|
borderBottomRightRadius: 24,
|
|
|
|
|
},
|
|
|
|
|
logo: {
|
|
|
|
|
fontSize: 28,
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
color: '#fff',
|
|
|
|
|
marginBottom: 15,
|
|
|
|
|
},
|
|
|
|
|
features: {
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
gap: 16,
|
|
|
|
|
},
|
|
|
|
|
featureItem: {
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
gap: 8,
|
|
|
|
|
},
|
|
|
|
|
featureIconContainer: {
|
|
|
|
|
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
width: 24,
|
|
|
|
|
height: 24,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
},
|
|
|
|
|
featureIcon: {
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
},
|
|
|
|
|
featureText: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: '#fff',
|
|
|
|
|
},
|
|
|
|
|
loginContainer: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
paddingHorizontal: 20,
|
|
|
|
|
paddingTop: Platform.OS === 'ios' ? 40 : 20,
|
|
|
|
|
},
|
|
|
|
|
titleContainer: {
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginBottom: 30,
|
|
|
|
|
paddingTop: 20,
|
|
|
|
|
position: 'relative',
|
|
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
fontSize: 24,
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
color: '#000',
|
|
|
|
|
marginBottom: 8,
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
},
|
|
|
|
|
subtitle: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: '#666',
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
},
|
|
|
|
|
loginButton: {
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
height: 50,
|
|
|
|
|
borderRadius: 25,
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: '#E1E1E1',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginBottom: 12,
|
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
},
|
|
|
|
|
loginButtonIcon: {
|
|
|
|
|
width: 24,
|
|
|
|
|
height: 24,
|
|
|
|
|
borderRadius: 12,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginRight: 16,
|
|
|
|
|
},
|
|
|
|
|
facebookIcon: {
|
|
|
|
|
backgroundColor: '#3b5998',
|
|
|
|
|
},
|
|
|
|
|
appleIconBg: {
|
|
|
|
|
backgroundColor: '#000',
|
|
|
|
|
},
|
|
|
|
|
instagramIcon: {
|
|
|
|
|
backgroundColor: '#E1306C',
|
|
|
|
|
},
|
|
|
|
|
loginButtonText: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: '#000',
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
marginRight: 16,
|
|
|
|
|
},
|
|
|
|
|
forgotPassword: {
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginVertical: 20,
|
|
|
|
|
},
|
|
|
|
|
forgotPasswordText: {
|
|
|
|
|
color: '#0066FF',
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
},
|
|
|
|
|
termsContainer: {
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginTop: 10,
|
|
|
|
|
},
|
|
|
|
|
terms: {
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: '#666',
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
lineHeight: 18,
|
|
|
|
|
},
|
|
|
|
|
link: {
|
|
|
|
|
color: '#0066FF',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 邮箱登录样式
|
|
|
|
|
emailLoginContainer: {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: Platform.OS === 'ios' ? 60 : 40,
|
|
|
|
|
height: Platform.OS === 'ios' ? 'auto' : '100%',
|
|
|
|
|
bottom: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
borderTopLeftRadius: 20,
|
|
|
|
|
borderTopRightRadius: 20,
|
|
|
|
|
shadowColor: '#000',
|
|
|
|
|
shadowOffset: { width: 0, height: -2 },
|
|
|
|
|
shadowOpacity: 0.1,
|
|
|
|
|
shadowRadius: 5,
|
|
|
|
|
elevation: 5,
|
|
|
|
|
zIndex: 10,
|
|
|
|
|
},
|
|
|
|
|
emailLoginHeader: {
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
paddingTop: 20,
|
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
|
paddingBottom: 15,
|
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
|
borderBottomColor: '#f0f0f0',
|
|
|
|
|
},
|
|
|
|
|
emailLoginCloseButton: {
|
|
|
|
|
padding: 8,
|
|
|
|
|
width: 36,
|
|
|
|
|
height: 36,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
},
|
|
|
|
|
emailLoginCloseButtonText: {
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
color: '#000',
|
|
|
|
|
},
|
|
|
|
|
emailLoginTitle: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
fontWeight: '600',
|
|
|
|
|
color: '#000',
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
marginRight: 36,
|
|
|
|
|
},
|
|
|
|
|
emailLoginContent: {
|
|
|
|
|
padding: 20,
|
|
|
|
|
paddingBottom: Platform.OS === 'ios' ? 50 : 30,
|
|
|
|
|
flex: 1,
|
|
|
|
|
},
|
|
|
|
|
emailInputContainer: {
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: '#E1E1E1',
|
|
|
|
|
borderRadius: 25,
|
|
|
|
|
height: 50,
|
|
|
|
|
marginBottom: 20,
|
|
|
|
|
},
|
|
|
|
|
emailInput: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
height: '100%',
|
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
paddingRight: 36,
|
|
|
|
|
},
|
|
|
|
|
emailClearButton: {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
right: 12,
|
|
|
|
|
top: '50%',
|
|
|
|
|
transform: [{ translateY: -12 }],
|
|
|
|
|
height: 24,
|
|
|
|
|
width: 24,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
},
|
|
|
|
|
emailClearButtonText: {
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: '#999',
|
|
|
|
|
fontWeight: '500',
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
},
|
|
|
|
|
suggestionsContainer: {
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: '#E1E1E1',
|
|
|
|
|
borderRadius: 10,
|
|
|
|
|
marginTop: -10,
|
|
|
|
|
marginBottom: 20,
|
|
|
|
|
maxHeight: 200,
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
},
|
|
|
|
|
suggestionsList: {
|
|
|
|
|
padding: 8,
|
|
|
|
|
},
|
|
|
|
|
suggestionItem: {
|
|
|
|
|
paddingVertical: 12,
|
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
|
borderBottomColor: '#F0F0F0',
|
|
|
|
|
},
|
|
|
|
|
suggestionText: {
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: '#333',
|
|
|
|
|
},
|
|
|
|
|
emailContinueButton: {
|
|
|
|
|
height: 50,
|
|
|
|
|
backgroundColor: '#0039CB',
|
|
|
|
|
borderRadius: 25,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginTop: 20,
|
|
|
|
|
},
|
|
|
|
|
emailDisabledButton: {
|
|
|
|
|
backgroundColor: '#CCCCCC',
|
|
|
|
|
},
|
|
|
|
|
emailContinueButtonText: {
|
|
|
|
|
color: '#fff',
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
fontWeight: '600',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 手机号登录样式
|
|
|
|
|
phoneLoginContainer: {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: Platform.OS === 'ios' ? 60 : 40,
|
|
|
|
|
height: Platform.OS === 'ios' ? 'auto' : '100%',
|
|
|
|
|
bottom: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
borderTopLeftRadius: 20,
|
|
|
|
|
borderTopRightRadius: 20,
|
|
|
|
|
shadowColor: '#000',
|
|
|
|
|
shadowOffset: { width: 0, height: -2 },
|
|
|
|
|
shadowOpacity: 0.1,
|
|
|
|
|
shadowRadius: 5,
|
|
|
|
|
elevation: 5,
|
|
|
|
|
zIndex: 10,
|
|
|
|
|
},
|
|
|
|
|
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',
|
|
|
|
|
},
|
|
|
|
|
countrySelector: {
|
|
|
|
|
flexDirection: 'row',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
paddingHorizontal: 12,
|
|
|
|
|
borderRightWidth: 1,
|
|
|
|
|
borderRightColor: '#E1E1E1',
|
|
|
|
|
backgroundColor: '#F7F7F7',
|
|
|
|
|
height: '100%',
|
|
|
|
|
minWidth: 90,
|
|
|
|
|
width: 90,
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
},
|
|
|
|
|
countryFlag: {
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
marginRight: 4,
|
|
|
|
|
},
|
|
|
|
|
countryCode: {
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: '#333',
|
|
|
|
|
marginRight: 4,
|
|
|
|
|
},
|
|
|
|
|
downArrow: {
|
|
|
|
|
fontSize: 8,
|
|
|
|
|
color: '#666',
|
|
|
|
|
},
|
|
|
|
|
phoneInput: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
height: '100%',
|
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
paddingRight: 36,
|
|
|
|
|
},
|
|
|
|
|
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',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 国家选择模态框样式
|
|
|
|
|
countryModalContainer: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
|
|
|
justifyContent: 'flex-end',
|
|
|
|
|
zIndex: 999,
|
|
|
|
|
},
|
|
|
|
|
countryModalContent: {
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
borderTopLeftRadius: 20,
|
|
|
|
|
borderTopRightRadius: 20,
|
|
|
|
|
maxHeight: '80%',
|
|
|
|
|
zIndex: 1000,
|
|
|
|
|
},
|
|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
countryItemName: {
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: '#333',
|
|
|
|
|
},
|
|
|
|
|
countryItemCode: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: '#666',
|
|
|
|
|
marginTop: 4,
|
|
|
|
|
},
|
|
|
|
|
// 密码输入框样式
|
|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
});
|