344 lines
8.4 KiB
TypeScript
344 lines
8.4 KiB
TypeScript
import { ThemedText } from "@/components/themed-text";
|
|
import { ThemedView } from "@/components/themed-view";
|
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
|
import { MatchDetailData } from "@/types/api";
|
|
import React, { useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import {
|
|
Image,
|
|
LayoutAnimation,
|
|
Platform,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
UIManager,
|
|
View,
|
|
} from "react-native";
|
|
|
|
// Enable LayoutAnimation for Android
|
|
if (Platform.OS === "android") {
|
|
if (UIManager.setLayoutAnimationEnabledExperimental) {
|
|
UIManager.setLayoutAnimationEnabledExperimental(true);
|
|
}
|
|
}
|
|
|
|
interface CricketLiveBroadcastProps {
|
|
data: MatchDetailData;
|
|
isDark: boolean;
|
|
}
|
|
|
|
interface OverGroup {
|
|
over: number;
|
|
balls: any[];
|
|
totalRuns: number;
|
|
}
|
|
|
|
export function CricketLiveBroadcast({
|
|
data,
|
|
isDark,
|
|
}: CricketLiveBroadcastProps) {
|
|
const { t } = useTranslation();
|
|
const { match } = data;
|
|
const comments = match.comments?.Live || [];
|
|
|
|
const [isSectionExpanded, setIsSectionExpanded] = useState(true);
|
|
const [activeTeamTab, setActiveTeamTab] = useState<0 | 1>(0); // 0: Home, 1: Away
|
|
|
|
// Group comments by over
|
|
const oversData = useMemo(() => {
|
|
const groups: Map<number, OverGroup> = new Map();
|
|
|
|
comments.forEach((ball) => {
|
|
let overNum = -1;
|
|
// Parse "1.4" -> 1, "19.2" -> 19
|
|
// If data is just integers in string "1", treat as Over 1
|
|
const parts = String(ball.overs).split(".");
|
|
const n = parseInt(parts[0]);
|
|
if (!isNaN(n)) {
|
|
overNum = n;
|
|
}
|
|
|
|
if (overNum === -1) return;
|
|
|
|
if (!groups.has(overNum)) {
|
|
groups.set(overNum, { over: overNum, balls: [], totalRuns: 0 });
|
|
}
|
|
|
|
const group = groups.get(overNum)!;
|
|
group.balls.push(ball);
|
|
|
|
// Accumulate runs safely
|
|
const r = parseInt(ball.runs);
|
|
if (!isNaN(r)) {
|
|
group.totalRuns += r;
|
|
}
|
|
});
|
|
|
|
// Convert to array and sort descending (High over number first = Latest)
|
|
return Array.from(groups.values()).sort((a, b) => b.over - a.over);
|
|
}, [comments]);
|
|
|
|
const toggleSection = () => {
|
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
setIsSectionExpanded(!isSectionExpanded);
|
|
};
|
|
|
|
const renderBall = (ball: any, index: number) => {
|
|
let bgColor = isDark ? "#333" : "#EEE";
|
|
let textColor = isDark ? "#BBB" : "#666";
|
|
let label = ball.runs;
|
|
|
|
const runs = String(ball.runs);
|
|
|
|
if (runs === "4") {
|
|
bgColor = "#2196F3"; // Blue
|
|
textColor = "#FFF";
|
|
} else if (runs === "6") {
|
|
bgColor = "#4CAF50"; // Green
|
|
textColor = "#FFF";
|
|
} else if (["W", "F", "OUT"].includes(runs?.toUpperCase()) || ball.wicket) {
|
|
bgColor = "#F44336"; // Red
|
|
textColor = "#FFF";
|
|
label = "W";
|
|
}
|
|
|
|
return (
|
|
<View
|
|
key={index}
|
|
style={[styles.ballCircle, { backgroundColor: bgColor }]}
|
|
>
|
|
<ThemedText style={[styles.ballText, { color: textColor }]}>
|
|
{label}
|
|
</ThemedText>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
if (comments.length === 0) return null;
|
|
|
|
return (
|
|
<ThemedView
|
|
style={[
|
|
styles.container,
|
|
{ backgroundColor: isDark ? "#1E1E20" : "#FFF" },
|
|
]}
|
|
>
|
|
<TouchableOpacity
|
|
style={styles.header}
|
|
onPress={toggleSection}
|
|
activeOpacity={0.7}
|
|
>
|
|
<ThemedText style={[styles.title, { color: isDark ? "#CCC" : "#666" }]}>
|
|
{t("detail.live_broadcast_title", "Live Broadcast")}
|
|
</ThemedText>
|
|
<IconSymbol
|
|
name={isSectionExpanded ? "chevron-up" : "chevron-down"}
|
|
size={16}
|
|
color={isDark ? "#888" : "#BBB"}
|
|
/>
|
|
</TouchableOpacity>
|
|
|
|
{/* Team Filter Tabs */}
|
|
<View style={styles.topRow}>
|
|
<View style={styles.teamTabs}>
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.teamTab,
|
|
activeTeamTab === 0 && styles.teamTabActive,
|
|
]}
|
|
onPress={() => setActiveTeamTab(0)}
|
|
>
|
|
<Image
|
|
source={{ uri: match.homeTeamLogo }}
|
|
style={styles.teamTabLogo}
|
|
/>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.teamTab,
|
|
activeTeamTab === 1 && styles.teamTabActive,
|
|
]}
|
|
onPress={() => setActiveTeamTab(1)}
|
|
>
|
|
<Image
|
|
source={{ uri: match.awayTeamLogo }}
|
|
style={styles.teamTabLogo}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Current Innings Overview (Mock based on Tab) */}
|
|
<View style={styles.inningsInfo}>
|
|
<Image
|
|
source={{
|
|
uri:
|
|
activeTeamTab === 0 ? match.homeTeamLogo : match.awayTeamLogo,
|
|
}}
|
|
style={styles.smallLogo}
|
|
/>
|
|
<View>
|
|
<ThemedText style={styles.inningsTeamName}>
|
|
{activeTeamTab === 0 ? match.eventHomeTeam : match.eventAwayTeam}
|
|
</ThemedText>
|
|
<ThemedText style={styles.inningsLabel}>
|
|
{activeTeamTab === 0 ? "1st Innings" : "2nd Innings"}
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{isSectionExpanded && (
|
|
<View style={styles.listContainer}>
|
|
{oversData.map((group) => {
|
|
return (
|
|
<View
|
|
key={group.over}
|
|
style={[
|
|
styles.overGroup,
|
|
{ borderBottomColor: isDark ? "#333" : "#F5F5F5" },
|
|
]}
|
|
>
|
|
<View style={styles.overHeader}>
|
|
<View style={styles.overInfo}>
|
|
<ThemedText style={styles.overTitle}>
|
|
{t("detail.cricket_over_round", "Over {{round}}", {
|
|
round: group.over,
|
|
})}
|
|
</ThemedText>
|
|
</View>
|
|
<ThemedText style={styles.runsSummary}>
|
|
{t("detail.cricket_runs_summary", "{{runs}} Runs", {
|
|
runs: group.totalRuns,
|
|
})}
|
|
</ThemedText>
|
|
</View>
|
|
|
|
<View style={styles.ballsContainer}>
|
|
{group.balls.map((ball, idx) => renderBall(ball, idx))}
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
)}
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
margin: 16,
|
|
marginBottom: 0,
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
shadowColor: "#000",
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 4,
|
|
elevation: 3,
|
|
},
|
|
header: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
marginBottom: 16,
|
|
},
|
|
title: {
|
|
fontSize: 14,
|
|
fontWeight: "bold",
|
|
},
|
|
topRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
marginBottom: 16,
|
|
},
|
|
teamTabs: {
|
|
flexDirection: "row",
|
|
backgroundColor: "#F5F5F5",
|
|
borderRadius: 18,
|
|
padding: 3,
|
|
height: 36,
|
|
width: 100,
|
|
},
|
|
teamTab: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
borderRadius: 15,
|
|
},
|
|
teamTabActive: {
|
|
backgroundColor: "#FFF",
|
|
shadowColor: "#000",
|
|
shadowOffset: { width: 0, height: 1 },
|
|
shadowOpacity: 0.1,
|
|
elevation: 2,
|
|
},
|
|
teamTabLogo: {
|
|
width: 20,
|
|
height: 20,
|
|
resizeMode: "contain",
|
|
},
|
|
inningsInfo: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
},
|
|
smallLogo: {
|
|
width: 28,
|
|
height: 28,
|
|
marginRight: 8,
|
|
resizeMode: "contain",
|
|
},
|
|
inningsTeamName: {
|
|
fontSize: 13,
|
|
fontWeight: "600",
|
|
},
|
|
inningsLabel: {
|
|
fontSize: 10,
|
|
color: "#888",
|
|
},
|
|
|
|
listContainer: {
|
|
marginTop: 8,
|
|
},
|
|
overGroup: {
|
|
borderBottomWidth: 1,
|
|
},
|
|
overHeader: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
paddingVertical: 12,
|
|
},
|
|
overInfo: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
},
|
|
overTitle: {
|
|
fontSize: 14,
|
|
fontWeight: "600",
|
|
},
|
|
runsSummary: {
|
|
fontSize: 12,
|
|
color: "#888",
|
|
},
|
|
ballsContainer: {
|
|
flexDirection: "row",
|
|
flexWrap: "wrap",
|
|
paddingBottom: 16,
|
|
paddingLeft: 24, // Indent to align with text
|
|
gap: 8,
|
|
},
|
|
ballCircle: {
|
|
width: 28,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
},
|
|
ballText: {
|
|
fontSize: 10,
|
|
fontWeight: "bold",
|
|
},
|
|
});
|