import type { Api } from "@jellyfin/sdk"; import type { BaseItemKind } from "@jellyfin/sdk/lib/generated-client"; import { getItemsApi } from "@jellyfin/sdk/lib/utils/api"; import { Image } from "expo-image"; import { useAtom } from "jotai"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import heart from "@/assets/icons/heart.fill.png"; import { Text } from "@/components/common/Text"; import { InfiniteScrollingCollectionList } from "@/components/home/InfiniteScrollingCollectionList.tv"; import { Colors } from "@/constants/Colors"; import { useScaledTVTypography } from "@/constants/TVTypography"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; const HORIZONTAL_PADDING = 60; const TOP_PADDING = 100; const SECTION_GAP = 10; type FavoriteTypes = | "Series" | "Movie" | "Episode" | "Video" | "BoxSet" | "Playlist"; type EmptyState = Record; export const Favorites = () => { const typography = useScaledTVTypography(); const { t } = useTranslation(); const insets = useSafeAreaInsets(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const pageSize = 20; const [emptyState, setEmptyState] = useState({ Series: false, Movie: false, Episode: false, Video: false, BoxSet: false, Playlist: false, }); const fetchFavoritesByType = useCallback( async ( itemType: BaseItemKind, startIndex: number = 0, limit: number = 20, ) => { const response = await getItemsApi(api as Api).getItems({ userId: user?.Id, sortBy: ["SeriesSortName", "SortName"], sortOrder: ["Ascending"], filters: ["IsFavorite"], recursive: true, fields: ["PrimaryImageAspectRatio"], collapseBoxSetItems: false, excludeLocationTypes: ["Virtual"], enableTotalRecordCount: false, startIndex: startIndex, limit: limit, includeItemTypes: [itemType], }); const items = response.data.Items || []; if (startIndex === 0) { setEmptyState((prev) => ({ ...prev, [itemType as FavoriteTypes]: items.length === 0, })); } return items; }, [api, user], ); useEffect(() => { setEmptyState({ Series: false, Movie: false, Episode: false, Video: false, BoxSet: false, Playlist: false, }); }, [api, user]); const areAllEmpty = () => { const loadedCategories = Object.values(emptyState); return ( loadedCategories.length > 0 && loadedCategories.every((isEmpty) => isEmpty) ); }; const fetchFavoriteSeries = useCallback( ({ pageParam }: { pageParam: number }) => fetchFavoritesByType("Series", pageParam, pageSize), [fetchFavoritesByType, pageSize], ); const fetchFavoriteMovies = useCallback( ({ pageParam }: { pageParam: number }) => fetchFavoritesByType("Movie", pageParam, pageSize), [fetchFavoritesByType, pageSize], ); const fetchFavoriteEpisodes = useCallback( ({ pageParam }: { pageParam: number }) => fetchFavoritesByType("Episode", pageParam, pageSize), [fetchFavoritesByType, pageSize], ); const fetchFavoriteVideos = useCallback( ({ pageParam }: { pageParam: number }) => fetchFavoritesByType("Video", pageParam, pageSize), [fetchFavoritesByType, pageSize], ); const fetchFavoriteBoxsets = useCallback( ({ pageParam }: { pageParam: number }) => fetchFavoritesByType("BoxSet", pageParam, pageSize), [fetchFavoritesByType, pageSize], ); const fetchFavoritePlaylists = useCallback( ({ pageParam }: { pageParam: number }) => fetchFavoritesByType("Playlist", pageParam, pageSize), [fetchFavoritesByType, pageSize], ); if (areAllEmpty()) { return ( {t("favorites.noDataTitle")} {t("favorites.noData")} ); } return ( ); };