添加卡牌设置功能,更新状态管理和界面显示逻辑
This commit is contained in:
@@ -37,7 +37,7 @@ const BOOKMAKERS = [
|
|||||||
export default function ProfileScreen() {
|
export default function ProfileScreen() {
|
||||||
const { theme, toggleTheme, setTheme, isSystemTheme, useSystemTheme } =
|
const { theme, toggleTheme, setTheme, isSystemTheme, useSystemTheme } =
|
||||||
useTheme();
|
useTheme();
|
||||||
const { state, updateOddsSettings } = useAppState();
|
const { state, updateOddsSettings, updateCardsSettings } = useAppState();
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
@@ -55,6 +55,13 @@ export default function ProfileScreen() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleCards = () => {
|
||||||
|
updateCardsSettings({
|
||||||
|
...state.cardsSettings,
|
||||||
|
enabled: !state.cardsSettings.enabled,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const selectBookmaker = (name: string) => {
|
const selectBookmaker = (name: string) => {
|
||||||
const current = state.oddsSettings.selectedBookmakers;
|
const current = state.oddsSettings.selectedBookmakers;
|
||||||
let next: string[];
|
let next: string[];
|
||||||
@@ -429,6 +436,38 @@ export default function ProfileScreen() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<ThemedText style={styles.sectionTitle}>
|
||||||
|
{t("settings.cards_title")}
|
||||||
|
</ThemedText>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.section,
|
||||||
|
{ backgroundColor: isDark ? "#1c1c1e" : "#fff" },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View style={styles.settingItem}>
|
||||||
|
<View style={styles.settingLabel}>
|
||||||
|
<IconSymbol
|
||||||
|
name="id-card"
|
||||||
|
size={20}
|
||||||
|
color={iconColor}
|
||||||
|
style={{ marginRight: 10 }}
|
||||||
|
/>
|
||||||
|
<ThemedText>{t("settings.cards_show")}</ThemedText>
|
||||||
|
</View>
|
||||||
|
<View style={styles.settingControl}>
|
||||||
|
<TouchableOpacity onPress={toggleCards} style={styles.button}>
|
||||||
|
<ThemedText>
|
||||||
|
{state.cardsSettings.enabled
|
||||||
|
? t("settings.cards_enabled")
|
||||||
|
: t("settings.cards_disabled")}
|
||||||
|
</ThemedText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* <ThemedText style={styles.sectionTitle}>登录</ThemedText>
|
{/* <ThemedText style={styles.sectionTitle}>登录</ThemedText>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ import { IconSymbol } from "@/components/ui/icon-symbol";
|
|||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
import { useAppState } from "@/context/AppStateContext";
|
import { useAppState } from "@/context/AppStateContext";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { addFavorite, fetchOdds, removeFavorite } from "@/lib/api";
|
import {
|
||||||
import { Match, OddsItem } from "@/types/api";
|
addFavorite,
|
||||||
|
fetchLiveScore,
|
||||||
|
fetchOdds,
|
||||||
|
removeFavorite,
|
||||||
|
} from "@/lib/api";
|
||||||
|
import { LiveScoreMatch, Match, OddsItem } from "@/types/api";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { LinearGradient } from "expo-linear-gradient";
|
import { LinearGradient } from "expo-linear-gradient";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
@@ -28,9 +33,11 @@ export function MatchCard({
|
|||||||
const [isFav, setIsFav] = useState(match.fav);
|
const [isFav, setIsFav] = useState(match.fav);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [odds, setOdds] = useState<OddsItem[]>(match.odds || []);
|
const [odds, setOdds] = useState<OddsItem[]>(match.odds || []);
|
||||||
|
const [liveDetail, setLiveDetail] = useState<LiveScoreMatch | null>(null);
|
||||||
// console.log("MatchCard render:", JSON.stringify(match));
|
// console.log("MatchCard render:", JSON.stringify(match));
|
||||||
|
|
||||||
const oddsSettings = state.oddsSettings;
|
const oddsSettings = state.oddsSettings;
|
||||||
|
const cardsSettings = state.cardsSettings;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@@ -52,6 +59,20 @@ export function MatchCard({
|
|||||||
match.odds,
|
match.odds,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Fetch live score detail for cards info
|
||||||
|
useEffect(() => {
|
||||||
|
if (cardsSettings.enabled && isLive && match.leagueKey) {
|
||||||
|
fetchLiveScore(match.sportId || 1, Number(match.leagueKey))
|
||||||
|
.then((matches) => {
|
||||||
|
const detail = matches.find((m) => String(m.event_key) === match.id);
|
||||||
|
if (detail) {
|
||||||
|
setLiveDetail(detail);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => console.log("Fetch live detail for cards error:", err));
|
||||||
|
}
|
||||||
|
}, [cardsSettings.enabled, match.id, match.leagueKey, match.sportId]);
|
||||||
|
|
||||||
// 当外部传入的 match.fav 改变时,更新内部状态
|
// 当外部传入的 match.fav 改变时,更新内部状态
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsFav(match.fav);
|
setIsFav(match.fav);
|
||||||
@@ -90,10 +111,53 @@ export function MatchCard({
|
|||||||
if (m) return { home: m[1], away: m[2], hasScore: true };
|
if (m) return { home: m[1], away: m[2], hasScore: true };
|
||||||
if (s && s !== "-" && s !== "0 - 0")
|
if (s && s !== "-" && s !== "0 - 0")
|
||||||
return { home: s, away: "", hasScore: true };
|
return { home: s, away: "", hasScore: true };
|
||||||
if (s === "0 - 0") return { home: "0", away: "0", hasScore: true };
|
if (s === "0 - 0" || s === "0-0")
|
||||||
|
return { home: "0", away: "0", hasScore: true };
|
||||||
return { home: "", away: "", hasScore: false };
|
return { home: "", away: "", hasScore: false };
|
||||||
}, [match.scoreText]);
|
}, [match.scoreText]);
|
||||||
|
|
||||||
|
const cardsCount = React.useMemo(() => {
|
||||||
|
if (!liveDetail?.cards || !cardsSettings.enabled) {
|
||||||
|
return { homeYellow: 0, homeRed: 0, awayYellow: 0, awayRed: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
let homeYellow = 0,
|
||||||
|
homeRed = 0,
|
||||||
|
awayYellow = 0,
|
||||||
|
awayRed = 0;
|
||||||
|
|
||||||
|
liveDetail.cards.forEach((card) => {
|
||||||
|
const cardType = (card.card || "").toLowerCase();
|
||||||
|
const isYellow = cardType.includes("yellow");
|
||||||
|
const isRed = cardType.includes("red");
|
||||||
|
if (!isYellow && !isRed) return;
|
||||||
|
|
||||||
|
const info = (card.info || "").toLowerCase();
|
||||||
|
const sideFromInfo = info.includes("home")
|
||||||
|
? "home"
|
||||||
|
: info.includes("away")
|
||||||
|
? "away"
|
||||||
|
: null;
|
||||||
|
const sideFromFault = card.home_fault
|
||||||
|
? "home"
|
||||||
|
: card.away_fault
|
||||||
|
? "away"
|
||||||
|
: null;
|
||||||
|
const side = sideFromInfo || sideFromFault;
|
||||||
|
if (!side) return;
|
||||||
|
|
||||||
|
if (side === "home") {
|
||||||
|
if (isYellow) homeYellow++;
|
||||||
|
if (isRed) homeRed++;
|
||||||
|
} else {
|
||||||
|
if (isYellow) awayYellow++;
|
||||||
|
if (isRed) awayRed++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { homeYellow, homeRed, awayYellow, awayRed };
|
||||||
|
}, [liveDetail, cardsSettings.enabled]);
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
if (onPress) {
|
if (onPress) {
|
||||||
onPress(match);
|
onPress(match);
|
||||||
@@ -191,6 +255,30 @@ export function MatchCard({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderCardsInline = (yellow: number, red: number) => {
|
||||||
|
if (!cardsSettings.enabled || !liveDetail) return null;
|
||||||
|
if (yellow <= 0 && red <= 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.cardsInline}>
|
||||||
|
{yellow > 0 && (
|
||||||
|
<View style={[styles.cardBadge, styles.cardBadgeYellow]}>
|
||||||
|
<ThemedText
|
||||||
|
style={[styles.cardBadgeText, styles.cardBadgeTextDark]}
|
||||||
|
>
|
||||||
|
{yellow}
|
||||||
|
</ThemedText>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{red > 0 && (
|
||||||
|
<View style={[styles.cardBadge, styles.cardBadgeRed]}>
|
||||||
|
<ThemedText style={styles.cardBadgeText}>{red}</ThemedText>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={handlePress}
|
onPress={handlePress}
|
||||||
@@ -244,6 +332,7 @@ export function MatchCard({
|
|||||||
>
|
>
|
||||||
{match.homeTeamName || match.home}
|
{match.homeTeamName || match.home}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
|
{renderCardsInline(cardsCount.homeYellow, cardsCount.homeRed)}
|
||||||
</View>
|
</View>
|
||||||
{renderOddsRow(oddsSettings.selectedBookmakers[0], true)}
|
{renderOddsRow(oddsSettings.selectedBookmakers[0], true)}
|
||||||
</View>
|
</View>
|
||||||
@@ -265,6 +354,7 @@ export function MatchCard({
|
|||||||
>
|
>
|
||||||
{match.awayTeamName || match.away}
|
{match.awayTeamName || match.away}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
|
{renderCardsInline(cardsCount.awayYellow, cardsCount.awayRed)}
|
||||||
</View>
|
</View>
|
||||||
{renderOddsRow(oddsSettings.selectedBookmakers[1], false)}
|
{renderOddsRow(oddsSettings.selectedBookmakers[1], false)}
|
||||||
</View>
|
</View>
|
||||||
@@ -368,7 +458,7 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
marginRight: 6,
|
minWidth: 0,
|
||||||
},
|
},
|
||||||
teamLogo: {
|
teamLogo: {
|
||||||
width: 22,
|
width: 22,
|
||||||
@@ -379,6 +469,7 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: "600",
|
fontWeight: "600",
|
||||||
marginLeft: 6,
|
marginLeft: 6,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
minWidth: 0,
|
||||||
},
|
},
|
||||||
bookmakerOddsRow: {
|
bookmakerOddsRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -427,4 +518,34 @@ const styles = StyleSheet.create({
|
|||||||
width: 36,
|
width: 36,
|
||||||
height: 54,
|
height: 54,
|
||||||
},
|
},
|
||||||
|
cardsInline: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 4,
|
||||||
|
marginLeft: 6,
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
cardBadge: {
|
||||||
|
minWidth: 16,
|
||||||
|
height: 16,
|
||||||
|
borderRadius: 3,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingHorizontal: 3,
|
||||||
|
},
|
||||||
|
cardBadgeYellow: {
|
||||||
|
backgroundColor: "#FFC400",
|
||||||
|
},
|
||||||
|
cardBadgeRed: {
|
||||||
|
backgroundColor: "#FF3B30",
|
||||||
|
},
|
||||||
|
cardBadgeText: {
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: "900",
|
||||||
|
lineHeight: 12,
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
cardBadgeTextDark: {
|
||||||
|
color: "#000",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { OddsSettings, storage } from "@/lib/storage";
|
import { CardsSettings, OddsSettings, storage } from "@/lib/storage";
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
@@ -13,6 +13,7 @@ interface AppState {
|
|||||||
selectedLeagueKey: string | null;
|
selectedLeagueKey: string | null;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
oddsSettings: OddsSettings;
|
oddsSettings: OddsSettings;
|
||||||
|
cardsSettings: CardsSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AppStateContextType {
|
interface AppStateContextType {
|
||||||
@@ -22,6 +23,7 @@ interface AppStateContextType {
|
|||||||
updateLeagueKey: (leagueKey: string | null) => void;
|
updateLeagueKey: (leagueKey: string | null) => void;
|
||||||
updateTimezone: (timezone: string) => void;
|
updateTimezone: (timezone: string) => void;
|
||||||
updateOddsSettings: (settings: OddsSettings) => void;
|
updateOddsSettings: (settings: OddsSettings) => void;
|
||||||
|
updateCardsSettings: (settings: CardsSettings) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppStateContext = createContext<AppStateContextType | undefined>(
|
const AppStateContext = createContext<AppStateContextType | undefined>(
|
||||||
@@ -35,13 +37,17 @@ export function AppStateProvider({ children }: { children: ReactNode }) {
|
|||||||
selectedLeagueKey: null,
|
selectedLeagueKey: null,
|
||||||
timezone: "UTC",
|
timezone: "UTC",
|
||||||
oddsSettings: { enabled: false, selectedBookmakers: [] },
|
oddsSettings: { enabled: false, selectedBookmakers: [] },
|
||||||
|
cardsSettings: { enabled: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initial load of odds settings
|
// Initial load of odds settings and cards settings
|
||||||
storage.getOddsSettings().then((settings) => {
|
storage.getOddsSettings().then((settings) => {
|
||||||
setState((prev) => ({ ...prev, oddsSettings: settings }));
|
setState((prev) => ({ ...prev, oddsSettings: settings }));
|
||||||
});
|
});
|
||||||
|
storage.getCardsSettings().then((settings) => {
|
||||||
|
setState((prev) => ({ ...prev, cardsSettings: settings }));
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const updateSportId = (sportId: number | null) => {
|
const updateSportId = (sportId: number | null) => {
|
||||||
@@ -65,6 +71,11 @@ export function AppStateProvider({ children }: { children: ReactNode }) {
|
|||||||
storage.setOddsSettings(settings);
|
storage.setOddsSettings(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateCardsSettings = (settings: CardsSettings) => {
|
||||||
|
setState((prev) => ({ ...prev, cardsSettings: settings }));
|
||||||
|
storage.setCardsSettings(settings);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppStateContext.Provider
|
<AppStateContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -74,6 +85,7 @@ export function AppStateProvider({ children }: { children: ReactNode }) {
|
|||||||
updateLeagueKey,
|
updateLeagueKey,
|
||||||
updateTimezone,
|
updateTimezone,
|
||||||
updateOddsSettings,
|
updateOddsSettings,
|
||||||
|
updateCardsSettings,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
"odds_unselected": "Unselected",
|
"odds_unselected": "Unselected",
|
||||||
"odds_modal_title": "Select Bookmakers (Max 2)",
|
"odds_modal_title": "Select Bookmakers (Max 2)",
|
||||||
"odds_confirm": "Confirm",
|
"odds_confirm": "Confirm",
|
||||||
|
"cards_title": "Cards Settings",
|
||||||
|
"cards_show": "Show Cards",
|
||||||
|
"cards_enabled": "On",
|
||||||
|
"cards_disabled": "Off",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"click_to_login": "Click to login",
|
"click_to_login": "Click to login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
"odds_unselected": "未选择",
|
"odds_unselected": "未选择",
|
||||||
"odds_modal_title": "选择赔率公司 (最多2项)",
|
"odds_modal_title": "选择赔率公司 (最多2项)",
|
||||||
"odds_confirm": "确定",
|
"odds_confirm": "确定",
|
||||||
|
"cards_title": "卡牌设置",
|
||||||
|
"cards_show": "显示红黄牌",
|
||||||
|
"cards_enabled": "开启",
|
||||||
|
"cards_disabled": "关闭",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
"click_to_login": "点击登录",
|
"click_to_login": "点击登录",
|
||||||
"logout": "登出",
|
"logout": "登出",
|
||||||
|
|||||||
57
lib/api.ts
57
lib/api.ts
@@ -41,12 +41,12 @@ apiClient.interceptors.request.use(async (config) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const refreshTokenApi = async (
|
const refreshTokenApi = async (
|
||||||
request: RefreshTokenRequest
|
request: RefreshTokenRequest,
|
||||||
): Promise<RefreshTokenResponse> => {
|
): Promise<RefreshTokenResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.post<ApiResponse<RefreshTokenResponse>>(
|
const response = await apiClient.post<ApiResponse<RefreshTokenResponse>>(
|
||||||
API_ENDPOINTS.REFRESH_TOKEN,
|
API_ENDPOINTS.REFRESH_TOKEN,
|
||||||
request
|
request,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -81,13 +81,13 @@ apiClient.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const fetchSports = async (): Promise<Sport[]> => {
|
export const fetchSports = async (): Promise<Sport[]> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<ApiListResponse<Sport>>>(
|
const response = await apiClient.get<ApiResponse<ApiListResponse<Sport>>>(
|
||||||
API_ENDPOINTS.SPORTS
|
API_ENDPOINTS.SPORTS,
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data.list;
|
return response.data.data.list;
|
||||||
@@ -103,7 +103,7 @@ export const fetchSports = async (): Promise<Sport[]> => {
|
|||||||
export const fetchCountries = async (): Promise<Country[]> => {
|
export const fetchCountries = async (): Promise<Country[]> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<ApiListResponse<Country>>>(
|
const response = await apiClient.get<ApiResponse<ApiListResponse<Country>>>(
|
||||||
API_ENDPOINTS.COUNTRIES
|
API_ENDPOINTS.COUNTRIES,
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data.list;
|
return response.data.data.list;
|
||||||
@@ -127,7 +127,7 @@ export const fetchLeagues = async (params: {
|
|||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<ApiListResponse<League>>>(
|
const response = await apiClient.get<ApiResponse<ApiListResponse<League>>>(
|
||||||
API_ENDPOINTS.LEAGUES,
|
API_ENDPOINTS.LEAGUES,
|
||||||
{ params }
|
{ params },
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
@@ -179,7 +179,7 @@ export const fetchTodayMatches = async (options: {
|
|||||||
|
|
||||||
const response = await apiClient.get<ApiResponse<ApiListResponse<Match>>>(
|
const response = await apiClient.get<ApiResponse<ApiListResponse<Match>>>(
|
||||||
API_ENDPOINTS.MATCHES_TODAY,
|
API_ENDPOINTS.MATCHES_TODAY,
|
||||||
{ params }
|
{ params },
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
@@ -192,11 +192,11 @@ export const fetchTodayMatches = async (options: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const fetchMatchDetail = async (
|
export const fetchMatchDetail = async (
|
||||||
id: string
|
id: string,
|
||||||
): Promise<MatchDetailData> => {
|
): Promise<MatchDetailData> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<MatchDetailData>>(
|
const response = await apiClient.get<ApiResponse<MatchDetailData>>(
|
||||||
API_ENDPOINTS.MATCH_DETAIL(id)
|
API_ENDPOINTS.MATCH_DETAIL(id),
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
@@ -211,7 +211,7 @@ export const fetchMatchDetail = async (
|
|||||||
export const fetchLiveScore = async (
|
export const fetchLiveScore = async (
|
||||||
sportId: number,
|
sportId: number,
|
||||||
leagueId?: number,
|
leagueId?: number,
|
||||||
timezone?: string
|
timezone?: string,
|
||||||
): Promise<LiveScoreMatch[]> => {
|
): Promise<LiveScoreMatch[]> => {
|
||||||
// console.log("Fetching live scores with params:", {
|
// console.log("Fetching live scores with params:", {
|
||||||
// sportId,
|
// sportId,
|
||||||
@@ -234,10 +234,11 @@ export const fetchLiveScore = async (
|
|||||||
|
|
||||||
const response = await apiClient.get<ApiResponse<LiveScoreMatch[]>>(
|
const response = await apiClient.get<ApiResponse<LiveScoreMatch[]>>(
|
||||||
API_ENDPOINTS.LIVESCORE,
|
API_ENDPOINTS.LIVESCORE,
|
||||||
{ params }
|
{ params },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
|
// console.log("Live score data:", JSON.stringify(response.data.data));
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +252,7 @@ export const fetchLiveScore = async (
|
|||||||
export const fetchUpcomingMatches = async (
|
export const fetchUpcomingMatches = async (
|
||||||
sportId: number,
|
sportId: number,
|
||||||
leagueKey: string,
|
leagueKey: string,
|
||||||
limit: number = 50
|
limit: number = 50,
|
||||||
): Promise<UpcomingMatch[]> => {
|
): Promise<UpcomingMatch[]> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<
|
const response = await apiClient.get<
|
||||||
@@ -278,7 +279,7 @@ export const fetchUpcomingMatches = async (
|
|||||||
// 获取实时赔率(足球/网球使用 LiveOdds,篮球/板球使用 Odds)
|
// 获取实时赔率(足球/网球使用 LiveOdds,篮球/板球使用 Odds)
|
||||||
export const fetchOdds = async (
|
export const fetchOdds = async (
|
||||||
sportId: number,
|
sportId: number,
|
||||||
matchId: number
|
matchId: number,
|
||||||
): Promise<OddsData> => {
|
): Promise<OddsData> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<OddsData>>(
|
const response = await apiClient.get<ApiResponse<OddsData>>(
|
||||||
@@ -288,7 +289,7 @@ export const fetchOdds = async (
|
|||||||
sport_id: sportId,
|
sport_id: sportId,
|
||||||
match_id: matchId,
|
match_id: matchId,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -305,7 +306,7 @@ export const fetchOdds = async (
|
|||||||
// 搜索联赛、球队或球员
|
// 搜索联赛、球队或球员
|
||||||
export const fetchSearch = async (
|
export const fetchSearch = async (
|
||||||
query: string,
|
query: string,
|
||||||
sportId?: number
|
sportId?: number,
|
||||||
): Promise<SearchResult> => {
|
): Promise<SearchResult> => {
|
||||||
try {
|
try {
|
||||||
const params: { q: string; sportId?: number } = { q: query };
|
const params: { q: string; sportId?: number } = { q: query };
|
||||||
@@ -315,7 +316,7 @@ export const fetchSearch = async (
|
|||||||
|
|
||||||
const response = await apiClient.get<ApiResponse<SearchResult>>(
|
const response = await apiClient.get<ApiResponse<SearchResult>>(
|
||||||
API_ENDPOINTS.SEARCH,
|
API_ENDPOINTS.SEARCH,
|
||||||
{ params }
|
{ params },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -338,7 +339,7 @@ export const fetchH2H = async (
|
|||||||
firstPlayerId?: number;
|
firstPlayerId?: number;
|
||||||
secondPlayerId?: number;
|
secondPlayerId?: number;
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
}
|
},
|
||||||
): Promise<H2HData> => {
|
): Promise<H2HData> => {
|
||||||
try {
|
try {
|
||||||
const params: {
|
const params: {
|
||||||
@@ -370,7 +371,7 @@ export const fetchH2H = async (
|
|||||||
|
|
||||||
const response = await apiClient.get<ApiResponse<H2HData>>(
|
const response = await apiClient.get<ApiResponse<H2HData>>(
|
||||||
API_ENDPOINTS.H2H,
|
API_ENDPOINTS.H2H,
|
||||||
{ params }
|
{ params },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -385,12 +386,12 @@ export const fetchH2H = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const appleSignIn = async (
|
export const appleSignIn = async (
|
||||||
request: AppleSignInRequest
|
request: AppleSignInRequest,
|
||||||
): Promise<AppleSignInResponse> => {
|
): Promise<AppleSignInResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.post<ApiResponse<AppleSignInResponse>>(
|
const response = await apiClient.post<ApiResponse<AppleSignInResponse>>(
|
||||||
API_ENDPOINTS.APPLE_SIGNIN,
|
API_ENDPOINTS.APPLE_SIGNIN,
|
||||||
request
|
request,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -407,7 +408,7 @@ export const appleSignIn = async (
|
|||||||
export const logout = async (): Promise<string> => {
|
export const logout = async (): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.post<ApiResponse<string>>(
|
const response = await apiClient.post<ApiResponse<string>>(
|
||||||
API_ENDPOINTS.LOGOUT
|
API_ENDPOINTS.LOGOUT,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -426,7 +427,7 @@ export const refreshToken = refreshTokenApi;
|
|||||||
export const fetchUserProfile = async (): Promise<UserProfile> => {
|
export const fetchUserProfile = async (): Promise<UserProfile> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<UserProfile>>(
|
const response = await apiClient.get<ApiResponse<UserProfile>>(
|
||||||
API_ENDPOINTS.USER_PROFILE
|
API_ENDPOINTS.USER_PROFILE,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
@@ -450,7 +451,7 @@ export const addFavorite = async (request: FavoriteRequest): Promise<any> => {
|
|||||||
// console.log("Adding favorite with request:", request);
|
// console.log("Adding favorite with request:", request);
|
||||||
const response = await apiClient.post<ApiResponse<any>>(
|
const response = await apiClient.post<ApiResponse<any>>(
|
||||||
API_ENDPOINTS.FAVORITES,
|
API_ENDPOINTS.FAVORITES,
|
||||||
request
|
request,
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
@@ -475,7 +476,7 @@ export const removeFavorite = async (request: {
|
|||||||
console.log("Removing favorite with request:", request);
|
console.log("Removing favorite with request:", request);
|
||||||
const response = await apiClient.delete<ApiResponse<any>>(
|
const response = await apiClient.delete<ApiResponse<any>>(
|
||||||
API_ENDPOINTS.FAVORITES,
|
API_ENDPOINTS.FAVORITES,
|
||||||
{ data: request }
|
{ data: request },
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
@@ -489,14 +490,14 @@ export const removeFavorite = async (request: {
|
|||||||
|
|
||||||
export const checkFavorite = async (
|
export const checkFavorite = async (
|
||||||
type: string,
|
type: string,
|
||||||
typeId: string
|
typeId: string,
|
||||||
): Promise<FavoriteCheckResponse> => {
|
): Promise<FavoriteCheckResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<FavoriteCheckResponse>>(
|
const response = await apiClient.get<ApiResponse<FavoriteCheckResponse>>(
|
||||||
API_ENDPOINTS.CHECK_FAVORITE,
|
API_ENDPOINTS.CHECK_FAVORITE,
|
||||||
{
|
{
|
||||||
params: { type, typeId },
|
params: { type, typeId },
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
@@ -511,14 +512,14 @@ export const checkFavorite = async (
|
|||||||
export const fetchFavorites = async (
|
export const fetchFavorites = async (
|
||||||
type: string,
|
type: string,
|
||||||
page: number = 1,
|
page: number = 1,
|
||||||
pageSize: number = 20
|
pageSize: number = 20,
|
||||||
): Promise<FavoriteListResponse> => {
|
): Promise<FavoriteListResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<ApiResponse<FavoriteListResponse>>(
|
const response = await apiClient.get<ApiResponse<FavoriteListResponse>>(
|
||||||
API_ENDPOINTS.FAVORITES,
|
API_ENDPOINTS.FAVORITES,
|
||||||
{
|
{
|
||||||
params: { type, page, pageSize },
|
params: { type, page, pageSize },
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
if (response.data.code === 0) {
|
if (response.data.code === 0) {
|
||||||
console.log("Fetched favorites:", JSON.stringify(response.data.data));
|
console.log("Fetched favorites:", JSON.stringify(response.data.data));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const STORAGE_KEYS = {
|
|||||||
REFRESH_TOKEN: "refresh_token",
|
REFRESH_TOKEN: "refresh_token",
|
||||||
USER: "user",
|
USER: "user",
|
||||||
ODDS_SETTINGS: "odds_settings",
|
ODDS_SETTINGS: "odds_settings",
|
||||||
|
CARDS_SETTINGS: "cards_settings",
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface OddsSettings {
|
export interface OddsSettings {
|
||||||
@@ -13,6 +14,10 @@ export interface OddsSettings {
|
|||||||
selectedBookmakers: string[];
|
selectedBookmakers: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CardsSettings {
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const storage = {
|
export const storage = {
|
||||||
async setAccessToken(token: string): Promise<void> {
|
async setAccessToken(token: string): Promise<void> {
|
||||||
await AsyncStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
|
await AsyncStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
|
||||||
@@ -61,12 +66,30 @@ export const storage = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async setCardsSettings(settings: CardsSettings): Promise<void> {
|
||||||
|
await AsyncStorage.setItem(
|
||||||
|
STORAGE_KEYS.CARDS_SETTINGS,
|
||||||
|
JSON.stringify(settings),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getCardsSettings(): Promise<CardsSettings> {
|
||||||
|
const settingsStr = await AsyncStorage.getItem(STORAGE_KEYS.CARDS_SETTINGS);
|
||||||
|
if (!settingsStr) return { enabled: false };
|
||||||
|
try {
|
||||||
|
return JSON.parse(settingsStr) as CardsSettings;
|
||||||
|
} catch {
|
||||||
|
return { enabled: false };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async clear(): Promise<void> {
|
async clear(): Promise<void> {
|
||||||
await AsyncStorage.multiRemove([
|
await AsyncStorage.multiRemove([
|
||||||
STORAGE_KEYS.ACCESS_TOKEN,
|
STORAGE_KEYS.ACCESS_TOKEN,
|
||||||
STORAGE_KEYS.REFRESH_TOKEN,
|
STORAGE_KEYS.REFRESH_TOKEN,
|
||||||
STORAGE_KEYS.USER,
|
STORAGE_KEYS.USER,
|
||||||
STORAGE_KEYS.ODDS_SETTINGS,
|
STORAGE_KEYS.ODDS_SETTINGS,
|
||||||
|
STORAGE_KEYS.CARDS_SETTINGS,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user