Merge pull request #114 from lostb1t/feature/collectiondefault

feat: Add Default option and use collection sorting as default
This commit is contained in:
Fredrik Burmester
2024-09-01 17:10:48 +02:00
committed by GitHub
6 changed files with 115 additions and 89 deletions

1
.gitignore vendored
View File

@@ -21,6 +21,7 @@ build-*
*.mp4
build-*
Streamyfin.app
package-lock.json
/ios
/android

View File

@@ -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 = () => {
<Text className="font-bold text-xl text-neutral-500">No results</Text>
</View>
}
extraData={[
selectedGenres,
selectedYears,
selectedTags,
sortBy,
sortOrder,
]}
contentInsetAdjustmentBehavior="automatic"
data={flatData}
renderItem={renderItem}

View File

@@ -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())
}
/>
),

View File

@@ -23,7 +23,7 @@ export const FilterButton = <T,>({
queryFn,
queryKey,
set,
values,
values, // selected values
title,
renderItemLabel,
searchFilter,

View File

@@ -186,7 +186,7 @@ export const FilterSheet = <T,>({
className=" bg-neutral-800 px-4 py-3 flex flex-row items-center justify-between"
>
<Text>{renderItemLabel(item)}</Text>
{values.includes(item) ? (
{values.some((i) => i === item) ? (
<Ionicons name="radio-button-on" size={24} color="white" />
) : (
<Ionicons name="radio-button-off" size={24} color="white" />

View File

@@ -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<string[]>([]);
export const tagsFilterAtom = atom<string[]>([]);
export const yearFilterAtom = atom<string[]>([]);
export const sortByAtom = atom<[typeof sortOptions][number]>([sortOptions[0]]);
export const sortOrderAtom = atom<[typeof sortOrderOptions][number]>([
sortOrderOptions[0],
export const sortByAtom = atom<SortByOption[]>([SortByOption.Default]);
export const sortOrderAtom = atom<SortOrderOption[]>([
SortOrderOption.Ascending,
]);