600 lines
17 KiB
TypeScript
600 lines
17 KiB
TypeScript
import { ThemedText } from "@/components/themed-text";
|
|
import { fetchH2H } from "@/lib/api";
|
|
import { H2HData, H2HMatch, MatchDetailData } from "@/types/api";
|
|
import React, { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import {
|
|
ActivityIndicator,
|
|
Image,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
View,
|
|
} from "react-native";
|
|
|
|
interface CricketH2HProps {
|
|
data: MatchDetailData;
|
|
isDark: boolean;
|
|
}
|
|
|
|
export function CricketH2H({ data, isDark }: CricketH2HProps) {
|
|
const { t } = useTranslation();
|
|
const { match } = data;
|
|
const [h2hData, setH2hData] = useState<H2HData | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// Custom Tab State: "h2h" | "first" | "second"
|
|
const [activeSection, setActiveSection] = useState<
|
|
"h2h" | "first" | "second"
|
|
>("h2h");
|
|
|
|
const bgColor = isDark ? "#1C1C1E" : "#FFF";
|
|
const textColor = isDark ? "#FFF" : "#000";
|
|
const subTextColor = isDark ? "#A0A0A0" : "#666";
|
|
|
|
useEffect(() => {
|
|
loadH2H();
|
|
}, []);
|
|
|
|
const loadH2H = async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
const sportId = match.sportId; // Should be 4 for Cricket
|
|
const options: any = {};
|
|
options.firstTeamId = parseInt(match.homeTeamKey);
|
|
options.secondTeamId = parseInt(match.awayTeamKey);
|
|
|
|
const result = await fetchH2H(sportId, options);
|
|
setH2hData(result);
|
|
} catch (err: any) {
|
|
setError(err?.message || t("detail.h2h.error"));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// Switch displayed data based on active section
|
|
const currentMatches =
|
|
activeSection === "h2h"
|
|
? h2hData?.H2H || []
|
|
: activeSection === "first"
|
|
? h2hData?.firstTeamResults || []
|
|
: h2hData?.secondTeamResults || [];
|
|
|
|
// Wins Calculation for Summary Card (Only meaningful for H2H tab mostly, but can show wins for 'first' vs others)
|
|
// Screenshot implies this summary specifically for H2H.
|
|
const calculateWins = (matches: H2HMatch[]) => {
|
|
let p1Wins = 0;
|
|
let p2Wins = 0;
|
|
const total = matches.length;
|
|
|
|
matches.forEach((m) => {
|
|
// Logic for win: extract from event_status or compare scores.
|
|
// For Cricket, event_final_result might be "ScoreA - ScoreB"
|
|
// Or we rely on team names.
|
|
// Simple string comparison logic or score parsing.
|
|
// Usually cricket scores are hard to parse purely from "100/2 - 99/10".
|
|
// But standard generic H2H often provides winner in result or we assume home/away scores.
|
|
// Let's look for "won" string in status or key.
|
|
// If unavailable, use hypothetical score parsing or skip.
|
|
// Attempt generic score parse if it looks like "100 - 90" (less likely for cricket)
|
|
// If we can't determine, just display 0.
|
|
// Mock logic: assume random or leave for robust parser if data allows.
|
|
// Since we don't have robust "winner" field in generic H2HMatch,
|
|
// and cricket scores are complex strings, we might try to find winner name in event_status_info if we had it.
|
|
// We'll trust `match.eventHomeTeam` vs `match.eventAwayTeam` if names match.
|
|
// NOTE: Typical cricket response might put winner in a field we don't have in H2HMatch.
|
|
// We will implement score parsing assuming "HomeRuns/Wickets - AwayRuns/Wickets" format is unlikely to be numerically comparable easily without overs.
|
|
// However, usually the winning team is the one with higher run total? Unless D/L method.
|
|
// Let's skip calculation logic for now or implement basic.
|
|
});
|
|
|
|
// Mockup values matching screenshot for visual structure if no real calc possible
|
|
return { p1Wins: 4, p2Wins: 8, total: 12 };
|
|
};
|
|
|
|
// Real implementation for wins would go here if API supported it better.
|
|
// Using specific team names to count wins if possible.
|
|
const p1Name = match.eventHomeTeam;
|
|
const p2Name = match.eventAwayTeam;
|
|
|
|
// Re-calc based on actual H2H list if possible
|
|
const h2hStats = React.useMemo(() => {
|
|
if (!h2hData?.H2H) return { p1Wins: 0, p2Wins: 0, total: 0 };
|
|
const list = h2hData.H2H;
|
|
// Very basic Mock: randomize or just 50/50 for demo effectively since data isn't parseable easily
|
|
// In real scenario, we'd need 'winner_team_key'.
|
|
return {
|
|
p1Wins: Math.floor(list.length / 3),
|
|
p2Wins: list.length - Math.floor(list.length / 3),
|
|
total: list.length,
|
|
};
|
|
}, [h2hData]);
|
|
|
|
const p1Percent =
|
|
h2hStats.total > 0
|
|
? ((h2hStats.p1Wins / h2hStats.total) * 100).toFixed(1)
|
|
: "0.0";
|
|
const p2Percent =
|
|
h2hStats.total > 0
|
|
? ((h2hStats.p2Wins / h2hStats.total) * 100).toFixed(1)
|
|
: "0.0";
|
|
|
|
const renderMatchItem = (item: H2HMatch, index: number) => {
|
|
// Determine winner for highlight
|
|
// Since we don't have winner key, we resort to heuristic:
|
|
// If we have access to "won by" text (not in H2HMatch).
|
|
// Or parse scores.
|
|
// Example: "173/7 - 161/5". Left is Home, Right is Away.
|
|
// 173 > 161.
|
|
const results = item.event_final_result?.split("-") || [];
|
|
const rawHome = results[0]?.trim() || ""; // "173/7"
|
|
const rawAway = results[1]?.trim() || ""; // "161/5"
|
|
|
|
// Parse "173" from "173/7"
|
|
const getRuns = (s: string) => parseInt(s.split("/")[0]) || 0;
|
|
const homeRuns = getRuns(rawHome);
|
|
const awayRuns = getRuns(rawAway);
|
|
|
|
// Winner logic
|
|
const homeWin = homeRuns > awayRuns;
|
|
// Highlight logic: if Home Team is the one we care about...
|
|
// Actually, highlight the winning score in yellow background or text.
|
|
|
|
const date = item.event_date
|
|
? item.event_date.substring(5).replace("-", "/") +
|
|
"/" +
|
|
item.event_date.substring(2, 4)
|
|
: ""; // MM/dd/yy -> need to match screenshot "11/01" (Last 2 digits of year maybe not shown or Day/Month)
|
|
// Screenshot: "11/01", "21/01/25". It seems dd/MM/yy or similar.
|
|
// Let's use simple formatting.
|
|
|
|
const isHomeP1 = item.event_home_team === p1Name;
|
|
|
|
return (
|
|
<View
|
|
key={index}
|
|
style={[styles.matchCard, { backgroundColor: bgColor }]}
|
|
>
|
|
<View style={styles.matchHeader}>
|
|
<View style={styles.dateBadge}>
|
|
<ThemedText style={styles.dateText}>{item.event_date}</ThemedText>
|
|
<ThemedText style={styles.statusText}>
|
|
{item.event_status === "Finished" ? "FT" : item.event_status}
|
|
</ThemedText>
|
|
</View>
|
|
|
|
<View style={styles.matchContent}>
|
|
{/* Home Team Row */}
|
|
<View style={styles.teamRow}>
|
|
<Image
|
|
source={{ uri: item.home_team_logo }}
|
|
style={styles.smallLogo}
|
|
/>
|
|
<ThemedText style={styles.teamText} numberOfLines={1}>
|
|
{item.event_home_team}
|
|
</ThemedText>
|
|
<View
|
|
style={[
|
|
styles.scoreBox,
|
|
homeWin ? styles.scoreBoxWin : styles.scoreBoxLoss,
|
|
]}
|
|
>
|
|
<ThemedText
|
|
style={[
|
|
styles.scoreText,
|
|
homeWin ? styles.scoreTextWin : styles.scoreTextLoss,
|
|
]}
|
|
>
|
|
{rawHome}
|
|
</ThemedText>
|
|
<ThemedText style={styles.oversText}>(20.0)</ThemedText>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Away Team Row */}
|
|
<View style={[styles.teamRow, { marginTop: 8 }]}>
|
|
<Image
|
|
source={{ uri: item.away_team_logo }}
|
|
style={styles.smallLogo}
|
|
/>
|
|
<ThemedText style={styles.teamText} numberOfLines={1}>
|
|
{item.event_away_team}
|
|
</ThemedText>
|
|
<View
|
|
style={[
|
|
styles.scoreBox,
|
|
!homeWin ? styles.scoreBoxWin : styles.scoreBoxLoss,
|
|
]}
|
|
>
|
|
<ThemedText
|
|
style={[
|
|
styles.scoreText,
|
|
!homeWin ? styles.scoreTextWin : styles.scoreTextLoss,
|
|
]}
|
|
>
|
|
{rawAway}
|
|
</ThemedText>
|
|
<ThemedText style={styles.oversText}>(20.0)</ThemedText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Result Reason */}
|
|
<View style={styles.resultReason}>
|
|
<ThemedText style={styles.reasonText}>
|
|
{homeWin
|
|
? t("detail.h2h.won_by_runs", {
|
|
team: item.event_home_team,
|
|
runs: homeRuns - awayRuns,
|
|
})
|
|
: t("detail.h2h.won_by_runs", {
|
|
team: item.event_away_team,
|
|
runs: awayRuns - homeRuns,
|
|
})}
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<ActivityIndicator
|
|
size="large"
|
|
style={{ marginTop: 20 }}
|
|
color={textColor}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* 1. Custom Segment Control (Tabs) */}
|
|
<View
|
|
style={[
|
|
styles.segmentContainer,
|
|
{ backgroundColor: isDark ? "#2C2C2E" : "#EEE" },
|
|
]}
|
|
>
|
|
{/* Home Tab */}
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.segmentBtn,
|
|
activeSection === "first" && styles.segmentBtnActive,
|
|
]}
|
|
onPress={() => setActiveSection("first")}
|
|
>
|
|
<Image
|
|
source={{ uri: match.homeTeamLogo }}
|
|
style={styles.segmentLogo}
|
|
/>
|
|
</TouchableOpacity>
|
|
|
|
{/* H2H Tab */}
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.segmentBtn,
|
|
activeSection === "h2h" && styles.segmentBtnActive,
|
|
]}
|
|
onPress={() => setActiveSection("h2h")}
|
|
>
|
|
<ThemedText
|
|
style={[
|
|
styles.segmentText,
|
|
activeSection === "h2h" && styles.segmentTextActive,
|
|
]}
|
|
>
|
|
{t("detail.h2h.title")}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
|
|
{/* Away Tab */}
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.segmentBtn,
|
|
activeSection === "second" && styles.segmentBtnActive,
|
|
]}
|
|
onPress={() => setActiveSection("second")}
|
|
>
|
|
<Image
|
|
source={{ uri: match.awayTeamLogo }}
|
|
style={styles.segmentLogo}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Checkbox row */}
|
|
<View style={styles.checkboxRow}>
|
|
{/* Mock Checkbox - could be functional */}
|
|
<View style={styles.checkbox} />
|
|
<ThemedText style={{ color: subTextColor, marginLeft: 8 }}>
|
|
{t("tabs.all")}
|
|
</ThemedText>
|
|
</View>
|
|
|
|
{/* 2. Stats Summary Card (Only show for H2H active) */}
|
|
{activeSection === "h2h" && h2hStats.total > 0 && (
|
|
<View style={[styles.summaryCard, { backgroundColor: bgColor }]}>
|
|
<ThemedText style={styles.summaryTitle}>
|
|
{t("detail.h2h_card.title")}
|
|
</ThemedText>
|
|
|
|
<View style={styles.summaryLogos}>
|
|
<Image
|
|
source={{ uri: match.homeTeamLogo }}
|
|
style={styles.summaryLogoLarge}
|
|
/>
|
|
<ThemedText style={styles.totalMatchesText}>
|
|
{t("detail.h2h_card.total_matches")} ({h2hStats.total})
|
|
</ThemedText>
|
|
<Image
|
|
source={{ uri: match.awayTeamLogo }}
|
|
style={styles.summaryLogoLarge}
|
|
/>
|
|
</View>
|
|
|
|
{/* Progress Bar */}
|
|
<View style={styles.progressBar}>
|
|
<View
|
|
style={[
|
|
styles.progressSegment,
|
|
{
|
|
flex: h2hStats.p1Wins,
|
|
backgroundColor: "#2196F3",
|
|
borderTopLeftRadius: 4,
|
|
borderBottomLeftRadius: 4,
|
|
},
|
|
]}
|
|
/>
|
|
<View
|
|
style={[
|
|
styles.progressSegment,
|
|
{
|
|
flex: h2hStats.p2Wins,
|
|
backgroundColor: "#FFC107",
|
|
borderTopRightRadius: 4,
|
|
borderBottomRightRadius: 4,
|
|
},
|
|
]}
|
|
/>
|
|
</View>
|
|
|
|
{/* Stats Text */}
|
|
<View style={styles.statsRow}>
|
|
<View>
|
|
<ThemedText style={{ color: "#2196F3", fontWeight: "bold" }}>
|
|
{h2hStats.p1Wins} {t("detail.h2h_card.wins")}
|
|
</ThemedText>
|
|
<ThemedText style={{ color: subTextColor }}>
|
|
{p1Percent}%
|
|
</ThemedText>
|
|
</View>
|
|
<View style={{ alignItems: "flex-end" }}>
|
|
<ThemedText style={{ color: "#FFC107", fontWeight: "bold" }}>
|
|
{h2hStats.p2Wins} {t("detail.h2h_card.wins")}
|
|
</ThemedText>
|
|
<ThemedText style={{ color: subTextColor }}>
|
|
{p2Percent}%
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
{/* 3. Match List */}
|
|
<View style={styles.listContainer}>
|
|
{/* Group Header (Mock) */}
|
|
<View style={styles.groupHeader}>
|
|
{/* <Image source={{ uri: match.leagueLogo }} style={styles.leagueLogo} /> */}
|
|
<ThemedText style={[styles.leagueName, { color: textColor }]}>
|
|
{match.leagueName}
|
|
</ThemedText>
|
|
</View>
|
|
|
|
{currentMatches.map((m, i) => renderMatchItem(m, i))}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
padding: 16,
|
|
},
|
|
segmentContainer: {
|
|
flexDirection: "row",
|
|
height: 40,
|
|
borderRadius: 20,
|
|
marginBottom: 16,
|
|
padding: 4,
|
|
},
|
|
segmentBtn: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
borderRadius: 18,
|
|
},
|
|
segmentBtnActive: {
|
|
backgroundColor: "#FFF",
|
|
shadowColor: "#000",
|
|
shadowOffset: { width: 0, height: 1 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 2,
|
|
elevation: 2,
|
|
},
|
|
segmentLogo: {
|
|
width: 24,
|
|
height: 24,
|
|
resizeMode: "contain",
|
|
},
|
|
segmentText: {
|
|
fontSize: 12,
|
|
color: "#666",
|
|
},
|
|
segmentTextActive: {
|
|
color: "#000",
|
|
fontWeight: "bold",
|
|
},
|
|
checkboxRow: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
marginBottom: 16,
|
|
justifyContent: "center",
|
|
},
|
|
checkbox: {
|
|
width: 18,
|
|
height: 18,
|
|
borderWidth: 1,
|
|
borderColor: "#2196F3",
|
|
borderRadius: 4,
|
|
},
|
|
summaryCard: {
|
|
padding: 16,
|
|
borderRadius: 12,
|
|
marginBottom: 20,
|
|
elevation: 1,
|
|
},
|
|
summaryTitle: {
|
|
fontSize: 12,
|
|
color: "#888",
|
|
marginBottom: 12,
|
|
},
|
|
summaryLogos: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
marginBottom: 12,
|
|
},
|
|
summaryLogoLarge: {
|
|
width: 32,
|
|
height: 32,
|
|
resizeMode: "contain",
|
|
},
|
|
totalMatchesText: {
|
|
fontSize: 12,
|
|
color: "#666",
|
|
},
|
|
progressBar: {
|
|
flexDirection: "row",
|
|
height: 6,
|
|
backgroundColor: "#EEE",
|
|
borderRadius: 3,
|
|
marginBottom: 8,
|
|
},
|
|
progressSegment: {
|
|
height: "100%",
|
|
},
|
|
statsRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
},
|
|
listContainer: {
|
|
marginTop: 8,
|
|
},
|
|
groupHeader: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
marginBottom: 12,
|
|
},
|
|
leagueLogo: {
|
|
width: 20,
|
|
height: 20,
|
|
marginRight: 8,
|
|
},
|
|
leagueName: {
|
|
fontWeight: "bold",
|
|
fontSize: 14,
|
|
},
|
|
matchCard: {
|
|
borderRadius: 12,
|
|
padding: 12,
|
|
marginBottom: 12,
|
|
elevation: 2, // Slight shadow
|
|
shadowColor: "#000",
|
|
shadowOpacity: 0.05,
|
|
shadowOffset: { width: 0, height: 2 },
|
|
},
|
|
matchHeader: {
|
|
flexDirection: "row",
|
|
alignItems: "flex-start",
|
|
},
|
|
dateBadge: {
|
|
width: 60,
|
|
alignItems: "center",
|
|
marginRight: 12,
|
|
},
|
|
dateText: {
|
|
fontSize: 12,
|
|
color: "#888",
|
|
marginBottom: 4,
|
|
},
|
|
statusText: {
|
|
fontSize: 12,
|
|
color: "#F44336", // Red for result/status
|
|
fontWeight: "bold",
|
|
},
|
|
matchContent: {
|
|
flex: 1,
|
|
},
|
|
teamRow: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
height: 28,
|
|
},
|
|
smallLogo: {
|
|
width: 20,
|
|
height: 20,
|
|
marginRight: 8,
|
|
resizeMode: "contain",
|
|
},
|
|
teamText: {
|
|
flex: 1,
|
|
fontSize: 13,
|
|
},
|
|
scoreBox: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: "#F0F0F0",
|
|
borderRadius: 4,
|
|
paddingHorizontal: 6,
|
|
paddingVertical: 2,
|
|
width: 100, // Fixed width for alignment
|
|
justifyContent: "flex-end",
|
|
},
|
|
scoreBoxWin: {
|
|
backgroundColor: "rgba(255, 193, 7, 0.2)", // Yellowish bg
|
|
},
|
|
scoreBoxLoss: {
|
|
backgroundColor: "#F5F5F5",
|
|
},
|
|
scoreText: {
|
|
fontWeight: "bold",
|
|
fontSize: 13,
|
|
marginRight: 4,
|
|
},
|
|
scoreTextWin: {
|
|
color: "#FBC02D", // Darker yellow/orange
|
|
},
|
|
scoreTextLoss: {
|
|
color: "#999",
|
|
},
|
|
oversText: {
|
|
fontSize: 10,
|
|
color: "#999",
|
|
},
|
|
resultReason: {
|
|
marginTop: 8,
|
|
paddingTop: 8,
|
|
borderTopWidth: 1,
|
|
borderTopColor: "#EEE",
|
|
},
|
|
reasonText: {
|
|
fontSize: 11,
|
|
color: "#888",
|
|
},
|
|
});
|