|
|
import React, { useEffect, useState, useRef } from "react"; |
|
|
import { |
|
|
View, |
|
|
Text, |
|
|
StyleSheet, |
|
|
TouchableOpacity, |
|
|
StatusBar, |
|
|
Platform, |
|
|
BackHandler, |
|
|
Image, |
|
|
Modal, |
|
|
SafeAreaView, |
|
|
Alert |
|
|
} from "react-native"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { useNavigation } from "@react-navigation/native"; |
|
|
import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; |
|
|
import fontSize from "../../utils/fontsizeUtils"; |
|
|
import EmailLoginModal from "./EmailLoginModal"; |
|
|
import PhoneLoginModal from "./PhoneLoginModal"; |
|
|
import { loginApi } from "../../services/api/login"; |
|
|
import { userApi } from "../../services"; |
|
|
import useUserStore from "../../store/user"; |
|
|
|
|
|
// 使用标准的ES6模块导入 |
|
|
import { |
|
|
GoogleSignin, |
|
|
statusCodes, |
|
|
} from "@react-native-google-signin/google-signin"; |
|
|
|
|
|
import { LoginManager, AccessToken, Settings } from "react-native-fbsdk-next"; |
|
|
|
|
|
const isDevelopment = __DEV__; // 开发模式检测 |
|
|
|
|
|
// 移出条件块,始终尝试配置 Google 登录 |
|
|
try { |
|
|
// 配置 Google 登录 |
|
|
GoogleSignin.configure({ |
|
|
iosClientId: "YOUR_IOS_CLIENT_ID_HERE.apps.googleusercontent.com", // iOS CLIENT_ID |
|
|
webClientId: |
|
|
"449517618313-av37nffa7rqkefu0ajh5auou3pb0mt51.apps.googleusercontent.com", // <-- 更新为此 Web Client ID |
|
|
scopes: ["profile", "email"], |
|
|
offlineAccess: false, // <-- 确保为 false 或移除 |
|
|
forceCodeForRefreshToken: false, // <-- 确保为 false 或移除 |
|
|
}); |
|
|
} catch (error) { |
|
|
console.log("Google Sign-in模块配置错误:", error); // 稍微修改了日志信息 |
|
|
} |
|
|
|
|
|
type RootStackParamList = { |
|
|
Login: undefined; |
|
|
EmailLogin: undefined; |
|
|
MainTabs: { screen: string }; |
|
|
Google: undefined; |
|
|
Home: { screen: string }; |
|
|
}; |
|
|
|
|
|
type LoginScreenProps = { |
|
|
onClose?: () => void; |
|
|
isModal?: boolean; |
|
|
}; |
|
|
|
|
|
export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => { |
|
|
const { setUser } = useUserStore(); |
|
|
const { t } = useTranslation(); |
|
|
const navigation = |
|
|
useNavigation<NativeStackNavigationProp<RootStackParamList>>(); |
|
|
|
|
|
// 全新的状态管理方式 |
|
|
const [emailModalVisible, setEmailModalVisible] = useState(false); |
|
|
const [phoneModalVisible, setPhoneModalVisible] = useState(false); |
|
|
|
|
|
// 防止多次触发 |
|
|
const isProcessingEmail = useRef(false); |
|
|
const isProcessingPhone = useRef(false); |
|
|
|
|
|
// 处理Android返回按钮 |
|
|
useEffect(() => { |
|
|
const backAction = () => { |
|
|
if (emailModalVisible) { |
|
|
setEmailModalVisible(false); |
|
|
return true; |
|
|
} |
|
|
if (phoneModalVisible) { |
|
|
setPhoneModalVisible(false); |
|
|
return true; |
|
|
} |
|
|
handleClose(); |
|
|
return true; |
|
|
}; |
|
|
|
|
|
const backHandler = BackHandler.addEventListener( |
|
|
"hardwareBackPress", |
|
|
backAction |
|
|
); |
|
|
|
|
|
return () => backHandler.remove(); |
|
|
}, [emailModalVisible, phoneModalVisible]); |
|
|
|
|
|
// 关闭主屏幕 |
|
|
const handleClose = () => { |
|
|
if (isModal && onClose) { |
|
|
onClose(); |
|
|
} else { |
|
|
navigation.goBack(); |
|
|
} |
|
|
}; |
|
|
|
|
|
// 处理谷歌登录 |
|
|
const handleGoogleLogin = async () => { |
|
|
try { |
|
|
if (!GoogleSignin || typeof GoogleSignin.signIn !== "function") { |
|
|
console.log("Google Sign-in模块未正确初始化或配置失败"); |
|
|
return; |
|
|
} |
|
|
await GoogleSignin.hasPlayServices(); |
|
|
const userInfo = await GoogleSignin.signIn(); |
|
|
console.log("Google 登录成功:", userInfo); |
|
|
try { |
|
|
const res = await loginApi.googleLogin(userInfo); |
|
|
const user = await userApi.getProfile(); |
|
|
setUser(user); |
|
|
navigation.navigate("MainTabs", { screen: "Home" }); |
|
|
} catch (err) { |
|
|
console.log("Google 登录失败:", err); |
|
|
navigation.navigate("Login"); |
|
|
} |
|
|
// 这里可以处理登录成功后的逻辑 |
|
|
// 比如导航到主页面或保存用户信息 |
|
|
// navigation.navigate("MainTabs", { screen: "Home" }); |
|
|
} catch (error: any) { |
|
|
console.log("Google 登录错误:", error); |
|
|
// 开发模式下的错误处理 |
|
|
if (isDevelopment) { |
|
|
console.log("开发模式:忽略Google登录错误,但已尝试真实登录"); // 修改日志,表明已尝试真实登录 |
|
|
return; |
|
|
} |
|
|
if (statusCodes && error.code === statusCodes.SIGN_IN_CANCELLED) { |
|
|
console.log("用户取消登录"); |
|
|
} else if (statusCodes && error.code === statusCodes.IN_PROGRESS) { |
|
|
console.log("登录正在进行中"); |
|
|
} else if ( |
|
|
statusCodes && |
|
|
error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE |
|
|
) { |
|
|
console.log("Play Services 不可用"); |
|
|
} else { |
|
|
console.log("其他错误:", error.message); |
|
|
navigation.navigate("Login"); |
|
|
} |
|
|
} |
|
|
}; |
|
|
const [userInfo, setUserInfo] = useState<any>(null); |
|
|
|
|
|
useEffect(() => { |
|
|
// 确保在 App 启动时初始化 SDK。这通常在您的 App.js 的顶层完成。 |
|
|
// 如果您在 app.json 中配置了 Facebook App ID,这里可以省略 Settings.setAppID 和 Settings.setDisplayName |
|
|
Settings.initializeSDK(); |
|
|
|
|
|
// 在应用程序启动时检查是否已经登录(可选) |
|
|
AccessToken.getCurrentAccessToken().then(data => { |
|
|
if (data) { |
|
|
console.log("已登录 Facebook,Token:", data.accessToken); |
|
|
// 可以尝试获取用户信息 |
|
|
// fetchFacebookProfile(data.accessToken); |
|
|
} |
|
|
}); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
// 辅助函数:获取 Facebook 用户资料 (可选,需要 'public_profile' 权限) |
|
|
const fetchFacebookProfile = async (token:string) => { |
|
|
try { |
|
|
const response = await fetch(`https://graph.facebook.com/me?fields=id,name,email&access_token=${token}`); |
|
|
const profile = await response.json(); |
|
|
setUserInfo(profile); |
|
|
console.log('Facebook User Info:', profile); |
|
|
return profile; |
|
|
} catch (error) { |
|
|
console.error('获取 Facebook 用户资料错误:', error); |
|
|
Alert.alert("获取资料失败", "无法从 Facebook 获取用户详细资料,请检查网络或权限设置。"); |
|
|
return null; |
|
|
} |
|
|
}; |
|
|
|
|
|
// 处理Facebook登录 |
|
|
const handleFacebookLogin = async () => { |
|
|
try { |
|
|
// 可选: 先退出登录,确保每次都是全新登录 (主要用于测试) |
|
|
// await LoginManager.logOut(); |
|
|
|
|
|
const result = await LoginManager.logInWithPermissions([ |
|
|
"public_profile", |
|
|
"email", |
|
|
]); |
|
|
|
|
|
if (result.isCancelled) { |
|
|
Alert.alert("登录取消", "用户取消了 Facebook 登录。"); |
|
|
return; |
|
|
} |
|
|
|
|
|
const data = await AccessToken.getCurrentAccessToken(); |
|
|
// 确保 accessToken 存在且为字符串 |
|
|
if (!data || !data.accessToken) { |
|
|
Alert.alert("登录失败", "无法获取有效的 Facebook AccessToken。"); |
|
|
return; |
|
|
} |
|
|
|
|
|
const tokenString = data.accessToken.toString(); |
|
|
console.log("Facebook Access Token:", tokenString); |
|
|
|
|
|
// 获取 Facebook 用户信息 |
|
|
const profile = await fetchFacebookProfile(tokenString); |
|
|
|
|
|
if (profile) { |
|
|
try { |
|
|
// 准备发送给后端的数据 - 扁平化格式 |
|
|
const facebookData = { |
|
|
id: profile.id, |
|
|
name: profile.name, |
|
|
email: profile.email, |
|
|
access_token: tokenString |
|
|
}; |
|
|
|
|
|
console.log("发送给后端的Facebook数据:", facebookData); |
|
|
|
|
|
// 调用后端Facebook登录API |
|
|
const res = await loginApi.facebookLogin(facebookData); |
|
|
console.log("Facebook 后端登录响应:", res); |
|
|
|
|
|
// 获取用户信息并更新状态 |
|
|
const user = await userApi.getProfile(); |
|
|
setUser(user); |
|
|
|
|
|
// 导航到主页面 |
|
|
navigation.navigate("MainTabs", { screen: "Home" }); |
|
|
|
|
|
} catch (err) { |
|
|
console.log("Facebook 后端登录失败:", err); |
|
|
Alert.alert("登录失败", "Facebook 登录验证失败,请重试。"); |
|
|
} |
|
|
} else { |
|
|
Alert.alert("登录失败", "无法获取 Facebook 用户信息,请重试。"); |
|
|
} |
|
|
|
|
|
} catch (error: any) { |
|
|
console.error("Facebook 登录或获取资料时发生错误:", error); |
|
|
let errorMessage = "发生未知错误"; |
|
|
if (error && typeof error.message === 'string') { |
|
|
errorMessage = error.message; |
|
|
} |
|
|
Alert.alert("登录错误", `Facebook 操作失败:${errorMessage}`); |
|
|
} |
|
|
}; |
|
|
|
|
|
// 处理Apple登录 |
|
|
const handleAppleLogin = () => { |
|
|
// 处理Apple登录 |
|
|
}; |
|
|
|
|
|
// 处理Instagram登录 |
|
|
const handleInstagramLogin = () => { |
|
|
// 处理Instagram登录 |
|
|
}; |
|
|
|
|
|
// 显示邮箱登录 |
|
|
const showEmailModal = () => { |
|
|
if (isProcessingEmail.current) return; |
|
|
|
|
|
isProcessingEmail.current = true; |
|
|
// 确保手机模态框已关闭 |
|
|
setPhoneModalVisible(false); |
|
|
|
|
|
// 延迟打开邮箱模态框,避免冲突 |
|
|
setTimeout(() => { |
|
|
setEmailModalVisible(true); |
|
|
isProcessingEmail.current = false; |
|
|
}, 100); |
|
|
}; |
|
|
|
|
|
// 显示手机登录 |
|
|
const showPhoneModal = () => { |
|
|
if (isProcessingPhone.current) return; |
|
|
|
|
|
isProcessingPhone.current = true; |
|
|
// 确保邮箱模态框已关闭 |
|
|
setEmailModalVisible(false); |
|
|
|
|
|
// 延迟打开手机模态框,避免冲突 |
|
|
setTimeout(() => { |
|
|
setPhoneModalVisible(true); |
|
|
isProcessingPhone.current = false; |
|
|
}, 100); |
|
|
}; |
|
|
|
|
|
// 关闭邮箱登录 |
|
|
const hideEmailModal = () => { |
|
|
console.log("Hiding email modal"); |
|
|
setEmailModalVisible(false); |
|
|
}; |
|
|
|
|
|
// 关闭手机登录 |
|
|
const hidePhoneModal = () => { |
|
|
console.log("Hiding phone modal"); |
|
|
setPhoneModalVisible(false); |
|
|
}; |
|
|
|
|
|
// 处理忘记密码 |
|
|
const handleForgotPassword = () => { |
|
|
// 处理忘记密码 |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<SafeAreaView style={styles.safeArea}> |
|
|
<StatusBar barStyle="light-content" backgroundColor="#0066FF" /> |
|
|
<View style={styles.safeAreaContent}> |
|
|
{/* 顶部蓝色背景区域 */} |
|
|
<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.subtitle}>{t("loginSubtitle")}</Text> |
|
|
</View> |
|
|
|
|
|
{/* 登录按钮 */} |
|
|
<TouchableOpacity |
|
|
style={styles.loginButton} |
|
|
onPress={handleGoogleLogin} |
|
|
> |
|
|
<View style={styles.loginButtonIcon}> |
|
|
<Image |
|
|
source={require("../../../assets/img/google.png")} |
|
|
style={{ width: 20, height: 20 }} |
|
|
/> |
|
|
</View> |
|
|
<Text style={styles.loginButtonText}> |
|
|
{isDevelopment |
|
|
? "🧪 " + t("continueWithGoogle") + " (测试模式)" |
|
|
: 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={showEmailModal}> |
|
|
<View style={styles.loginButtonIcon}> |
|
|
<Text>✉️</Text> |
|
|
</View> |
|
|
<Text style={styles.loginButtonText}>{t("continueWithEmail")}</Text> |
|
|
</TouchableOpacity> |
|
|
|
|
|
<TouchableOpacity style={styles.loginButton} onPress={showPhoneModal}> |
|
|
<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> |
|
|
</View> |
|
|
|
|
|
{/* 邮箱登录模态框 - 直接渲染 */} |
|
|
<EmailLoginModal visible={emailModalVisible} onClose={hideEmailModal} /> |
|
|
|
|
|
{/* 手机登录模态框 - 直接渲染 */} |
|
|
<PhoneLoginModal visible={phoneModalVisible} onClose={hidePhoneModal} /> |
|
|
</SafeAreaView> |
|
|
); |
|
|
}; |
|
|
|
|
|
const styles = StyleSheet.create({ |
|
|
safeArea: { |
|
|
flex: 1, |
|
|
}, |
|
|
safeAreaContent: { |
|
|
flex: 1, |
|
|
paddingTop: 0, |
|
|
}, |
|
|
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: fontSize(24), |
|
|
fontWeight: "300", |
|
|
}, |
|
|
blueHeader: { |
|
|
backgroundColor: "#0066FF", |
|
|
paddingHorizontal: 20, |
|
|
paddingBottom: 20, |
|
|
paddingTop: Platform.OS === "ios" ? 60 : 40, |
|
|
borderBottomLeftRadius: 24, |
|
|
borderBottomRightRadius: 24, |
|
|
}, |
|
|
logo: { |
|
|
fontSize: 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: fontSize(12), |
|
|
}, |
|
|
featureText: { |
|
|
fontSize: fontSize(14), |
|
|
color: "#fff", |
|
|
}, |
|
|
loginContainer: { |
|
|
flex: 1, |
|
|
paddingHorizontal: 20, |
|
|
paddingTop: Platform.OS === "ios" ? 40 : 20, |
|
|
}, |
|
|
titleContainer: { |
|
|
alignItems: "center", |
|
|
marginBottom: 30, |
|
|
paddingTop: 20, |
|
|
position: "relative", |
|
|
}, |
|
|
subtitle: { |
|
|
fontSize: 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: fontSize(16), |
|
|
color: "#000", |
|
|
textAlign: "center", |
|
|
marginRight: 16, |
|
|
}, |
|
|
forgotPassword: { |
|
|
alignItems: "center", |
|
|
marginVertical: 20, |
|
|
}, |
|
|
forgotPasswordText: { |
|
|
color: "#0066FF", |
|
|
fontSize: fontSize(14), |
|
|
}, |
|
|
termsContainer: { |
|
|
alignItems: "center", |
|
|
marginTop: 10, |
|
|
}, |
|
|
terms: { |
|
|
fontSize: fontSize(12), |
|
|
color: "#666", |
|
|
textAlign: "center", |
|
|
lineHeight: 18, |
|
|
}, |
|
|
link: { |
|
|
color: "#0066FF", |
|
|
}, |
|
|
}); |
|
|
|
|
|
export default LoginScreen;
|
|
|
|