Files
physical-expo/components/matches-by-league.tsx
2026-01-19 14:50:47 +08:00

268 lines
7.1 KiB
TypeScript

import { MatchCardLeague } from "@/components/match-card-league";
import { ThemedText } from "@/components/themed-text";
import { useTheme } from "@/context/ThemeContext";
import { 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 {
matches: Match[];
onFavoriteToggle?: (matchId: string, isFav: boolean) => void;
/**
* 是否支持折叠收起
* @default true
*/
enableCollapsible?: boolean;
}
export function MatchesByLeague({
matches,
onFavoriteToggle,
enableCollapsible = true,
}: MatchesByLeagueProps) {
const { theme } = useTheme();
const isDark = theme === "dark";
// 数据分组逻辑
const matchesByLeague = React.useMemo(() => {
const grouped: Record<string, Match[]> = {};
matches.forEach((match) => {
const league = match.league || match.leagueName || "其他";
if (!grouped[league]) {
grouped[league] = [];
}
grouped[league].push(match);
});
return grouped;
}, [matches]);
const leagueNames = Object.keys(matchesByLeague);
// 状态:记录哪些联赛被折叠了 (Key 为联赛名称),默认全部收起
const [collapsedSections, setCollapsedSections] = useState<
Record<string, boolean>
>({});
// 当联赛列表变化时,确保所有联赛默认都是收起状态
React.useEffect(() => {
setCollapsedSections((prev) => {
const updated: Record<string, boolean> = {};
leagueNames.forEach((name) => {
updated[name] = prev[name] !== undefined ? prev[name] : true;
});
return updated;
});
}, [leagueNames.join(",")]);
// 切换折叠状态
const toggleCollapse = (leagueName: string) => {
if (!enableCollapsible) return;
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setCollapsedSections((prev) => ({
...prev,
[leagueName]: !prev[leagueName],
}));
};
if (leagueNames.length === 0) {
return (
<View style={styles.emptyContainer}>
<ThemedText></ThemedText>
</View>
);
}
return (
<ScrollView
style={[
styles.container,
{ backgroundColor: isDark ? "#000000" : "#F2F2F7" }
]}
contentContainerStyle={{ paddingBottom: 40 }}
>
{leagueNames.map((leagueName) => {
const leagueMatches = matchesByLeague[leagueName];
// 取该组第一场比赛的数据作为头部信息的来源(图标、国家等)
const firstMatch = leagueMatches[0];
const isCollapsed = collapsedSections[leagueName];
return (
<View key={leagueName} style={styles.leagueSection}>
{/* 联赛头部 */}
<TouchableOpacity
activeOpacity={enableCollapsible ? 0.7 : 1}
onPress={() => toggleCollapse(leagueName)}
style={styles.leagueHeaderWrapper}
>
<View style={styles.leagueHeaderLeft}>
{/* 联赛 Logo */}
<Image
source={{
uri: firstMatch.leagueLogo || (firstMatch as any).leagueLogoUrl || "https://placehold.co/40x40/png",
}}
style={styles.leagueLogo}
/>
<View style={styles.leagueInfoText}>
{/* 联赛名称 */}
<ThemedText style={styles.leagueTitle}>{leagueName}</ThemedText>
{/* 国家信息行 */}
<View style={styles.countryRow}>
<Image
source={{ uri: firstMatch.countryLogo || (firstMatch as any).countryFlagUrl || "https://placehold.co/20x20/png" }}
style={styles.countryFlag}
/>
<ThemedText style={styles.countryName}>
{firstMatch.countryName || (firstMatch as any).countryName || "International"}
</ThemedText>
</View>
</View>
</View>
<View style={styles.leagueHeaderRight}>
{/* 比赛数量 */}
<ThemedText style={styles.matchCount}>
{leagueMatches.length}
</ThemedText>
{/* 折叠箭头 (仅当支持折叠时显示) */}
{enableCollapsible && (
<ThemedText style={styles.chevron}>
{isCollapsed ? "⌄" : "⌃"}
</ThemedText>
)}
</View>
</TouchableOpacity>
{/* 比赛列表内容 (根据状态显示/隐藏) */}
{!isCollapsed && (
<View style={styles.matchListContainer}>
{leagueMatches.map((match, index) => (
<View
key={match.id}
style={[
styles.matchCardWrapper,
index < leagueMatches.length - 1 && styles.matchCardDivider
]}
>
<MatchCardLeague
match={match}
onFavoriteToggle={onFavoriteToggle}
/>
</View>
))}
</View>
)}
</View>
);
})}
</ScrollView>
);
}
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",
},
matchCardWrapper: {
// 卡片包装器
},
matchCardDivider: {
borderBottomWidth: 0.5,
borderBottomColor: "#3A3A3C",
},
emptyContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
paddingTop: 100,
},
});