diff --git a/.gitignore b/.gitignore
index aedc9ceb..4c3672cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ build-*
*.mp4
build-*
Streamyfin.app
+package-lock.json
/ios
/android
diff --git a/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx b/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx
index b9a657be..b766391e 100644
--- a/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx
+++ b/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx
@@ -8,8 +8,10 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import {
genreFilterAtom,
sortByAtom,
+ SortByOption,
sortOptions,
sortOrderAtom,
+ SortOrderOption,
sortOrderOptions,
tagsFilterAtom,
yearFilterAtom,
@@ -17,6 +19,7 @@ import {
import {
BaseItemDto,
BaseItemDtoQueryResult,
+ ItemSortBy,
} from "@jellyfin/sdk/lib/generated-client/models";
import {
getFilterApi,
@@ -56,21 +59,6 @@ const page: React.FC = () => {
const [sortBy, setSortBy] = useAtom(sortByAtom);
const [sortOrder, setSortOrder] = useAtom(sortOrderAtom);
- useLayoutEffect(() => {
- setSortBy([
- {
- key: "PremiereDate",
- value: "Premiere Date",
- },
- ]);
- setSortOrder([
- {
- key: "Ascending",
- value: "Ascending",
- },
- ]);
- }, []);
-
const { data: collection } = useQuery({
queryKey: ["collection", collectionId],
queryFn: async () => {
@@ -88,6 +76,18 @@ const page: React.FC = () => {
useEffect(() => {
navigation.setOptions({ title: collection?.Name || "" });
+ setSortOrder([SortOrderOption.Ascending]);
+
+ if (!collection) return;
+
+ // Convert the DisplayOrder to SortByOption
+ const displayOrder = collection.DisplayOrder as ItemSortBy;
+ const sortByOption = displayOrder
+ ? SortByOption[displayOrder as keyof typeof SortByOption] ||
+ SortByOption.PremiereDate
+ : SortByOption.PremiereDate;
+
+ setSortBy([sortByOption]);
}, [navigation, collection]);
const fetchItems = useCallback(
@@ -103,8 +103,9 @@ const page: React.FC = () => {
parentId: collectionId,
limit: 18,
startIndex: pageParam,
- sortBy: [sortBy[0].key, "SortName", "ProductionYear"],
- sortOrder: [sortOrder[0].key],
+ // Set one ordering at a time. As collections do not work with correctly with multiple.
+ sortBy: [sortBy[0]],
+ sortOrder: [sortOrder[0]],
fields: [
"ItemCounts",
"PrimaryImageAspectRatio",
@@ -216,6 +217,13 @@ const page: React.FC = () => {
paddingVertical: 16,
flexDirection: "row",
}}
+ extraData={[
+ selectedGenres,
+ selectedYears,
+ selectedTags,
+ sortBy,
+ sortOrder,
+ ]}
data={[
{
key: "reset",
@@ -307,13 +315,15 @@ const page: React.FC = () => {
className="mr-1"
collectionId={collectionId}
queryKey="sortBy"
- queryFn={async () => sortOptions}
+ queryFn={async () => sortOptions.map((s) => s.key)}
set={setSortBy}
values={sortBy}
title="Sort By"
- renderItemLabel={(item) => item.value}
+ renderItemLabel={(item) =>
+ sortOptions.find((i) => i.key === item)?.value || ""
+ }
searchFilter={(item, search) =>
- item.value.toLowerCase().includes(search.toLowerCase())
+ item.toLowerCase().includes(search.toLowerCase())
}
/>
),
@@ -325,13 +335,15 @@ const page: React.FC = () => {
className="mr-1"
collectionId={collectionId}
queryKey="sortOrder"
- queryFn={async () => sortOrderOptions}
+ queryFn={async () => sortOrderOptions.map((s) => s.key)}
set={setSortOrder}
values={sortOrder}
title="Sort Order"
- renderItemLabel={(item) => item.value}
+ renderItemLabel={(item) =>
+ sortOrderOptions.find((i) => i.key === item)?.value || ""
+ }
searchFilter={(item, search) =>
- item.value.toLowerCase().includes(search.toLowerCase())
+ item.toLowerCase().includes(search.toLowerCase())
}
/>
),
@@ -369,6 +381,13 @@ const page: React.FC = () => {
No results
}
+ extraData={[
+ selectedGenres,
+ selectedYears,
+ selectedTags,
+ sortBy,
+ sortOrder,
+ ]}
contentInsetAdjustmentBehavior="automatic"
data={flatData}
renderItem={renderItem}
diff --git a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx
index 77b2e3ff..e021efee 100644
--- a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx
+++ b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx
@@ -9,25 +9,23 @@ import React, {
useMemo,
useState,
} from "react";
-import {
- FlatList,
- RefreshControl,
- useWindowDimensions,
- View,
-} from "react-native";
+import { FlatList, useWindowDimensions, View } from "react-native";
import { Text } from "@/components/common/Text";
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
import { FilterButton } from "@/components/filters/FilterButton";
import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton";
import { ItemCardText } from "@/components/ItemCardText";
+import { Loader } from "@/components/Loader";
import MoviePoster from "@/components/posters/MoviePoster";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import {
genreFilterAtom,
sortByAtom,
+ SortByOption,
sortOptions,
sortOrderAtom,
+ SortOrderOption,
sortOrderOptions,
tagsFilterAtom,
yearFilterAtom,
@@ -35,7 +33,6 @@ import {
import {
BaseItemDto,
BaseItemDtoQueryResult,
- BaseItemKind,
} from "@jellyfin/sdk/lib/generated-client/models";
import {
getFilterApi,
@@ -43,7 +40,6 @@ import {
getUserLibraryApi,
} from "@jellyfin/sdk/lib/utils/api";
import { FlashList } from "@shopify/flash-list";
-import { Loader } from "@/components/Loader";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { orientationAtom } from "@/utils/atoms/orientation";
@@ -55,7 +51,6 @@ const Page = () => {
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
- const navigation = useNavigation();
const { width: screenWidth } = useWindowDimensions();
const [selectedGenres, setSelectedGenres] = useAtom(genreFilterAtom);
@@ -75,18 +70,8 @@ const Page = () => {
}, [screenWidth, orientation]);
useLayoutEffect(() => {
- setSortBy([
- {
- key: "SortName",
- value: "Name",
- },
- ]);
- setSortOrder([
- {
- key: "Ascending",
- value: "Ascending",
- },
- ]);
+ setSortBy([SortByOption.SortName]);
+ setSortOrder([SortOrderOption.Ascending]);
}, []);
const { data: library, isLoading: isLibraryLoading } = useQuery({
@@ -116,8 +101,8 @@ const Page = () => {
parentId: libraryId,
limit: 36,
startIndex: pageParam,
- sortBy: [sortBy[0].key, "SortName", "ProductionYear"],
- sortOrder: [sortOrder[0].key],
+ sortBy: [sortBy[0], "SortName", "ProductionYear"],
+ sortOrder: [sortOrder[0]],
enableImageTypes: ["Primary", "Backdrop", "Banner", "Thumb"],
recursive: false,
imageTypeLimit: 1,
@@ -321,13 +306,15 @@ const Page = () => {
className="mr-1"
collectionId={libraryId}
queryKey="sortBy"
- queryFn={async () => sortOptions}
+ queryFn={async () => sortOptions.map((s) => s.key)}
set={setSortBy}
values={sortBy}
title="Sort By"
- renderItemLabel={(item) => item.value}
+ renderItemLabel={(item) =>
+ sortOptions.find((i) => i.key === item)?.value || ""
+ }
searchFilter={(item, search) =>
- item.value.toLowerCase().includes(search.toLowerCase())
+ item.toLowerCase().includes(search.toLowerCase())
}
/>
),
@@ -339,13 +326,15 @@ const Page = () => {
className="mr-1"
collectionId={libraryId}
queryKey="sortOrder"
- queryFn={async () => sortOrderOptions}
+ queryFn={async () => sortOrderOptions.map((s) => s.key)}
set={setSortOrder}
values={sortOrder}
title="Sort Order"
- renderItemLabel={(item) => item.value}
+ renderItemLabel={(item) =>
+ sortOrderOptions.find((i) => i.key === item)?.value || ""
+ }
searchFilter={(item, search) =>
- item.value.toLowerCase().includes(search.toLowerCase())
+ item.toLowerCase().includes(search.toLowerCase())
}
/>
),
diff --git a/components/filters/FilterButton.tsx b/components/filters/FilterButton.tsx
index e50e4010..6d976b26 100644
--- a/components/filters/FilterButton.tsx
+++ b/components/filters/FilterButton.tsx
@@ -23,7 +23,7 @@ export const FilterButton = ({
queryFn,
queryKey,
set,
- values,
+ values, // selected values
title,
renderItemLabel,
searchFilter,
diff --git a/components/filters/FilterSheet.tsx b/components/filters/FilterSheet.tsx
index 7f163b72..fe6d9f6a 100644
--- a/components/filters/FilterSheet.tsx
+++ b/components/filters/FilterSheet.tsx
@@ -186,7 +186,7 @@ export const FilterSheet = ({
className=" bg-neutral-800 px-4 py-3 flex flex-row items-center justify-between"
>
{renderItemLabel(item)}
- {values.includes(item) ? (
+ {values.some((i) => i === item) ? (
) : (
diff --git a/utils/atoms/filters.ts b/utils/atoms/filters.ts
index 7324f569..f49b17d4 100644
--- a/utils/atoms/filters.ts
+++ b/utils/atoms/filters.ts
@@ -1,50 +1,67 @@
-import {
- ItemFilter,
- ItemSortBy,
- NameGuidPair,
- SortOrder,
-} from "@jellyfin/sdk/lib/generated-client/models";
-import { atom, useAtom } from "jotai";
+import { atom } from "jotai";
+
+export enum SortByOption {
+ Default = "Default",
+ SortName = "SortName",
+ CommunityRating = "CommunityRating",
+ CriticRating = "CriticRating",
+ DateCreated = "DateCreated",
+ DatePlayed = "DatePlayed",
+ PlayCount = "PlayCount",
+ ProductionYear = "ProductionYear",
+ Runtime = "Runtime",
+ OfficialRating = "OfficialRating",
+ PremiereDate = "PremiereDate",
+ StartDate = "StartDate",
+ IsUnplayed = "IsUnplayed",
+ IsPlayed = "IsPlayed",
+ AirTime = "AirTime",
+ Studio = "Studio",
+ IsFavoriteOrLiked = "IsFavoriteOrLiked",
+ Random = "Random",
+}
+
+export enum SortOrderOption {
+ Ascending = "Ascending",
+ Descending = "Descending",
+}
export const sortOptions: {
- key: ItemSortBy;
+ key: SortByOption;
value: string;
}[] = [
- { key: "SortName", value: "Name" },
- { key: "CommunityRating", value: "Community Rating" },
- { key: "CriticRating", value: "Critics Rating" },
- { key: "DateCreated", value: "Date Added" },
- // Only works for shows (last episode added) keeping for future ref.
- // { key: "DateLastContentAdded", value: "Content Added" },
- { key: "DatePlayed", value: "Date Played" },
- { key: "PlayCount", value: "Play Count" },
- { key: "ProductionYear", value: "Production Year" },
- { key: "Runtime", value: "Runtime" },
- { key: "OfficialRating", value: "Official Rating" },
- { key: "PremiereDate", value: "Premiere Date" },
- { key: "StartDate", value: "Start Date" },
- { key: "IsUnplayed", value: "Is Unplayed" },
- { key: "IsPlayed", value: "Is Played" },
- // Broken in JF
- // { key: "VideoBitRate", value: "Video Bit Rate" },
- { key: "AirTime", value: "Air Time" },
- { key: "Studio", value: "Studio" },
- { key: "IsFavoriteOrLiked", value: "Is Favorite Or Liked" },
- { key: "Random", value: "Random" },
+ { key: SortByOption.Default, value: "Default" },
+ { key: SortByOption.SortName, value: "Name" },
+ { key: SortByOption.CommunityRating, value: "Community Rating" },
+ { key: SortByOption.CriticRating, value: "Critics Rating" },
+ { key: SortByOption.DateCreated, value: "Date Added" },
+ { key: SortByOption.DatePlayed, value: "Date Played" },
+ { key: SortByOption.PlayCount, value: "Play Count" },
+ { key: SortByOption.ProductionYear, value: "Production Year" },
+ { key: SortByOption.Runtime, value: "Runtime" },
+ { key: SortByOption.OfficialRating, value: "Official Rating" },
+ { key: SortByOption.PremiereDate, value: "Premiere Date" },
+ { key: SortByOption.StartDate, value: "Start Date" },
+ { key: SortByOption.IsUnplayed, value: "Is Unplayed" },
+ { key: SortByOption.IsPlayed, value: "Is Played" },
+ { key: SortByOption.AirTime, value: "Air Time" },
+ { key: SortByOption.Studio, value: "Studio" },
+ { key: SortByOption.IsFavoriteOrLiked, value: "Is Favorite Or Liked" },
+ { key: SortByOption.Random, value: "Random" },
];
export const sortOrderOptions: {
- key: SortOrder;
+ key: SortOrderOption;
value: string;
}[] = [
- { key: "Ascending", value: "Ascending" },
- { key: "Descending", value: "Descending" },
+ { key: SortOrderOption.Ascending, value: "Ascending" },
+ { key: SortOrderOption.Descending, value: "Descending" },
];
export const genreFilterAtom = atom([]);
export const tagsFilterAtom = atom([]);
export const yearFilterAtom = atom([]);
-export const sortByAtom = atom<[typeof sortOptions][number]>([sortOptions[0]]);
-export const sortOrderAtom = atom<[typeof sortOrderOptions][number]>([
- sortOrderOptions[0],
+export const sortByAtom = atom([SortByOption.Default]);
+export const sortOrderAtom = atom([
+ SortOrderOption.Ascending,
]);