import { HomeHeader } from "@/components/home-header"; import { LeagueModal } from "@/components/league-modal"; import { SelectionModal } from "@/components/selection-modal"; import { ThemedText } from "@/components/themed-text"; import { ThemedView } from "@/components/themed-view"; import { IconSymbol } from "@/components/ui/icon-symbol"; import { UpcomingMatchCard } from "@/components/upcoming-match-card"; import { Colors } from "@/constants/theme"; import { useTheme } from "@/context/ThemeContext"; import { checkFavorite, fetchLeagues, fetchSports, fetchUpcomingMatches, } from "@/lib/api"; import { storage } from "@/lib/storage"; import { League, Sport, UpcomingMatch } from "@/types/api"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { ActivityIndicator, FlatList, StyleSheet, TouchableOpacity, View, } from "react-native"; export default function HomeScreen() { const { theme } = useTheme(); const { t } = useTranslation(); const isDark = theme === "dark"; const iconColor = isDark ? Colors.dark.icon : Colors.light.icon; const filterBg = isDark ? "#2C2C2E" : "#F2F2F7"; const [sports, setSports] = useState([]); const [leagues, setLeagues] = useState([]); const [matches, setMatches] = useState([]); const [loading, setLoading] = useState(true); const [loadingLeagues, setLoadingLeagues] = useState(false); // Selection States // 默认足球 const [selectedSportId, setSelectedSportId] = useState(1); const [selectedLeagueKey, setSelectedLeagueKey] = useState(""); // 默认空字符串 // Modal Visibilities const [showSportModal, setShowSportModal] = useState(false); const [showLeagueModal, setShowLeagueModal] = useState(false); // Load Sports and Leagues useEffect(() => { loadSports(); loadLeagues(); }, []); // Load Matches when sport or league changes useEffect(() => { if (selectedSportId !== null) { loadMatches(); } }, [selectedSportId, selectedLeagueKey]); // Load Leagues when sport changes useEffect(() => { if (selectedSportId !== null) { loadLeagues(); } }, [selectedSportId]); const loadSports = async () => { try { const apiList = await fetchSports(); // 创建8个运动的完整列表 const defaultSports: Sport[] = [ { id: 1, name: "football", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 2, name: "basketball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 3, name: "tennis", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 4, name: "cricket", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 5, name: "baseball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 6, name: "badminton", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 7, name: "snooker", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 8, name: "volleyball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, ]; // 合并API返回的运动和默认列表 const sportsMap = new Map(); apiList.forEach((sport) => { sportsMap.set(sport.id, sport); }); // 补充默认运动到8个 defaultSports.forEach((sport) => { if (!sportsMap.has(sport.id)) { sportsMap.set(sport.id, sport); } }); const allSports = Array.from(sportsMap.values()) .sort((a, b) => a.id - b.id) .slice(0, 8); setSports(allSports); } catch (e) { console.error(e); // API失败时使用默认8个运动 const defaultSports: Sport[] = [ { id: 1, name: "football", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 2, name: "basketball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 3, name: "tennis", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 4, name: "cricket", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 5, name: "baseball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 6, name: "badminton", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 7, name: "snooker", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, { id: 8, name: "volleyball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "", }, ]; setSports(defaultSports); setSelectedSportId(1); } }; // 加载联赛 const loadLeagues = async () => { try { if (selectedSportId !== null) { setLoadingLeagues(true); const list = await fetchLeagues(selectedSportId, ""); setLeagues(list); } } catch (e) { console.error(e); } finally { setLoadingLeagues(false); } }; const loadMatches = async () => { const token = await storage.getAccessToken(); if (selectedSportId === null || !token) return; setLoading(true); try { // 使用 fetchUpcomingMatches,默认 leagueKey 为空字符串 const list = await fetchUpcomingMatches( selectedSportId, selectedLeagueKey || "" ); const token = await storage.getAccessToken(); let listWithFavStatus = list; if (token) { // 直接传递 match.id 查询是否收藏,并更新列表状态 listWithFavStatus = await Promise.all( list.map(async (m) => { try { const favRes = await checkFavorite("match", m.id.toString()); return { ...m, fav: favRes.isFavorite }; } catch (error) { console.error(`Check favorite failed for match ${m.id}:`, error); return m; } }) ); } // 将收藏的比赛置顶 const sortedList = [...listWithFavStatus].sort((a, b) => { if (a.fav === b.fav) return 0; return a.fav ? -1 : 1; }); setMatches(sortedList); } catch (e) { console.error(e); } finally { setLoading(false); } }; const handleFavoriteToggle = (matchId: string, isFav: boolean) => { setMatches((prev) => { const updated = prev.map((m) => m.id.toString() === matchId ? { ...m, fav: isFav } : m ); return [...updated].sort((a, b) => { if (a.fav === b.fav) return 0; return a.fav ? -1 : 1; }); }); }; const currentSport = sports.find((s) => s.id === selectedSportId); // 获取当前运动的国际化名称 const getSportName = (sport: Sport | undefined): string => { if (!sport) return t("home.select_sport"); const sportKeyMap: { [key: number]: string } = { 1: "football", 2: "basketball", 3: "tennis", 4: "cricket", 5: "baseball", 6: "badminton", 7: "snooker", 8: "volleyball", }; const sportKey = sportKeyMap[sport.id] || sport.name.toLowerCase(); return t(`sports.${sportKey}`, { defaultValue: sport.name }); }; const handleLeagueSelect = (leagueKey: string) => { setSelectedLeagueKey(leagueKey); console.log("Selected league:", leagueKey); }; const renderHeader = () => ( {/* League Filter (Fixed) */} {t("home.league")} {/* Sport Selector */} setShowSportModal(true)} > {getSportName(currentSport)} {/* League Selector */} { // 立即显示弹窗 setShowLeagueModal(true); // 如果联赛列表为空,立即设置loading状态并加载 if (selectedSportId !== null) { if (leagues.length === 0) { setLoadingLeagues(true); } loadLeagues(); } }} > {selectedLeagueKey ? leagues.find((l) => l.key === selectedLeagueKey)?.name || t("home.select_league") : t("home.all_leagues")} ); return ( {renderHeader()} {loading ? ( {t("home.loading")} ) : ( item.id.toString()} renderItem={({ item }) => ( )} contentContainerStyle={styles.listContent} ListEmptyComponent={ {t("home.no_matches")} } /> )} {/* Modals */} setShowSportModal(false)} title={t("home.select_sport")} options={sports.map((s) => { const sportKeyMap: { [key: number]: string } = { 1: "football", 2: "basketball", 3: "tennis", 4: "cricket", 5: "baseball", 6: "badminton", 7: "snooker", 8: "volleyball", }; const sportKey = sportKeyMap[s.id] || s.name.toLowerCase(); return { id: s.id, label: s.name, // 保留原始名称用于图标识别 value: s.id, icon: s.icon, }; })} selectedValue={selectedSportId} onSelect={setSelectedSportId} /> setShowLeagueModal(false)} leagues={leagues} selectedLeagueKey={selectedLeagueKey} loading={loadingLeagues} onSelect={handleLeagueSelect} /> ); } const styles = StyleSheet.create({ container: { flex: 1, }, center: { flex: 1, justifyContent: "center", alignItems: "center", paddingTop: 50, }, filterContainer: { flexDirection: "row", paddingHorizontal: 16, paddingVertical: 12, gap: 12, }, filterBtn: { flex: 1, height: 44, // Increased from 36 flexDirection: "column", // Stacked logic for Date, or Row for others justifyContent: "center", alignItems: "center", borderRadius: 8, // Rounded corners // iOS shadow shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 2, // Android elevation elevation: 2, }, mainFilterBtn: { flex: 2, // Wider for sport flexDirection: "row", gap: 8, }, filterText: { fontSize: 14, fontWeight: "500", }, dateDayText: { fontSize: 16, fontWeight: "bold", }, dateMonthText: { fontSize: 10, opacity: 0.6, }, listContent: { padding: 16, paddingTop: 8, }, });