diff --git a/app/(auth)/(tabs)/libraries/[libraryId].tsx b/app/(auth)/(tabs)/libraries/[libraryId].tsx index c374eabb..76da43fc 100644 --- a/app/(auth)/(tabs)/libraries/[libraryId].tsx +++ b/app/(auth)/(tabs)/libraries/[libraryId].tsx @@ -1,4 +1,3 @@ -import { Text } from "@/components/common/Text"; import { TouchableItemRouter } from "@/components/common/TouchableItemRouter"; import { FilterButton } from "@/components/filters/FilterButton"; import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton"; @@ -7,9 +6,10 @@ import { Loader } from "@/components/Loader"; import MoviePoster from "@/components/posters/MoviePoster"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { + currentCollectionIdAtom, genreFilterAtom, sortByAtom, - sortOptions, + sortByOptions, sortOrderAtom, sortOrderOptions, tagsFilterAtom, @@ -25,7 +25,7 @@ import { getUserLibraryApi, } from "@jellyfin/sdk/lib/utils/api"; import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; -import { Stack, useLocalSearchParams, useNavigation } from "expo-router"; +import { useLocalSearchParams } from "expo-router"; import { useAtom } from "jotai"; import React, { useCallback, useEffect, useMemo } from "react"; import { NativeScrollEvent, ScrollView, View } from "react-native"; @@ -46,9 +46,14 @@ const page: React.FC = () => { const searchParams = useLocalSearchParams(); const { libraryId } = searchParams as { libraryId: string }; + const [, setCurrentCollectionId] = useAtom(currentCollectionIdAtom); + + useEffect(() => { + setCurrentCollectionId(libraryId); + }, [libraryId]); + const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); - const navigation = useNavigation(); const [selectedGenres, setSelectedGenres] = useAtom(genreFilterAtom); const [selectedYears, setSelectedYears] = useAtom(yearFilterAtom); @@ -204,7 +209,7 @@ const page: React.FC = () => { }); return response.data.Genres || []; }} - set={setSelectedGenres} + set={(value) => setSelectedGenres(value, libraryId)} values={selectedGenres} title="Genres" renderItemLabel={(item) => item.toString()} @@ -226,7 +231,7 @@ const page: React.FC = () => { }); return response.data.Tags || []; }} - set={setSelectedTags} + set={(value) => setSelectedTags(value, libraryId)} values={selectedTags} title="Tags" renderItemLabel={(item) => item.toString()} @@ -252,7 +257,7 @@ const page: React.FC = () => { ) || [] ); }} - set={setSelectedYears} + set={(value) => setSelectedYears(value, libraryId)} values={selectedYears} title="Years" renderItemLabel={(item) => item.toString()} @@ -265,9 +270,9 @@ const page: React.FC = () => { collectionId={libraryId} queryKey="sortByFilter" queryFn={async () => { - return sortOptions; + return sortByOptions; }} - set={setSortBy} + set={(value) => setSortBy(value, libraryId)} values={sortBy} title="Sort by" renderItemLabel={(item) => item.value} @@ -285,7 +290,7 @@ const page: React.FC = () => { queryFn={async () => { return sortOrderOptions; }} - set={setSortOrder} + set={(value) => setSortOrder(value, libraryId)} values={sortOrder} title="Order by" renderItemLabel={(item) => item.value} diff --git a/app/(auth)/(tabs)/libraries/index.tsx b/app/(auth)/(tabs)/libraries/index.tsx index 3c02844e..e250e85b 100644 --- a/app/(auth)/(tabs)/libraries/index.tsx +++ b/app/(auth)/(tabs)/libraries/index.tsx @@ -1,6 +1,7 @@ import { Text } from "@/components/common/Text"; import { Loader } from "@/components/Loader"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; +import { currentCollectionIdAtom } from "@/utils/atoms/filters"; import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { getUserViewsApi } from "@jellyfin/sdk/lib/utils/api"; @@ -66,6 +67,10 @@ const LibraryItemCard: React.FC = ({ library }) => { const [api] = useAtom(apiAtom); + const [currentCollection, setCurrentCollection] = useAtom( + currentCollectionIdAtom + ); + const url = useMemo( () => getPrimaryImageUrl({ @@ -80,6 +85,8 @@ const LibraryItemCard: React.FC = ({ library }) => { return ( { + if (!library.Id) return; + setCurrentCollection(library.Id); router.push(`/libraries/${library.Id}`); }} > diff --git a/app/(auth)/collections/[collectionId].tsx b/app/(auth)/collections/[collectionId].tsx index 35e0bdf7..2eeaa890 100644 --- a/app/(auth)/collections/[collectionId].tsx +++ b/app/(auth)/collections/[collectionId].tsx @@ -7,9 +7,10 @@ import { Loader } from "@/components/Loader"; import MoviePoster from "@/components/posters/MoviePoster"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { + currentCollectionIdAtom, genreFilterAtom, sortByAtom, - sortOptions, + sortByOptions, sortOrderAtom, sortOrderOptions, tagsFilterAtom, @@ -55,20 +56,29 @@ const page: React.FC = () => { const [selectedTags, setSelectedTags] = useAtom(tagsFilterAtom); const [sortBy, setSortBy] = useAtom(sortByAtom); const [sortOrder, setSortOrder] = useAtom(sortOrderAtom); + const [currentCollection, setCurrentCollection] = useAtom( + currentCollectionIdAtom + ); useEffect(() => { - setSortBy([ - { - key: "PremiereDate", - value: "Premiere Date", - }, - ]); - setSortOrder([ - { - key: "Ascending", - value: "Ascending", - }, - ]); + setSortBy( + [ + { + key: "PremiereDate", + value: "Premiere Date", + }, + ], + collectionId + ); + setSortOrder( + [ + { + key: "Ascending", + value: "Ascending", + }, + ], + collectionId + ); }, []); const { data: collection } = useQuery({ @@ -208,7 +218,7 @@ const page: React.FC = () => { }); return response.data.Genres || []; }} - set={setSelectedGenres} + set={(value) => setSelectedGenres(value, collectionId)} values={selectedGenres} title="Genres" renderItemLabel={(item) => item.toString()} @@ -230,7 +240,7 @@ const page: React.FC = () => { }); return response.data.Tags || []; }} - set={setSelectedTags} + set={(value) => setSelectedTags(value, collectionId)} values={selectedTags} title="Tags" renderItemLabel={(item) => item.toString()} @@ -256,7 +266,7 @@ const page: React.FC = () => { ) || [] ); }} - set={setSelectedYears} + set={(value) => setSelectedYears(value, collectionId)} values={selectedYears} title="Years" renderItemLabel={(item) => item.toString()} @@ -269,9 +279,9 @@ const page: React.FC = () => { collectionId={collectionId} queryKey="sortByFilter" queryFn={async () => { - return sortOptions; + return sortByOptions; }} - set={setSortBy} + set={(value) => setSortBy(value, collectionId)} values={sortBy} title="Sort by" renderItemLabel={(item) => item.value} @@ -289,7 +299,7 @@ const page: React.FC = () => { queryFn={async () => { return sortOrderOptions; }} - set={setSortOrder} + set={(value) => setSortOrder(value, collectionId)} values={sortOrder} title="Order by" renderItemLabel={(item) => item.value} diff --git a/components/filters/_SortButton.tsx b/components/filters/_SortButton.tsx deleted file mode 100644 index bff476a0..00000000 --- a/components/filters/_SortButton.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import * as DropdownMenu from "zeego/dropdown-menu"; -import { Text } from "@/components/common/Text"; -import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; -import { Ionicons } from "@expo/vector-icons"; -import { useQuery } from "@tanstack/react-query"; -import { useAtom } from "jotai"; -import { TouchableOpacity, View, ViewProps } from "react-native"; -import { - sortByAtom, - sortOptions, - sortOrderAtom, - sortOrderOptions, -} from "@/utils/atoms/filters"; - -interface Props extends ViewProps { - title: string; -} - -export const SortButton: React.FC = ({ title, ...props }) => { - const [api] = useAtom(apiAtom); - const [user] = useAtom(userAtom); - - const [sortBy, setSortBy] = useAtom(sortByAtom); - const [sortOrder, setSortOrder] = useAtom(sortOrderAtom); - - return ( - - - - - Sort by - - - - - - {sortOptions?.map((g) => ( - { - if (next === "on") { - setSortBy(g); - } else { - setSortBy(sortOptions[0]); - } - }} - key={g.key} - textValue={g.value} - > - - - ))} - - - {sortOrderOptions.map((g) => ( - { - if (next === "on") { - setSortOrder(g); - } else { - setSortOrder(sortOrderOptions[0]); - } - }} - key={g.key} - textValue={g.value} - > - - - ))} - - - - ); -}; diff --git a/utils/atoms/filters.ts b/utils/atoms/filters.ts index e9ab2e95..1fefdaef 100644 --- a/utils/atoms/filters.ts +++ b/utils/atoms/filters.ts @@ -5,8 +5,9 @@ import { SortOrder, } from "@jellyfin/sdk/lib/generated-client/models"; import { atom, useAtom } from "jotai"; +import { atomWithStorage } from "jotai/utils"; -export const sortOptions: { +export const sortByOptions: { key: ItemSortBy; value: string; }[] = [ @@ -38,10 +39,111 @@ export const sortOrderOptions: { { key: "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], -]); +// Define the keys for our preferences +type PreferenceKey = + | "genreFilter" + | "tagsFilter" + | "yearFilter" + | "sortBy" + | "sortOrder"; + +// Define the type for a single collection's preferences +type CollectionPreference = { + genreFilter: string[]; + tagsFilter: string[]; + yearFilter: string[]; + sortBy: [typeof sortByOptions][number]; + sortOrder: [typeof sortOrderOptions][number]; +}; + +// Define the type for all sort preferences +type SortPreference = { + [collectionId: string]: CollectionPreference; +}; + +// Create a base atom with storage +const baseSortPreferenceAtom = atomWithStorage( + "sortPreferences", + {} +); + +// Create a derived atom with logging +export const sortPreferenceAtom = atom( + (get) => { + const value = get(baseSortPreferenceAtom); + console.log("Getting sortPreferences:", value); + return value; + }, + (get, set, newValue: SortPreference) => { + console.log("Setting sortPreferences:", newValue); + set(baseSortPreferenceAtom, newValue); + } +); + +export const currentCollectionIdAtom = atomWithStorage( + "currentCollectionId", + null +); + +// Helper function to create an atom with custom getter and setter +const createFilterAtom = ( + key: PreferenceKey, + initialValue: T +) => { + const baseAtom = atom(initialValue); + + return atom( + (get): T => { + const preferences = get(sortPreferenceAtom); + const currentCollectionId = get(currentCollectionIdAtom); + if (currentCollectionId && preferences[currentCollectionId]) { + const preferenceValue = preferences[currentCollectionId][key]; + + // Ensure the returned value matches the expected type T + if (Array.isArray(initialValue) && Array.isArray(preferenceValue)) { + return preferenceValue as T; + } else if ( + typeof initialValue === "object" && + typeof preferenceValue === "object" + ) { + return preferenceValue as T; + } else if (typeof initialValue === typeof preferenceValue) { + return preferenceValue as T; + } + } + return get(baseAtom); + }, + (get, set, newValue: T, collectionId: string) => { + set(baseAtom, newValue); + const preferences = get(sortPreferenceAtom); + console.log("Set", preferences); + set(sortPreferenceAtom, { + ...preferences, + [collectionId]: { + ...preferences[collectionId], + [key]: newValue, + }, + }); + } + ); +}; + +type SortByOption = ItemSortBy | { key: ItemSortBy; value: string }; +type SortOrderOption = SortOrder | { key: SortOrder; value: string }; + +function getSortKey( + option: SortByOption | SortOrderOption +): ItemSortBy | SortOrder { + return typeof option === "string" ? option : option.key; +} + +export const genreFilterAtom = createFilterAtom("genreFilter", []); +export const tagsFilterAtom = createFilterAtom("tagsFilter", []); +export const yearFilterAtom = createFilterAtom("yearFilter", []); +export const sortByAtom = createFilterAtom<[typeof sortByOptions][number]>( + "sortBy", + [sortByOptions[0]] +); +export const sortOrderAtom = createFilterAtom< + [typeof sortOrderOptions][number] +>("sortOrder", [sortOrderOptions[0]]);