网球详情页

This commit is contained in:
xianyi
2026-01-22 14:05:39 +08:00
parent 9e7f8dadec
commit a279083252
13 changed files with 443 additions and 46 deletions

View File

@@ -1,6 +1,7 @@
import { ThemedText } from "@/components/themed-text";
import { IconSymbol } from "@/components/ui/icon-symbol";
import { addFavorite, checkFavorite, removeFavorite } from "@/lib/api";
import { getInitials, getLogoGradient } from "@/lib/avatar-utils";
import { storage } from "@/lib/storage";
import { MatchDetailData } from "@/types/api";
import { LinearGradient } from "expo-linear-gradient";
@@ -19,6 +20,7 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
const router = useRouter();
const { t } = useTranslation();
const { match } = data;
const isTennis = match.sportId === 3;
const [isFav, setIsFav] = useState(false);
const [favLoading, setFavLoading] = useState(false);
@@ -45,39 +47,45 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
}
}, [match.eventKey]);
// 检查主队收藏状态
// 检查主队/第一球员收藏状态
React.useEffect(() => {
const loadHomeFav = async () => {
const token = await storage.getAccessToken();
if (!token) return;
try {
const res = await checkFavorite("team", match.homeTeamKey.toString());
setIsHomeFav(res.isFavorite);
const teamKey = isTennis ? match.firstPlayerKey : match.homeTeamKey;
if (teamKey) {
const res = await checkFavorite("team", teamKey.toString());
setIsHomeFav(res.isFavorite);
}
} catch (error) {
console.error("Check home team favorite status error:", error);
}
};
if (match.homeTeamKey) {
if (isTennis ? match.firstPlayerKey : match.homeTeamKey) {
loadHomeFav();
}
}, [match.homeTeamKey]);
}, [match.homeTeamKey, match.firstPlayerKey, isTennis]);
// 检查客队收藏状态
// 检查客队/第二球员收藏状态
React.useEffect(() => {
const loadAwayFav = async () => {
const token = await storage.getAccessToken();
if (!token) return;
try {
const res = await checkFavorite("team", match.awayTeamKey.toString());
setIsAwayFav(res.isFavorite);
const teamKey = isTennis ? match.secondPlayerKey : match.awayTeamKey;
if (teamKey) {
const res = await checkFavorite("team", teamKey.toString());
setIsAwayFav(res.isFavorite);
}
} catch (error) {
console.error("Check away team favorite status error:", error);
}
};
if (match.awayTeamKey) {
if (isTennis ? match.secondPlayerKey : match.awayTeamKey) {
loadAwayFav();
}
}, [match.awayTeamKey]);
}, [match.awayTeamKey, match.secondPlayerKey, isTennis]);
const toggleFavorite = async () => {
if (favLoading) return;
@@ -111,7 +119,7 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
const setFav = isHome ? setIsHomeFav : setIsAwayFav;
const loading = isHome ? homeFavLoading : awayFavLoading;
if (loading) return;
if (loading || !teamKey) return;
setLoading(true);
const newFavState = !isTeamFav;
@@ -137,6 +145,36 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
}
};
const getScoreDisplay = () => {
if (isTennis) {
const scores = (match.scores as any) || [];
if (scores.length > 0) {
const lastSet = scores[scores.length - 1];
return `${lastSet.score_first || "0"}-${lastSet.score_second || "0"}`;
}
return match.eventGameResult && match.eventGameResult !== "-"
? match.eventGameResult
: "0-0";
}
return match.eventFinalResult && match.eventFinalResult !== "-"
? match.eventFinalResult
: "0-0";
};
const firstPlayerName = isTennis ? match.eventFirstPlayer : match.eventHomeTeam;
const secondPlayerName = isTennis ? match.eventSecondPlayer : match.eventAwayTeam;
const firstPlayerLogo = isTennis ? match.eventFirstPlayerLogo : match.homeTeamLogo;
const secondPlayerLogo = isTennis ? match.eventSecondPlayerLogo : match.awayTeamLogo;
const firstPlayerKey = isTennis ? match.firstPlayerKey : match.homeTeamKey;
const secondPlayerKey = isTennis ? match.secondPlayerKey : match.awayTeamKey;
const hasFirstLogo = firstPlayerLogo && firstPlayerLogo.trim() !== "" && !firstPlayerLogo.includes("placehold");
const hasSecondLogo = secondPlayerLogo && secondPlayerLogo.trim() !== "" && !secondPlayerLogo.includes("placehold");
const firstGradient = getLogoGradient(firstPlayerName || "");
const secondGradient = getLogoGradient(secondPlayerName || "");
const firstInitials = getInitials(firstPlayerName || "");
const secondInitials = getInitials(secondPlayerName || "");
return (
<LinearGradient
colors={["#521e10", "#0e0e10"]}
@@ -184,8 +222,12 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
<View style={styles.teamInfo}>
<View style={styles.teamLogoRow}>
<TouchableOpacity
onPress={() => toggleTeamFavorite(match.homeTeamKey, true)}
disabled={homeFavLoading}
onPress={() => {
if (firstPlayerKey) {
toggleTeamFavorite(firstPlayerKey, true);
}
}}
disabled={homeFavLoading || !firstPlayerKey}
style={styles.starBtnLeft}
>
<IconSymbol
@@ -195,41 +237,69 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
/>
</TouchableOpacity>
<View style={styles.logoContainer}>
<Image
source={{ uri: match.homeTeamLogo }}
style={styles.teamLogo}
/>
{hasFirstLogo ? (
<Image
source={{ uri: firstPlayerLogo }}
style={styles.teamLogo}
/>
) : (
<LinearGradient
colors={[firstGradient.color1, firstGradient.color2]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.teamLogoGradient}
>
<ThemedText style={styles.teamLogoText}>{firstInitials}</ThemedText>
</LinearGradient>
)}
</View>
</View>
<ThemedText style={styles.teamName} numberOfLines={2}>
{match.eventHomeTeam}
{firstPlayerName}
</ThemedText>
</View>
<View style={styles.centerScore}>
<View style={styles.scoreBox}>
<ThemedText style={styles.scoreValue}>
{match.eventFinalResult && match.eventFinalResult !== "-"
? match.eventFinalResult
: "0-0"}
{getScoreDisplay()}
</ThemedText>
</View>
<TouchableOpacity style={styles.fieldBtn}>
<IconSymbol name="football-outline" size={16} color="#FFF" />
<IconSymbol
name={isTennis ? "tennisball-outline" : "football-outline"}
size={16}
color="#FFF"
/>
</TouchableOpacity>
</View>
<View style={styles.teamInfo}>
<View style={styles.teamLogoRow}>
<View style={styles.logoContainer}>
<Image
source={{ uri: match.awayTeamLogo }}
style={styles.teamLogo}
/>
{hasSecondLogo ? (
<Image
source={{ uri: secondPlayerLogo }}
style={styles.teamLogo}
/>
) : (
<LinearGradient
colors={[secondGradient.color1, secondGradient.color2]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.teamLogoGradient}
>
<ThemedText style={styles.teamLogoText}>{secondInitials}</ThemedText>
</LinearGradient>
)}
</View>
<TouchableOpacity
onPress={() => toggleTeamFavorite(match.awayTeamKey, false)}
disabled={awayFavLoading}
onPress={() => {
if (secondPlayerKey) {
toggleTeamFavorite(secondPlayerKey, false);
}
}}
disabled={awayFavLoading || !secondPlayerKey}
style={styles.starBtnRight}
>
<IconSymbol
@@ -240,7 +310,7 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
</TouchableOpacity>
</View>
<ThemedText style={styles.teamName} numberOfLines={2}>
{match.eventAwayTeam}
{secondPlayerName}
</ThemedText>
</View>
</View>
@@ -326,6 +396,19 @@ const styles = StyleSheet.create({
width: 60,
height: 60,
resizeMode: "contain",
borderRadius: 30,
},
teamLogoGradient: {
width: 60,
height: 60,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
},
teamLogoText: {
fontSize: 20,
fontWeight: "700",
color: "rgba(255, 255, 255, 0.92)",
},
teamName: {
color: "#FFF",