update: gemini fixes

This commit is contained in:
Yordan Simeonov
2025-10-29 20:50:21 +11:00
parent 04a44bad7b
commit 9725d8bff1
23 changed files with 473 additions and 647 deletions

View File

@@ -1,5 +1,5 @@
import { AutoCompleteItem } from "@/apis/autoCompleteApi";
import { getSearchResults, SearchResultItem } from "@/apis/searchApi";
import { SearchResultItem } from "@/apis/searchApi";
import { Season } from "@/apis/seasonApi";
import { Show } from "@/apis/showApi";
import styles from "@/app/tabStyles/indexStyles";
@@ -10,6 +10,7 @@ import Feather from "@expo/vector-icons/Feather";
import React from "react";
import { Keyboard, ScrollView, Text, TextInput, TouchableOpacity, View } from "react-native";
import { useSearch } from "@/hooks/useSearch";
import { getShowById } from "@/apis/showApi";
import PersonRow from "@/components/discovery/PersonRow";
import SeasonCarousel from "@/components/discovery/SeasonCarousel";
@@ -20,25 +21,19 @@ export default function ExploreScreen() {
const { query, setQuery, suggestions } = useDiscoveryContext();
const [tags, setTags] = React.useState<AutoCompleteItem[]>([]);
const [results, setResults] = React.useState<SearchResultItem[]>([]);
const tagStrings = React.useMemo(() => tags.map((t) => t.text), [tags]);
const { data: results = [] } = useSearch(tagStrings);
// Show metadata cache by id (filled from SHOW results and lazy-loaded by id)
const [showsById, setShowsById] = React.useState<Record<number, Show>>({});
// --- helpers ---
const tagStrings = React.useMemo(() => tags.map((t) => t.text), [tags]);
function tagAdded(tag: AutoCompleteItem) {
const nextTags = tags.some((t) => t.text === tag.text) ? tags : [...tags, tag];
setTags(nextTags);
const inputs = nextTags.map((t) => t.text);
getSearchResults(inputs, 50)
.then((items) => {
setResults(items || []);
})
.catch(console.error);
setQuery("");
Keyboard.dismiss();
}
@@ -46,12 +41,6 @@ export default function ExploreScreen() {
function tagRemoved(tag: AutoCompleteItem) {
const nextTags = tags.filter((t) => t.text !== tag.text);
setTags(nextTags);
const inputs = nextTags.map((t) => t.text);
getSearchResults(inputs, 50)
.then((items) => {
setResults(items || []);
})
.catch(console.error);
}
// Keep our local show cache in sync with SHOW items returned by search
@@ -135,7 +124,7 @@ export default function ExploreScreen() {
<Text style={[styles.title, { fontSize: 28 }]}>Durchsuchen</Text>
</View>
<View style={{ paddingHorizontal: 10 }}>
<View style={styles.sectionContainer}>
{/* Search bar */}
<View style={styles.searchContainer}>
<TextInput
@@ -143,13 +132,7 @@ export default function ExploreScreen() {
onChangeText={setQuery}
placeholder="Wonach suchst du?"
placeholderTextColor=""
style={{
fontSize: 18,
fontWeight: "500",
color: "hsl(221, 39%, 80%)",
width: "90%",
height: "100%",
}}
style={styles.searchInput}
returnKeyType="search"
onSubmitEditing={() => {
if (!query.trim()) return;
@@ -205,8 +188,8 @@ export default function ExploreScreen() {
<ScrollView keyboardShouldPersistTaps="handled">
{/* Personen Section (top) */}
{persons.length > 0 && (
<View style={{ width: "100%", paddingHorizontal: 10, marginBottom: 12 }}>
<Text style={{ color: "white", fontSize: 18, fontWeight: "600", marginBottom: 6 }}>Personen</Text>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Personen</Text>
{persons.slice(0, 5).map((p) => (
<PersonRow key={`p-${p.personId ?? p.id}`} person={p}/>
))}
@@ -214,8 +197,8 @@ export default function ExploreScreen() {
)}
{/* Staffeln grouped by Show with page view */}
<View style={{ width: "100%", paddingHorizontal: 10 }}>
<Text style={{ color: "white", fontSize: 18, fontWeight: "600", marginBottom: 6 }}>Staffeln</Text>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Staffeln</Text>
{Array.from(seasonsByShowId.entries()).map(([showId, seasons]) => {
const show = showsById[Number(showId)];
@@ -242,7 +225,7 @@ export default function ExploreScreen() {
})}
{seasonsByShowId.size === 0 && (
<Text style={{ color: "white", fontSize: 16, textAlign: "center", marginTop: 14 }}>
<Text style={styles.centerText}>
Keine Staffeln gefunden. Passe deine Tags an.
</Text>
)}

View File

@@ -1,8 +1,8 @@
import styles from "@/app/tabStyles/indexStyles";
import ShowCard from "@/components/ui/ShowCard";
import { useShowContext } from "@/contexts/ShowContext";
import { useStreamingServiceContext } from "@/contexts/StreamingServiceContext";
import * as Haptics from 'expo-haptics';
import { useShows } from "@/hooks/useShows";
import { useStreamingServices } from "@/hooks/useStreamingServices";
import * as Haptics from "expo-haptics";
import { router } from "expo-router";
import React from "react";
import {
@@ -18,54 +18,43 @@ import {
} from "react-native-gesture-handler";
export default function HomeScreen() {
const { shows, error, loading } = useShowContext();
const { streamingServices } = useStreamingServiceContext();
const [filteredShows, setFilteredShows] = React.useState(shows);
const { data: shows = [], error, isLoading: loading } = useShows();
const { data: streamingServices = {} } = useStreamingServices();
const [activeFilter, setActiveFilter] = React.useState<string>("all");
const haptikFeedback = () => {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
React.useEffect(() => {
setFilteredShows(shows);
}, [shows]);
};
const handleFilter = (type: string) => {
haptikFeedback();
setActiveFilter(type);
if (type === "all") {
setFilteredShows(shows);
return;
}
if (type === "live") {
const filtered = shows.filter((show) => show.running);
setFilteredShows(filtered);
return;
}
if (type === activeFilter) {
setFilteredShows(shows);
setActiveFilter('all');
return;
setActiveFilter("all");
} else {
setActiveFilter(type);
}
const filtered = shows.filter((show) =>
show.streamingService.split(',').map(s => s.trim()).includes(type)
);
setFilteredShows(filtered);
};
const filteredShows = React.useMemo(() => {
if (activeFilter === "all") {
return shows;
}
if (activeFilter === "live") {
return shows.filter((show) => show.running);
}
return shows.filter((show) =>
show.streamingService
.split(",")
.map((s) => s.trim())
.includes(activeFilter)
);
}, [shows, activeFilter]);
const uniqueStreamingServices = React.useMemo(() => {
const uniqueServices = new Set<string>();
shows.forEach((show) => {
const services = show.streamingService.split(', ').map(s => s.trim());
services.forEach(service => uniqueServices.add(service));
const services = show.streamingService.split(", ").map((s) => s.trim());
services.forEach((service) => uniqueServices.add(service));
});
return Array.from(uniqueServices);
}, [shows]);
@@ -91,7 +80,7 @@ export default function HomeScreen() {
{ justifyContent: "center", alignItems: "center" },
]}
>
<Text>Error: {error}</Text>
<Text>Error: {error?.message || String(error)}</Text>
</View>
);
}
@@ -102,7 +91,10 @@ export default function HomeScreen() {
<View style={styles.header}>
<Text style={styles.title}>FLTR</Text>
</View>
<ScrollView contentContainerStyle={{ paddingBottom: 30 }}>
<ScrollView
contentContainerStyle={{ paddingBottom: 30 }}
showsHorizontalScrollIndicator={false}
>
<View style={styles.filterSection}>
<ScrollView
horizontal
@@ -127,7 +119,9 @@ export default function HomeScreen() {
}}
onPress={() => handleFilter("all")}
>
<Text style={{fontWeight: 'bold', color: 'white'}}>ALLE</Text>
<Text style={{ fontWeight: "bold", color: "white" }}>
ALLE
</Text>
</TouchableOpacity>
)}
{activeFilter !== "live" && (
@@ -143,25 +137,35 @@ export default function HomeScreen() {
}}
onPress={() => handleFilter("live")}
>
<View style={{backgroundColor: "red", paddingHorizontal: 5, paddingVertical: 2, borderRadius: 5}}>
<Text style={{fontWeight: 'bold', color: 'white'}}>LIVE</Text>
<View
style={{
backgroundColor: "red",
paddingHorizontal: 5,
paddingVertical: 2,
borderRadius: 5,
}}
>
<Text style={{ fontWeight: "bold", color: "white" }}>
LIVE
</Text>
</View>
</TouchableOpacity>
)}
<View style={{
height: 60,
width: 2,
backgroundColor: "hsla(0, 0%, 37%, 1.00)",
marginHorizontal: 5,
borderRadius: 5,
}} />
<View
style={{
height: 60,
width: 2,
backgroundColor: "hsla(0, 0%, 37%, 1.00)",
marginHorizontal: 5,
borderRadius: 5,
}}
/>
{uniqueStreamingServices.map((serviceName) => {
const streamingService =
streamingServices[
`assets.images.streamingServices.${serviceName.toLowerCase()}`
`assets.images.streamingServices.${serviceName.toLowerCase()}`
];
return (
<TouchableOpacity
@@ -214,13 +218,20 @@ export default function HomeScreen() {
})
}
imageUri={show.bannerUri}
streamingServicesUris={show.streamingService.split(', ').map(s => streamingServices[`assets.images.streamingServices.${s.toLowerCase()}`])}
streamingServicesUris={show.streamingService
.split(", ")
.map(
(s) =>
streamingServices[
`assets.images.streamingServices.${s.toLowerCase()}`
]
)}
genres={show.genres}
{...(showLiveBadge
? {
liveBadgeText: "LIVE",
liveBadgeContainerStyle: styles.liveBadgeContainer,
}
liveBadgeText: "LIVE",
liveBadgeContainerStyle: styles.liveBadgeContainer,
}
: {})}
/>
);