diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 3c6386f..2b802e2 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,4 +1,5 @@ import { HomeHeader } from "@/components/home-header"; +import { LeagueModal } from "@/components/league-modal"; import { MatchCard } from "@/components/match-card"; import { SelectionModal } from "@/components/selection-modal"; import { CalendarModal } from "@/components/simple-calendar"; @@ -7,8 +8,8 @@ import { ThemedView } from "@/components/themed-view"; import { IconSymbol } from "@/components/ui/icon-symbol"; import { Colors } from "@/constants/theme"; import { useTheme } from "@/context/ThemeContext"; -import { fetchSports, fetchTodayMatches } from "@/lib/api"; -import { Match, Sport } from "@/types/api"; +import { fetchLeagues, fetchSports, fetchTodayMatches } from "@/lib/api"; +import { League, Match, Sport } from "@/types/api"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { @@ -27,6 +28,7 @@ export default function HomeScreen() { const filterBg = isDark ? "#2C2C2E" : "#F2F2F7"; const [sports, setSports] = useState([]); + const [leagues, setLeagues] = useState([]); const [matches, setMatches] = useState([]); const [loading, setLoading] = useState(true); @@ -34,14 +36,18 @@ export default function HomeScreen() { // 默认足球 const [selectedSportId, setSelectedSportId] = useState(1); const [selectedDate, setSelectedDate] = useState(new Date()); + const [selectedLeagueKey, setSelectedLeagueKey] = useState(null); + const [filterMode, setFilterMode] = useState<"time" | "league">("time"); // 时间或联赛模式 // Modal Visibilities const [showSportModal, setShowSportModal] = useState(false); const [showCalendarModal, setShowCalendarModal] = useState(false); + const [showLeagueModal, setShowLeagueModal] = useState(false); - // Load Sports + // Load Sports and Leagues useEffect(() => { loadSports(); + loadLeagues(); }, []); // Load Matches when sport or date changes @@ -51,6 +57,13 @@ export default function HomeScreen() { } }, [selectedSportId, selectedDate]); + // Load Leagues when sport changes + useEffect(() => { + if (selectedSportId !== null) { + loadLeagues(); + } + }, [selectedSportId]); + const loadSports = async () => { try { const apiList = await fetchSports(); @@ -102,6 +115,18 @@ export default function HomeScreen() { } }; + // 加载联赛 + const loadLeagues = async () => { + try { + if (selectedSportId !== null) { + const list = await fetchLeagues(selectedSportId, ""); + setLeagues(list); + } + } catch (e) { + console.error(e); + } + }; + const loadMatches = async (sportId: number) => { setLoading(true); try { @@ -134,14 +159,26 @@ export default function HomeScreen() { return t(`sports.${sportKey}`, { defaultValue: sport.name }); }; + const handleLeagueSelect = (leagueKey: string) => { + setSelectedLeagueKey(leagueKey); + console.log("Selected league:", leagueKey); + }; + const renderHeader = () => ( - {/* Time Filter (Mock) */} + {/* Time/League Filter Toggle */} setFilterMode(filterMode === "time" ? "league" : "time")} > - - {t("home.time")} + + + {filterMode === "time" ? t("home.time") : t("home.league")} + {/* Sport Selector */} @@ -159,19 +196,33 @@ export default function HomeScreen() { - {/* Date Selector */} - setShowCalendarModal(true)} - > - - {selectedDate.getDate()} - - - {selectedDate.getHours()}: - {selectedDate.getMinutes().toString().padStart(2, "0")} - - + {/* Date/League Selector */} + {filterMode === "time" ? ( + setShowCalendarModal(true)} + > + + {selectedDate.getDate()} + + + {selectedDate.getHours()}: + {selectedDate.getMinutes().toString().padStart(2, "0")} + + + ) : ( + setShowLeagueModal(true)} + > + + + {selectedLeagueKey + ? leagues.find(l => l.key === selectedLeagueKey)?.name || t("home.select_league") + : t("home.select_league")} + + + )} ); @@ -234,6 +285,14 @@ export default function HomeScreen() { selectedDate={selectedDate} onSelectDate={setSelectedDate} /> + + setShowLeagueModal(false)} + leagues={leagues} + selectedLeagueKey={selectedLeagueKey} + onSelect={handleLeagueSelect} + /> ); } diff --git a/components/league-modal.tsx b/components/league-modal.tsx new file mode 100644 index 0000000..f6af4ad --- /dev/null +++ b/components/league-modal.tsx @@ -0,0 +1,209 @@ +import { ThemedText } from "@/components/themed-text"; +import { IconSymbol } from "@/components/ui/icon-symbol"; +import { Colors } from "@/constants/theme"; +import { useTheme } from "@/context/ThemeContext"; +import { League } from "@/types/api"; +import { Image } from "expo-image"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Modal, Pressable, ScrollView, StyleSheet, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +interface LeagueModalProps { + visible: boolean; + onClose: () => void; + leagues: League[]; + selectedLeagueKey: string | null; + onSelect: (leagueKey: string) => void; +} + +export function LeagueModal({ + visible, + onClose, + leagues, + selectedLeagueKey, + onSelect, +}: LeagueModalProps) { + const { theme } = useTheme(); + const { t } = useTranslation(); + const isDark = theme === "dark"; + const bg = isDark ? "#1C1C1E" : "#FFFFFF"; + const text = isDark ? "#FFFFFF" : "#000000"; + + return ( + + + + + + + + + + {t("home.select_league")} + + + + + + + {leagues.length === 0 ? ( + + + {t("home.no_leagues")} + + + ) : ( + leagues.map((league) => { + const isSelected = selectedLeagueKey === league.key; + const optionBg = isDark ? "#2C2C2E" : "#F5F5F5"; + const iconBg = isDark ? "#3A3A3C" : "#E5E5EA"; + + return ( + { + onSelect(league.key); + onClose(); + }} + > + {/* 左侧图标 */} + + {league.logo ? ( + + ) : ( + + )} + + + {/* 中间内容 */} + + + {league.name} + + {league.countryName && ( + + {league.countryName} + + )} + + + {/* 右侧指示点 */} + + + ); + }) + )} + + + + + ); +} + +const styles = StyleSheet.create({ + overlay: { + flex: 1, + backgroundColor: "rgba(0,0,0,0.5)", + }, + sheet: { + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + padding: 20, + height: "55%", + }, + header: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginBottom: 20, + }, + title: { + fontSize: 18, + fontWeight: "600", + }, + closeButton: { + width: 32, + height: 32, + alignItems: "center", + justifyContent: "center", + }, + scrollView: { + maxHeight: 400, + }, + emptyContainer: { + padding: 40, + alignItems: "center", + justifyContent: "center", + }, + emptyText: { + fontSize: 16, + opacity: 0.6, + }, + option: { + flexDirection: "row", + alignItems: "center", + paddingVertical: 16, + paddingHorizontal: 16, + marginBottom: 12, + borderRadius: 12, + }, + iconContainer: { + width: 48, + height: 48, + borderRadius: 24, + alignItems: "center", + justifyContent: "center", + marginRight: 12, + }, + leagueLogo: { + width: 32, + height: 32, + }, + content: { + flex: 1, + }, + leagueName: { + fontSize: 16, + marginBottom: 4, + }, + countryName: { + fontSize: 12, + opacity: 0.6, + }, + indicator: { + width: 8, + height: 8, + borderRadius: 4, + marginLeft: 12, + }, +}); diff --git a/i18n/locales/en.json b/i18n/locales/en.json index d3d92da..290f7a0 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -20,9 +20,12 @@ "home": { "title": "ScoreNow", "time": "Time", + "league": "League", "select_sport": "Sport", + "select_league": "Select League", "loading": "Loading...", - "no_matches": "No matches found." + "no_matches": "No matches found.", + "no_leagues": "No leagues available" }, "profile": { "title": "My Profile", diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json index fa50ff9..4dd60ac 100644 --- a/i18n/locales/zh.json +++ b/i18n/locales/zh.json @@ -20,9 +20,12 @@ "home": { "title": "ScoreNow", "time": "时间", + "league": "联赛", "select_sport": "运动", + "select_league": "选择联赛", "loading": "加载中...", - "no_matches": "暂无比赛" + "no_matches": "暂无比赛", + "no_leagues": "暂无联赛" }, "profile": { "title": "我的",