mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-18 01:14:22 +02:00
Initial work: support showing studios for jellyfin, allow pill to be clickable (#1566)
This commit is contained in:
@@ -49,6 +49,7 @@ import {
|
||||
AlbumListSort,
|
||||
ExplicitStatus,
|
||||
LibraryItem,
|
||||
ServerType,
|
||||
Song,
|
||||
SongListSort,
|
||||
SortOrder,
|
||||
@@ -152,8 +153,15 @@ const AlbumMetadataTags = ({ album }: AlbumMetadataTagsProps) => {
|
||||
if (!album?.recordLabels || album.recordLabels.length === 0) return [];
|
||||
|
||||
return album.recordLabels.map((label) => {
|
||||
if (album._serverType === ServerType.SUBSONIC) {
|
||||
return { id: label, label: label, url: null };
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams();
|
||||
const customFilters = { recordlabel: [label] };
|
||||
const customFilters =
|
||||
album._serverType === ServerType.JELLYFIN
|
||||
? { Studios: [label] }
|
||||
: { recordlabel: [label] };
|
||||
const paramsWithCustom = setJsonSearchParam(
|
||||
searchParams,
|
||||
FILTER_KEYS.ALBUM._CUSTOM,
|
||||
@@ -183,15 +191,21 @@ const AlbumMetadataTags = ({ album }: AlbumMetadataTagsProps) => {
|
||||
</Text>
|
||||
<div className={styles['pill-group-wrapper']}>
|
||||
<Pill.Group>
|
||||
{recordLabels.map((recordLabel) => (
|
||||
<PillLink
|
||||
key={`recordlabel-${recordLabel.id}`}
|
||||
size="md"
|
||||
to={recordLabel.url}
|
||||
>
|
||||
{recordLabel.label}
|
||||
</PillLink>
|
||||
))}
|
||||
{recordLabels.map((recordLabel) =>
|
||||
recordLabel.url ? (
|
||||
<PillLink
|
||||
key={`recordlabel-${recordLabel.id}`}
|
||||
size="md"
|
||||
to={recordLabel.url}
|
||||
>
|
||||
{recordLabel.label}
|
||||
</PillLink>
|
||||
) : (
|
||||
<Pill key={`recordlabel-${recordLabel.id}`} size="md">
|
||||
{recordLabel.label}
|
||||
</Pill>
|
||||
),
|
||||
)}
|
||||
</Pill.Group>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
@@ -3,16 +3,15 @@ import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { getItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||
import { MultiSelectWithInvalidData } from '/@/renderer/components/select-with-invalid-data';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { useAlbumListFilters } from '/@/renderer/features/albums/hooks/use-album-list-filters';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { genresQueries } from '/@/renderer/features/genres/api/genres-api';
|
||||
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
|
||||
import {
|
||||
ArtistMultiSelectRow,
|
||||
GenreMultiSelectRow,
|
||||
} from '/@/renderer/features/shared/components/multi-select-rows';
|
||||
import { TagFilters } from '/@/renderer/features/shared/components/tag-filter';
|
||||
import { useCurrentServerId } from '/@/renderer/store';
|
||||
import { useAppStore, useAppStoreActions } from '/@/renderer/store/app.store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
@@ -78,19 +77,6 @@ export const JellyfinAlbumFilters = ({ disableArtistFilter }: JellyfinAlbumFilte
|
||||
}));
|
||||
}, [genreListQuery.data]);
|
||||
|
||||
const tagsQuery = useQuery(
|
||||
sharedQueries.tagList({
|
||||
options: {
|
||||
gcTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
query: {
|
||||
type: LibraryItem.ALBUM,
|
||||
},
|
||||
serverId,
|
||||
}),
|
||||
);
|
||||
|
||||
const yesNoFilter = useMemo(() => {
|
||||
const filters = [
|
||||
{
|
||||
@@ -204,13 +190,6 @@ export const JellyfinAlbumFilters = ({ disableArtistFilter }: JellyfinAlbumFilte
|
||||
[setAlbumArtist],
|
||||
);
|
||||
|
||||
const handleTagFilter = useCallback(
|
||||
(e: null | string[]) => {
|
||||
setCustom({ Tags: e && e.length > 0 ? e.join('|') : null });
|
||||
},
|
||||
[setCustom],
|
||||
);
|
||||
|
||||
const debouncedHandleMinYearFilter = useDebouncedCallback(handleMinYearFilter, 300);
|
||||
const debouncedHandleMaxYearFilter = useDebouncedCallback(handleMaxYearFilter, 300);
|
||||
|
||||
@@ -358,17 +337,7 @@ export const JellyfinAlbumFilters = ({ disableArtistFilter }: JellyfinAlbumFilte
|
||||
value={query.maxYear ?? undefined}
|
||||
/>
|
||||
</Group>
|
||||
{tagsQuery.data?.boolTags && tagsQuery.data.boolTags.length > 0 && (
|
||||
<MultiSelectWithInvalidData
|
||||
clearable
|
||||
data={tagsQuery.data.boolTags}
|
||||
label={t('common.tags', { postProcess: 'sentenceCase' })}
|
||||
onChange={handleTagFilter}
|
||||
searchable
|
||||
value={query._custom?.[tagsQuery.data.boolTags.join('|')] || []}
|
||||
width={250}
|
||||
/>
|
||||
)}
|
||||
<TagFilters query={query} setCustom={setCustom} type={LibraryItem.ALBUM} />
|
||||
<Divider my="md" />
|
||||
<Button fullWidth onClick={clear} variant="subtle">
|
||||
{t('common.reset', { postProcess: 'sentenceCase' })}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { useQuery } 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 { MultiSelectWithInvalidData } from '/@/renderer/components/select-with-invalid-data';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { useAlbumListFilters } from '/@/renderer/features/albums/hooks/use-album-list-filters';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { useGenreList } from '/@/renderer/features/genres/api/genres-api';
|
||||
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
|
||||
import {
|
||||
ArtistMultiSelectRow,
|
||||
GenreMultiSelectRow,
|
||||
} from '/@/renderer/features/shared/components/multi-select-rows';
|
||||
import { useCurrentServer, useCurrentServerId } from '/@/renderer/store';
|
||||
import { TagFilters } from '/@/renderer/features/shared/components/tag-filter';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { useAppStore, useAppStoreActions } from '/@/renderer/store/app.store';
|
||||
import { titleCase } from '/@/renderer/utils';
|
||||
import { NDSongQueryFieldsLabelMap } from '/@/shared/api/navidrome/navidrome-types';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
@@ -50,6 +47,7 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
|
||||
query,
|
||||
setAlbumArtist,
|
||||
setCompilation,
|
||||
setCustom,
|
||||
setFavorite,
|
||||
setGenreId,
|
||||
setHasRating,
|
||||
@@ -355,8 +353,7 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
|
||||
onChange={(e) => debouncedHandleYearFilter(e)}
|
||||
value={query.minYear ?? undefined}
|
||||
/>
|
||||
<Divider my="md" />
|
||||
<TagFilters />
|
||||
<TagFilters query={query} setCustom={setCustom} type={LibraryItem.ALBUM} />
|
||||
<Divider my="md" />
|
||||
<Button fullWidth onClick={clear} variant="subtle">
|
||||
{t('common.reset', { postProcess: 'sentenceCase' })}
|
||||
@@ -364,111 +361,3 @@ export const NavidromeAlbumFilters = ({ disableArtistFilter }: NavidromeAlbumFil
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
interface TagFilterItemProps {
|
||||
label: string;
|
||||
onChange: (value: null | string[]) => void;
|
||||
options: Array<{ id: string; name: string }>;
|
||||
tagValue: string;
|
||||
value: string | string[] | undefined;
|
||||
}
|
||||
|
||||
const TagFilterItem = ({ label, onChange, options, tagValue, value }: TagFilterItemProps) => {
|
||||
const selectData = useMemo(
|
||||
() =>
|
||||
options.map((option) => ({
|
||||
label: option.name,
|
||||
value: option.id,
|
||||
})),
|
||||
[options],
|
||||
);
|
||||
|
||||
const currentValue = useMemo(() => {
|
||||
if (!value) return [];
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}, [value]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: null | string[]) => {
|
||||
if (e && e.length > 0) {
|
||||
onChange(e);
|
||||
} else {
|
||||
onChange(null);
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelectWithInvalidData
|
||||
clearable
|
||||
data={selectData}
|
||||
key={tagValue}
|
||||
label={label}
|
||||
limit={100}
|
||||
onChange={handleChange}
|
||||
searchable
|
||||
value={currentValue}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
TagFilterItem.displayName = 'TagFilterItem';
|
||||
|
||||
const TagFilters = () => {
|
||||
const { query, setCustom } = useAlbumListFilters();
|
||||
|
||||
const serverId = useCurrentServerId();
|
||||
|
||||
const tagsQuery = useSuspenseQuery(
|
||||
sharedQueries.tagList({
|
||||
options: {
|
||||
gcTime: 1000 * 60 * 60,
|
||||
staleTime: 1000 * 60 * 60,
|
||||
},
|
||||
query: {
|
||||
type: LibraryItem.ALBUM,
|
||||
},
|
||||
serverId,
|
||||
}),
|
||||
);
|
||||
|
||||
const handleTagFilter = useMemo(
|
||||
() => (tag: string, e: null | string[]) => {
|
||||
setCustom({ [tag]: e });
|
||||
},
|
||||
[setCustom],
|
||||
);
|
||||
|
||||
const tags = useMemo(() => {
|
||||
const results: { label: string; options: { id: string; name: string }[]; value: string }[] =
|
||||
[];
|
||||
|
||||
for (const tag of tagsQuery.data?.enumTags || []) {
|
||||
if (!tagsQuery.data?.excluded.album.includes(tag.name)) {
|
||||
results.push({
|
||||
label: NDSongQueryFieldsLabelMap[tag.name] ?? titleCase(tag.name),
|
||||
options: tag.options,
|
||||
value: tag.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}, [tagsQuery.data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{tags.map((tag) => (
|
||||
<TagFilterItem
|
||||
key={tag.value}
|
||||
label={tag.label}
|
||||
onChange={(e) => handleTagFilter(tag.value, e)}
|
||||
options={tag.options}
|
||||
tagValue={tag.value}
|
||||
value={query._custom?.[tag.value] as string | string[] | undefined}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user