import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { useAtom } from "jotai"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Input } from "@/components/common/Input"; import { Text } from "@/components/common/Text"; import { TVDiscover } from "@/components/jellyseerr/discover/TVDiscover"; import { useScaledTVTypography } from "@/constants/TVTypography"; import { apiAtom } from "@/providers/JellyfinProvider"; import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl"; import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; import type { MovieResult, PersonResult, TvResult, } from "@/utils/jellyseerr/server/models/Search"; import { TVJellyseerrSearchResults } from "./TVJellyseerrSearchResults"; import { TVSearchSection } from "./TVSearchSection"; import { TVSearchTabBadges } from "./TVSearchTabBadges"; const HORIZONTAL_PADDING = 60; const TOP_PADDING = 100; const SECTION_GAP = 10; const SCALE_PADDING = 20; // Loading skeleton for TV const TVLoadingSkeleton: React.FC = () => { const typography = useScaledTVTypography(); const itemWidth = 210; return ( {[1, 2, 3, 4, 5].map((i) => ( Placeholder text here ))} ); }; type SearchType = "Library" | "Discover"; interface TVSearchPageProps { search: string; setSearch: (text: string) => void; debouncedSearch: string; // Library search results movies?: BaseItemDto[]; series?: BaseItemDto[]; episodes?: BaseItemDto[]; collections?: BaseItemDto[]; actors?: BaseItemDto[]; artists?: BaseItemDto[]; albums?: BaseItemDto[]; songs?: BaseItemDto[]; playlists?: BaseItemDto[]; loading: boolean; noResults: boolean; onItemPress: (item: BaseItemDto) => void; onItemLongPress?: (item: BaseItemDto) => void; // Jellyseerr/Discover props searchType: SearchType; setSearchType: (type: SearchType) => void; showDiscover: boolean; jellyseerrMovies?: MovieResult[]; jellyseerrTv?: TvResult[]; jellyseerrPersons?: PersonResult[]; jellyseerrLoading?: boolean; jellyseerrNoResults?: boolean; onJellyseerrMoviePress?: (item: MovieResult) => void; onJellyseerrTvPress?: (item: TvResult) => void; onJellyseerrPersonPress?: (item: PersonResult) => void; // Discover sliders for empty state discoverSliders?: DiscoverSlider[]; } export const TVSearchPage: React.FC = ({ search, setSearch, debouncedSearch, movies, series, episodes, collections, actors, artists, albums, songs, playlists, loading, noResults, onItemPress, onItemLongPress, searchType, setSearchType, showDiscover, jellyseerrMovies = [], jellyseerrTv = [], jellyseerrPersons = [], jellyseerrLoading = false, jellyseerrNoResults = false, onJellyseerrMoviePress, onJellyseerrTvPress, onJellyseerrPersonPress, discoverSliders, }) => { const typography = useScaledTVTypography(); const { t } = useTranslation(); const insets = useSafeAreaInsets(); const [api] = useAtom(apiAtom); // Image URL getter for music items const getImageUrl = useMemo(() => { return (item: BaseItemDto): string | undefined => { if (!api) return undefined; const url = getPrimaryImageUrl({ api, item }); return url ?? undefined; }; }, [api]); // Determine which section should have initial focus const sections = useMemo(() => { const allSections: { key: string; title: string; items: BaseItemDto[] | undefined; orientation?: "horizontal" | "vertical"; }[] = [ { key: "movies", title: t("search.movies"), items: movies }, { key: "series", title: t("search.series"), items: series }, { key: "episodes", title: t("search.episodes"), items: episodes, orientation: "horizontal" as const, }, { key: "collections", title: t("search.collections"), items: collections, }, { key: "actors", title: t("search.actors"), items: actors }, { key: "artists", title: t("search.artists"), items: artists }, { key: "albums", title: t("search.albums"), items: albums }, { key: "songs", title: t("search.songs"), items: songs }, { key: "playlists", title: t("search.playlists"), items: playlists }, ]; return allSections.filter((s) => s.items && s.items.length > 0); }, [ movies, series, episodes, collections, actors, artists, albums, songs, playlists, t, ]); const isLibraryMode = searchType === "Library"; const isDiscoverMode = searchType === "Discover"; const currentLoading = isLibraryMode ? loading : jellyseerrLoading; const currentNoResults = isLibraryMode ? noResults : jellyseerrNoResults; return ( {/* Search Input */} {/* Search Type Tab Badges */} {showDiscover && ( )} {/* Loading State */} {currentLoading && ( )} {/* Library Search Results */} {isLibraryMode && !loading && ( {sections.map((section, index) => ( ))} )} {/* Jellyseerr/Discover Search Results */} {isDiscoverMode && !jellyseerrLoading && debouncedSearch.length > 0 && ( {})} onTvPress={onJellyseerrTvPress || (() => {})} onPersonPress={onJellyseerrPersonPress || (() => {})} /> )} {/* Discover Content (when no search query in Discover mode) */} {isDiscoverMode && !jellyseerrLoading && debouncedSearch.length === 0 && ( )} {/* No Results State */} {!currentLoading && currentNoResults && debouncedSearch.length > 0 && ( {t("search.no_results_found_for")} "{debouncedSearch}" )} ); };