feat: Custom homescreen support (#424)

This commit is contained in:
lostb1t
2025-01-13 19:48:19 +01:00
committed by GitHub
parent 3a836462f5
commit 4022ccb213
5 changed files with 178 additions and 300 deletions

View File

@@ -8,7 +8,7 @@ import { Colors } from "@/constants/Colors";
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
import { useDownload } from "@/providers/DownloadProvider";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
import { HomeSectionStyle, useSettings } from "@/utils/atoms/settings";
import { Feather, Ionicons } from "@expo/vector-icons";
import { Api } from "@jellyfin/sdk";
import {
@@ -141,29 +141,6 @@ export default function index() {
[data, settings?.hiddenLibraries]
);
const {
data: mediaListCollections,
isError: e2,
isLoading: l2,
} = useQuery({
queryKey: ["home", "sf_promoted", user?.Id, settings?.usePopularPlugin],
queryFn: async () => {
if (!api || !user?.Id) return [];
const response = await getItemsApi(api).getItems({
userId: user.Id,
tags: ["sf_promoted"],
recursive: true,
fields: ["Tags"],
includeItemTypes: ["BoxSet"],
});
return response.data.Items || [];
},
enabled: !!api && !!user?.Id && settings?.usePopularPlugin === true,
staleTime: 60 * 1000,
});
const collections = useMemo(() => {
const allow = ["movies", "tvshows"];
return (
@@ -211,107 +188,139 @@ export default function index() {
[api, user?.Id]
);
const sections = useMemo(() => {
if (!api || !user?.Id) return [];
let sections: Section[] = [];
if (settings?.home === null || settings?.home?.sections === null) {
sections = useMemo(() => {
if (!api || !user?.Id) return [];
const latestMediaViews = collections.map((c) => {
const includeItemTypes: BaseItemKind[] =
c.CollectionType === "tvshows" ? ["Series"] : ["Movie"];
const title = "Recently Added in " + c.Name;
const queryKey = [
"home",
"recentlyAddedIn" + c.CollectionType,
user?.Id!,
c.Id!,
];
return createCollectionConfig(
title || "",
queryKey,
includeItemTypes,
c.Id
);
});
const latestMediaViews = collections.map((c) => {
const includeItemTypes: BaseItemKind[] =
c.CollectionType === "tvshows" ? ["Series"] : ["Movie"];
const title = "Recently Added in " + c.Name;
const queryKey = [
"home",
"recentlyAddedIn" + c.CollectionType,
user?.Id!,
c.Id!,
];
return createCollectionConfig(
title || "",
queryKey,
includeItemTypes,
c.Id
);
});
const ss: Section[] = [
{
title: "Continue Watching",
queryKey: ["home", "resumeItems"],
queryFn: async () =>
(
await getItemsApi(api).getResumeItems({
userId: user.Id,
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
includeItemTypes: ["Movie", "Series", "Episode"],
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: "horizontal",
},
{
title: "Next Up",
queryKey: ["home", "nextUp-all"],
queryFn: async () =>
(
await getTvShowsApi(api).getNextUp({
userId: user?.Id,
fields: ["MediaSourceCount"],
limit: 20,
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
enableResumable: false,
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: "horizontal",
},
...latestMediaViews,
...(mediaListCollections?.map(
(ml) =>
({
title: ml.Name,
queryKey: ["home", "mediaList", ml.Id!],
queryFn: async () => ml,
type: "MediaListSection",
orientation: "vertical",
} as Section)
) || []),
{
title: "Suggested Movies",
queryKey: ["home", "suggestedMovies", user?.Id],
queryFn: async () =>
(
await getSuggestionsApi(api).getSuggestions({
userId: user?.Id,
limit: 10,
mediaType: ["Video"],
type: ["Movie"],
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: "vertical",
},
{
title: "Suggested Episodes",
queryKey: ["home", "suggestedEpisodes", user?.Id],
queryFn: async () => {
try {
const suggestions = await getSuggestions(api, user.Id);
const nextUpPromises = suggestions.map((series) =>
getNextUp(api, user.Id, series.Id)
);
const nextUpResults = await Promise.all(nextUpPromises);
return nextUpResults.filter((item) => item !== null) || [];
} catch (error) {
console.error("Error fetching data:", error);
return [];
}
const ss: Section[] = [
{
title: "Continue Watching",
queryKey: ["home", "resumeItems"],
queryFn: async () =>
(
await getItemsApi(api).getResumeItems({
userId: user.Id,
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
includeItemTypes: ["Movie", "Series", "Episode"],
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: "horizontal",
},
type: "ScrollingCollectionList",
orientation: "horizontal",
},
];
return ss;
}, [api, user?.Id, collections, mediaListCollections]);
{
title: "Next Up",
queryKey: ["home", "nextUp-all"],
queryFn: async () =>
(
await getTvShowsApi(api).getNextUp({
userId: user?.Id,
fields: ["MediaSourceCount"],
limit: 20,
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
enableResumable: false,
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: "horizontal",
},
...latestMediaViews,
// ...(mediaListCollections?.map(
// (ml) =>
// ({
// title: ml.Name,
// queryKey: ["home", "mediaList", ml.Id!],
// queryFn: async () => ml,
// type: "MediaListSection",
// orientation: "vertical",
// } as Section)
// ) || []),
{
title: "Suggested Movies",
queryKey: ["home", "suggestedMovies", user?.Id],
queryFn: async () =>
(
await getSuggestionsApi(api).getSuggestions({
userId: user?.Id,
limit: 10,
mediaType: ["Video"],
type: ["Movie"],
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: "vertical",
},
{
title: "Suggested Episodes",
queryKey: ["home", "suggestedEpisodes", user?.Id],
queryFn: async () => {
try {
const suggestions = await getSuggestions(api, user.Id);
const nextUpPromises = suggestions.map((series) =>
getNextUp(api, user.Id, series.Id)
);
const nextUpResults = await Promise.all(nextUpPromises);
return nextUpResults.filter((item) => item !== null) || [];
} catch (error) {
console.error("Error fetching data:", error);
return [];
}
},
type: "ScrollingCollectionList",
orientation: "horizontal",
},
];
return ss;
}, [api, user?.Id, collections]);
} else {
sections = useMemo(() => {
if (!api || !user?.Id) return [];
const ss: Section[] = [];
for (const key in settings.home?.sections) {
const section = settings.home?.sections[key];
ss.push({
title: key,
queryKey: ["home", key, user?.Id],
queryFn: async () =>
(
await getItemsApi(api).getItems({
userId: user?.Id,
limit: section.items?.limit || 20,
recursive: true,
includeItemTypes: section.items?.includeItemTypes,
sortBy: section.items?.sortBy,
sortOrder: section.items?.sortOrder,
filters: section.items?.filters,
parentId: section.items?.parentId,
})
).data.Items || [],
type: "ScrollingCollectionList",
orientation: section?.orientation || "vertical",
});
}
return ss;
}, [api, user?.Id, settings.home?.sections]);
}
if (isConnected === false) {
return (
@@ -355,7 +364,7 @@ export default function index() {
);
}
if (e1 || e2)
if (e1)
return (
<View className="flex flex-col items-center justify-center h-full -mt-6">
<Text className="text-3xl font-bold mb-2">Oops!</Text>
@@ -365,7 +374,7 @@ export default function index() {
</View>
);
if (l1 || l2)
if (l1)
return (
<View className="justify-center items-center h-full">
<Loader />