添加卡牌设置功能,更新状态管理和界面显示逻辑

This commit is contained in:
yuchenglong
2026-01-20 10:12:12 +08:00
parent 97f61e68bd
commit 08d05df8be
7 changed files with 242 additions and 38 deletions

View File

@@ -3,8 +3,13 @@ import { IconSymbol } from "@/components/ui/icon-symbol";
import { Colors } from "@/constants/theme";
import { useAppState } from "@/context/AppStateContext";
import { useTheme } from "@/context/ThemeContext";
import { addFavorite, fetchOdds, removeFavorite } from "@/lib/api";
import { Match, OddsItem } from "@/types/api";
import {
addFavorite,
fetchLiveScore,
fetchOdds,
removeFavorite,
} from "@/lib/api";
import { LiveScoreMatch, Match, OddsItem } from "@/types/api";
import { Image } from "expo-image";
import { LinearGradient } from "expo-linear-gradient";
import { useRouter } from "expo-router";
@@ -28,9 +33,11 @@ export function MatchCard({
const [isFav, setIsFav] = useState(match.fav);
const [loading, setLoading] = useState(false);
const [odds, setOdds] = useState<OddsItem[]>(match.odds || []);
const [liveDetail, setLiveDetail] = useState<LiveScoreMatch | null>(null);
// console.log("MatchCard render:", JSON.stringify(match));
const oddsSettings = state.oddsSettings;
const cardsSettings = state.cardsSettings;
useEffect(() => {
if (
@@ -52,6 +59,20 @@ export function MatchCard({
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 改变时,更新内部状态
useEffect(() => {
setIsFav(match.fav);
@@ -90,10 +111,53 @@ export function MatchCard({
if (m) return { home: m[1], away: m[2], hasScore: true };
if (s && s !== "-" && s !== "0 - 0")
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 };
}, [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 = () => {
if (onPress) {
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 (
<Pressable
onPress={handlePress}
@@ -244,6 +332,7 @@ export function MatchCard({
>
{match.homeTeamName || match.home}
</ThemedText>
{renderCardsInline(cardsCount.homeYellow, cardsCount.homeRed)}
</View>
{renderOddsRow(oddsSettings.selectedBookmakers[0], true)}
</View>
@@ -265,6 +354,7 @@ export function MatchCard({
>
{match.awayTeamName || match.away}
</ThemedText>
{renderCardsInline(cardsCount.awayYellow, cardsCount.awayRed)}
</View>
{renderOddsRow(oddsSettings.selectedBookmakers[1], false)}
</View>
@@ -368,7 +458,7 @@ const styles = StyleSheet.create({
flexDirection: "row",
alignItems: "center",
flex: 1,
marginRight: 6,
minWidth: 0,
},
teamLogo: {
width: 22,
@@ -379,6 +469,7 @@ const styles = StyleSheet.create({
fontWeight: "600",
marginLeft: 6,
flex: 1,
minWidth: 0,
},
bookmakerOddsRow: {
flexDirection: "row",
@@ -427,4 +518,34 @@ const styles = StyleSheet.create({
width: 36,
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",
},
});