添加赔率设置功能,支持选择博彩公司并展示赔率信息;优化状态管理和国际化文本
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
import { ThemedText } from "@/components/themed-text";
|
||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||
import { Colors } from "@/constants/theme";
|
||||
import { useAppState } from "@/context/AppStateContext";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { addFavorite, removeFavorite } from "@/lib/api";
|
||||
import { Match } from "@/types/api";
|
||||
import { addFavorite, fetchOdds, removeFavorite } from "@/lib/api";
|
||||
import { Match, OddsItem } from "@/types/api";
|
||||
import { Image } from "expo-image";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { useRouter } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Pressable, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||
|
||||
interface MatchCardProps {
|
||||
@@ -23,12 +24,36 @@ export function MatchCard({
|
||||
}: MatchCardProps) {
|
||||
const router = useRouter();
|
||||
const { theme } = useTheme();
|
||||
const { state } = useAppState();
|
||||
const [isFav, setIsFav] = useState(match.fav);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [odds, setOdds] = useState<OddsItem[]>(match.odds || []);
|
||||
// console.log("MatchCard render:", JSON.stringify(match));
|
||||
|
||||
const oddsSettings = state.oddsSettings;
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
oddsSettings.enabled &&
|
||||
oddsSettings.selectedBookmakers.length > 0 &&
|
||||
!match.odds
|
||||
) {
|
||||
fetchOdds(match.sportId || 1, parseInt(match.id))
|
||||
.then((res) => {
|
||||
const matchOdds = res[match.id]?.data || [];
|
||||
setOdds(matchOdds);
|
||||
})
|
||||
.catch((err) => console.log("Fetch match card odds error:", err));
|
||||
}
|
||||
}, [
|
||||
oddsSettings.enabled,
|
||||
oddsSettings.selectedBookmakers,
|
||||
match.id,
|
||||
match.odds,
|
||||
]);
|
||||
|
||||
// 当外部传入的 match.fav 改变时,更新内部状态
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
setIsFav(match.fav);
|
||||
}, [match.fav]);
|
||||
|
||||
@@ -39,6 +64,8 @@ export function MatchCard({
|
||||
const scoreBorder = isDark ? "rgba(255,255,255,0.18)" : "rgba(0,0,0,0.12)";
|
||||
const scoreBg = isDark ? "rgba(255,255,255,0.04)" : "rgba(255,255,255,0.6)";
|
||||
|
||||
const oddBadgeBg = isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.03)";
|
||||
|
||||
const isLive = React.useMemo(() => {
|
||||
return !!match.isLive;
|
||||
}, [match.isLive]);
|
||||
@@ -149,42 +176,103 @@ export function MatchCard({
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
{/* Middle: Teams */}
|
||||
{/* Middle: Teams & Odds */}
|
||||
<View style={styles.middle}>
|
||||
<View style={styles.teamRow}>
|
||||
{match.homeTeamLogo ? (
|
||||
<Image
|
||||
source={{ uri: match.homeTeamLogo }}
|
||||
style={styles.teamLogo}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : null}
|
||||
<ThemedText
|
||||
type="defaultSemiBold"
|
||||
style={styles.teamLine}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{match.homeTeamName || match.home}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<View style={styles.teamRow}>
|
||||
{match.awayTeamLogo ? (
|
||||
<Image
|
||||
source={{ uri: match.awayTeamLogo }}
|
||||
style={styles.teamLogo}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : null}
|
||||
<ThemedText
|
||||
type="defaultSemiBold"
|
||||
style={styles.teamLine}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{match.awayTeamName || match.away}
|
||||
</ThemedText>
|
||||
<View style={styles.teamContainer}>
|
||||
<View style={styles.teamRow}>
|
||||
{match.homeTeamLogo ? (
|
||||
<Image
|
||||
source={{ uri: match.homeTeamLogo }}
|
||||
style={styles.teamLogo}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : null}
|
||||
<ThemedText
|
||||
type="defaultSemiBold"
|
||||
style={styles.teamLine}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{match.homeTeamName || match.home}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<View style={styles.teamRow}>
|
||||
{match.awayTeamLogo ? (
|
||||
<Image
|
||||
source={{ uri: match.awayTeamLogo }}
|
||||
style={styles.teamLogo}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : null}
|
||||
<ThemedText
|
||||
type="defaultSemiBold"
|
||||
style={styles.teamLine}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{match.awayTeamName || match.away}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Odds Section */}
|
||||
{oddsSettings.enabled && odds.length > 0 && (
|
||||
<View style={styles.oddsContainer}>
|
||||
{oddsSettings.selectedBookmakers.map((bookmaker, idx) => {
|
||||
const item = odds.find((o) => o.odd_bookmakers === bookmaker);
|
||||
if (!item) return null;
|
||||
// Pick 3 values to display. Using odd_1, odd_x, odd_2 for example.
|
||||
// Or try to match the screenshot's 3-column style.
|
||||
const val1 = item.odd_1 || item.ah0_1 || "-";
|
||||
const val2 = item.odd_x || "0" || "-";
|
||||
const val3 = item.odd_2 || item.ah0_2 || "-";
|
||||
|
||||
return (
|
||||
<View key={bookmaker} style={styles.bookmakerOddsRow}>
|
||||
<View
|
||||
style={[styles.oddBadge, { backgroundColor: oddBadgeBg }]}
|
||||
>
|
||||
<ThemedText
|
||||
style={[
|
||||
styles.oddText,
|
||||
{ color: isDark ? "#fff" : "#000" },
|
||||
idx === 0 && styles.oddTextHighlight,
|
||||
]}
|
||||
>
|
||||
{val1}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<View
|
||||
style={[styles.oddBadge, { backgroundColor: oddBadgeBg }]}
|
||||
>
|
||||
<ThemedText
|
||||
style={[
|
||||
styles.oddText,
|
||||
{ color: isDark ? "#fff" : "#000" },
|
||||
idx === 0 && styles.oddTextHighlight,
|
||||
]}
|
||||
>
|
||||
{val2}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<View
|
||||
style={[styles.oddBadge, { backgroundColor: oddBadgeBg }]}
|
||||
>
|
||||
<ThemedText
|
||||
style={[
|
||||
styles.oddText,
|
||||
{ color: isDark ? "#fff" : "#000" },
|
||||
idx === 0 && styles.oddTextHighlight,
|
||||
]}
|
||||
>
|
||||
{val3}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Right: Score box + favorite */}
|
||||
@@ -270,6 +358,14 @@ const styles = StyleSheet.create({
|
||||
fontWeight: "bold",
|
||||
},
|
||||
middle: {
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: 8,
|
||||
minWidth: 0,
|
||||
},
|
||||
teamContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
@@ -289,6 +385,31 @@ const styles = StyleSheet.create({
|
||||
lineHeight: 18,
|
||||
flex: 1,
|
||||
},
|
||||
oddsContainer: {
|
||||
gap: 8,
|
||||
alignItems: "flex-end",
|
||||
},
|
||||
bookmakerOddsRow: {
|
||||
flexDirection: "row",
|
||||
gap: 4,
|
||||
},
|
||||
oddBadge: {
|
||||
backgroundColor: "rgba(0,0,0,0.03)",
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 6,
|
||||
minWidth: 40,
|
||||
alignItems: "center",
|
||||
},
|
||||
oddText: {
|
||||
fontSize: 11,
|
||||
fontWeight: "600",
|
||||
opacity: 0.8,
|
||||
},
|
||||
oddTextHighlight: {
|
||||
color: "#FF9500",
|
||||
opacity: 1,
|
||||
},
|
||||
right: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
|
||||
Reference in New Issue
Block a user