mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
update shared filter components
This commit is contained in:
@@ -127,7 +127,8 @@
|
||||
"year": "year",
|
||||
"yes": "yes",
|
||||
"explicit": "explicit",
|
||||
"clean": "clean"
|
||||
"clean": "clean",
|
||||
"tableColumns": "table columns"
|
||||
},
|
||||
"entity": {
|
||||
"album_one": "album",
|
||||
@@ -826,13 +827,33 @@
|
||||
"config": {
|
||||
"general": {
|
||||
"autoFitColumns": "auto fit columns",
|
||||
"autosize": "autosize",
|
||||
"moveUp": "move up",
|
||||
"moveDown": "move down",
|
||||
"pinToLeft": "pin to left",
|
||||
"pinToRight": "pin to right",
|
||||
"alignLeft": "align left",
|
||||
"alignCenter": "align center",
|
||||
"alignRight": "align right",
|
||||
"followCurrentSong": "follow current song",
|
||||
"displayType": "display type",
|
||||
"gap": "$t(common.gap)",
|
||||
"itemGap": "item gap (px)",
|
||||
"itemSize": "item size (px)",
|
||||
"itemsPerRow": "items per row",
|
||||
"size": "$t(common.size)",
|
||||
"tableColumns": "table columns"
|
||||
"size_default": "default",
|
||||
"size_compact": "compact",
|
||||
"size_large": "large",
|
||||
"tableColumns": "table columns",
|
||||
"pagination": "pagination",
|
||||
"pagination_itemsPerPage": "items per page",
|
||||
"pagination_infinite": "infinite",
|
||||
"pagination_paginate": "paginated",
|
||||
"alternateRowColors": "alternate row colors",
|
||||
"horizontalBorders": "horizontal borders",
|
||||
"rowHoverHighlight": "row hover highlight",
|
||||
"verticalBorders": "vertical borders"
|
||||
},
|
||||
"label": {
|
||||
"actions": "$t(common.action_other)",
|
||||
@@ -849,6 +870,8 @@
|
||||
"duration": "$t(common.duration)",
|
||||
"favorite": "$t(common.favorite)",
|
||||
"genre": "$t(entity.genre_one)",
|
||||
"genreBadge": "$t(entity.genre_one) (badges)",
|
||||
"image": "image",
|
||||
"lastPlayed": "last played",
|
||||
"note": "$t(common.note)",
|
||||
"owner": "$t(common.owner)",
|
||||
@@ -865,10 +888,8 @@
|
||||
"year": "$t(common.year)"
|
||||
},
|
||||
"view": {
|
||||
"card": "card",
|
||||
"grid": "grid",
|
||||
"list": "list",
|
||||
"poster": "poster",
|
||||
"table": "table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
.filter-bar {
|
||||
z-index: 1;
|
||||
padding: var(--theme-spacing-md) var(--theme-spacing-sm);
|
||||
|
||||
@mixin dark {
|
||||
box-shadow: 0 5px 15px rgb(0 0 0 / 65%);
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
box-shadow: 0 2px 0 var(--theme-colors-border);
|
||||
}
|
||||
padding: var(--theme-spacing-sm);
|
||||
border-bottom: 1px solid var(--theme-colors-border);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export const FolderButton = ({ isActive, ...props }: FolderButtonProps) => {
|
||||
<ActionIcon
|
||||
icon="folder"
|
||||
iconProps={{
|
||||
fill: isActive ? 'primary' : undefined,
|
||||
color: isActive ? 'primary' : undefined,
|
||||
size: 'lg',
|
||||
...props.iconProps,
|
||||
}}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { ListConfigTable } from '/@/renderer/features/shared/components/list-config-menu';
|
||||
import {
|
||||
DataGridProps,
|
||||
DataListProps,
|
||||
ItemListSettings,
|
||||
useSettingsStore,
|
||||
useSettingsStoreActions,
|
||||
} from '/@/renderer/store';
|
||||
@@ -28,8 +28,8 @@ type GridConfigProps = {
|
||||
export const GridConfig = ({ extraOptions, listKey }: GridConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const list = useSettingsStore((state) => state.lists[listKey]) as DataListProps;
|
||||
const grid = useSettingsStore((state) => state.lists[listKey].grid) as DataGridProps;
|
||||
const list = useSettingsStore((state) => state.lists[listKey]) as ItemListSettings;
|
||||
const grid = list.grid as DataGridProps;
|
||||
const { setList } = useSettingsStoreActions();
|
||||
|
||||
const options = useMemo(() => {
|
||||
@@ -182,6 +182,7 @@ export const GridConfig = ({ extraOptions, listKey }: GridConfigProps) => {
|
||||
grid: { itemsPerRowEnabled: e.target.checked },
|
||||
})
|
||||
}
|
||||
pr="md"
|
||||
size="xs"
|
||||
/>
|
||||
</Group>
|
||||
@@ -192,9 +193,5 @@ export const GridConfig = ({ extraOptions, listKey }: GridConfigProps) => {
|
||||
];
|
||||
}, [list, t, grid, extraOptions, setList, listKey]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListConfigTable options={options} />
|
||||
</>
|
||||
);
|
||||
return <ListConfigTable options={options} />;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { JellyfinAlbumFilters } from '/@/renderer/features/albums/components/jellyfin-album-filters';
|
||||
import { NavidromeAlbumFilters } from '/@/renderer/features/albums/components/navidrome-album-filters';
|
||||
import { SubsonicAlbumFilters } from '/@/renderer/features/albums/components/subsonic-album-filters';
|
||||
import { FilterButton } from '/@/renderer/features/shared/components/filter-button';
|
||||
import { JellyfinSongFilters } from '/@/renderer/features/songs/components/jellyfin-song-filters';
|
||||
import { NavidromeSongFilters } from '/@/renderer/features/songs/components/navidrome-song-filters';
|
||||
import { SubsonicSongFilters } from '/@/renderer/features/songs/components/subsonic-song-filter';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { Modal } from '/@/shared/components/modal/modal';
|
||||
import { useDisclosure } from '/@/shared/hooks/use-disclosure';
|
||||
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
|
||||
|
||||
interface ListFiltersProps {
|
||||
isActive?: boolean;
|
||||
itemType: LibraryItem;
|
||||
}
|
||||
|
||||
export const ListFilters = ({ isActive, itemType }: ListFiltersProps) => {
|
||||
const { t } = useTranslation();
|
||||
const server = useCurrentServer();
|
||||
|
||||
const serverType = server.type;
|
||||
|
||||
const FilterComponent = FILTERS[serverType][itemType];
|
||||
|
||||
const [isOpen, handlers] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterButton isActive={isActive} onClick={handlers.toggle} />
|
||||
<Modal handlers={handlers} opened={isOpen} title={t('common.filters')}>
|
||||
<FilterComponent />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const FILTERS = {
|
||||
[ServerType.JELLYFIN]: {
|
||||
[LibraryItem.ALBUM]: JellyfinAlbumFilters,
|
||||
[LibraryItem.SONG]: JellyfinSongFilters,
|
||||
},
|
||||
[ServerType.NAVIDROME]: {
|
||||
[LibraryItem.ALBUM]: NavidromeAlbumFilters,
|
||||
[LibraryItem.SONG]: NavidromeSongFilters,
|
||||
},
|
||||
[ServerType.SUBSONIC]: {
|
||||
[LibraryItem.ALBUM]: SubsonicAlbumFilters,
|
||||
[LibraryItem.SONG]: SubsonicSongFilters,
|
||||
},
|
||||
};
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
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 { 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;
|
||||
@@ -20,13 +21,10 @@ export const ListMusicFolderDropdown = ({ listKey }: ListMusicFolderDropdownProp
|
||||
|
||||
const [persisted, setPersisted] = useLocalStorage({
|
||||
defaultValue: '',
|
||||
key: getPersistenceKey(listKey),
|
||||
key: getPersistenceKey(server.id, listKey),
|
||||
});
|
||||
|
||||
const [musicFolderId, setMusicFolderId] = useQueryState(
|
||||
getPersistenceKey(listKey),
|
||||
parseAsString.withDefault(persisted || ''),
|
||||
);
|
||||
const { musicFolderId, setMusicFolderId } = useMusicFolderIdFilter(persisted);
|
||||
|
||||
const handleSetMusicFolder = (e: string) => {
|
||||
if (e === musicFolderId) {
|
||||
@@ -60,6 +58,6 @@ export const ListMusicFolderDropdown = ({ listKey }: ListMusicFolderDropdownProp
|
||||
);
|
||||
};
|
||||
|
||||
const getPersistenceKey = (listKey: ItemListKey) => {
|
||||
return `list-${listKey}-musicFolder`;
|
||||
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
|
||||
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.MUSIC_FOLDER_ID}`;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
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, ServerType, SortOrder } from '/@/shared/types/domain-types';
|
||||
import {
|
||||
AlbumListSort,
|
||||
LibraryItem,
|
||||
ServerType,
|
||||
SongListSort,
|
||||
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;
|
||||
@@ -28,13 +33,10 @@ export const ListSortByDropdown = ({
|
||||
|
||||
const [persisted, setPersisted] = useLocalStorage({
|
||||
defaultValue: defaultSortByValue,
|
||||
key: getPersistenceKey(listKey),
|
||||
key: getPersistenceKey(server.id, listKey),
|
||||
});
|
||||
|
||||
const [sortBy, setSortBy] = useQueryState(
|
||||
FILTER_KEYS.SORT_BY,
|
||||
parseAsString.withDefault(persisted || defaultSortByValue),
|
||||
);
|
||||
const { sortBy, setSortBy } = useSortByFilter(persisted || defaultSortByValue);
|
||||
|
||||
const sortByLabel =
|
||||
(itemType && FILTERS[itemType][server.type].find((f) => f.value === sortBy)?.name) || '—';
|
||||
@@ -217,10 +219,157 @@ const ALBUM_LIST_FILTERS: Partial<
|
||||
],
|
||||
};
|
||||
|
||||
const FILTERS: Partial<Record<LibraryItem, any>> = {
|
||||
[LibraryItem.ALBUM]: ALBUM_LIST_FILTERS,
|
||||
const SONG_LIST_FILTERS: Partial<
|
||||
Record<ServerType, Array<{ defaultOrder: SortOrder; name: string; value: string }>>
|
||||
> = {
|
||||
[ServerType.JELLYFIN]: [
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.album', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.ALBUM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.albumArtist', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.ALBUM_ARTIST,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.artist', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.ARTIST,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.DURATION,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.playCount', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.PLAY_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RANDOM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RECENTLY_ADDED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.recentlyPlayed', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RECENTLY_PLAYED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.releaseDate', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RELEASE_DATE,
|
||||
},
|
||||
],
|
||||
[ServerType.NAVIDROME]: [
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.album', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.ALBUM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.albumArtist', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.ALBUM_ARTIST,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.artist', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.ARTIST,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.bpm', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.BPM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('common.channel', { count: 2, postProcess: 'titleCase' }),
|
||||
value: SongListSort.CHANNELS,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.comment', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.COMMENT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.DURATION,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.FAVORITED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.genre', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.GENRE,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.playCount', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.PLAY_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RANDOM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RATING,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RECENTLY_ADDED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.recentlyPlayed', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.RECENTLY_PLAYED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.releaseYear', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.YEAR,
|
||||
},
|
||||
],
|
||||
[ServerType.SUBSONIC]: [
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: SongListSort.NAME,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const getPersistenceKey = (listKey: ItemListKey) => {
|
||||
return `item_list_${listKey}-${FILTER_KEYS.SORT_BY}`;
|
||||
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,7 +1,7 @@
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
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';
|
||||
@@ -11,15 +11,14 @@ interface ListSortOrderToggleButtonProps {
|
||||
}
|
||||
|
||||
export const ListSortOrderToggleButton = ({ listKey }: ListSortOrderToggleButtonProps) => {
|
||||
const server = useCurrentServer();
|
||||
|
||||
const [persisted, setPersisted] = useLocalStorage({
|
||||
defaultValue: SortOrder.ASC,
|
||||
key: getPersistenceKey(listKey),
|
||||
key: getPersistenceKey(server.id, listKey),
|
||||
});
|
||||
|
||||
const [sortOrder, setSortOrder] = useQueryState(
|
||||
FILTER_KEYS.SORT_ORDER,
|
||||
parseAsString.withDefault(persisted || SortOrder.ASC),
|
||||
);
|
||||
const { sortOrder, setSortOrder } = useSortOrderFilter(persisted || SortOrder.ASC);
|
||||
|
||||
const handleToggleSortOrder = () => {
|
||||
const newSortOrder = sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||
@@ -32,6 +31,6 @@ export const ListSortOrderToggleButton = ({ listKey }: ListSortOrderToggleButton
|
||||
);
|
||||
};
|
||||
|
||||
const getPersistenceKey = (listKey: ItemListKey) => {
|
||||
return `item_list_${listKey}-${FILTER_KEYS.SORT_ORDER}`;
|
||||
const getPersistenceKey = (serverId: string, listKey: ItemListKey) => {
|
||||
return `${serverId}-list-${listKey}-${FILTER_KEYS.SHARED.SORT_ORDER}`;
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
ListConfigBooleanControl,
|
||||
ListConfigTable,
|
||||
} from '/@/renderer/features/shared/components/list-config-menu';
|
||||
import { DataListProps, useSettingsStore, useSettingsStoreActions } from '/@/renderer/store';
|
||||
import { ItemListSettings, useSettingsStore, useSettingsStoreActions } from '/@/renderer/store';
|
||||
import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Badge } from '/@/shared/components/badge/badge';
|
||||
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
||||
@@ -40,7 +40,7 @@ interface TableConfigProps {
|
||||
export const TableConfig = ({ extraOptions, listKey, tableColumnsData }: TableConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const list = useSettingsStore((state) => state.lists[listKey]) as DataListProps;
|
||||
const list = useSettingsStore((state) => state.lists[listKey]) as ItemListSettings;
|
||||
const { setList } = useSettingsStoreActions();
|
||||
|
||||
const options = useMemo(() => {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
|
||||
export const useMusicFolderIdFilter = (defaultValue?: string) => {
|
||||
const [musicFolderId, setMusicFolderId] = useQueryState(
|
||||
FILTER_KEYS.SHARED.MUSIC_FOLDER_ID,
|
||||
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
|
||||
);
|
||||
|
||||
return {
|
||||
[FILTER_KEYS.SHARED.MUSIC_FOLDER_ID]: musicFolderId ?? undefined,
|
||||
setMusicFolderId,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
|
||||
export const useSearchTermFilter = (defaultValue?: string) => {
|
||||
const [searchTerm, setSearchTerm] = useQueryState(
|
||||
FILTER_KEYS.SHARED.SEARCH_TERM,
|
||||
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
|
||||
);
|
||||
|
||||
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
||||
|
||||
return {
|
||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: debouncedSearchTerm ?? undefined,
|
||||
rawSearchTerm: searchTerm ?? undefined,
|
||||
setSearchTerm,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
|
||||
export const useSortByFilter = <TSortBy>(defaultValue?: string) => {
|
||||
const [sortBy, setSortBy] = useQueryState(
|
||||
FILTER_KEYS.SHARED.SORT_BY,
|
||||
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
|
||||
);
|
||||
|
||||
return {
|
||||
[FILTER_KEYS.SHARED.SORT_BY]: (sortBy as TSortBy) ?? undefined,
|
||||
setSortBy,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
import { SortOrder } from '/@/shared/types/domain-types';
|
||||
|
||||
export const useSortOrderFilter = (defaultValue?: string) => {
|
||||
const [sortOrder, setSortOrder] = useQueryState(
|
||||
FILTER_KEYS.SHARED.SORT_ORDER,
|
||||
defaultValue ? parseAsString.withDefault(defaultValue) : parseAsString,
|
||||
);
|
||||
|
||||
return {
|
||||
[FILTER_KEYS.SHARED.SORT_ORDER]: (sortOrder as SortOrder) ?? undefined,
|
||||
setSortOrder,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
import z from 'zod';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
|
||||
@@ -20,9 +22,35 @@ export const PLAY_TYPES = [
|
||||
},
|
||||
];
|
||||
|
||||
export const FILTER_KEYS = {
|
||||
export const customFiltersSchema = z.record(z.string(), z.any());
|
||||
|
||||
enum AlbumFilterKeys {
|
||||
_CUSTOM = '_custom',
|
||||
ARTIST_IDS = 'artistIds',
|
||||
COMPILATION = 'compilation',
|
||||
FAVORITE = 'favorite',
|
||||
GENRE_ID = 'genreId',
|
||||
GENRES = 'genres',
|
||||
HAS_RATING = 'hasRating',
|
||||
MAX_YEAR = 'maxYear',
|
||||
MIN_YEAR = 'minYear',
|
||||
RECENTLY_PLAYED = 'recentlyPlayed',
|
||||
}
|
||||
|
||||
enum SharedFilterKeys {
|
||||
MUSIC_FOLDER_ID = 'musicFolderId',
|
||||
SEARCH_TERM = 'searchTerm',
|
||||
SORT_BY = 'sortBy',
|
||||
SORT_ORDER = 'sortOrder',
|
||||
}
|
||||
|
||||
const PaginationFilterKeys = {
|
||||
CURRENT_PAGE: 'currentPage',
|
||||
SCROLL_OFFSET: 'scrollOffset',
|
||||
SORT_BY: 'sortBy',
|
||||
SORT_ORDER: 'sortOrder',
|
||||
};
|
||||
|
||||
export const FILTER_KEYS = {
|
||||
ALBUM: AlbumFilterKeys,
|
||||
PAGINATION: PaginationFilterKeys,
|
||||
SHARED: SharedFilterKeys,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user