import type { BaseItemDto, MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client/models"; import { Image } from "expo-image"; import { useNavigation } from "expo-router"; import { useAtom } from "jotai"; import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Platform, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { type Bitrate } from "@/components/BitrateSelector"; import { ItemImage } from "@/components/common/ItemImage"; import { DownloadSingleItem } from "@/components/DownloadItem"; import { OverviewText } from "@/components/OverviewText"; import { ParallaxScrollView } from "@/components/ParallaxPage"; // const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null; import { PlayButton } from "@/components/PlayButton"; import { PlayedStatus } from "@/components/PlayedStatus"; import { SimilarItems } from "@/components/SimilarItems"; import { CastAndCrew } from "@/components/series/CastAndCrew"; import { CurrentSeries } from "@/components/series/CurrentSeries"; import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel"; import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings"; import { useImageColorsReturn } from "@/hooks/useImageColorsReturn"; import { useOrientation } from "@/hooks/useOrientation"; import * as ScreenOrientation from "@/packages/expo-screen-orientation"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById"; import { AddToFavorites } from "./AddToFavorites"; import { BitrateSheet } from "./BitRateSheet"; import { ItemHeader } from "./ItemHeader"; import { ItemTechnicalDetails } from "./ItemTechnicalDetails"; import { MediaSourceSheet } from "./MediaSourceSheet"; import { MoreMoviesWithActor } from "./MoreMoviesWithActor"; import { PlayInRemoteSessionButton } from "./PlayInRemoteSession"; import { TrackSheet } from "./TrackSheet"; const Chromecast = !Platform.isTV ? require("./Chromecast") : null; export type SelectedOptions = { bitrate: Bitrate; mediaSource: MediaSourceInfo | undefined; audioIndex: number | undefined; subtitleIndex: number; }; interface ItemContentProps { item: BaseItemDto; isOffline: boolean; } export const ItemContent: React.FC = React.memo( ({ item, isOffline }) => { const [api] = useAtom(apiAtom); const { settings } = useSettings(); const { orientation } = useOrientation(); const navigation = useNavigation(); const insets = useSafeAreaInsets(); const [user] = useAtom(userAtom); const { t } = useTranslation(); const itemColors = useImageColorsReturn({ item }); const [loadingLogo, setLoadingLogo] = useState(true); const [headerHeight, setHeaderHeight] = useState(350); const [selectedOptions, setSelectedOptions] = useState< SelectedOptions | undefined >(undefined); const { defaultAudioIndex, defaultBitrate, defaultMediaSource, defaultSubtitleIndex, } = useDefaultPlaySettings(item!, settings); const logoUrl = useMemo( () => (item ? getLogoImageUrlById({ api, item }) : null), [api, item], ); const loading = useMemo(() => { return Boolean(logoUrl && loadingLogo); }, [loadingLogo, logoUrl]); // Needs to automatically change the selected to the default values for default indexes. useEffect(() => { setSelectedOptions(() => ({ bitrate: defaultBitrate, mediaSource: defaultMediaSource, subtitleIndex: defaultSubtitleIndex ?? -1, audioIndex: defaultAudioIndex, })); }, [ defaultAudioIndex, defaultBitrate, defaultSubtitleIndex, defaultMediaSource, ]); useEffect(() => { if (!Platform.isTV) { navigation.setOptions({ headerRight: () => item && (Platform.OS === "ios" ? ( {item.Type !== "Program" && ( {!Platform.isTV && ( )} {user?.Policy?.IsAdministrator && ( )} )} ) : ( {item.Type !== "Program" && ( {!Platform.isTV && ( )} {user?.Policy?.IsAdministrator && ( )} )} )), }); } }, [item, navigation, user]); useEffect(() => { if (item) { if (orientation !== ScreenOrientation.OrientationLock.PORTRAIT_UP) setHeaderHeight(230); else if (item.Type === "Movie") setHeaderHeight(500); else setHeaderHeight(350); } }, [item, orientation]); if (!item || !selectedOptions) return null; return ( } logo={ logoUrl ? ( setLoadingLogo(false)} onError={() => setLoadingLogo(false)} /> ) : ( ) } > {item.Type !== "Program" && !Platform.isTV && !isOffline && ( setSelectedOptions( (prev) => prev && { ...prev, bitrate: val }, ) } selected={selectedOptions.bitrate} /> setSelectedOptions( (prev) => prev && { ...prev, mediaSource: val, }, ) } selected={selectedOptions.mediaSource} /> { setSelectedOptions( (prev) => prev && { ...prev, audioIndex: val, }, ); }} selected={selectedOptions.audioIndex} /> setSelectedOptions( (prev) => prev && { ...prev, subtitleIndex: val, }, ) } selected={selectedOptions.subtitleIndex} /> )} {item.Type === "Episode" && ( )} {!isOffline && ( )} {item.Type !== "Program" && ( <> {item.Type === "Episode" && !isOffline && ( )} {!isOffline && ( )} {item.People && item.People.length > 0 && !isOffline && ( {item.People.slice(0, 3).map((person, idx) => ( ))} )} {!isOffline && } )} ); }, );