304 lines
9.2 KiB
TypeScript
304 lines
9.2 KiB
TypeScript
import { EventsTimeline } from "@/components/live-detail/events-timeline";
|
|
import { LiveLeagueInfo } from "@/components/live-detail/live-league-info";
|
|
import { LiveMatchTabs } from "@/components/live-detail/live-match-tabs";
|
|
import { LiveScoreHeader } from "@/components/live-detail/live-score-header";
|
|
import { OddsCard } from "@/components/live-detail/odds-card";
|
|
import { OtherInfoCard } from "@/components/live-detail/other-info-card";
|
|
import { StatsCard } from "@/components/live-detail/stats-card";
|
|
import { TennisPowerGraph } from "@/components/live-detail/tennis-power-graph";
|
|
import { TennisScoreboard } from "@/components/live-detail/tennis-scoreboard";
|
|
import { TennisStatsCard } from "@/components/live-detail/tennis-stats-card";
|
|
import { BasketballOverallStats } from "@/components/match-detail/basketball/basketball-overall-stats";
|
|
import { BasketballScoreTable } from "@/components/match-detail/basketball/basketball-score-table";
|
|
import { BasketballStats } from "@/components/match-detail/basketball/basketball-stats";
|
|
import { ThemedText } from "@/components/themed-text";
|
|
import { ThemedView } from "@/components/themed-view";
|
|
import { Colors } from "@/constants/theme";
|
|
import { useTheme } from "@/context/ThemeContext";
|
|
import { fetchLiveScore } from "@/lib/api";
|
|
import { LiveScoreMatch, MatchDetailData } from "@/types/api";
|
|
import { useLocalSearchParams } from "expo-router";
|
|
import React, { useEffect, useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import {
|
|
ActivityIndicator,
|
|
ScrollView,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
View,
|
|
} from "react-native";
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
|
|
export default function LiveDetailScreen() {
|
|
const { id, league_id, sport_id } = useLocalSearchParams<{
|
|
id: string;
|
|
league_id: string;
|
|
sport_id: string;
|
|
}>();
|
|
const { theme } = useTheme();
|
|
const { t } = useTranslation();
|
|
const insets = useSafeAreaInsets();
|
|
const isDark = theme === "dark";
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
const [match, setMatch] = useState<LiveScoreMatch | null>(null);
|
|
const [activeTab, setActiveTab] = useState("detail");
|
|
|
|
const convertToMatchDetailData = useMemo((): MatchDetailData | null => {
|
|
if (!match) return null;
|
|
|
|
return {
|
|
events: [],
|
|
match: {
|
|
ID: 0,
|
|
CreatedAt: "",
|
|
UpdatedAt: "",
|
|
DeletedAt: null,
|
|
eventKey: match.event_key?.toString() || "",
|
|
eventDate: match.event_date,
|
|
eventTime: match.event_time,
|
|
eventHomeTeam: match.event_home_team,
|
|
homeTeamKey: match.home_team_key?.toString() || "",
|
|
homeTeamLogo: match.home_team_logo || "",
|
|
eventAwayTeam: match.event_away_team,
|
|
awayTeamKey: match.away_team_key?.toString() || "",
|
|
awayTeamLogo: match.away_team_logo || "",
|
|
eventHalftimeResult: match.event_halftime_result || "",
|
|
eventFinalResult: match.event_final_result,
|
|
eventFtResult: "",
|
|
eventPenaltyResult: "",
|
|
eventStatus: match.event_status,
|
|
countryName: match.country_name,
|
|
leagueName: match.league_name,
|
|
leagueKey: match.league_key?.toString() || "",
|
|
leagueRound: match.league_round,
|
|
leagueSeason: match.league_season,
|
|
eventLive: match.event_live,
|
|
eventStadium: "",
|
|
eventReferee: "",
|
|
eventCountryKey: match.event_country_key?.toString() || "",
|
|
leagueLogo: match.league_logo || "",
|
|
countryLogo: match.country_logo || "",
|
|
eventHomeFormation: "",
|
|
eventAwayFormation: "",
|
|
fkStageKey: "",
|
|
stageName: "",
|
|
leagueGroup: "",
|
|
sportId: parseInt(sport_id || "1"),
|
|
eventQuarter: match.event_quarter || "",
|
|
eventSet: "",
|
|
eventType: "",
|
|
eventToss: "",
|
|
eventManOfMatch: "",
|
|
scores: match.scores,
|
|
stats: match.statistics,
|
|
players: match.player_statistics
|
|
? {
|
|
home_team: (match.player_statistics.home_team || []).map((p) => ({
|
|
...p,
|
|
player_oncourt: p.player_oncourt || undefined,
|
|
})),
|
|
away_team: (match.player_statistics.away_team || []).map((p) => ({
|
|
...p,
|
|
player_oncourt: p.player_oncourt || undefined,
|
|
})),
|
|
}
|
|
: undefined,
|
|
},
|
|
};
|
|
}, [match, sport_id]);
|
|
|
|
useEffect(() => {
|
|
loadLiveDetail();
|
|
const timer = setInterval(() => {
|
|
refreshLiveDetail();
|
|
}, 15000);
|
|
return () => clearInterval(timer);
|
|
}, [id, league_id]);
|
|
|
|
const loadLiveDetail = async () => {
|
|
try {
|
|
setLoading(true);
|
|
await refreshLiveDetail();
|
|
} catch (err) {
|
|
console.error(err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const refreshLiveDetail = async () => {
|
|
try {
|
|
const sportId = parseInt(sport_id || "1");
|
|
const leagueId = parseInt(league_id || "0");
|
|
|
|
const liveData = await fetchLiveScore(sportId, leagueId);
|
|
|
|
if (liveData && Array.isArray(liveData)) {
|
|
const found = liveData.find((m) => m.event_key.toString() === id);
|
|
// console.log("found", JSON.stringify(found, null, 2));
|
|
if (found) {
|
|
setMatch(found);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("Refresh live detail error:", err);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<ThemedView style={styles.center}>
|
|
<ActivityIndicator size="large" color={Colors[theme].tint} />
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
if (!match) {
|
|
return (
|
|
<ThemedView style={styles.center}>
|
|
<ThemedText>{t("detail.not_found")}</ThemedText>
|
|
<TouchableOpacity style={styles.retryButton} onPress={loadLiveDetail}>
|
|
<ThemedText style={styles.retryText}>{t("detail.retry")}</ThemedText>
|
|
</TouchableOpacity>
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
const renderTabContent = () => {
|
|
if (!match) return null;
|
|
|
|
const numericSportId = parseInt(sport_id || "1");
|
|
// Tennis Check
|
|
const isTennis =
|
|
!!match.event_first_player ||
|
|
(match.league_name &&
|
|
/ATP|WTA|ITF|Challenger/i.test(match.league_name || ""));
|
|
|
|
if (numericSportId === 2 && convertToMatchDetailData) {
|
|
switch (activeTab) {
|
|
case "stats":
|
|
return (
|
|
<BasketballStats data={convertToMatchDetailData} isDark={isDark} />
|
|
);
|
|
case "overall":
|
|
return (
|
|
<BasketballOverallStats
|
|
data={convertToMatchDetailData}
|
|
isDark={isDark}
|
|
/>
|
|
);
|
|
case "odds":
|
|
return (
|
|
<OddsCard match={match} isDark={isDark} sportId={numericSportId} />
|
|
);
|
|
case "detail":
|
|
return (
|
|
<>
|
|
<BasketballScoreTable
|
|
data={convertToMatchDetailData}
|
|
isDark={isDark}
|
|
/>
|
|
</>
|
|
);
|
|
default:
|
|
return (
|
|
<View style={styles.center}>
|
|
<ThemedText style={{ opacity: 0.5 }}>
|
|
{t("detail.empty_stats")}
|
|
</ThemedText>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
switch (activeTab) {
|
|
case "stats":
|
|
return !isTennis ? (
|
|
<StatsCard match={match} isDark={isDark} />
|
|
) : (
|
|
<View style={styles.center}>
|
|
<ThemedText style={{ opacity: 0.5 }}>
|
|
{t("detail.empty_stats")}
|
|
</ThemedText>
|
|
</View>
|
|
);
|
|
case "odds":
|
|
return (
|
|
<OddsCard match={match} isDark={isDark} sportId={numericSportId} />
|
|
);
|
|
case "detail":
|
|
if (isTennis) {
|
|
return (
|
|
<>
|
|
<TennisScoreboard match={match} isDark={isDark} />
|
|
<TennisStatsCard match={match} isDark={isDark} />
|
|
<TennisPowerGraph match={match} isDark={isDark} />
|
|
</>
|
|
);
|
|
}
|
|
return (
|
|
<>
|
|
<OddsCard match={match} isDark={isDark} sportId={numericSportId} />
|
|
<StatsCard match={match} isDark={isDark} />
|
|
<EventsTimeline match={match} isDark={isDark} />
|
|
<OtherInfoCard match={match} isDark={isDark} />
|
|
</>
|
|
);
|
|
default:
|
|
return (
|
|
<View style={styles.center}>
|
|
<ThemedText style={{ opacity: 0.5 }}>
|
|
{t("detail.empty_stats")}
|
|
</ThemedText>
|
|
</View>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ThemedView style={styles.container}>
|
|
<ScrollView
|
|
bounces={false}
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={{ paddingBottom: insets.bottom + 20 }}
|
|
>
|
|
<LiveScoreHeader match={match} topInset={insets.top} />
|
|
<LiveLeagueInfo match={match} />
|
|
<LiveMatchTabs
|
|
activeTab={activeTab}
|
|
onTabChange={setActiveTab}
|
|
isDark={isDark}
|
|
sportId={parseInt(sport_id || "1")}
|
|
/>
|
|
|
|
{renderTabContent()}
|
|
</ScrollView>
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
center: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
padding: 20,
|
|
minHeight: 200,
|
|
},
|
|
retryButton: {
|
|
marginTop: 20,
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 10,
|
|
backgroundColor: "#007AFF",
|
|
borderRadius: 8,
|
|
},
|
|
retryText: {
|
|
color: "#FFF",
|
|
fontWeight: "600",
|
|
},
|
|
});
|