diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx
index 5fd125c9..80144416 100644
--- a/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx
+++ b/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx
@@ -326,7 +326,7 @@ const page: React.FC = () => {
data={[
{
key: "reset",
- component: ,
+ component: ,
},
{
key: "genre",
diff --git a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx
index b8b9d132..0d04e136 100644
--- a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx
+++ b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx
@@ -39,6 +39,7 @@ import { TVPosterCard } from "@/components/tv/TVPosterCard";
import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import { useScaledTVTypography } from "@/constants/TVTypography";
import useRouter from "@/hooks/useAppRouter";
+import { useFilterReset } from "@/hooks/useFilterReset";
import { useOrientation } from "@/hooks/useOrientation";
import { useRefreshLibraryOnFocus } from "@/hooks/useRefreshLibraryOnFocus";
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
@@ -521,7 +522,7 @@ const Page = () => {
data={[
{
key: "reset",
- component: ,
+ component: ,
},
{
key: "genre",
@@ -688,19 +689,9 @@ const Page = () => {
],
);
- // TV Filter bar header
- const hasActiveFilters =
- selectedGenres.length > 0 ||
- selectedYears.length > 0 ||
- selectedTags.length > 0 ||
- filterBy.length > 0;
-
- const resetAllFilters = useCallback(() => {
- setSelectedGenres([]);
- setSelectedYears([]);
- setSelectedTags([]);
- _setFilterBy([]);
- }, [setSelectedGenres, setSelectedYears, setSelectedTags, _setFilterBy]);
+ // Filter bar reset + visibility, shared with the mobile ResetFiltersButton so
+ // sort/order can't be forgotten on one path (it used to be reset on neither).
+ const { hasActiveFilters, resetAllFilters } = useFilterReset(libraryId);
// TV Filter options - with "All" option for clearable filters
const tvGenreFilterOptions = useMemo(
diff --git a/components/filters/ResetFiltersButton.tsx b/components/filters/ResetFiltersButton.tsx
index 856ccd3b..1635f4dd 100644
--- a/components/filters/ResetFiltersButton.tsx
+++ b/components/filters/ResetFiltersButton.tsx
@@ -1,38 +1,24 @@
import { Ionicons } from "@expo/vector-icons";
-import { useAtom } from "jotai";
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
-import {
- filterByAtom,
- genreFilterAtom,
- tagsFilterAtom,
- yearFilterAtom,
-} from "@/utils/atoms/filters";
+import { useFilterReset } from "@/hooks/useFilterReset";
-interface Props extends TouchableOpacityProps {}
+interface Props extends TouchableOpacityProps {
+ libraryId: string;
+}
-export const ResetFiltersButton: React.FC = ({ ...props }) => {
- const [selectedGenres, setSelectedGenres] = useAtom(genreFilterAtom);
- const [selectedTags, setSelectedTags] = useAtom(tagsFilterAtom);
- const [selectedYears, setSelectedYears] = useAtom(yearFilterAtom);
- const [selectedFilters, setSelectedFilters] = useAtom(filterByAtom);
+export const ResetFiltersButton: React.FC = ({
+ libraryId,
+ ...props
+}) => {
+ const { hasActiveFilters, resetAllFilters } = useFilterReset(libraryId);
- if (
- selectedGenres.length === 0 &&
- selectedTags.length === 0 &&
- selectedYears.length === 0 &&
- selectedFilters.length === 0
- ) {
+ if (!hasActiveFilters) {
return null;
}
return (
{
- setSelectedGenres([]);
- setSelectedTags([]);
- setSelectedYears([]);
- setSelectedFilters([]);
- }}
+ onPress={resetAllFilters}
className='bg-purple-600 rounded-full w-[30px] h-[30px] flex items-center justify-center mr-1'
{...props}
>
diff --git a/hooks/useFilterReset.ts b/hooks/useFilterReset.ts
new file mode 100644
index 00000000..aad25f85
--- /dev/null
+++ b/hooks/useFilterReset.ts
@@ -0,0 +1,84 @@
+import { useAtom } from "jotai";
+import { useCallback } from "react";
+import {
+ FilterByPreferenceAtom,
+ filterByAtom,
+ genreFilterAtom,
+ SortByOption,
+ SortOrderOption,
+ sortByAtom,
+ sortByPreferenceAtom,
+ sortOrderAtom,
+ sortOrderPreferenceAtom,
+ tagsFilterAtom,
+ yearFilterAtom,
+} from "@/utils/atoms/filters";
+
+/**
+ * Single source of truth for the library filter bar's "reset" action and its
+ * visibility. The mobile ResetFiltersButton and the TV filter header both use
+ * this so they can't drift — sort/order used to be reset on neither path, so
+ * the reset (X) never reflected a changed sort.
+ *
+ * A reset clears the session filters AND the per-library persisted preferences
+ * (sort, order, filterBy); otherwise the saved preference resurfaces when the
+ * library's mount effect re-applies it on the next entry.
+ */
+export const useFilterReset = (libraryId: string) => {
+ const [selectedGenres, setSelectedGenres] = useAtom(genreFilterAtom);
+ const [selectedYears, setSelectedYears] = useAtom(yearFilterAtom);
+ const [selectedTags, setSelectedTags] = useAtom(tagsFilterAtom);
+ const [filterBy, setFilterBy] = useAtom(filterByAtom);
+ const [sortBy, setSortBy] = useAtom(sortByAtom);
+ const [sortOrder, setSortOrder] = useAtom(sortOrderAtom);
+ const [, setSortByPreference] = useAtom(sortByPreferenceAtom);
+ const [, setSortOrderPreference] = useAtom(sortOrderPreferenceAtom);
+ const [, setFilterByPreference] = useAtom(FilterByPreferenceAtom);
+
+ // SortName / Ascending is the baseline a library opens with (mount-effect
+ // fallback), so any other value counts as an active, resettable sort.
+ const hasActiveFilters =
+ selectedGenres.length > 0 ||
+ selectedYears.length > 0 ||
+ selectedTags.length > 0 ||
+ filterBy.length > 0 ||
+ sortBy[0] !== SortByOption.SortName ||
+ sortOrder[0] !== SortOrderOption.Ascending;
+
+ const resetAllFilters = useCallback(() => {
+ setSelectedGenres([]);
+ setSelectedYears([]);
+ setSelectedTags([]);
+ setFilterBy([]);
+ setSortBy([SortByOption.SortName]);
+ setSortOrder([SortOrderOption.Ascending]);
+ setSortByPreference((prev) => {
+ const next = { ...prev };
+ delete next[libraryId];
+ return next;
+ });
+ setSortOrderPreference((prev) => {
+ const next = { ...prev };
+ delete next[libraryId];
+ return next;
+ });
+ setFilterByPreference((prev) => {
+ const next = { ...prev };
+ delete next[libraryId];
+ return next;
+ });
+ }, [
+ libraryId,
+ setSelectedGenres,
+ setSelectedYears,
+ setSelectedTags,
+ setFilterBy,
+ setSortBy,
+ setSortOrder,
+ setSortByPreference,
+ setSortOrderPreference,
+ setFilterByPreference,
+ ]);
+
+ return { hasActiveFilters, resetAllFilters };
+};