import { MatchCardLeague } from "@/components/match-card-league"; import { ThemedText } from "@/components/themed-text"; import { useTheme } from "@/context/ThemeContext"; import { fetchLeagues, fetchTodayMatches } from "@/lib/api"; import { League, Match } from "@/types/api"; import React, { useState } from "react"; import { Image, LayoutAnimation, Platform, ScrollView, StyleSheet, TouchableOpacity, UIManager, View, } from "react-native"; // 开启 Android 上的 LayoutAnimation if ( Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental ) { UIManager.setLayoutAnimationEnabledExperimental(true); } interface MatchesByLeagueProps { sportId: number; date: Date; timezone: string; onFavoriteToggle?: (matchId: string, isFav: boolean) => void; enableCollapsible?: boolean; } export function MatchesByLeague({ sportId, date, timezone, onFavoriteToggle, enableCollapsible = true, }: MatchesByLeagueProps) { const { theme } = useTheme(); const isDark = theme === "dark"; const [leagues, setLeagues] = useState([]); const [collapsed, setCollapsed] = useState>({}); const [matchesByLeagueKey, setMatchesByLeagueKey] = useState< Record >({}); const [loadingLeagueKey, setLoadingLeagueKey] = useState< Record >({}); const dateStr = React.useMemo(() => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; }, [date]); React.useEffect(() => { let mounted = true; fetchLeagues({ sportId, date: dateStr, page: 1, pageSize: 200, sortBy: "matchCount", sortOrder: "desc", }) .then((res) => { if (!mounted) return; setLeagues(res.list); setCollapsed((prev) => { const next: Record = {}; res.list.forEach((l) => { next[l.key] = prev[l.key] !== undefined ? prev[l.key] : true; }); return next; }); setMatchesByLeagueKey({}); setLoadingLeagueKey({}); }) .catch(() => { }); return () => { mounted = false; }; }, [sportId, dateStr]); const toggleCollapse = async (leagueKey: string) => { if (!enableCollapsible) return; LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); setCollapsed((prev) => ({ ...prev, [leagueKey]: !prev[leagueKey] })); const nextCollapsed = !collapsed[leagueKey]; if (nextCollapsed) return; if (matchesByLeagueKey[leagueKey]) return; setLoadingLeagueKey((prev) => ({ ...prev, [leagueKey]: true })); try { const res = await fetchTodayMatches({ sportId, date: dateStr, timezone, leagueKey, page: 1, pageSize: 50, }); setMatchesByLeagueKey((prev) => ({ ...prev, [leagueKey]: res.list })); } finally { setLoadingLeagueKey((prev) => ({ ...prev, [leagueKey]: false })); } }; if (leagues.length === 0) { return ( 暂无联赛 ); } return ( {leagues.map((league) => { const isCollapsed = collapsed[league.key] !== false; const leagueMatches = matchesByLeagueKey[league.key] || []; const isLoading = !!loadingLeagueKey[league.key]; return ( 0 ? 0.7 : 1} onPress={() => { if (enableCollapsible && league.matchCount > 0) { toggleCollapse(league.key); } }} style={styles.leagueHeaderWrapper} > {league.name} {league.countryName || "International"} {league.matchCount} {enableCollapsible && league.matchCount > 0 && ( {isCollapsed ? "⌄" : "⌃"} )} {!isCollapsed && ( {isLoading ? ( <> {[0, 1, 2].map((i) => ( ))} ) : ( leagueMatches.map((match, index) => ( )) )} )} ); })} ); } const styles = StyleSheet.create({ container: { flex: 1, }, leagueSection: { marginBottom: 16, }, leagueHeaderWrapper: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingHorizontal: 16, paddingVertical: 12, backgroundColor: "transparent", }, leagueHeaderLeft: { flexDirection: "row", alignItems: "center", flex: 1, }, leagueLogo: { width: 36, height: 36, borderRadius: 6, marginRight: 12, backgroundColor: "#3A3A3C", }, leagueInfoText: { justifyContent: "center", }, leagueTitle: { fontSize: 17, fontWeight: "600", color: "#FFFFFF", marginBottom: 3, }, countryRow: { flexDirection: "row", alignItems: "center", }, countryFlag: { width: 16, height: 12, marginRight: 5, borderRadius: 2, }, countryName: { fontSize: 13, color: "#8E8E93", fontWeight: "500", }, leagueHeaderRight: { flexDirection: "row", alignItems: "center", gap: 8, }, matchCount: { fontSize: 15, color: "#8E8E93", fontWeight: "600", }, chevron: { fontSize: 16, color: "#8E8E93", fontWeight: '600', }, matchListContainer: { backgroundColor: "#1C1C1E", borderRadius: 12, marginHorizontal: 16, overflow: "hidden", }, // 布局与 MatchCardLeague 保持一致,便于骨架对齐 leftColumn: { width: 50, justifyContent: "center", alignItems: "flex-start", marginRight: 8, }, teamsColumn: { flex: 1, justifyContent: "center", paddingRight: 8, }, rightWrapper: { flexDirection: "row", alignItems: "center", }, matchCardWrapper: { }, matchCardDivider: { borderBottomWidth: 0.5, borderBottomColor: "#3A3A3C", }, favoriteButton: { paddingLeft: 12, justifyContent: "center", alignItems: "center", }, skeletonRow: { flexDirection: "row", alignItems: "center", paddingHorizontal: 16, paddingVertical: 14, }, skeletonLine: { height: 8, borderRadius: 4, backgroundColor: "#2C2C2E", }, skeletonAvatar: { width: 20, height: 20, borderRadius: 10, backgroundColor: "#2C2C2E", marginRight: 10, }, skeletonTeamRow: { flexDirection: "row", alignItems: "center", }, skeletonScoreBox: { width: 24, height: 32, borderRadius: 8, backgroundColor: "#2C2C2E", }, skeletonCircle: { width: 16, height: 16, borderRadius: 8, backgroundColor: "#2C2C2E", }, emptyContainer: { flex: 1, justifyContent: "center", alignItems: "center", paddingTop: 100, }, });