Files
physical-expo/app/(tabs)/live.tsx
yuchenglong d20080eaf3 添加收藏
2026-01-16 14:41:15 +08:00

164 lines
4.8 KiB
TypeScript

import { HomeHeader } from "@/components/home-header";
import { MatchCard } from "@/components/match-card";
import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view";
import { Colors } from "@/constants/theme";
import { useAppState } from "@/context/AppStateContext";
import { useTheme } from "@/context/ThemeContext";
import { checkFavorite, fetchLiveScore } from "@/lib/api";
import { LiveScoreMatch, Match } from "@/types/api";
import { useRouter } from "expo-router";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { ActivityIndicator, FlatList, StyleSheet, View } from "react-native";
export default function LiveScreen() {
const router = useRouter();
const { theme } = useTheme();
const { t } = useTranslation();
const isDark = theme === "dark";
const { state } = useAppState();
const [matches, setMatches] = useState<Match[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadLiveMatches();
// 每30秒自动刷新一次实时比分
const interval = setInterval(loadLiveMatches, 30000);
return () => clearInterval(interval);
}, [state.selectedSportId, state.selectedLeagueKey, state.timezone]);
const loadLiveMatches = async () => {
if (!state.selectedSportId) {
setLoading(false);
return;
}
setLoading(true);
try {
const liveData = await fetchLiveScore(
state.selectedSportId,
state.selectedLeagueKey ? parseInt(state.selectedLeagueKey) : undefined,
state.timezone
);
// 检查返回的数据是否为空或无效
if (!liveData || !Array.isArray(liveData)) {
console.warn("LiveScore returned invalid data:", liveData);
setMatches([]);
return;
}
// 将 LiveScoreMatch 转换为 Match 格式
const converted: Match[] = liveData.map((item: LiveScoreMatch) => ({
id: item.event_key.toString(),
league: item.league_name,
time: item.event_time,
home: item.event_home_team,
away: item.event_away_team,
meta: item.event_status,
scoreText: item.event_final_result || "0 - 0",
fav: false,
leagueId: item.league_key,
sportId: state.selectedSportId ?? undefined,
isLive: true,
}));
// 直接传递 match.id 查询是否收藏,并更新列表状态
const listWithFavStatus = await Promise.all(
converted.map(async (m) => {
try {
const favRes = await checkFavorite("match", m.id);
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 (error) {
console.error("Load live matches error:", error);
setMatches([]);
} finally {
setLoading(false);
}
};
const handleFavoriteToggle = (matchId: string, isFav: boolean) => {
setMatches((prev) => {
const updated = prev.map((m) =>
m.id === matchId ? { ...m, fav: isFav } : m
);
return [...updated].sort((a, b) => {
if (a.fav === b.fav) return 0;
return a.fav ? -1 : 1;
});
});
};
return (
<ThemedView style={styles.container}>
<HomeHeader />
{loading ? (
<View style={styles.center}>
<ActivityIndicator size="large" color={Colors[theme].tint} />
<ThemedText style={{ marginTop: 10 }}>{t("home.loading")}</ThemedText>
</View>
) : (
<FlatList
data={matches}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<MatchCard
match={item}
onFavoriteToggle={handleFavoriteToggle}
onPress={(m) => {
router.push({
pathname: "/live-detail/[id]",
params: {
id: m.id,
league_id: m.leagueId?.toString() || "",
sport_id: m.sportId?.toString() || "",
},
});
}}
/>
)}
contentContainerStyle={styles.listContent}
ListEmptyComponent={
<View style={styles.center}>
<ThemedText>{t("home.no_matches")}</ThemedText>
</View>
}
/>
)}
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
center: {
flex: 1,
justifyContent: "center",
alignItems: "center",
paddingTop: 50,
},
listContent: {
padding: 16,
paddingTop: 8,
},
});