update query persistence to hooks

This commit is contained in:
jeffvli
2025-10-13 20:09:01 -07:00
parent 1a5b771ae0
commit 872543b5aa
11 changed files with 165 additions and 113 deletions
@@ -54,8 +54,7 @@ export const AlbumListView = ({
}: ItemListSettings) => {
const server = useCurrentServer();
const filters = useAlbumListFilters();
const query = filters.query;
const { query } = useAlbumListFilters();
switch (display) {
case ListDisplayType.GRID: {
@@ -9,7 +9,7 @@ import { useContainerQuery } from '/@/renderer/hooks';
import { Divider } from '/@/shared/components/divider/divider';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { AlbumListSort, LibraryItem } from '/@/shared/types/domain-types';
import { AlbumListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-types';
import { ItemListKey } from '/@/shared/types/types';
export const AlbumListHeaderFilters = () => {
@@ -24,7 +24,10 @@ export const AlbumListHeaderFilters = () => {
listKey={ItemListKey.ALBUM}
/>
<Divider orientation="vertical" />
<ListSortOrderToggleButton listKey={ItemListKey.ALBUM} />
<ListSortOrderToggleButton
defaultSortOrder={SortOrder.ASC}
listKey={ItemListKey.ALBUM}
/>
<ListMusicFolderDropdown listKey={ItemListKey.ALBUM} />
<ListFilters itemType={LibraryItem.ALBUM} />
<ListRefreshButton listKey={ItemListKey.ALBUM} />
@@ -42,14 +42,14 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
const {
query,
setAlbumArtist,
setAlbumCompilation,
setAlbumFavorite,
setAlbumGenre,
setAlbumHasRating,
setAlbumRecentlyPlayed,
setCompilation,
setCustom,
setMaxAlbumYear,
setMinAlbumYear,
setFavorite,
setGenreId,
setHasRating,
setMaxYear,
setMinYear,
setRecentlyPlayed,
} = useAlbumListFilters();
const genreListQuery = useQuery(
@@ -92,14 +92,14 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
{
label: t('filter.isFavorited', { postProcess: 'sentenceCase' }),
onChange: (favorite?: boolean) => {
setAlbumFavorite(favorite ?? null);
setFavorite(favorite ?? null);
},
value: query.favorite,
},
{
label: t('filter.isCompilation', { postProcess: 'sentenceCase' }),
onChange: (compilation?: boolean) => {
setAlbumCompilation(compilation ?? null);
setCompilation(compilation ?? null);
},
value: query.compilation,
},
@@ -110,7 +110,7 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
label: t('filter.isRated', { postProcess: 'sentenceCase' }),
onChange: (e: ChangeEvent<HTMLInputElement>) => {
const hasRating = e.currentTarget.checked ? true : undefined;
setAlbumHasRating(hasRating ?? null);
setHasRating(hasRating ?? null);
},
value: query.hasRating,
},
@@ -118,7 +118,7 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
label: t('filter.isRecentlyPlayed', { postProcess: 'sentenceCase' }),
onChange: (e: ChangeEvent<HTMLInputElement>) => {
const recentlyPlayed = e.currentTarget.checked ? true : undefined;
setAlbumRecentlyPlayed(recentlyPlayed ?? null);
setRecentlyPlayed(recentlyPlayed ?? null);
},
value: query.recentlyPlayed,
},
@@ -126,8 +126,8 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
const handleYearFilter = debounce((e: number | string) => {
const year = e === '' ? undefined : (e as number);
setMinAlbumYear(year ?? null);
setMaxAlbumYear(year ?? null);
setMinYear(year ?? null);
setMaxYear(year ?? null);
}, 500);
const albumArtistListQuery = useQuery(
@@ -194,9 +194,9 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
<SelectWithInvalidData
clearable
data={genreList}
defaultValue={query.genres ? query.genres[0] : undefined}
defaultValue={query.genreId ? query.genreId[0] : undefined}
label={t('entity.genre', { count: 1, postProcess: 'titleCase' })}
onChange={(e) => (e ? setAlbumGenre([e]) : undefined)}
onChange={(e) => (e ? setGenreId([e]) : undefined)}
searchable
/>
</Group>
@@ -12,19 +12,20 @@ import { useSearchTermFilter } from '/@/renderer/features/shared/hooks/use-searc
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
import { customFiltersSchema, FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { AlbumListSort, SortOrder } from '/@/shared/types/domain-types';
import { AlbumListSort } from '/@/shared/types/domain-types';
import { ItemListKey } from '/@/shared/types/types';
export const useAlbumListFilters = () => {
const { sortBy } = useSortByFilter<AlbumListSort>(AlbumListSort.NAME);
const { sortBy } = useSortByFilter<AlbumListSort>(null, ItemListKey.ALBUM);
const { sortOrder } = useSortOrderFilter(SortOrder.ASC);
const { sortOrder } = useSortOrderFilter(null, ItemListKey.ALBUM);
const { musicFolderId } = useMusicFolderIdFilter('');
const { musicFolderId } = useMusicFolderIdFilter(null, ItemListKey.ALBUM);
const { searchTerm, setSearchTerm } = useSearchTermFilter('');
const [albumGenre, setAlbumGenre] = useQueryState(
FILTER_KEYS.ALBUM.GENRES,
const [genreId, setGenreId] = useQueryState(
FILTER_KEYS.ALBUM.GENRE_ID,
parseAsArrayOf(parseAsString),
);
@@ -33,32 +34,20 @@ export const useAlbumListFilters = () => {
parseAsArrayOf(parseAsString),
);
const [minAlbumYear, setMinAlbumYear] = useQueryState(
FILTER_KEYS.ALBUM.MIN_YEAR,
parseAsInteger,
);
const [minYear, setMinYear] = useQueryState(FILTER_KEYS.ALBUM.MIN_YEAR, parseAsInteger);
const [maxAlbumYear, setMaxAlbumYear] = useQueryState(
FILTER_KEYS.ALBUM.MAX_YEAR,
parseAsInteger,
);
const [maxYear, setMaxYear] = useQueryState(FILTER_KEYS.ALBUM.MAX_YEAR, parseAsInteger);
const [albumFavorite, setAlbumFavorite] = useQueryState(
FILTER_KEYS.ALBUM.FAVORITE,
parseAsBoolean,
);
const [favorite, setFavorite] = useQueryState(FILTER_KEYS.ALBUM.FAVORITE, parseAsBoolean);
const [albumCompilation, setAlbumCompilation] = useQueryState(
const [compilation, setCompilation] = useQueryState(
FILTER_KEYS.ALBUM.COMPILATION,
parseAsBoolean,
);
const [albumHasRating, setAlbumHasRating] = useQueryState(
FILTER_KEYS.ALBUM.HAS_RATING,
parseAsBoolean,
);
const [hasRating, setHasRating] = useQueryState(FILTER_KEYS.ALBUM.HAS_RATING, parseAsBoolean);
const [albumRecentlyPlayed, setAlbumRecentlyPlayed] = useQueryState(
const [recentlyPlayed, setRecentlyPlayed] = useQueryState(
FILTER_KEYS.ALBUM.RECENTLY_PLAYED,
parseAsBoolean,
);
@@ -71,13 +60,13 @@ export const useAlbumListFilters = () => {
const query = {
[FILTER_KEYS.ALBUM._CUSTOM]: custom ?? undefined,
[FILTER_KEYS.ALBUM.ARTIST_IDS]: albumArtist ?? undefined,
[FILTER_KEYS.ALBUM.COMPILATION]: albumCompilation ?? undefined,
[FILTER_KEYS.ALBUM.FAVORITE]: albumFavorite ?? undefined,
[FILTER_KEYS.ALBUM.GENRES]: albumGenre ?? undefined,
[FILTER_KEYS.ALBUM.HAS_RATING]: albumHasRating ?? undefined,
[FILTER_KEYS.ALBUM.MAX_YEAR]: maxAlbumYear ?? undefined,
[FILTER_KEYS.ALBUM.MIN_YEAR]: minAlbumYear ?? undefined,
[FILTER_KEYS.ALBUM.RECENTLY_PLAYED]: albumRecentlyPlayed ?? undefined,
[FILTER_KEYS.ALBUM.COMPILATION]: compilation ?? undefined,
[FILTER_KEYS.ALBUM.FAVORITE]: favorite ?? undefined,
[FILTER_KEYS.ALBUM.GENRE_ID]: genreId ?? undefined,
[FILTER_KEYS.ALBUM.HAS_RATING]: hasRating ?? undefined,
[FILTER_KEYS.ALBUM.MAX_YEAR]: maxYear ?? undefined,
[FILTER_KEYS.ALBUM.MIN_YEAR]: minYear ?? undefined,
[FILTER_KEYS.ALBUM.RECENTLY_PLAYED]: recentlyPlayed ?? undefined,
[FILTER_KEYS.SHARED.MUSIC_FOLDER_ID]: musicFolderId ?? undefined,
[FILTER_KEYS.SHARED.SEARCH_TERM]: searchTerm ?? undefined,
[FILTER_KEYS.SHARED.SORT_BY]: sortBy ?? undefined,
@@ -87,14 +76,14 @@ export const useAlbumListFilters = () => {
return {
query,
setAlbumArtist,
setAlbumCompilation,
setAlbumFavorite,
setAlbumGenre,
setAlbumHasRating,
setAlbumRecentlyPlayed,
setCompilation,
setCustom,
setMaxAlbumYear,
setMinAlbumYear,
setFavorite,
setGenreId,
setHasRating,
setMaxYear,
setMinYear,
setRecentlyPlayed,
setSearchTerm,
};
};
@@ -30,7 +30,11 @@ export const ListFilters = ({ isActive, itemType }: ListFiltersProps) => {
return (
<>
<FilterButton isActive={isActive} onClick={handlers.toggle} />
<Modal handlers={handlers} opened={isOpen} title={t('common.filters')}>
<Modal
handlers={handlers}
opened={isOpen}
title={t('common.filters', { postProcess: 'sentenceCase' })}
>
<FilterComponent />
</Modal>
</>
@@ -2,12 +2,10 @@ import { useQuery } from '@tanstack/react-query';
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
import { FolderButton } from '/@/renderer/features/shared/components/folder-button';
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { useMusicFolderIdFilter } from '/@/renderer/features/shared/hooks/use-music-folder-id-filter';
import { useCurrentServer } from '/@/renderer/store';
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
import { ItemListKey } from '/@/shared/types/types';
import { useMusicFolderIdFilter } from '/@/renderer/features/shared/hooks/use-music-folder-id-filter';
interface ListMusicFolderDropdownProps {
listKey: ItemListKey;
@@ -19,22 +17,15 @@ export const ListMusicFolderDropdown = ({ listKey }: ListMusicFolderDropdownProp
sharedQueries.musicFolders({ query: null, serverId: server.id }),
);
const [persisted, setPersisted] = useLocalStorage({
defaultValue: '',
key: getPersistenceKey(server.id, listKey),
});
const { musicFolderId, setMusicFolderId } = useMusicFolderIdFilter(persisted);
const { musicFolderId, setMusicFolderId } = useMusicFolderIdFilter('', listKey);
const handleSetMusicFolder = (e: string) => {
if (e === musicFolderId) {
setMusicFolderId('');
setPersisted('');
return;
}
setMusicFolderId(e);
setPersisted(e);
};
return (
@@ -57,7 +48,3 @@ export const ListMusicFolderDropdown = ({ listKey }: ListMusicFolderDropdownProp
</DropdownMenu>
);
};
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.MUSIC_FOLDER_ID}`;
};
@@ -1,9 +1,8 @@
import i18n from '/@/i18n/i18n';
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
import { useCurrentServer } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
import {
AlbumListSort,
LibraryItem,
@@ -12,7 +11,6 @@ import {
SortOrder,
} from '/@/shared/types/domain-types';
import { ItemListKey } from '/@/shared/types/types';
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
interface ListSortByDropdownProps {
defaultSortByValue: string;
@@ -31,19 +29,13 @@ export const ListSortByDropdown = ({
}: ListSortByDropdownProps) => {
const server = useCurrentServer();
const [persisted, setPersisted] = useLocalStorage({
defaultValue: defaultSortByValue,
key: getPersistenceKey(server.id, listKey),
});
const { sortBy, setSortBy } = useSortByFilter(persisted || defaultSortByValue);
const { setSortBy, sortBy } = useSortByFilter(defaultSortByValue, listKey);
const sortByLabel =
(itemType && FILTERS[itemType][server.type].find((f) => f.value === sortBy)?.name) || '—';
const handleSortByChange = (sortBy: string) => {
setSortBy(sortBy);
setPersisted(sortBy);
onChange?.(sortBy);
};
@@ -369,7 +361,3 @@ const FILTERS: Partial<Record<LibraryItem, any>> = {
[LibraryItem.ALBUM]: ALBUM_LIST_FILTERS,
[LibraryItem.SONG]: SONG_LIST_FILTERS,
};
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.SORT_BY}`;
};
@@ -1,36 +1,25 @@
import { OrderToggleButton } from '/@/renderer/features/shared/components/order-toggle-button';
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { useCurrentServer } from '/@/renderer/store';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
import { SortOrder } from '/@/shared/types/domain-types';
import { ItemListKey } from '/@/shared/types/types';
interface ListSortOrderToggleButtonProps {
defaultSortOrder: SortOrder;
listKey: ItemListKey;
}
export const ListSortOrderToggleButton = ({ listKey }: ListSortOrderToggleButtonProps) => {
const server = useCurrentServer();
const [persisted, setPersisted] = useLocalStorage({
defaultValue: SortOrder.ASC,
key: getPersistenceKey(server.id, listKey),
});
const { sortOrder, setSortOrder } = useSortOrderFilter(persisted || SortOrder.ASC);
export const ListSortOrderToggleButton = ({
defaultSortOrder,
listKey,
}: ListSortOrderToggleButtonProps) => {
const { setSortOrder, sortOrder } = useSortOrderFilter(defaultSortOrder, listKey);
const handleToggleSortOrder = () => {
const newSortOrder = sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
setSortOrder(newSortOrder);
setPersisted(newSortOrder);
};
return (
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={sortOrder as SortOrder} />
);
};
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.SORT_ORDER}`;
};
@@ -1,15 +1,46 @@
import { parseAsString, useQueryState } from 'nuqs';
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { useCurrentServer } from '/@/renderer/store';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
import { ItemListKey } from '/@/shared/types/types';
export const useMusicFolderIdFilter = (defaultValue: null | string, listKey: ItemListKey) => {
const server = useCurrentServer();
const [persisted, setPersisted] = useLocalStorage({
defaultValue: '',
key: getPersistenceKey(server.id, listKey),
});
export const useMusicFolderIdFilter = (defaultValue?: string) => {
const [musicFolderId, setMusicFolderId] = useQueryState(
FILTER_KEYS.SHARED.MUSIC_FOLDER_ID,
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
getDefaultMusicFolderId(defaultValue, persisted),
);
const handleSetMusicFolderId = (musicFolderId: string) => {
setMusicFolderId(musicFolderId);
setPersisted(musicFolderId);
};
return {
[FILTER_KEYS.SHARED.MUSIC_FOLDER_ID]: musicFolderId ?? undefined,
setMusicFolderId,
setMusicFolderId: handleSetMusicFolderId,
};
};
const getDefaultMusicFolderId = (defaultValue: null | string, persisted: null | string) => {
if (persisted) {
return parseAsString.withDefault(persisted);
}
if (defaultValue) {
return parseAsString.withDefault(defaultValue);
}
return parseAsString;
};
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.MUSIC_FOLDER_ID}`;
};
@@ -1,15 +1,46 @@
import { parseAsString, useQueryState } from 'nuqs';
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { useCurrentServer } from '/@/renderer/store';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
import { ItemListKey } from '/@/shared/types/types';
export const useSortByFilter = <TSortBy>(defaultValue: null | string, listKey: ItemListKey) => {
const server = useCurrentServer();
const [persisted, setPersisted] = useLocalStorage({
defaultValue: defaultValue,
key: getPersistenceKey(server.id, listKey),
});
export const useSortByFilter = <TSortBy>(defaultValue: string) => {
const [sortBy, setSortBy] = useQueryState(
FILTER_KEYS.SHARED.SORT_BY,
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
getDefaultSortBy(defaultValue, persisted),
);
const handleSetSortBy = (sortBy: string) => {
setSortBy(sortBy);
setPersisted(sortBy);
};
return {
[FILTER_KEYS.SHARED.SORT_BY]: sortBy as TSortBy,
setSortBy,
setSortBy: handleSetSortBy,
};
};
const getDefaultSortBy = (defaultValue: null | string, persisted: null | string) => {
if (persisted) {
return parseAsString.withDefault(persisted);
}
if (defaultValue) {
return parseAsString.withDefault(defaultValue);
}
return parseAsString;
};
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.SORT_BY}`;
};
@@ -1,16 +1,47 @@
import { parseAsString, useQueryState } from 'nuqs';
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
import { useCurrentServer } from '/@/renderer/store';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
import { SortOrder } from '/@/shared/types/domain-types';
import { ItemListKey } from '/@/shared/types/types';
export const useSortOrderFilter = (defaultValue: null | string, listKey: ItemListKey) => {
const server = useCurrentServer();
const [persisted, setPersisted] = useLocalStorage({
defaultValue: SortOrder.ASC,
key: getPersistenceKey(server.id, listKey),
});
export const useSortOrderFilter = (defaultValue: string) => {
const [sortOrder, setSortOrder] = useQueryState(
FILTER_KEYS.SHARED.SORT_ORDER,
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
getDefaultSortOrder(defaultValue, persisted),
);
const handleSetSortOrder = (sortOrder: SortOrder) => {
setSortOrder(sortOrder);
setPersisted(sortOrder);
};
return {
[FILTER_KEYS.SHARED.SORT_ORDER]: sortOrder as SortOrder,
setSortOrder,
setSortOrder: handleSetSortOrder,
};
};
const getDefaultSortOrder = (defaultValue: null | string, persisted: null | string) => {
if (persisted) {
return parseAsString.withDefault(persisted);
}
if (defaultValue) {
return parseAsString.withDefault(defaultValue);
}
return parseAsString;
};
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.SORT_ORDER}`;
};