This commit is contained in:
xianyi
2026-01-19 17:42:42 +08:00
parent e1320a67e4
commit ee66f383b1
3 changed files with 115 additions and 48 deletions

View File

@@ -49,6 +49,9 @@ export default function HomeScreen() {
const [liveLeagueIdByMatchId, setLiveLeagueIdByMatchId] = useState<
Record<string, number>
>({});
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loadingMore, setLoadingMore] = useState(false);
const deviceTimeZone = useMemo(() => {
try {
@@ -96,6 +99,11 @@ export default function HomeScreen() {
}
}, [selectedSportId, selectedDate]);
useEffect(() => {
setPage(1);
setTotal(0);
}, [selectedSportId, selectedDate, state.selectedLeagueKey]);
const timezoneLabel = useMemo(() => {
// 仅展示 UTC 偏移(不展示时区名)
const offsetMin = -now.getTimezoneOffset();
@@ -303,8 +311,8 @@ export default function HomeScreen() {
try {
if (selectedSportId !== null) {
setLoadingLeagues(true);
const list = await fetchLeagues(selectedSportId, "");
setLeagues(list);
const res = await fetchLeagues({ sportId: selectedSportId });
setLeagues(res.list);
}
} catch (e) {
console.error(e);
@@ -316,11 +324,16 @@ export default function HomeScreen() {
const loadMatches = async (sportId: number) => {
setLoading(true);
try {
const list = await fetchTodayMatches(
const res = await fetchTodayMatches({
sportId,
selectedDate,
deviceTimeZone,
);
date: selectedDate,
timezone: deviceTimeZone,
leagueKey: state.selectedLeagueKey || "",
page: 1,
pageSize: 50,
});
setPage(1);
setTotal(res.total);
const normalizeDate = (d: Date) => {
const year = d.getFullYear();
@@ -333,7 +346,7 @@ export default function HomeScreen() {
const selectedStr = normalizeDate(selectedDate);
const shouldMergeLive = selectedStr === todayStr;
let merged: Match[] = list.map((m) => ({
let merged: Match[] = res.list.map((m) => ({
...m,
date: m.date || selectedStr,
sportId: m.sportId ?? sportId,
@@ -402,11 +415,9 @@ export default function HomeScreen() {
let listWithFavStatus = merged;
if (token) {
// 直接传递 match.id 查询是否收藏,并更新列表状态
listWithFavStatus = await Promise.all(
merged.map(async (m) => {
try {
// 查询比赛是否已被收藏
const favRes = await checkFavorite("match", m.id);
return { ...m, fav: favRes.isFavorite };
} catch (error) {
@@ -434,6 +445,41 @@ export default function HomeScreen() {
}
};
const loadMoreMatches = async () => {
if (loadingMore || loading) return;
if (!selectedSportId) return;
if (matches.length >= total) return;
setLoadingMore(true);
try {
const nextPage = page + 1;
const res = await fetchTodayMatches({
sportId: selectedSportId,
date: selectedDate,
timezone: deviceTimeZone,
leagueKey: state.selectedLeagueKey || "",
page: nextPage,
pageSize: 50,
});
setPage(nextPage);
setTotal(res.total);
setMatches((prev) => {
const byId = new Map<string, Match>();
prev.forEach((m) => byId.set(m.id, m));
res.list.forEach((m) => {
if (!byId.has(m.id)) byId.set(m.id, m);
});
return Array.from(byId.values());
});
} catch (e) {
console.error(e);
} finally {
setLoadingMore(false);
}
};
const handleFavoriteToggle = (matchId: string, isFav: boolean) => {
setMatches((prev) => {
const updated = prev.map((m) =>
@@ -558,6 +604,15 @@ export default function HomeScreen() {
renderItem={({ item }) => (
<MatchCard match={item} onFavoriteToggle={handleFavoriteToggle} />
)}
onEndReached={loadMoreMatches}
onEndReachedThreshold={0.4}
ListFooterComponent={
loadingMore ? (
<View style={styles.footer}>
<ActivityIndicator size="small" color={Colors[theme].tint} />
</View>
) : null
}
contentContainerStyle={styles.listContent}
ListEmptyComponent={
<View style={styles.center}>
@@ -650,4 +705,9 @@ const styles = StyleSheet.create({
padding: 16,
paddingTop: 8,
},
footer: {
paddingVertical: 16,
alignItems: "center",
justifyContent: "center",
},
});

View File

@@ -115,22 +115,22 @@ export const fetchCountries = async (): Promise<Country[]> => {
}
};
export const fetchLeagues = async (
sportId: number,
countryKey: string
): Promise<League[]> => {
export const fetchLeagues = async (params: {
sportId?: number;
countryKey?: string;
date?: string;
page?: number;
pageSize?: number;
sortBy?: "name" | "matchCount" | "createdAt";
sortOrder?: "asc" | "desc";
}): Promise<ApiListResponse<League>> => {
try {
const response = await apiClient.get<ApiResponse<ApiListResponse<League>>>(
API_ENDPOINTS.LEAGUES,
{
params: {
sportId,
countryKey,
},
}
{ params }
);
if (response.data.code === 0) {
return response.data.data.list;
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
@@ -139,48 +139,54 @@ export const fetchLeagues = async (
}
};
export const fetchTodayMatches = async (
sportId: number,
date?: Date | string,
timezone?: string
): Promise<Match[]> => {
export const fetchTodayMatches = async (options: {
sportId: number;
date?: Date | string;
timezone?: string;
leagueKey?: string;
page?: number;
pageSize?: number;
}): Promise<ApiListResponse<Match>> => {
try {
const params: { sportId: number; date?: string; timezone?: string } = {
sportId,
const params: {
sportId: number;
date?: string;
timezone?: string;
leagueKey?: string;
page?: number;
pageSize?: number;
} = {
sportId: options.sportId,
leagueKey: options.leagueKey,
page: options.page,
pageSize: options.pageSize,
};
// 如果提供了日期,格式化为 YYYY-MM-DD 格式
if (date) {
let dateStr: string;
if (date instanceof Date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
dateStr = `${year}-${month}-${day}`;
if (options.date) {
if (options.date instanceof Date) {
const year = options.date.getFullYear();
const month = String(options.date.getMonth() + 1).padStart(2, "0");
const day = String(options.date.getDate()).padStart(2, "0");
params.date = `${year}-${month}-${day}`;
} else {
dateStr = date;
params.date = options.date;
}
params.date = dateStr;
}
// 如果提供了时区,传给后端;不传则由后端使用本地时区
if (timezone) {
params.timezone = timezone;
if (options.timezone) {
params.timezone = options.timezone;
}
const response = await apiClient.get<ApiResponse<ApiListResponse<Match>>>(
API_ENDPOINTS.MATCHES_TODAY,
{
params,
}
{ params }
);
if (response.data.code === 0) {
return response.data.data.list;
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch matches error:", error);
// Let the caller handle errors; rethrow the original error
throw error;
}
};
@@ -214,9 +220,9 @@ export const fetchLiveScore = async (
// });
try {
const params: { sport_id: number; league_id?: number; timezone?: string } =
{
sport_id: sportId,
};
{
sport_id: sportId,
};
if (leagueId) {
params.league_id = leagueId;

View File

@@ -152,6 +152,7 @@ export interface League {
isActive: boolean;
createdAt: string;
updatedAt: string;
matchCount?: number;
}
export interface GoalEvent {