import * as AppleAuthentication from "expo-apple-authentication"; import Constants from "expo-constants"; import { Image } from "expo-image"; import { Stack, useRouter } from "expo-router"; import React from "react"; import { useTranslation } from "react-i18next"; import { Modal, Platform, ScrollView, StyleSheet, TouchableOpacity, View, } from "react-native"; import { ThemedText } from "@/components/themed-text"; import { IconSymbol } from "@/components/ui/icon-symbol"; import { useAppState } from "@/context/AppStateContext"; import { useTheme } from "@/context/ThemeContext"; import { changeLanguage } from "@/i18n"; import { appleSignIn, fetchUserProfile, logout } from "@/lib/api"; import { storage } from "@/lib/storage"; import type { UserProfile } from "@/types/api"; const BOOKMAKERS = [ "10Bet", "WilliamHill", "bet365", "Marathon", "Unibet", "Betfair", "188bet", "Pncl", "Sbo", ]; export default function ProfileScreen() { const { theme, toggleTheme, setTheme, isSystemTheme, useSystemTheme } = useTheme(); const { state, updateOddsSettings } = useAppState(); const { t, i18n } = useTranslation(); const router = useRouter(); const isDark = theme === "dark"; const [appleAvailable, setAppleAvailable] = React.useState(false); const [user, setUser] = React.useState(null); const [loginModalVisible, setLoginModalVisible] = React.useState(false); const [oddsModalVisible, setOddsModalVisible] = React.useState(false); const currentLanguage = i18n.language; const toggleOdds = () => { updateOddsSettings({ ...state.oddsSettings, enabled: !state.oddsSettings.enabled, }); }; const selectBookmaker = (name: string) => { const current = state.oddsSettings.selectedBookmakers; let next: string[]; if (current.includes(name)) { next = current.filter((b) => b !== name); } else { next = [...current, name].slice(-2); // Keep last 2 } updateOddsSettings({ ...state.oddsSettings, selectedBookmakers: next, }); }; const toggleLanguage = () => { const nextLang = currentLanguage.startsWith("en") ? "zh" : "en"; changeLanguage(nextLang); }; const handleAppleSignIn = async () => { try { const credential = await AppleAuthentication.signInAsync({ requestedScopes: [ AppleAuthentication.AppleAuthenticationScope.FULL_NAME, AppleAuthentication.AppleAuthenticationScope.EMAIL, ], }); console.log("Apple credential:", { fullName: credential.fullName, email: credential.email, user: credential.user, }); const deviceId = Constants.deviceId || Constants.sessionId || "unknown"; const platformVersion = Platform.Version.toString(); const options = { authorizationCode: credential.authorizationCode || "", identityToken: credential.identityToken || "", deviceInfo: { deviceId, platform: Platform.OS, platformVersion, }, user: credential.fullName && (credential.fullName.givenName || credential.fullName.familyName) ? { name: { firstName: credential.fullName.givenName || undefined, lastName: credential.fullName.familyName || undefined, }, } : undefined, }; console.log("登录参数", options); const res = await appleSignIn(options); console.log("登录响应", res); await storage.setAccessToken(res.accessToken); await storage.setRefreshToken(res.refreshToken); if (res.user) { await storage.setUser(res.user); setUser(res.user); } else { const profile = await fetchUserProfile(); await storage.setUser(profile); setUser(profile); } setLoginModalVisible(false); } catch (error: any) { if (error?.code === "ERR_REQUEST_CANCELED") { return; } console.error("Apple sign in error:", error); } }; const handleLoginPress = () => { setLoginModalVisible(true); }; React.useEffect(() => { AppleAuthentication.isAvailableAsync().then(setAppleAvailable); }, []); React.useEffect(() => { const loadUser = async () => { const savedUser = await storage.getUser(); if (savedUser) { setUser(savedUser); try { const profile = await fetchUserProfile(); await storage.setUser(profile); setUser(profile); } catch { setUser(savedUser); } } }; loadUser(); }, []); const handleLogout = async () => { try { await logout(); } catch (error) { console.error("Logout error:", error); } finally { await storage.clear(); setUser(null); } }; const isLoggedIn = !!user; const iconColor = isDark ? "#FFFFFF" : "#000000"; const textColor = isDark ? "#FFFFFF" : "#000000"; const subTextColor = isDark ? "#AAAAAA" : "#666666"; return ( <> {isLoggedIn ? ( <> {user?.nickname || t("profile.name")} {user?.appleId || ""} {t("settings.logout")} ) : ( {t("settings.login")} {t("settings.click_to_login")} )} {t("settings.title")} {t("settings.theme")} {theme === "light" ? t("settings.light") : t("settings.dark")} {t("settings.language")} {currentLanguage.startsWith("zh") ? t("settings.chinese") : t("settings.english")} router.push("/privacy" as any)} > {t("profile.privacy")} {t("settings.odds_title")} {t("settings.odds_show")} {state.oddsSettings.enabled ? t("settings.odds_enabled") : t("settings.odds_disabled")} setOddsModalVisible(true)} disabled={!state.oddsSettings.enabled} > {t("settings.odds_select_company")} {state.oddsSettings.selectedBookmakers.join(", ") || t("settings.odds_unselected")} {/* 登录 {appleAvailable && ( )} {}}> Google 登录 */} setLoginModalVisible(false)} > setLoginModalVisible(false)} > {t("settings.select_login_method")} {appleAvailable && ( )} {}}> {t("settings.google_login")} setLoginModalVisible(false)} > {t("settings.cancel")} setOddsModalVisible(false)} > setOddsModalVisible(false)} > {t("settings.odds_modal_title")} {BOOKMAKERS.map((name) => { const isSelected = state.oddsSettings.selectedBookmakers.includes(name); return ( selectBookmaker(name)} > {name} {isSelected && ( )} ); })} setOddsModalVisible(false)} > {t("settings.odds_confirm")} ); } const styles = StyleSheet.create({ container: { flex: 1, }, section: { marginTop: 20, paddingVertical: 10, paddingHorizontal: 16, borderRadius: 10, // iOS style groups marginHorizontal: 16, }, sectionTitle: { marginLeft: 32, marginTop: 20, marginBottom: 5, fontSize: 13, textTransform: "uppercase", opacity: 0.6, }, profileHeader: { flexDirection: "row", alignItems: "center", paddingVertical: 10, }, avatar: { width: 60, height: 60, borderRadius: 30, backgroundColor: "#ccc", }, profileInfo: { marginLeft: 15, }, settingItem: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingVertical: 12, }, settingItemContent: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", flex: 1, }, settingLabel: { flexDirection: "row", alignItems: "center", }, settingControl: { flexDirection: "row", alignItems: "center", }, button: { paddingHorizontal: 10, paddingVertical: 5, }, googleButton: { width: "100%", height: 44, borderRadius: 8, borderWidth: 1, borderColor: "#c6c6c8", alignItems: "center", justifyContent: "center", }, loginCard: { flexDirection: "row", alignItems: "center", paddingVertical: 12, }, loginIcon: { width: 48, height: 48, borderRadius: 24, backgroundColor: "#c6c6c8", }, loginInfo: { marginLeft: 12, }, loginTitle: { fontSize: 18, }, modalMask: { flex: 1, backgroundColor: "rgba(0,0,0,0.4)", justifyContent: "center", paddingHorizontal: 24, }, modalCard: { borderRadius: 12, padding: 16, gap: 12, }, modalTitle: { fontSize: 16, textAlign: "center", }, modalCancel: { height: 40, alignItems: "center", justifyContent: "center", }, bookmakerItem: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingVertical: 14, borderBottomWidth: StyleSheet.hairlineWidth, }, logoutButton: { paddingVertical: 12, alignItems: "center", justifyContent: "center", marginTop: 10, }, });