Merge branch 'main' of https://git.ambigrat.com/shenyan/physical-expo
@@ -390,7 +390,7 @@ export default function HomeScreen() {
|
|||||||
awayTeamName: item.event_away_team,
|
awayTeamName: item.event_away_team,
|
||||||
homeTeamLogo: item.home_team_logo || "",
|
homeTeamLogo: item.home_team_logo || "",
|
||||||
awayTeamLogo: item.away_team_logo || "",
|
awayTeamLogo: item.away_team_logo || "",
|
||||||
scoreText: item.event_halftime_result || "0 - 0",
|
scoreText: item.event_final_result || item.event_halftime_result || "0 - 0",
|
||||||
fav: false,
|
fav: false,
|
||||||
sportId: sportId,
|
sportId: sportId,
|
||||||
isLive: true,
|
isLive: true,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default function LiveScreen() {
|
|||||||
homeTeamLogo: item.home_team_logo,
|
homeTeamLogo: item.home_team_logo,
|
||||||
awayTeamLogo: item.away_team_logo,
|
awayTeamLogo: item.away_team_logo,
|
||||||
meta: item.event_status,
|
meta: item.event_status,
|
||||||
scoreText: item.event_final_result || "0 - 0",
|
scoreText: item.event_final_result || item.event_halftime_result || "0 - 0",
|
||||||
fav: false,
|
fav: false,
|
||||||
leagueId: item.league_key,
|
leagueId: item.league_key,
|
||||||
sportId: state.selectedSportId ?? undefined,
|
sportId: state.selectedSportId ?? undefined,
|
||||||
@@ -157,7 +157,7 @@ export default function LiveScreen() {
|
|||||||
contentContainerStyle={styles.listContent}
|
contentContainerStyle={styles.listContent}
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
<View style={styles.center}>
|
<View style={styles.center}>
|
||||||
<ThemedText>{t("home.no_matches")}</ThemedText>
|
<ThemedText>{t("home.no_matches_live")}</ThemedText>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -90,19 +90,23 @@ export default function LiveDetailScreen() {
|
|||||||
eventType: "",
|
eventType: "",
|
||||||
eventToss: "",
|
eventToss: "",
|
||||||
eventManOfMatch: "",
|
eventManOfMatch: "",
|
||||||
scores: match.scores,
|
scores: Array.isArray(match.scores) && match.scores.length > 0 && 'type' in match.scores[0]
|
||||||
stats: match.statistics,
|
? match.scores as { type: string; home: string; away: string; }[]
|
||||||
|
: undefined,
|
||||||
|
stats: Array.isArray(match.statistics) && match.statistics.length > 0 && 'type' in match.statistics[0]
|
||||||
|
? match.statistics as { type: string; home: string; away: string; }[]
|
||||||
|
: undefined,
|
||||||
players: match.player_statistics
|
players: match.player_statistics
|
||||||
? {
|
? {
|
||||||
home_team: (match.player_statistics.home_team || []).map((p) => ({
|
home_team: (match.player_statistics.home_team || []).map((p) => ({
|
||||||
...p,
|
...p,
|
||||||
player_oncourt: p.player_oncourt || undefined,
|
player_oncourt: p.player_oncourt || undefined,
|
||||||
})),
|
})),
|
||||||
away_team: (match.player_statistics.away_team || []).map((p) => ({
|
away_team: (match.player_statistics.away_team || []).map((p) => ({
|
||||||
...p,
|
...p,
|
||||||
player_oncourt: p.player_oncourt || undefined,
|
player_oncourt: p.player_oncourt || undefined,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,8 +113,10 @@ export default function SearchScreen() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
|
const textColor = isDark ? "#FFFFFF" : "#000000";
|
||||||
const cardBg = isDark ? "#1C1C1E" : "#FFFFFF";
|
const cardBg = isDark ? "#1C1C1E" : "#FFFFFF";
|
||||||
const borderColor = isDark ? "#2C2C2E" : "rgba(0,0,0,0.06)";
|
const borderColor = isDark ? "#2C2C2E" : "rgba(0,0,0,0.1)";
|
||||||
|
const pageBg = isDark ? Colors.dark.background : "#f2f2f7";
|
||||||
const { state } = useAppState();
|
const { state } = useAppState();
|
||||||
|
|
||||||
const [searchType, setSearchType] = useState<SearchType>("all");
|
const [searchType, setSearchType] = useState<SearchType>("all");
|
||||||
@@ -485,7 +487,7 @@ export default function SearchScreen() {
|
|||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.iconBtn,
|
styles.sheetBtn,
|
||||||
{
|
{
|
||||||
backgroundColor: cardBg,
|
backgroundColor: cardBg,
|
||||||
borderColor: borderColor,
|
borderColor: borderColor,
|
||||||
@@ -688,49 +690,28 @@ export default function SearchScreen() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemedView style={styles.container}>
|
<ThemedView style={[styles.container, { backgroundColor: pageBg }]}>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
headerShown: false,
|
title: t("search.title"),
|
||||||
|
headerShown: true,
|
||||||
|
headerBackTitle: t("settings.back"),
|
||||||
|
// Ensure header matches theme to avoid white flash
|
||||||
|
headerStyle: {
|
||||||
|
backgroundColor: pageBg,
|
||||||
|
},
|
||||||
|
headerTintColor: textColor,
|
||||||
|
headerShadowVisible: false,
|
||||||
|
// Present the screen as a normal card and slide from right
|
||||||
|
presentation: "card",
|
||||||
animation: "slide_from_right",
|
animation: "slide_from_right",
|
||||||
|
// Set the scene/content background to match theme during transition
|
||||||
|
contentStyle: {
|
||||||
|
backgroundColor: pageBg,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BlurView
|
|
||||||
intensity={80}
|
|
||||||
tint={isDark ? "dark" : "light"}
|
|
||||||
style={[
|
|
||||||
styles.nav,
|
|
||||||
{
|
|
||||||
backgroundColor: cardBg,
|
|
||||||
},
|
|
||||||
{ paddingTop: insets.top + 10 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.iconBtn,
|
|
||||||
{
|
|
||||||
backgroundColor: cardBg,
|
|
||||||
borderColor: borderColor,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPress={() => router.back()}
|
|
||||||
>
|
|
||||||
<IconSymbol
|
|
||||||
name="chevron-back"
|
|
||||||
size={20}
|
|
||||||
color={isDark ? "rgba(255,255,255,0.9)" : "rgba(0,0,0,0.9)"}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<View style={styles.brand}>
|
|
||||||
<ThemedText style={styles.brandTitle}>{t("search.title")}</ThemedText>
|
|
||||||
<ThemedText style={styles.brandSub}>
|
|
||||||
{t("search.subtitle")}
|
|
||||||
</ThemedText>
|
|
||||||
</View>
|
|
||||||
</BlurView>
|
|
||||||
|
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<View style={styles.contentContainer}>
|
<View style={styles.contentContainer}>
|
||||||
{renderSearchBar()}
|
{renderSearchBar()}
|
||||||
@@ -792,39 +773,6 @@ const styles = StyleSheet.create({
|
|||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
nav: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 10,
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingBottom: 12,
|
|
||||||
position: "sticky",
|
|
||||||
top: 0,
|
|
||||||
zIndex: 40,
|
|
||||||
},
|
|
||||||
iconBtn: {
|
|
||||||
width: 38,
|
|
||||||
height: 38,
|
|
||||||
borderRadius: 14,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
borderWidth: 1,
|
|
||||||
},
|
|
||||||
brand: {
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 0,
|
|
||||||
gap: 2,
|
|
||||||
},
|
|
||||||
brandTitle: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: "700",
|
|
||||||
letterSpacing: 0.2,
|
|
||||||
},
|
|
||||||
brandSub: {
|
|
||||||
fontSize: 12,
|
|
||||||
opacity: 0.5,
|
|
||||||
fontWeight: "600",
|
|
||||||
},
|
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
@@ -862,7 +810,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
chip: {
|
chip: {
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
paddingVertical: 8,
|
paddingVertical: 6,
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
},
|
},
|
||||||
@@ -920,14 +868,14 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 12,
|
gap: 12,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderRadius: 18,
|
borderRadius: 10,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
logoDot: {
|
logoDot: {
|
||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
borderRadius: 14,
|
borderRadius: 8,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
|||||||
8
assets/empty/country_dark.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="32" cy="32" r="28" fill="#2C2F36"/>
|
||||||
|
<path d="M12 32H52" stroke="#3A3F47" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M32 12C24 20 24 44 32 52" stroke="#3A3F47" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M32 12C40 20 40 44 32 52" stroke="#3A3F47" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M18 22C24 26 40 26 46 22" stroke="#3A3F47" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M18 42C24 38 40 38 46 42" stroke="#3A3F47" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 617 B |
8
assets/empty/country_light.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="32" cy="32" r="28" fill="#DADADA"/>
|
||||||
|
<path d="M12 32H52" stroke="#EEEEEE" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M32 12C24 20 24 44 32 52" stroke="#EEEEEE" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M32 12C40 20 40 44 32 52" stroke="#EEEEEE" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M18 22C24 26 40 26 46 22" stroke="#EEEEEE" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M18 42C24 38 40 38 46 42" stroke="#EEEEEE" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 617 B |
7
assets/empty/league_dark.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M18 12H46V20C46 30 40 37 32 40C24 37 18 30 18 20V12Z" fill="#2C2F36"/>
|
||||||
|
<path d="M24 16H40V20C40 26.5 36.5 31 32 33C27.5 31 24 26.5 24 20V16Z" fill="#3A3F47"/>
|
||||||
|
<path d="M26 42H38V46H26V42Z" fill="#3A3F47"/>
|
||||||
|
<path d="M22 46H42V50H22V46Z" fill="#2C2F36"/>
|
||||||
|
<path d="M24 50H40V54H24V50Z" fill="#3A3F47"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 422 B |
7
assets/empty/league_light.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M18 12H46V20C46 30 40 37 32 40C24 37 18 30 18 20V12Z" fill="#DADADA"/>
|
||||||
|
<path d="M24 16H40V20C40 26.5 36.5 31 32 33C27.5 31 24 26.5 24 20V16Z" fill="#EEEEEE"/>
|
||||||
|
<path d="M26 42H38V46H26V42Z" fill="#EEEEEE"/>
|
||||||
|
<path d="M22 46H42V50H22V46Z" fill="#DADADA"/>
|
||||||
|
<path d="M24 50H40V54H24V50Z" fill="#EEEEEE"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 421 B |
5
assets/empty/player_dark.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="32" cy="32" r="28" fill="#2C2F36"/>
|
||||||
|
<circle cx="32" cy="26" r="9" fill="#3A3F47"/>
|
||||||
|
<path d="M16 52C18.8 42.5 25 38 32 38C39 38 45.2 42.5 48 52" fill="#3A3F47"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 281 B |
5
assets/empty/player_light.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="32" cy="32" r="28" fill="#DADADA"/>
|
||||||
|
<circle cx="32" cy="26" r="9" fill="#EEEEEE"/>
|
||||||
|
<path d="M16 52C18.8 42.5 25 38 32 38C39 38 45.2 42.5 48 52" fill="#EEEEEE"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 281 B |
4
assets/empty/team_dark.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 10C12 10 24 6 32 6C40 6 52 10 52 10V30C52 44 38 54 32 58C26 54 12 44 12 30V10Z" fill="#2C2F36"/>
|
||||||
|
<path d="M18 14C18 14 26 12 32 12C38 12 46 14 46 14V29C46 40 36 48 32 51C28 48 18 40 18 29V14Z" fill="#3A3F47"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 329 B |
4
assets/empty/team_light.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 10C12 10 24 6 32 6C40 6 52 10 52 10V30C52 44 38 54 32 58C26 54 12 44 12 30V10Z" fill="#DADADA"/>
|
||||||
|
<path d="M18 14C18 14 26 12 32 12C38 12 46 14 46 14V29C46 40 36 48 32 51C28 48 18 40 18 29V14Z" fill="#EEEEEE"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 329 B |
7
assets/league_dark.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M18 12H46V20C46 30 40 37 32 40C24 37 18 30 18 20V12Z" fill="#2C2F36"/>
|
||||||
|
<path d="M24 16H40V20C40 26.5 36.5 31 32 33C27.5 31 24 26.5 24 20V16Z" fill="#3A3F47"/>
|
||||||
|
<path d="M26 42H38V46H26V42Z" fill="#3A3F47"/>
|
||||||
|
<path d="M22 46H42V50H22V46Z" fill="#2C2F36"/>
|
||||||
|
<path d="M24 50H40V54H24V50Z" fill="#3A3F47"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 421 B |
34
components/empty-placeholder.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
|
import { Image } from "expo-image";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface EmptyPlaceholderProps {
|
||||||
|
type: "team" | "league" | "player" | "country";
|
||||||
|
size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageMap: { [key: string]: any } = {
|
||||||
|
team_light: require("@/assets/empty/team_light.svg"),
|
||||||
|
team_dark: require("@/assets/empty/team_dark.svg"),
|
||||||
|
league_light: require("@/assets/empty/league_light.svg"),
|
||||||
|
league_dark: require("@/assets/empty/league_dark.svg"),
|
||||||
|
player_light: require("@/assets/empty/player_light.svg"),
|
||||||
|
player_dark: require("@/assets/empty/player_dark.svg"),
|
||||||
|
country_light: require("@/assets/empty/country_light.svg"),
|
||||||
|
country_dark: require("@/assets/empty/country_dark.svg"),
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EmptyPlaceholder({ type, size = 64 }: EmptyPlaceholderProps) {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
const suffix = isDark ? "_dark" : "_light";
|
||||||
|
const imageKey = `${type}${suffix}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
source={imageMap[imageKey] || imageMap.team_light}
|
||||||
|
style={{ width: size, height: size }}
|
||||||
|
contentFit="contain"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
@@ -47,14 +48,14 @@ export function LeagueModal({
|
|||||||
>
|
>
|
||||||
{/* 左侧图标 */}
|
{/* 左侧图标 */}
|
||||||
<View style={[styles.iconContainer, { backgroundColor: iconBg }]}>
|
<View style={[styles.iconContainer, { backgroundColor: iconBg }]}>
|
||||||
{league.logo ? (
|
{league.logo && league.logo.trim() !== "" && !league.logo.includes("placehold") ? (
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: league.logo }}
|
source={{ uri: league.logo }}
|
||||||
style={styles.leagueLogo}
|
style={styles.leagueLogo}
|
||||||
contentFit="contain"
|
contentFit="contain"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<IconSymbol name="trophy-outline" size={24} color={text} />
|
<EmptyPlaceholder type="league" size={24} />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { LiveScoreMatch } from "@/types/api";
|
import { LiveScoreMatch } from "@/types/api";
|
||||||
@@ -23,15 +24,13 @@ export function LiveLeagueInfo({ match }: LiveLeagueInfoProps) {
|
|||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<View style={styles.left}>
|
<View style={styles.left}>
|
||||||
{match.league_logo ? (
|
{match.league_logo && match.league_logo.trim() !== "" && !match.league_logo.includes("placehold") ? (
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: match.league_logo }}
|
source={{ uri: match.league_logo }}
|
||||||
style={styles.leagueLogo}
|
style={styles.leagueLogo}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View style={[styles.fallbackLogo, { backgroundColor: "#333" }]}>
|
<EmptyPlaceholder type="league" size={24} />
|
||||||
<IconSymbol name="trophy-outline" size={14} color="#AAA" />
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
<ThemedText style={[styles.leagueName, { color: textColor }]}>
|
<ThemedText style={[styles.leagueName, { color: textColor }]}>
|
||||||
{match.league_name}
|
{match.league_name}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { addFavorite, checkFavorite, removeFavorite } from "@/lib/api";
|
import { addFavorite, checkFavorite, removeFavorite } from "@/lib/api";
|
||||||
@@ -265,10 +266,14 @@ export function LiveScoreHeader({ match, topInset }: LiveScoreHeaderProps) {
|
|||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style={styles.logoContainer}>
|
<View style={styles.logoContainer}>
|
||||||
<Image
|
{homeLogo && homeLogo.trim() !== "" && !homeLogo.includes("placehold") ? (
|
||||||
source={{ uri: homeLogo }}
|
<Image
|
||||||
style={[styles.teamLogo, isTennis && styles.tennisAvatar]}
|
source={{ uri: homeLogo }}
|
||||||
/>
|
style={[styles.teamLogo, isTennis && styles.tennisAvatar]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type={isTennis ? "player" : "team"} size={60} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<ThemedText style={styles.teamName} numberOfLines={2}>
|
<ThemedText style={styles.teamName} numberOfLines={2}>
|
||||||
@@ -318,10 +323,14 @@ export function LiveScoreHeader({ match, topInset }: LiveScoreHeaderProps) {
|
|||||||
<View style={styles.teamInfo}>
|
<View style={styles.teamInfo}>
|
||||||
<View style={styles.teamLogoRow}>
|
<View style={styles.teamLogoRow}>
|
||||||
<View style={styles.logoContainer}>
|
<View style={styles.logoContainer}>
|
||||||
<Image
|
{awayLogo && awayLogo.trim() !== "" && !awayLogo.includes("placehold") ? (
|
||||||
source={{ uri: awayLogo }}
|
<Image
|
||||||
style={[styles.teamLogo, isTennis && styles.tennisAvatar]}
|
source={{ uri: awayLogo }}
|
||||||
/>
|
style={[styles.teamLogo, isTennis && styles.tennisAvatar]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type={isTennis ? "player" : "team"} size={60} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => toggleTeamFavorite(match.away_team_key, false)}
|
onPress={() => toggleTeamFavorite(match.away_team_key, false)}
|
||||||
@@ -437,7 +446,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
scoreValue: {
|
scoreValue: {
|
||||||
color: "#000",
|
color: "#000",
|
||||||
fontSize: 25,
|
fontSize: 20,
|
||||||
fontWeight: "700",
|
fontWeight: "700",
|
||||||
lineHeight: 30,
|
lineHeight: 30,
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
import { useAppState } from "@/context/AppStateContext";
|
import { useAppState } from "@/context/AppStateContext";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { addFavorite, removeFavorite } from "@/lib/api";
|
import { addFavorite, removeFavorite } from "@/lib/api";
|
||||||
import { getInitials, getLogoGradient } from "@/lib/avatar-utils";
|
|
||||||
import { Match } from "@/types/api";
|
import { Match } from "@/types/api";
|
||||||
import { LinearGradient } from "expo-linear-gradient";
|
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Image, Pressable, StyleSheet, TouchableOpacity, View } from "react-native";
|
import { Image, Pressable, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||||
@@ -182,13 +181,10 @@ export function MatchCardLeague({
|
|||||||
<View style={styles.teamsColumn}>
|
<View style={styles.teamsColumn}>
|
||||||
<View style={styles.teamRow}>
|
<View style={styles.teamRow}>
|
||||||
{(() => {
|
{(() => {
|
||||||
const teamName = isTennis ? (match as any).eventFirstPlayer : (match.home || match.homeTeamName);
|
|
||||||
const logoUri = isTennis
|
const logoUri = isTennis
|
||||||
? (match as any).eventFirstPlayerLogo
|
? (match as any).eventFirstPlayerLogo
|
||||||
: ((match as any).homeLogo || match.homeTeamLogo);
|
: ((match as any).homeLogo || match.homeTeamLogo);
|
||||||
const hasLogo = logoUri && logoUri.trim() !== "" && !logoUri.includes("placehold");
|
const hasLogo = logoUri && logoUri.trim() !== "" && !logoUri.includes("placehold");
|
||||||
const gradient = getLogoGradient(teamName || "");
|
|
||||||
const initials = getInitials(teamName || "");
|
|
||||||
|
|
||||||
return hasLogo ? (
|
return hasLogo ? (
|
||||||
<Image
|
<Image
|
||||||
@@ -196,14 +192,7 @@ export function MatchCardLeague({
|
|||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type="team" size={24} />
|
||||||
colors={[gradient.color1, gradient.color2]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 1, y: 1 }}
|
|
||||||
style={styles.teamLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.teamLogoText}>{initials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
<View style={styles.teamNameContainer}>
|
<View style={styles.teamNameContainer}>
|
||||||
@@ -217,13 +206,10 @@ export function MatchCardLeague({
|
|||||||
|
|
||||||
<View style={[styles.teamRow, { marginTop: 10 }]}>
|
<View style={[styles.teamRow, { marginTop: 10 }]}>
|
||||||
{(() => {
|
{(() => {
|
||||||
const teamName = isTennis ? (match as any).eventSecondPlayer : (match.away || match.awayTeamName);
|
|
||||||
const logoUri = isTennis
|
const logoUri = isTennis
|
||||||
? (match as any).eventSecondPlayerLogo
|
? (match as any).eventSecondPlayerLogo
|
||||||
: ((match as any).awayLogo || match.awayTeamLogo);
|
: ((match as any).awayLogo || match.awayTeamLogo);
|
||||||
const hasLogo = logoUri && logoUri.trim() !== "" && !logoUri.includes("placehold");
|
const hasLogo = logoUri && logoUri.trim() !== "" && !logoUri.includes("placehold");
|
||||||
const gradient = getLogoGradient(teamName || "");
|
|
||||||
const initials = getInitials(teamName || "");
|
|
||||||
|
|
||||||
return hasLogo ? (
|
return hasLogo ? (
|
||||||
<Image
|
<Image
|
||||||
@@ -231,14 +217,7 @@ export function MatchCardLeague({
|
|||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type="team" size={24} />
|
||||||
colors={[gradient.color1, gradient.color2]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 1, y: 1 }}
|
|
||||||
style={styles.teamLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.teamLogoText}>{initials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
<View style={styles.teamNameContainer}>
|
<View style={styles.teamNameContainer}>
|
||||||
@@ -386,7 +365,7 @@ const styles = StyleSheet.create({
|
|||||||
gap: 6,
|
gap: 6,
|
||||||
},
|
},
|
||||||
scoreBox: {
|
scoreBox: {
|
||||||
width: 36,
|
width: 45,
|
||||||
height: 54,
|
height: 54,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
borderWidth: 1.5,
|
borderWidth: 1.5,
|
||||||
@@ -416,7 +395,7 @@ const styles = StyleSheet.create({
|
|||||||
color: "#FF9500",
|
color: "#FF9500",
|
||||||
},
|
},
|
||||||
scoreBoxPlaceholder: {
|
scoreBoxPlaceholder: {
|
||||||
width: 36,
|
width: 45,
|
||||||
height: 54,
|
height: 54,
|
||||||
},
|
},
|
||||||
favoriteButton: {
|
favoriteButton: {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
@@ -467,13 +468,16 @@ export function MatchCard({
|
|||||||
<View style={styles.contentRow}>
|
<View style={styles.contentRow}>
|
||||||
<View style={styles.teamInfo}>
|
<View style={styles.teamInfo}>
|
||||||
<View style={styles.teamLogoPlaceholder}>
|
<View style={styles.teamLogoPlaceholder}>
|
||||||
<Image
|
{homeLogo && homeLogo.trim() !== "" && !homeLogo.includes("placehold") ? (
|
||||||
source={{
|
<Image
|
||||||
uri: homeLogo,
|
source={{ uri: homeLogo }}
|
||||||
}}
|
style={styles.teamLogo}
|
||||||
style={styles.teamLogo}
|
contentFit="contain"
|
||||||
contentFit="contain"
|
onError={() => {}}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type="team" size={18} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<ThemedText
|
<ThemedText
|
||||||
type="defaultSemiBold"
|
type="defaultSemiBold"
|
||||||
@@ -492,13 +496,16 @@ export function MatchCard({
|
|||||||
<View style={styles.contentRow}>
|
<View style={styles.contentRow}>
|
||||||
<View style={styles.teamInfo}>
|
<View style={styles.teamInfo}>
|
||||||
<View style={styles.teamLogoPlaceholder}>
|
<View style={styles.teamLogoPlaceholder}>
|
||||||
<Image
|
{awayLogo && awayLogo.trim() !== "" && !awayLogo.includes("placehold") ? (
|
||||||
source={{
|
<Image
|
||||||
uri: awayLogo,
|
source={{ uri: awayLogo }}
|
||||||
}}
|
style={styles.teamLogo}
|
||||||
style={styles.teamLogo}
|
contentFit="contain"
|
||||||
contentFit="contain"
|
onError={() => {}}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type="team" size={18} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<ThemedText
|
<ThemedText
|
||||||
type="defaultSemiBold"
|
type="defaultSemiBold"
|
||||||
@@ -709,7 +716,7 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
scoreContainer: {
|
scoreContainer: {
|
||||||
width: 25,
|
width: 35,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
extraStatsContainer: {
|
extraStatsContainer: {
|
||||||
@@ -722,7 +729,7 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
scoreBox: {
|
scoreBox: {
|
||||||
width: 28,
|
width: 35,
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
borderWidth: 0.3,
|
borderWidth: 0.3,
|
||||||
@@ -751,7 +758,7 @@ const styles = StyleSheet.create({
|
|||||||
backgroundColor: "rgba(0,0,0,0.06)",
|
backgroundColor: "rgba(0,0,0,0.06)",
|
||||||
},
|
},
|
||||||
scoreBoxPlaceholder: {
|
scoreBoxPlaceholder: {
|
||||||
width: 28,
|
width: 35,
|
||||||
height: 48,
|
height: 48,
|
||||||
},
|
},
|
||||||
cardsInline: {
|
cardsInline: {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { MatchDetailData } from "@/types/api";
|
import { MatchDetailData } from "@/types/api";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -143,7 +144,9 @@ export function BasketballScoreTable({
|
|||||||
<View style={styles.teamCell}>
|
<View style={styles.teamCell}>
|
||||||
{row.logo && row.logo.trim() !== "" && !row.logo.includes("placehold") ? (
|
{row.logo && row.logo.trim() !== "" && !row.logo.includes("placehold") ? (
|
||||||
<Image source={{ uri: row.logo }} style={styles.teamLogo} />
|
<Image source={{ uri: row.logo }} style={styles.teamLogo} />
|
||||||
) : null}
|
) : (
|
||||||
|
<EmptyPlaceholder type="team" size={28} />
|
||||||
|
)}
|
||||||
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
||||||
{row.name}
|
{row.name}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { getInitials, getLogoGradient } from "@/lib/avatar-utils";
|
|
||||||
import { MatchDetailData } from "@/types/api";
|
import { MatchDetailData } from "@/types/api";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { LinearGradient } from "expo-linear-gradient";
|
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
ScrollView,
|
ScrollView,
|
||||||
@@ -161,10 +160,6 @@ export function BasketballStats({ data, isDark }: BasketballStatsProps) {
|
|||||||
const awayTeamLogo = match.awayTeamLogo || "";
|
const awayTeamLogo = match.awayTeamLogo || "";
|
||||||
const hasHomeLogo = homeTeamLogo && homeTeamLogo.trim() !== "" && !homeTeamLogo.includes("placehold");
|
const hasHomeLogo = homeTeamLogo && homeTeamLogo.trim() !== "" && !homeTeamLogo.includes("placehold");
|
||||||
const hasAwayLogo = awayTeamLogo && awayTeamLogo.trim() !== "" && !awayTeamLogo.includes("placehold");
|
const hasAwayLogo = awayTeamLogo && awayTeamLogo.trim() !== "" && !awayTeamLogo.includes("placehold");
|
||||||
const homeGradient = getLogoGradient(homeTeamName);
|
|
||||||
const awayGradient = getLogoGradient(awayTeamName);
|
|
||||||
const homeInitials = getInitials(homeTeamName);
|
|
||||||
const awayInitials = getInitials(awayTeamName);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: bgColor }]}>
|
<View style={[styles.container, { backgroundColor: bgColor }]}>
|
||||||
@@ -199,12 +194,7 @@ export function BasketballStats({ data, isDark }: BasketballStatsProps) {
|
|||||||
{hasHomeLogo ? (
|
{hasHomeLogo ? (
|
||||||
<Image source={{ uri: homeTeamLogo }} style={styles.teamTabLogo} contentFit="contain" />
|
<Image source={{ uri: homeTeamLogo }} style={styles.teamTabLogo} contentFit="contain" />
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type="team" size={24} />
|
||||||
colors={[homeGradient.color1, homeGradient.color2]}
|
|
||||||
style={styles.teamTabLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.teamTabLogoText}>{homeInitials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -218,12 +208,7 @@ export function BasketballStats({ data, isDark }: BasketballStatsProps) {
|
|||||||
{hasAwayLogo ? (
|
{hasAwayLogo ? (
|
||||||
<Image source={{ uri: awayTeamLogo }} style={styles.teamTabLogo} contentFit="contain" />
|
<Image source={{ uri: awayTeamLogo }} style={styles.teamTabLogo} contentFit="contain" />
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type="team" size={24} />
|
||||||
colors={[awayGradient.color1, awayGradient.color2]}
|
|
||||||
style={styles.teamTabLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.teamTabLogoText}>{awayInitials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { MatchDetailData } from "@/types/api";
|
import { MatchDetailData } from "@/types/api";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -89,7 +90,11 @@ export function FootballScoreTable({ data, isDark }: FootballScoreTableProps) {
|
|||||||
{rows.map((row, idx) => (
|
{rows.map((row, idx) => (
|
||||||
<View key={idx} style={[styles.row, idx === 0 && styles.rowBorder]}>
|
<View key={idx} style={[styles.row, idx === 0 && styles.rowBorder]}>
|
||||||
<View style={styles.teamCell}>
|
<View style={styles.teamCell}>
|
||||||
<Image source={{ uri: row.logo }} style={styles.teamLogo} />
|
{row.logo && row.logo.trim() !== "" && !row.logo.includes("placehold") ? (
|
||||||
|
<Image source={{ uri: row.logo }} style={styles.teamLogo} />
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type="team" size={28} />
|
||||||
|
)}
|
||||||
<ThemedText style={styles.teamName} numberOfLines={1}>
|
<ThemedText style={styles.teamName} numberOfLines={1}>
|
||||||
{row.name}
|
{row.name}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { MatchDetailData } from "@/types/api";
|
import { MatchDetailData } from "@/types/api";
|
||||||
@@ -25,12 +26,10 @@ export function LeagueInfo({ data, isDark }: LeagueInfoProps) {
|
|||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<View style={styles.left}>
|
<View style={styles.left}>
|
||||||
{match.leagueLogo ? (
|
{match.leagueLogo && match.leagueLogo.trim() !== "" && !match.leagueLogo.includes("placehold") ? (
|
||||||
<Image source={{ uri: match.leagueLogo }} style={styles.leagueLogo} />
|
<Image source={{ uri: match.leagueLogo }} style={styles.leagueLogo} />
|
||||||
) : (
|
) : (
|
||||||
<View style={[styles.fallbackLogo, { backgroundColor: "#333" }]}>
|
<EmptyPlaceholder type="league" size={24} />
|
||||||
<IconSymbol name="trophy-outline" size={14} color="#AAA" />
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
<ThemedText style={[styles.leagueName, { color: textColor }]}>
|
<ThemedText style={[styles.leagueName, { color: textColor }]}>
|
||||||
{match.leagueName}
|
{match.leagueName}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { addFavorite, checkFavorite, removeFavorite } from "@/lib/api";
|
import { addFavorite, checkFavorite, removeFavorite } from "@/lib/api";
|
||||||
import { getInitials, getLogoGradient } from "@/lib/avatar-utils";
|
|
||||||
import { storage } from "@/lib/storage";
|
import { storage } from "@/lib/storage";
|
||||||
import { MatchDetailData } from "@/types/api";
|
import { MatchDetailData } from "@/types/api";
|
||||||
import { LinearGradient } from "expo-linear-gradient";
|
import { LinearGradient } from "expo-linear-gradient";
|
||||||
@@ -170,10 +170,6 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
|
|||||||
|
|
||||||
const hasFirstLogo = firstPlayerLogo && firstPlayerLogo.trim() !== "" && !firstPlayerLogo.includes("placehold");
|
const hasFirstLogo = firstPlayerLogo && firstPlayerLogo.trim() !== "" && !firstPlayerLogo.includes("placehold");
|
||||||
const hasSecondLogo = secondPlayerLogo && secondPlayerLogo.trim() !== "" && !secondPlayerLogo.includes("placehold");
|
const hasSecondLogo = secondPlayerLogo && secondPlayerLogo.trim() !== "" && !secondPlayerLogo.includes("placehold");
|
||||||
const firstGradient = getLogoGradient(firstPlayerName || "");
|
|
||||||
const secondGradient = getLogoGradient(secondPlayerName || "");
|
|
||||||
const firstInitials = getInitials(firstPlayerName || "");
|
|
||||||
const secondInitials = getInitials(secondPlayerName || "");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
@@ -243,14 +239,7 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
|
|||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type={isTennis ? "player" : "team"} size={60} />
|
||||||
colors={[firstGradient.color1, firstGradient.color2]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 1, y: 1 }}
|
|
||||||
style={styles.teamLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.teamLogoText}>{firstInitials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -283,14 +272,7 @@ export function ScoreHeader({ data, isDark, topInset }: ScoreHeaderProps) {
|
|||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type={isTennis ? "player" : "team"} size={60} />
|
||||||
colors={[secondGradient.color1, secondGradient.color2]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 1, y: 1 }}
|
|
||||||
style={styles.teamLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.teamLogoText}>{secondInitials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { MatchCardLeague } from "@/components/match-card-league";
|
import { MatchCardLeague } from "@/components/match-card-league";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { fetchLeagues, fetchTodayMatches } from "@/lib/api";
|
import { fetchLeagues, fetchTodayMatches } from "@/lib/api";
|
||||||
import { getInitials, getLogoGradient } from "@/lib/avatar-utils";
|
|
||||||
import { League, Match } from "@/types/api";
|
import { League, Match } from "@/types/api";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { LinearGradient } from "expo-linear-gradient";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
@@ -212,8 +211,6 @@ export function MatchesByLeague({
|
|||||||
<View style={styles.leagueHeaderLeft}>
|
<View style={styles.leagueHeaderLeft}>
|
||||||
{(() => {
|
{(() => {
|
||||||
const hasLogo = league.logo && league.logo.trim() !== "" && !league.logo.includes("placehold");
|
const hasLogo = league.logo && league.logo.trim() !== "" && !league.logo.includes("placehold");
|
||||||
const gradient = getLogoGradient(league.name || "");
|
|
||||||
const initials = getInitials(league.name || "");
|
|
||||||
|
|
||||||
return hasLogo ? (
|
return hasLogo ? (
|
||||||
<Image
|
<Image
|
||||||
@@ -221,14 +218,7 @@ export function MatchesByLeague({
|
|||||||
style={[styles.leagueLogo, { backgroundColor: isDark ? "#3A3A3C" : "#E5E5E5" }]}
|
style={[styles.leagueLogo, { backgroundColor: isDark ? "#3A3A3C" : "#E5E5E5" }]}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LinearGradient
|
<EmptyPlaceholder type="league" size={32} />
|
||||||
colors={[gradient.color1, gradient.color2]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 1, y: 1 }}
|
|
||||||
style={styles.leagueLogoGradient}
|
|
||||||
>
|
|
||||||
<ThemedText style={styles.leagueLogoText}>{initials}</ThemedText>
|
|
||||||
</LinearGradient>
|
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EmptyPlaceholder } from "@/components/empty-placeholder";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
@@ -122,12 +123,14 @@ export function UpcomingMatchCard({
|
|||||||
|
|
||||||
<View style={styles.teamsContainer}>
|
<View style={styles.teamsContainer}>
|
||||||
<View style={styles.team}>
|
<View style={styles.team}>
|
||||||
{match.homeTeamLogo && (
|
{match.homeTeamLogo && match.homeTeamLogo.trim() !== "" && !match.homeTeamLogo.includes("placehold") ? (
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: match.homeTeamLogo }}
|
source={{ uri: match.homeTeamLogo }}
|
||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
contentFit="contain"
|
contentFit="contain"
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type="team" size={24} />
|
||||||
)}
|
)}
|
||||||
<ThemedText
|
<ThemedText
|
||||||
type="defaultSemiBold"
|
type="defaultSemiBold"
|
||||||
@@ -143,12 +146,14 @@ export function UpcomingMatchCard({
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.team}>
|
<View style={styles.team}>
|
||||||
{match.awayTeamLogo && (
|
{match.awayTeamLogo && match.awayTeamLogo.trim() !== "" && !match.awayTeamLogo.includes("placehold") ? (
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: match.awayTeamLogo }}
|
source={{ uri: match.awayTeamLogo }}
|
||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
contentFit="contain"
|
contentFit="contain"
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyPlaceholder type="team" size={24} />
|
||||||
)}
|
)}
|
||||||
<ThemedText
|
<ThemedText
|
||||||
type="defaultSemiBold"
|
type="defaultSemiBold"
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"all_leagues": "All Leagues",
|
"all_leagues": "All Leagues",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"no_matches": "No matches found.",
|
"no_matches": "No matches found.",
|
||||||
|
"no_matches_live": "No live matches found.",
|
||||||
"no_leagues": "No leagues available"
|
"no_leagues": "No leagues available"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"all_leagues": "सभी लीग",
|
"all_leagues": "सभी लीग",
|
||||||
"loading": "लोड हो रहा है...",
|
"loading": "लोड हो रहा है...",
|
||||||
"no_matches": "कोई मैच नहीं मिला",
|
"no_matches": "कोई मैच नहीं मिला",
|
||||||
|
"no_matches_live": "कोई लाइव मैच नहीं मिला",
|
||||||
"no_leagues": "कोई लीग उपलब्ध नहीं है"
|
"no_leagues": "कोई लीग उपलब्ध नहीं है"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"all_leagues": "Semua Liga",
|
"all_leagues": "Semua Liga",
|
||||||
"loading": "Memuat...",
|
"loading": "Memuat...",
|
||||||
"no_matches": "Tidak ada pertandingan",
|
"no_matches": "Tidak ada pertandingan",
|
||||||
|
"no_matches_live": "Tidak ada pertandingan langsung",
|
||||||
"no_leagues": "Tidak ada liga tersedia"
|
"no_leagues": "Tidak ada liga tersedia"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"all_leagues": "Semua Liga",
|
"all_leagues": "Semua Liga",
|
||||||
"loading": "Memuatkan...",
|
"loading": "Memuatkan...",
|
||||||
"no_matches": "Tiada perlawanan ditemui",
|
"no_matches": "Tiada perlawanan ditemui",
|
||||||
|
"no_matches_live": "Tiada perlawanan langsung ditemui",
|
||||||
"no_leagues": "Tiada liga tersedia"
|
"no_leagues": "Tiada liga tersedia"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"all_leagues": "ทุกลีก",
|
"all_leagues": "ทุกลีก",
|
||||||
"loading": "กำลังโหลด...",
|
"loading": "กำลังโหลด...",
|
||||||
"no_matches": "ไม่พบการแข่งขัน",
|
"no_matches": "ไม่พบการแข่งขัน",
|
||||||
|
"no_matches_live": "ไม่พบการแข่งขันสด",
|
||||||
"no_leagues": "ไม่มีลีกให้เลือก"
|
"no_leagues": "ไม่มีลีกให้เลือก"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"all_leagues": "Tất cả giải",
|
"all_leagues": "Tất cả giải",
|
||||||
"loading": "Đang tải...",
|
"loading": "Đang tải...",
|
||||||
"no_matches": "Không tìm thấy trận đấu",
|
"no_matches": "Không tìm thấy trận đấu",
|
||||||
|
"no_matches_live": "Không tìm thấy trận đấu trực tiếp",
|
||||||
"no_leagues": "Không có giải đấu"
|
"no_leagues": "Không có giải đấu"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"all_leagues": "全部联赛",
|
"all_leagues": "全部联赛",
|
||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
"no_matches": "暂无比赛",
|
"no_matches": "暂无比赛",
|
||||||
|
"no_matches_live": "暂无直播比赛",
|
||||||
"no_leagues": "暂无联赛"
|
"no_leagues": "暂无联赛"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|||||||