import { AutoCompleteItem } from "@/apis/autoCompleteApi"; import { getSearchResults, SearchResultItem } from "@/apis/searchApi"; import styles from "@/app/tabStyles/indexStyles"; import { Season, Show } from "@/app/types"; import ShowBox from "@/components/discovery/ShowBox"; import { useDiscoveryContext } from "@/contexts/DiscoveryContext"; import { FontAwesome } from "@expo/vector-icons"; import Feather from "@expo/vector-icons/Feather"; import React from "react"; import { Keyboard, ScrollView, Text, TextInput, TouchableOpacity, View } from "react-native"; import { getShowById } from "@/apis/showApi"; import PersonRow from "@/components/discovery/PersonRow"; import SeasonCarousel from "@/components/discovery/SeasonCarousel"; import TagChip from "@/components/discovery/TagChip"; import { getIconName, mapApiPersonToUI, mapApiSeasonToUI, mapApiShowToUI } from "@/utils/searchMapping"; export default function ExploreScreen() { const { query, setQuery, suggestions } = useDiscoveryContext(); const [tags, setTags] = React.useState([]); const [results, setResults] = React.useState([]); // Show metadata cache by id (filled from SHOW results and lazy-loaded by id) const [showsById, setShowsById] = React.useState>({}); // --- 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(); } // Keep our local show cache in sync with SHOW items returned by search React.useEffect(() => { const fromResults: Record = {}; for (const r of results) { if (r.type === "SHOW") { const uiShow = mapApiShowToUI(r.data); if (uiShow.showId != null) fromResults[Number(uiShow.showId)] = uiShow as Show; } } if (Object.keys(fromResults).length) { setShowsById((prev) => ({ ...prev, ...fromResults })); } }, [results]); // Group SEASON results by showId const seasonsByShowId = React.useMemo(() => { const map = new Map(); for (const r of results) { if (r.type !== "SEASON") continue; const s = mapApiSeasonToUI(r.data); if (!s || s.showId == null) continue; const key = Number(s.showId); const list = map.get(key) ?? []; list.push(s as Season); map.set(key, list); } // sort seasons per show by startDate asc for (const [k, list] of map) { list.sort((a, b) => { const da = a?.startDate ? new Date(a.startDate).getTime() : Number.POSITIVE_INFINITY; const db = b?.startDate ? new Date(b.startDate).getTime() : Number.POSITIVE_INFINITY; return da - db; }); map.set(k, list); } return map; }, [results]); // Lazy fetch missing shows needed for Season carousels React.useEffect(() => { const needed = Array.from(seasonsByShowId.keys()).filter((id) => !showsById[id]); if (needed.length === 0) return; let cancelled = false; (async () => { try { const fetched = await Promise.all(needed.map((id) => getShowById(id))); if (cancelled) return; const next: Record = {}; for (const s of fetched) { if (!s) continue; next[Number((s as any).showId)] = s as Show; } if (Object.keys(next).length) setShowsById((prev) => ({ ...prev, ...next })); } catch (e) { console.error(e); } })(); return () => { cancelled = true; }; }, [seasonsByShowId, showsById]); // PERSON hits shown at the top (like old screen) const persons = React.useMemo(() => { return results .filter((r) => r.type === "PERSON") .map((r) => mapApiPersonToUI(r.data)) .sort((a, b) => (a.name || "").localeCompare(b.name || "")); }, [results]); return ( Durchsuchen {/* Search bar */} { if (!query.trim()) return; tagAdded({ type: "CUSTOM", text: query.trim() }); }} autoCapitalize="none" /> {query.length === 0 ? ( ) : ( setQuery("")} /> )} {/* Tag chips */} {tags.map((tag) => ( setTags((prev) => prev.filter((t) => t.text !== tag.text))} /> ))} {/* Suggestions dropdown */} {query.length > 0 && ( Suchvorschläge {suggestions.map((suggestion, idx) => ( tagAdded(suggestion)} > {suggestion.text} ))} )} {/* Results */} {/* Personen Section (top) */} {persons.length > 0 && ( Personen {persons.slice(0, 5).map((p) => ( ))} )} {/* Staffeln grouped by Show with page view */} Staffeln {Array.from(seasonsByShowId.entries()).map(([showId, seasons]) => { const show = showsById[Number(showId)]; if (!seasons || seasons.length === 0) return null; // If show metadata is not yet loaded, render a minimal ShowBox fallback once per page item if (!show) { return ( } /> ); } return ( } /> ); })} {seasonsByShowId.size === 0 && ( Keine Staffeln gefunden. Passe deine Tags an. )} ); }