diff --git a/app/(auth)/(tabs)/(home)/index.tsx b/app/(auth)/(tabs)/(home)/index.tsx index b894a9d8..75ae50b8 100644 --- a/app/(auth)/(tabs)/(home)/index.tsx +++ b/app/(auth)/(tabs)/(home)/index.tsx @@ -22,6 +22,24 @@ import { useAtom } from "jotai"; import { useCallback, useEffect, useMemo, useState } from "react"; import { RefreshControl, ScrollView, View } from "react-native"; +type BaseSection = { + title: string; + queryKey: (string | undefined)[]; +}; + +type ScrollingCollectionListSection = BaseSection & { + type: "ScrollingCollectionList"; + queryFn: () => Promise; + orientation?: "horizontal" | "vertical"; +}; + +type MediaListSection = BaseSection & { + type: "MediaListSection"; + queryFn: () => Promise; +}; + +type Section = ScrollingCollectionListSection | MediaListSection; + export default function index() { const router = useRouter(); const queryClient = useQueryClient(); @@ -50,42 +68,12 @@ export default function index() { }; }, []); - const { data, isLoading, isError } = useQuery({ - queryKey: ["resumeItems", user?.Id], - queryFn: async () => - (api && - ( - await getItemsApi(api).getResumeItems({ - userId: user?.Id, - }) - ).data.Items) || - [], - enabled: !!api && !!user?.Id, - staleTime: 60 * 1000, - }); - - const { data: _nextUpData, isLoading: isLoadingNextUp } = useQuery({ - queryKey: ["nextUp-all", user?.Id], - queryFn: async () => - (api && - ( - await getTvShowsApi(api).getNextUp({ - userId: user?.Id, - fields: ["MediaSourceCount"], - limit: 20, - }) - ).data.Items) || - [], - enabled: !!api && !!user?.Id, - staleTime: 0, - }); - - const nextUpData = useMemo(() => { - return _nextUpData?.filter((i) => !data?.find((d) => d.Id === i.Id)); - }, [_nextUpData]); - - const { data: collections } = useQuery({ - queryKey: ["collectinos", user?.Id], + const { + data: userViews, + isError: e1, + isLoading: l1, + } = useQuery({ + queryKey: ["userViews", user?.Id], queryFn: async () => { if (!api || !user?.Id) { return null; @@ -101,77 +89,11 @@ export default function index() { staleTime: 60 * 1000, }); - const movieCollectionId = useMemo(() => { - return collections?.find((c) => c.CollectionType === "movies")?.Id; - }, [collections]); - - const tvShowCollectionId = useMemo(() => { - return collections?.find((c) => c.CollectionType === "tvshows")?.Id; - }, [collections]); - const { - data: recentlyAddedInMovies, - isLoading: isLoadingRecentlyAddedMovies, - } = useQuery({ - queryKey: ["recentlyAddedInMovies", user?.Id, movieCollectionId], - queryFn: async () => - (api && - ( - await getUserLibraryApi(api).getLatestMedia({ - userId: user?.Id, - limit: 50, - fields: ["PrimaryImageAspectRatio", "Path"], - imageTypeLimit: 1, - enableImageTypes: ["Primary", "Backdrop", "Thumb"], - parentId: movieCollectionId, - }) - ).data) || - [], - enabled: !!api && !!user?.Id && !!movieCollectionId, - staleTime: 60 * 1000, - }); - - const { - data: recentlyAddedInTVShows, - isLoading: isLoadingRecentlyAddedTVShows, - } = useQuery({ - queryKey: ["recentlyAddedInTVShows", user?.Id, tvShowCollectionId], - queryFn: async () => - (api && - ( - await getUserLibraryApi(api).getLatestMedia({ - userId: user?.Id, - limit: 50, - fields: ["PrimaryImageAspectRatio", "Path"], - imageTypeLimit: 1, - enableImageTypes: ["Primary", "Backdrop", "Thumb"], - parentId: tvShowCollectionId, - }) - ).data) || - [], - enabled: !!api && !!user?.Id && !!tvShowCollectionId, - staleTime: 60 * 1000, - }); - - const { data: suggestions, isLoading: isLoadingSuggestions } = useQuery< - BaseItemDto[] - >({ - queryKey: ["suggestions", user?.Id], - queryFn: async () => - (api && - ( - await getSuggestionsApi(api).getSuggestions({ - userId: user?.Id, - limit: 5, - mediaType: ["Video"], - }) - ).data.Items) || - [], - enabled: !!api && !!user?.Id, - staleTime: 60 * 1000, - }); - - const { data: mediaListCollections } = useQuery({ + data: mediaListCollections, + isError: e2, + isLoading: l2, + } = useQuery({ queryKey: ["sf_promoted", user?.Id, settings?.usePopularPlugin], queryFn: async () => { if (!api || !user?.Id) return []; @@ -190,8 +112,17 @@ export default function index() { staleTime: 0, }); + const movieCollectionId = useMemo(() => { + return userViews?.find((c) => c.CollectionType === "movies")?.Id; + }, [userViews]); + + const tvShowCollectionId = useMemo(() => { + return userViews?.find((c) => c.CollectionType === "tvshows")?.Id; + }, [userViews]); + const refetch = useCallback(async () => { setLoading(true); + await queryClient.refetchQueries({ queryKey: ["userViews"] }); await queryClient.refetchQueries({ queryKey: ["resumeItems"] }); await queryClient.refetchQueries({ queryKey: ["nextUp-all"] }); await queryClient.refetchQueries({ queryKey: ["recentlyAddedInMovies"] }); @@ -206,30 +137,125 @@ export default function index() { setLoading(false); }, [queryClient, user?.Id]); - if (isConnected === false) { - return ( - - No Internet - - No worries, you can still watch{"\n"}downloaded content. - - - - - - ); - } + const sections = useMemo(() => { + if (!api || !user?.Id) return []; - if (isError) + const ss: Section[] = [ + { + title: "Continue Watching", + queryKey: ["resumeItems", user.Id], + queryFn: async () => + ( + await getItemsApi(api).getResumeItems({ + userId: user.Id, + }) + ).data.Items || [], + type: "ScrollingCollectionList", + orientation: "horizontal", + }, + { + title: "Next Up", + queryKey: ["nextUp-all", user?.Id], + queryFn: async () => + ( + await getTvShowsApi(api).getNextUp({ + userId: user?.Id, + fields: ["MediaSourceCount"], + limit: 20, + }) + ).data.Items || [], + type: "ScrollingCollectionList", + orientation: "horizontal", + }, + ...(mediaListCollections?.map( + (ml) => + ({ + title: ml.Name || "", + queryKey: ["mediaList", ml.Id], + queryFn: async () => ml, + type: "MediaListSection", + } as MediaListSection) + ) || []), + { + title: "Recently Added in Movies", + queryKey: ["recentlyAddedInMovies", user?.Id, movieCollectionId], + queryFn: async () => + ( + await getUserLibraryApi(api).getLatestMedia({ + userId: user?.Id, + limit: 50, + fields: ["PrimaryImageAspectRatio", "Path"], + imageTypeLimit: 1, + enableImageTypes: ["Primary", "Backdrop", "Thumb"], + parentId: movieCollectionId, + }) + ).data || [], + type: "ScrollingCollectionList", + }, + { + title: "Recently Added in TV-Shows", + queryKey: ["recentlyAddedInTVShows", user?.Id, tvShowCollectionId], + queryFn: async () => + ( + await getUserLibraryApi(api).getLatestMedia({ + userId: user?.Id, + limit: 50, + fields: ["PrimaryImageAspectRatio", "Path"], + imageTypeLimit: 1, + enableImageTypes: ["Primary", "Backdrop", "Thumb"], + parentId: tvShowCollectionId, + }) + ).data || [], + type: "ScrollingCollectionList", + }, + { + title: "Suggestions", + queryKey: ["suggestions", user?.Id], + queryFn: async () => + ( + await getSuggestionsApi(api).getSuggestions({ + userId: user?.Id, + limit: 5, + mediaType: ["Video"], + }) + ).data.Items || [], + type: "ScrollingCollectionList", + orientation: "horizontal", + }, + ]; + return ss; + }, [ + api, + user?.Id, + movieCollectionId, + tvShowCollectionId, + mediaListCollections, + ]); + + // if (isConnected === false) { + // return ( + // + // No Internet + // + // No worries, you can still watch{"\n"}downloaded content. + // + // + // + // + // + // ); + // } + + if (e1 || e2) return ( Oops! @@ -239,13 +265,12 @@ export default function index() { ); - if (isLoading) + if (l1 || l2) return ( ); - return ( - - - - - {mediaListCollections?.map((ml) => ( - - ))} - - - - - - + {sections.map((section, index) => { + if (section.type === "ScrollingCollectionList") { + return ( + + ); + } else if (section.type === "MediaListSection") { + return ( + + ); + } + return null; + })} ); diff --git a/components/home/ScrollingCollectionList.tsx b/components/home/ScrollingCollectionList.tsx index 35b7dac1..df86bb88 100644 --- a/components/home/ScrollingCollectionList.tsx +++ b/components/home/ScrollingCollectionList.tsx @@ -6,26 +6,38 @@ import ContinueWatchingPoster from "../ContinueWatchingPoster"; import { ItemCardText } from "../ItemCardText"; import { HorizontalScroll } from "../common/HorrizontalScroll"; import { TouchableItemRouter } from "../common/TouchableItemRouter"; +import { + type QueryKey, + useQuery, + type QueryFunction, +} from "@tanstack/react-query"; interface Props extends ViewProps { - title: string; - loading?: boolean; + title?: string | null; orientation?: "horizontal" | "vertical"; - data?: BaseItemDto[] | null; height?: "small" | "large"; disabled?: boolean; + queryKey: QueryKey; + queryFn: QueryFunction; } export const ScrollingCollectionList: React.FC = ({ title, - data, orientation = "vertical", height = "small", - loading = false, disabled = false, + queryFn, + queryKey, ...props }) => { - if (disabled) return null; + const { data, isLoading } = useQuery({ + queryKey, + queryFn, + enabled: !disabled, + staleTime: 0, + }); + + if (disabled || !title) return null; return ( @@ -35,7 +47,7 @@ export const ScrollingCollectionList: React.FC = ({ data={data} height={orientation === "vertical" ? 247 : 164} - loading={loading} + loading={isLoading} renderItem={(item, index) => ( ; } -export const MediaListSection: React.FC = ({ collection, ...props }) => { +export const MediaListSection: React.FC = ({ + queryFn, + queryKey, + ...props +}) => { const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); + const { data: collection, isLoading } = useQuery({ + queryKey, + queryFn, + staleTime: 0, + }); + const fetchItems = useCallback( async ({ pageParam, }: { pageParam: number; }): Promise => { - if (!api || !user?.Id) return null; + if (!api || !user?.Id || !collection) return null; const response = await getItemsApi(api).getItems({ userId: user.Id, @@ -38,7 +54,7 @@ export const MediaListSection: React.FC = ({ collection, ...props }) => { return response.data; }, - [api, user?.Id, collection.Id] + [api, user?.Id, collection?.Id] ); if (!collection) return null;