mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
135 lines
4.2 KiB
TypeScript
135 lines
4.2 KiB
TypeScript
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { useRouter } from "expo-router";
|
|
import { atom, useAtom } from "jotai";
|
|
import { useMemo } from "react";
|
|
import { TouchableOpacity, View } from "react-native";
|
|
import * as DropdownMenu from "zeego/dropdown-menu";
|
|
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
|
import { ItemCardText } from "../ItemCardText";
|
|
import { HorizontalScroll } from "../common/HorrizontalScroll";
|
|
import { Text } from "../common/Text";
|
|
|
|
type Props = {
|
|
item: BaseItemDto;
|
|
};
|
|
|
|
export const seasonIndexAtom = atom<number>(1);
|
|
|
|
export const SeasonPicker: React.FC<Props> = ({ item }) => {
|
|
const [api] = useAtom(apiAtom);
|
|
const [user] = useAtom(userAtom);
|
|
const [seasonIndex, setSeasonIndex] = useAtom(seasonIndexAtom);
|
|
|
|
const router = useRouter();
|
|
|
|
const { data: seasons } = useQuery({
|
|
queryKey: ["seasons", item.Id],
|
|
queryFn: async () => {
|
|
if (!api || !user?.Id || !item.Id) return [];
|
|
const response = await api.axiosInstance.get(
|
|
`${api.basePath}/Shows/${item.Id}/Seasons`,
|
|
{
|
|
params: {
|
|
userId: user?.Id,
|
|
itemId: item.Id,
|
|
Fields:
|
|
"ItemCounts,PrimaryImageAspectRatio,CanDelete,MediaSourceCount",
|
|
},
|
|
headers: {
|
|
Authorization: `MediaBrowser DeviceId="${api.deviceInfo.id}", Token="${api.accessToken}"`,
|
|
},
|
|
},
|
|
);
|
|
|
|
return response.data.Items;
|
|
},
|
|
enabled: !!api && !!user?.Id && !!item.Id,
|
|
});
|
|
|
|
const selectedSeasonId: string | null = useMemo(
|
|
() =>
|
|
seasons?.find((season: any) => season.IndexNumber === seasonIndex)?.Id,
|
|
[seasons, seasonIndex],
|
|
);
|
|
|
|
const { data: episodes } = useQuery({
|
|
queryKey: ["episodes", item.Id, selectedSeasonId],
|
|
queryFn: async () => {
|
|
if (!api || !user?.Id || !item.Id) return [];
|
|
const response = await api.axiosInstance.get(
|
|
`${api.basePath}/Shows/${item.Id}/Episodes`,
|
|
{
|
|
params: {
|
|
userId: user?.Id,
|
|
seasonId: selectedSeasonId,
|
|
Fields:
|
|
"ItemCounts,PrimaryImageAspectRatio,CanDelete,MediaSourceCount,Overview",
|
|
},
|
|
headers: {
|
|
Authorization: `MediaBrowser DeviceId="${api.deviceInfo.id}", Token="${api.accessToken}"`,
|
|
},
|
|
},
|
|
);
|
|
|
|
return response.data.Items as BaseItemDto[];
|
|
},
|
|
enabled: !!api && !!user?.Id && !!item.Id && !!selectedSeasonId,
|
|
});
|
|
|
|
return (
|
|
<View className="mb-2">
|
|
<DropdownMenu.Root>
|
|
<DropdownMenu.Trigger>
|
|
<View className="flex flex-row px-4">
|
|
<TouchableOpacity className="bg-neutral-900 rounded-2xl border-neutral-900 border px-3 py-2 flex flex-row items-center justify-between">
|
|
<Text>Season {seasonIndex}</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</DropdownMenu.Trigger>
|
|
<DropdownMenu.Content
|
|
loop={true}
|
|
side="bottom"
|
|
align="start"
|
|
alignOffset={0}
|
|
avoidCollisions={true}
|
|
collisionPadding={8}
|
|
sideOffset={8}
|
|
>
|
|
<DropdownMenu.Label>Seasons</DropdownMenu.Label>
|
|
{seasons?.map((season: any) => (
|
|
<DropdownMenu.Item
|
|
key={season.Name}
|
|
onSelect={() => {
|
|
setSeasonIndex(season.IndexNumber);
|
|
}}
|
|
>
|
|
<DropdownMenu.ItemTitle>{season.Name}</DropdownMenu.ItemTitle>
|
|
</DropdownMenu.Item>
|
|
))}
|
|
</DropdownMenu.Content>
|
|
</DropdownMenu.Root>
|
|
{episodes && (
|
|
<View className="mt-4">
|
|
<HorizontalScroll<BaseItemDto>
|
|
data={episodes}
|
|
renderItem={(item, index) => (
|
|
<TouchableOpacity
|
|
key={item.Id}
|
|
onPress={() => {
|
|
router.push(`/(auth)/items/${item.Id}`);
|
|
}}
|
|
className="flex flex-col w-48"
|
|
>
|
|
<ContinueWatchingPoster item={item} />
|
|
<ItemCardText item={item} />
|
|
</TouchableOpacity>
|
|
)}
|
|
/>
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|