添加实时比分功能,更新比赛数据结构,优化比赛卡片显示
This commit is contained in:
@@ -13,11 +13,13 @@ import { useTheme } from "@/context/ThemeContext";
|
||||
import {
|
||||
checkFavorite,
|
||||
fetchLeagues,
|
||||
fetchLiveScore,
|
||||
fetchSports,
|
||||
fetchTodayMatches,
|
||||
} from "@/lib/api";
|
||||
import { storage } from "@/lib/storage";
|
||||
import { League, Match, Sport } from "@/types/api";
|
||||
import { useRouter } from "expo-router";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
@@ -29,6 +31,7 @@ import {
|
||||
} from "react-native";
|
||||
|
||||
export default function HomeScreen() {
|
||||
const router = useRouter();
|
||||
const { theme } = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const isDark = theme === "dark";
|
||||
@@ -44,12 +47,15 @@ export default function HomeScreen() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadingLeagues, setLoadingLeagues] = useState(false);
|
||||
const [now, setNow] = useState(() => new Date());
|
||||
const [liveLeagueIdByMatchId, setLiveLeagueIdByMatchId] = useState<
|
||||
Record<string, number>
|
||||
>({});
|
||||
|
||||
const deviceTimeZone = useMemo(() => {
|
||||
try {
|
||||
console.log(
|
||||
"deviceTimeZone",
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
);
|
||||
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
||||
} catch {
|
||||
@@ -315,16 +321,90 @@ export default function HomeScreen() {
|
||||
const list = await fetchTodayMatches(
|
||||
sportId,
|
||||
selectedDate,
|
||||
deviceTimeZone
|
||||
deviceTimeZone,
|
||||
);
|
||||
|
||||
//将isLive全改为true
|
||||
list.forEach((m) => {
|
||||
(m as Match).isLive = true;
|
||||
});
|
||||
|
||||
const normalizeDate = (d: Date) => {
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
const todayStr = normalizeDate(new Date());
|
||||
const selectedStr = normalizeDate(selectedDate);
|
||||
const shouldMergeLive = selectedStr === todayStr;
|
||||
|
||||
let merged: Match[] = list.map((m) => ({
|
||||
...m,
|
||||
date: (m as any).date || selectedStr,
|
||||
sport: (m as any).sport ?? sportId,
|
||||
}));
|
||||
|
||||
if (shouldMergeLive) {
|
||||
try {
|
||||
const liveData = await fetchLiveScore(
|
||||
sportId,
|
||||
state.selectedLeagueKey
|
||||
? parseInt(state.selectedLeagueKey)
|
||||
: undefined,
|
||||
state.timezone || deviceTimeZone,
|
||||
);
|
||||
|
||||
const formatLiveTime = (status: string, fallback: string) => {
|
||||
const s = (status || "").trim();
|
||||
if (!s) return (fallback || "").trim();
|
||||
if (/^\d{1,3}$/.test(s)) return `${s}'`;
|
||||
return s;
|
||||
};
|
||||
|
||||
const map: Record<string, number> = {};
|
||||
const liveMatches: Match[] = (liveData || []).map((item) => {
|
||||
const id = item.event_key.toString();
|
||||
map[id] = item.league_key;
|
||||
return {
|
||||
id,
|
||||
league: item.league_name,
|
||||
time: formatLiveTime(item.event_status, item.event_time),
|
||||
date: item.event_date,
|
||||
home: item.event_home_team,
|
||||
away: item.event_away_team,
|
||||
scoreText: item.event_final_result || "0 - 0",
|
||||
fav: false,
|
||||
sport: sportId,
|
||||
isLive: true,
|
||||
};
|
||||
});
|
||||
|
||||
setLiveLeagueIdByMatchId(map);
|
||||
|
||||
// Merge by id: live takes precedence
|
||||
const byId = new Map<string, Match>();
|
||||
liveMatches.forEach((m) => byId.set(m.id, m));
|
||||
merged.forEach((m) => {
|
||||
if (!byId.has(m.id)) byId.set(m.id, m);
|
||||
});
|
||||
merged = Array.from(byId.values());
|
||||
} catch (e) {
|
||||
// Live merge failure should not block base list
|
||||
console.warn("Fetch live score failed:", e);
|
||||
}
|
||||
} else {
|
||||
setLiveLeagueIdByMatchId({});
|
||||
}
|
||||
|
||||
const token = await storage.getAccessToken();
|
||||
let listWithFavStatus = list;
|
||||
let listWithFavStatus = merged;
|
||||
|
||||
if (token) {
|
||||
// 直接传递 match.id 查询是否收藏,并更新列表状态
|
||||
listWithFavStatus = await Promise.all(
|
||||
list.map(async (m) => {
|
||||
merged.map(async (m) => {
|
||||
try {
|
||||
// 查询比赛是否已被收藏
|
||||
const favRes = await checkFavorite("match", m.id);
|
||||
@@ -333,15 +413,26 @@ export default function HomeScreen() {
|
||||
console.error(`Check favorite failed for match ${m.id}:`, error);
|
||||
return m;
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 将收藏的比赛置顶
|
||||
const isLiveRow = (m: Match) => {
|
||||
const t = (m.time || "").trim();
|
||||
return (
|
||||
/\d+'$/.test(t) || /^\d{1,3}$/.test(t) || /\b(ht|half)\b/i.test(t)
|
||||
);
|
||||
};
|
||||
|
||||
// 收藏置顶,其次直播置顶
|
||||
const sortedList = [...listWithFavStatus].sort((a, b) => {
|
||||
if (a.fav === b.fav) return 0;
|
||||
return a.fav ? -1 : 1;
|
||||
if (a.fav !== b.fav) return a.fav ? -1 : 1;
|
||||
const aLive = isLiveRow(a);
|
||||
const bLive = isLiveRow(b);
|
||||
if (aLive !== bLive) return aLive ? -1 : 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
setMatches(sortedList);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -353,7 +444,7 @@ export default function HomeScreen() {
|
||||
const handleFavoriteToggle = (matchId: string, isFav: boolean) => {
|
||||
setMatches((prev) => {
|
||||
const updated = prev.map((m) =>
|
||||
m.id === matchId ? { ...m, fav: isFav } : m
|
||||
m.id === matchId ? { ...m, fav: isFav } : m,
|
||||
);
|
||||
return [...updated].sort((a, b) => {
|
||||
if (a.fav === b.fav) return 0;
|
||||
|
||||
@@ -65,6 +65,8 @@ export default function LiveScreen() {
|
||||
leagueId: item.league_key,
|
||||
sportId: state.selectedSportId ?? undefined,
|
||||
isLive: true,
|
||||
date: item.event_date,
|
||||
// sport: item.sport_name,
|
||||
}));
|
||||
|
||||
const token = await storage.getAccessToken();
|
||||
|
||||
Reference in New Issue
Block a user