添加赔率设置功能,支持选择博彩公司并展示赔率信息;优化状态管理和国际化文本
This commit is contained in:
207
app/profile.tsx
207
app/profile.tsx
@@ -15,24 +15,59 @@ import {
|
||||
|
||||
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<UserProfile | null>(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);
|
||||
@@ -65,7 +100,8 @@ export default function ProfileScreen() {
|
||||
platformVersion,
|
||||
},
|
||||
user:
|
||||
credential.fullName && (credential.fullName.givenName || credential.fullName.familyName)
|
||||
credential.fullName &&
|
||||
(credential.fullName.givenName || credential.fullName.familyName)
|
||||
? {
|
||||
name: {
|
||||
firstName: credential.fullName.givenName || undefined,
|
||||
@@ -178,7 +214,9 @@ export default function ProfileScreen() {
|
||||
<>
|
||||
<View style={styles.profileHeader}>
|
||||
<Image
|
||||
source={{ uri: user?.avatar || "https://via.placeholder.com/100" }}
|
||||
source={{
|
||||
uri: user?.avatar || "https://via.placeholder.com/100",
|
||||
}}
|
||||
style={styles.avatar}
|
||||
contentFit="cover"
|
||||
/>
|
||||
@@ -201,7 +239,9 @@ export default function ProfileScreen() {
|
||||
]}
|
||||
onPress={handleLogout}
|
||||
>
|
||||
<ThemedText style={{ color: "#FF3B30" }}>登出</ThemedText>
|
||||
<ThemedText style={{ color: "#FF3B30" }}>
|
||||
{t("settings.logout")}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
) : (
|
||||
@@ -212,9 +252,11 @@ export default function ProfileScreen() {
|
||||
>
|
||||
<View style={styles.loginIcon} />
|
||||
<View style={styles.loginInfo}>
|
||||
<ThemedText style={styles.loginTitle}>登录</ThemedText>
|
||||
<ThemedText style={styles.loginTitle}>
|
||||
{t("settings.login")}
|
||||
</ThemedText>
|
||||
<ThemedText style={{ color: subTextColor }}>
|
||||
点击登录
|
||||
{t("settings.click_to_login")}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
@@ -310,6 +352,83 @@ export default function ProfileScreen() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<ThemedText style={styles.sectionTitle}>
|
||||
{t("settings.odds_title")}
|
||||
</ThemedText>
|
||||
|
||||
<View
|
||||
style={[
|
||||
styles.section,
|
||||
{ backgroundColor: isDark ? "#1c1c1e" : "#fff" },
|
||||
]}
|
||||
>
|
||||
<View style={styles.settingItem}>
|
||||
<View style={styles.settingLabel}>
|
||||
<IconSymbol
|
||||
name="stats-chart"
|
||||
size={20}
|
||||
color={iconColor}
|
||||
style={{ marginRight: 10 }}
|
||||
/>
|
||||
<ThemedText>{t("settings.odds_show")}</ThemedText>
|
||||
</View>
|
||||
<View style={styles.settingControl}>
|
||||
<TouchableOpacity onPress={toggleOdds} style={styles.button}>
|
||||
<ThemedText>
|
||||
{state.oddsSettings.enabled
|
||||
? t("settings.odds_enabled")
|
||||
: t("settings.odds_disabled")}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
styles.settingItem,
|
||||
{
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: isDark ? "#38383a" : "#c6c6c8",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={styles.settingItemContent}
|
||||
onPress={() => setOddsModalVisible(true)}
|
||||
disabled={!state.oddsSettings.enabled}
|
||||
>
|
||||
<View style={styles.settingLabel}>
|
||||
<IconSymbol
|
||||
name="list"
|
||||
size={20}
|
||||
color={state.oddsSettings.enabled ? iconColor : subTextColor}
|
||||
style={{ marginRight: 10 }}
|
||||
/>
|
||||
<ThemedText
|
||||
style={{
|
||||
color: state.oddsSettings.enabled
|
||||
? textColor
|
||||
: subTextColor,
|
||||
}}
|
||||
>
|
||||
{t("settings.odds_select_company")}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<ThemedText style={{ color: subTextColor, marginRight: 4 }}>
|
||||
{state.oddsSettings.selectedBookmakers.join(", ") ||
|
||||
t("settings.odds_unselected")}
|
||||
</ThemedText>
|
||||
<IconSymbol
|
||||
name="chevron-forward"
|
||||
size={16}
|
||||
color={subTextColor}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* <ThemedText style={styles.sectionTitle}>登录</ThemedText>
|
||||
|
||||
<View
|
||||
@@ -367,7 +486,9 @@ export default function ProfileScreen() {
|
||||
{ backgroundColor: isDark ? "#1c1c1e" : "#fff" },
|
||||
]}
|
||||
>
|
||||
<ThemedText style={styles.modalTitle}>选择登录方式</ThemedText>
|
||||
<ThemedText style={styles.modalTitle}>
|
||||
{t("settings.select_login_method")}
|
||||
</ThemedText>
|
||||
{appleAvailable && (
|
||||
<AppleAuthentication.AppleAuthenticationButton
|
||||
buttonType={
|
||||
@@ -384,13 +505,76 @@ export default function ProfileScreen() {
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity style={styles.googleButton} onPress={() => {}}>
|
||||
<ThemedText>Google 登录</ThemedText>
|
||||
<ThemedText>{t("settings.google_login")}</ThemedText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.modalCancel}
|
||||
onPress={() => setLoginModalVisible(false)}
|
||||
>
|
||||
<ThemedText>取消</ThemedText>
|
||||
<ThemedText>{t("settings.cancel")}</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
visible={oddsModalVisible}
|
||||
transparent
|
||||
animationType="slide"
|
||||
onRequestClose={() => setOddsModalVisible(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={styles.modalMask}
|
||||
activeOpacity={1}
|
||||
onPress={() => setOddsModalVisible(false)}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.modalCard,
|
||||
{
|
||||
backgroundColor: isDark ? "#1c1c1e" : "#fff",
|
||||
maxHeight: "70%",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ThemedText style={styles.modalTitle}>
|
||||
{t("settings.odds_modal_title")}
|
||||
</ThemedText>
|
||||
<ScrollView style={{ marginVertical: 10 }}>
|
||||
{BOOKMAKERS.map((name) => {
|
||||
const isSelected =
|
||||
state.oddsSettings.selectedBookmakers.includes(name);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={name}
|
||||
style={[
|
||||
styles.bookmakerItem,
|
||||
{ borderColor: isDark ? "#38383a" : "#eee" },
|
||||
]}
|
||||
onPress={() => selectBookmaker(name)}
|
||||
>
|
||||
<ThemedText
|
||||
style={{
|
||||
color: isSelected ? "#FF9500" : textColor,
|
||||
fontWeight: isSelected ? "bold" : "normal",
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</ThemedText>
|
||||
{isSelected && (
|
||||
<IconSymbol name="checkmark" size={18} color="#FF9500" />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
<TouchableOpacity
|
||||
style={styles.modalCancel}
|
||||
onPress={() => setOddsModalVisible(false)}
|
||||
>
|
||||
<ThemedText type="defaultSemiBold">
|
||||
{t("settings.odds_confirm")}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
@@ -502,6 +686,13 @@ const styles = StyleSheet.create({
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
bookmakerItem: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingVertical: 14,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
logoutButton: {
|
||||
paddingVertical: 12,
|
||||
alignItems: "center",
|
||||
|
||||
Reference in New Issue
Block a user