mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
fix: type errors and design
This commit is contained in:
@@ -1,37 +1,48 @@
|
||||
import {Text} from "@/components/common/Text";
|
||||
import {useDownload} from "@/providers/DownloadProvider";
|
||||
import {router, useLocalSearchParams, useNavigation} from "expo-router";
|
||||
import React, {useCallback, useEffect, useMemo, useState} from "react";
|
||||
import {ScrollView, TouchableOpacity, View} from "react-native";
|
||||
import {EpisodeCard} from "@/components/downloads/EpisodeCard";
|
||||
import {BaseItemDto} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import {SeasonDropdown, SeasonIndexState} from "@/components/series/SeasonDropdown";
|
||||
import {storage} from "@/utils/mmkv";
|
||||
import {Ionicons} from "@expo/vector-icons";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { router, useLocalSearchParams, useNavigation } from "expo-router";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { ScrollView, TouchableOpacity, View } from "react-native";
|
||||
import { EpisodeCard } from "@/components/downloads/EpisodeCard";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import {
|
||||
SeasonDropdown,
|
||||
SeasonIndexState,
|
||||
} from "@/components/series/SeasonDropdown";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function page() {
|
||||
const navigation = useNavigation();
|
||||
const local = useLocalSearchParams();
|
||||
const {seriesId, episodeSeasonIndex} = local as {
|
||||
seriesId: string,
|
||||
episodeSeasonIndex: number | string | undefined
|
||||
const { seriesId, episodeSeasonIndex } = local as {
|
||||
seriesId: string;
|
||||
episodeSeasonIndex: number | string | undefined;
|
||||
};
|
||||
|
||||
const [seasonIndexState, setSeasonIndexState] = useState<SeasonIndexState>({});
|
||||
const {downloadedFiles, deleteItems} = useDownload();
|
||||
const [seasonIndexState, setSeasonIndexState] = useState<SeasonIndexState>(
|
||||
{}
|
||||
);
|
||||
const { downloadedFiles, deleteItems } = useDownload();
|
||||
|
||||
const series = useMemo(() => {
|
||||
try {
|
||||
return downloadedFiles
|
||||
return (
|
||||
downloadedFiles
|
||||
?.filter((f) => f.item.SeriesId == seriesId)
|
||||
?.sort((a, b) => a?.item.ParentIndexNumber! - b.item.ParentIndexNumber!)
|
||||
|| [];
|
||||
?.sort(
|
||||
(a, b) => a?.item.ParentIndexNumber! - b.item.ParentIndexNumber!
|
||||
) || []
|
||||
);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}, [downloadedFiles]);
|
||||
|
||||
const seasonIndex = seasonIndexState[series?.[0]?.item?.ParentId ?? ""] || episodeSeasonIndex || "";
|
||||
const seasonIndex =
|
||||
seasonIndexState[series?.[0]?.item?.ParentId ?? ""] ||
|
||||
episodeSeasonIndex ||
|
||||
"";
|
||||
|
||||
const groupBySeason = useMemo<BaseItemDto[]>(() => {
|
||||
const seasons: Record<string, BaseItemDto[]> = {};
|
||||
@@ -43,13 +54,16 @@ export default function page() {
|
||||
|
||||
seasons[episode.item.ParentIndexNumber!].push(episode.item);
|
||||
});
|
||||
return seasons[seasonIndex]
|
||||
?.sort((a, b) => a.IndexNumber! - b.IndexNumber!)
|
||||
?? []
|
||||
return (
|
||||
seasons[seasonIndex]?.sort((a, b) => a.IndexNumber! - b.IndexNumber!) ??
|
||||
[]
|
||||
);
|
||||
}, [series, seasonIndex]);
|
||||
|
||||
const initialSeasonIndex = useMemo(() =>
|
||||
Object.values(groupBySeason)?.[0]?.ParentIndexNumber ?? series?.[0]?.item?.ParentIndexNumber,
|
||||
const initialSeasonIndex = useMemo(
|
||||
() =>
|
||||
Object.values(groupBySeason)?.[0]?.ParentIndexNumber ??
|
||||
series?.[0]?.item?.ParentIndexNumber,
|
||||
[groupBySeason]
|
||||
);
|
||||
|
||||
@@ -58,8 +72,7 @@ export default function page() {
|
||||
navigation.setOptions({
|
||||
title: series[0].item.SeriesName,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
storage.delete(seriesId);
|
||||
router.back();
|
||||
}
|
||||
@@ -70,37 +83,38 @@ export default function page() {
|
||||
[groupBySeason]
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{series.length > 0 && <View className="my-4 flex flex-row items-center justify-start">
|
||||
<SeasonDropdown
|
||||
item={series[0].item}
|
||||
seasons={series.map(s => s.item)}
|
||||
state={seasonIndexState}
|
||||
initialSeasonIndex={initialSeasonIndex!}
|
||||
onSelect={(season) => {
|
||||
setSeasonIndexState((prev) => ({
|
||||
...prev,
|
||||
[series[0].item.ParentId ?? ""]: season.ParentIndexNumber,
|
||||
}));
|
||||
}}/>
|
||||
<View className="flex flex-row items-center justify-between w-72">
|
||||
<View className="bg-purple-600 rounded-full h-6 w-6 flex items-center justify-center">
|
||||
<Text className="text-xs font-bold">{groupBySeason.length}</Text>
|
||||
</View>
|
||||
<View className="bg-neutral-800/80 rounded-full h-10 w-10 flex items-center justify-center">
|
||||
<TouchableOpacity onPress={deleteSeries}>
|
||||
<Ionicons name="trash" size={22} color="white"/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View className="px-4 flex-1">
|
||||
{series.length > 0 && (
|
||||
<View className="flex flex-row items-center justify-start my-2">
|
||||
<SeasonDropdown
|
||||
item={series[0].item}
|
||||
seasons={series.map((s) => s.item)}
|
||||
state={seasonIndexState}
|
||||
initialSeasonIndex={initialSeasonIndex!}
|
||||
onSelect={(season) => {
|
||||
setSeasonIndexState((prev) => ({
|
||||
...prev,
|
||||
[series[0].item.ParentId ?? ""]: season.ParentIndexNumber,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<View className="bg-purple-600 rounded-full h-6 w-6 flex items-center justify-center ml-2">
|
||||
<Text className="text-xs font-bold">{groupBySeason.length}</Text>
|
||||
</View>
|
||||
</View>}
|
||||
<View className="bg-neutral-800/80 rounded-full h-10 w-10 flex items-center justify-center ml-auto">
|
||||
<TouchableOpacity onPress={deleteSeries}>
|
||||
<Ionicons name="trash" size={22} color="white" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
<ScrollView key={seasonIndex}>
|
||||
{groupBySeason.map((episode, index) => (
|
||||
<View className="px-4 flex flex-col my-4" key={index}>
|
||||
<EpisodeCard item={episode}/>
|
||||
<View className="flex flex-col mb-4" key={index}>
|
||||
<EpisodeCard item={episode} />
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,11 @@ export const SeasonDropdown: React.FC<Props> = ({
|
||||
},
|
||||
[item]
|
||||
);
|
||||
const seasonIndex = useMemo(() => state[item[keys.id] ?? ""], [state]);
|
||||
|
||||
const seasonIndex = useMemo(
|
||||
() => state[(item[keys.id] as string) ?? ""],
|
||||
[state]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (seasons && seasons.length > 0 && seasonIndex === undefined) {
|
||||
@@ -80,12 +84,12 @@ export const SeasonDropdown: React.FC<Props> = ({
|
||||
}, [seasons, seasonIndex, item[keys.id], initialSeasonIndex]);
|
||||
|
||||
const sortByIndex = (a: BaseItemDto, b: BaseItemDto) =>
|
||||
a[keys.index] - b[keys.index];
|
||||
Number(a[keys.index]) - Number(b[keys.index]);
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<View className="flex flex-row px-4">
|
||||
<View className="flex flex-row">
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user