mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 12:30:06 +02:00
conditionally disable Subsonic list filters based on availability (#1567)
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { ChangeEvent, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { getItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { useGenreList } from '/@/renderer/features/genres/api/genres-api';
|
||||
import { GenreMultiSelectRow } from '/@/renderer/features/shared/components/multi-select-rows';
|
||||
import {
|
||||
ArtistMultiSelectRow,
|
||||
GenreMultiSelectRow,
|
||||
} from '/@/renderer/features/shared/components/multi-select-rows';
|
||||
import { useSongListFilters } from '/@/renderer/features/songs/hooks/use-song-list-filters';
|
||||
import { useCurrentServerId } from '/@/renderer/store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
@@ -12,10 +19,12 @@ import { VirtualMultiSelect } from '/@/shared/components/multi-select/virtual-mu
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { AlbumArtistListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-types';
|
||||
|
||||
export const SubsonicSongFilters = () => {
|
||||
const { t } = useTranslation();
|
||||
const { clear, query, setFavorite, setGenreId } = useSongListFilters();
|
||||
const serverId = useCurrentServerId();
|
||||
const { clear, query, setArtistIds, setFavorite, setGenreId } = useSongListFilters();
|
||||
|
||||
const { customFilters } = useListContext();
|
||||
|
||||
@@ -35,15 +44,75 @@ export const SubsonicSongFilters = () => {
|
||||
|
||||
const selectedGenreIds = useMemo(() => query.genreIds || [], [query.genreIds]);
|
||||
|
||||
const albumArtistListQuery = useSuspenseQuery(
|
||||
artistsQueries.albumArtistList({
|
||||
options: {
|
||||
gcTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
query: {
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
startIndex: 0,
|
||||
},
|
||||
serverId,
|
||||
}),
|
||||
);
|
||||
|
||||
const items = albumArtistListQuery?.data?.items;
|
||||
|
||||
const selectableAlbumArtists = useMemo(() => {
|
||||
if (!items) return [];
|
||||
|
||||
return items.map((artist) => ({
|
||||
albumCount: artist.albumCount,
|
||||
imageUrl: getItemImageUrl({
|
||||
id: artist.id,
|
||||
itemType: LibraryItem.ARTIST,
|
||||
type: 'table',
|
||||
}),
|
||||
label: artist.name,
|
||||
songCount: artist.songCount,
|
||||
value: artist.id,
|
||||
}));
|
||||
}, [items]);
|
||||
|
||||
const selectedArtistIds = useMemo(() => query.artistIds || [], [query.artistIds]);
|
||||
|
||||
const hasFavorite = query.favorite === true;
|
||||
const hasArtist = query.artistIds && query.artistIds.length > 0;
|
||||
const hasGenre = query.genreIds && query.genreIds.length > 0;
|
||||
|
||||
const isFavoriteDisabled = hasArtist || hasGenre;
|
||||
const isArtistDisabled = hasFavorite || hasGenre;
|
||||
const isGenreDisabled = hasFavorite || hasArtist;
|
||||
|
||||
const handleArtistFilter = useCallback(
|
||||
(e: null | string[]) => {
|
||||
if (isArtistDisabled && e !== null) return;
|
||||
setArtistIds(e ?? null);
|
||||
},
|
||||
[isArtistDisabled, setArtistIds],
|
||||
);
|
||||
|
||||
const artistFilterLabel = useMemo(() => {
|
||||
return (
|
||||
<Text fw={500} size="sm">
|
||||
{t('entity.artist', { count: 2, postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
);
|
||||
}, [t]);
|
||||
|
||||
const handleGenresFilter = useCallback(
|
||||
(e: null | string[]) => {
|
||||
if (isGenreDisabled && e !== null && e.length > 0) return;
|
||||
if (e && e.length > 0) {
|
||||
setGenreId([e[0]]);
|
||||
} else {
|
||||
setGenreId(null);
|
||||
}
|
||||
},
|
||||
[setGenreId],
|
||||
[isGenreDisabled, setGenreId],
|
||||
);
|
||||
|
||||
const genreFilterLabel = useMemo(() => {
|
||||
@@ -59,13 +128,14 @@ export const SubsonicSongFilters = () => {
|
||||
{
|
||||
label: t('filter.isFavorited', { postProcess: 'sentenceCase' }),
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (isFavoriteDisabled && e.target.checked) return;
|
||||
const favoriteValue = e.target.checked ? true : undefined;
|
||||
setFavorite(favoriteValue ?? null);
|
||||
},
|
||||
value: query.favorite,
|
||||
},
|
||||
],
|
||||
[t, query.favorite, setFavorite],
|
||||
[isFavoriteDisabled, query.favorite, setFavorite, t],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -73,13 +143,30 @@ export const SubsonicSongFilters = () => {
|
||||
{toggleFilters.map((filter) => (
|
||||
<Group justify="space-between" key={`ss-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<Switch checked={filter.value ?? false} onChange={filter.onChange} />
|
||||
<Switch
|
||||
checked={filter.value ?? false}
|
||||
disabled={isFavoriteDisabled}
|
||||
onChange={filter.onChange}
|
||||
/>
|
||||
</Group>
|
||||
))}
|
||||
<Divider my="md" />
|
||||
<VirtualMultiSelect
|
||||
disabled={isArtistDisabled}
|
||||
displayCountType="song"
|
||||
height={300}
|
||||
label={artistFilterLabel}
|
||||
onChange={handleArtistFilter}
|
||||
options={selectableAlbumArtists}
|
||||
RowComponent={ArtistMultiSelectRow}
|
||||
singleSelect={true}
|
||||
value={selectedArtistIds}
|
||||
/>
|
||||
{!isGenrePage && (
|
||||
<>
|
||||
<Divider my="md" />
|
||||
<VirtualMultiSelect
|
||||
disabled={isGenreDisabled}
|
||||
displayCountType="song"
|
||||
height={220}
|
||||
label={genreFilterLabel}
|
||||
|
||||
Reference in New Issue
Block a user