mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
Refactor all api instances in components
This commit is contained in:
@@ -1,13 +1,5 @@
|
||||
import { MutableRefObject, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Button,
|
||||
getColumnDefs,
|
||||
GridCarousel,
|
||||
Text,
|
||||
TextTitle,
|
||||
useFixedTableHeader,
|
||||
VirtualTable,
|
||||
} from '/@/renderer/components';
|
||||
import { Button, GridCarousel, Text, TextTitle } from '/@/renderer/components';
|
||||
import { ColDef, RowDoubleClickedEvent, RowHeightParams, RowNode } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Box, Group, Stack } from '@mantine/core';
|
||||
@@ -33,6 +25,12 @@ import { PlayButton, useCreateFavorite, useDeleteFavorite } from '/@/renderer/fe
|
||||
import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query';
|
||||
import { AlbumListSort, LibraryItem, QueueSong, SortOrder } from '/@/renderer/api/types';
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import {
|
||||
getColumnDefs,
|
||||
useFixedTableHeader,
|
||||
VirtualTable,
|
||||
} from '/@/renderer/components/virtual-table';
|
||||
|
||||
const isFullWidthRow = (node: RowNode) => {
|
||||
return node.id?.includes('disc-');
|
||||
@@ -60,7 +58,8 @@ interface AlbumDetailContentProps {
|
||||
|
||||
export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
|
||||
const { albumId } = useParams() as { albumId: string };
|
||||
const detailQuery = useAlbumDetail({ id: albumId });
|
||||
const server = useCurrentServer();
|
||||
const detailQuery = useAlbumDetail({ query: { id: albumId }, serverId: server?.id });
|
||||
const cq = useContainerQuery();
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
|
||||
@@ -165,26 +164,29 @@ export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
|
||||
|
||||
const itemsPerPage = cq.isXl ? 9 : cq.isLg ? 7 : cq.isMd ? 5 : cq.isSm ? 4 : 3;
|
||||
|
||||
const artistQuery = useAlbumList(
|
||||
{
|
||||
jfParams: {
|
||||
albumArtistIds: detailQuery?.data?.albumArtists[0]?.id,
|
||||
},
|
||||
limit: itemsPerPage,
|
||||
ndParams: {
|
||||
artist_id: detailQuery?.data?.albumArtists[0]?.id,
|
||||
},
|
||||
sortBy: AlbumListSort.YEAR,
|
||||
sortOrder: SortOrder.DESC,
|
||||
startIndex: pagination.artist * itemsPerPage,
|
||||
},
|
||||
{
|
||||
const artistQuery = useAlbumList({
|
||||
options: {
|
||||
cacheTime: 1000 * 60,
|
||||
enabled: detailQuery?.data?.albumArtists[0]?.id !== undefined,
|
||||
keepPreviousData: true,
|
||||
staleTime: 1000 * 60,
|
||||
},
|
||||
);
|
||||
query: {
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
albumArtistIds: detailQuery?.data?.albumArtists[0]?.id,
|
||||
},
|
||||
navidrome: {
|
||||
artist_id: detailQuery?.data?.albumArtists[0]?.id,
|
||||
},
|
||||
},
|
||||
limit: itemsPerPage,
|
||||
sortBy: AlbumListSort.YEAR,
|
||||
sortOrder: SortOrder.DESC,
|
||||
startIndex: pagination.artist * itemsPerPage,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
|
||||
const carousels = [
|
||||
{
|
||||
@@ -227,8 +229,8 @@ export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const createFavoriteMutation = useCreateFavorite();
|
||||
const deleteFavoriteMutation = useDeleteFavorite();
|
||||
const createFavoriteMutation = useCreateFavorite({});
|
||||
const deleteFavoriteMutation = useDeleteFavorite({});
|
||||
|
||||
const handleFavorite = () => {
|
||||
if (!detailQuery?.data) return;
|
||||
|
||||
@@ -5,9 +5,10 @@ import { Link } from 'react-router-dom';
|
||||
import { LibraryItem, ServerType } from '/@/renderer/api/types';
|
||||
import { Button, Rating, Text } from '/@/renderer/components';
|
||||
import { useAlbumDetail } from '/@/renderer/features/albums/queries/album-detail-query';
|
||||
import { LibraryHeader, useUpdateRating } from '/@/renderer/features/shared';
|
||||
import { LibraryHeader, useSetRating } from '/@/renderer/features/shared';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { formatDurationString } from '/@/renderer/utils';
|
||||
|
||||
interface AlbumDetailHeaderProps {
|
||||
@@ -17,7 +18,8 @@ interface AlbumDetailHeaderProps {
|
||||
export const AlbumDetailHeader = forwardRef(
|
||||
({ background }: AlbumDetailHeaderProps, ref: Ref<HTMLDivElement>) => {
|
||||
const { albumId } = useParams() as { albumId: string };
|
||||
const detailQuery = useAlbumDetail({ id: albumId });
|
||||
const server = useCurrentServer();
|
||||
const detailQuery = useAlbumDetail({ query: { id: albumId }, serverId: server?.id });
|
||||
const cq = useContainerQuery();
|
||||
|
||||
const metadataItems = [
|
||||
@@ -38,17 +40,17 @@ export const AlbumDetailHeader = forwardRef(
|
||||
},
|
||||
];
|
||||
|
||||
const updateRatingMutation = useUpdateRating();
|
||||
const updateRatingMutation = useSetRating({});
|
||||
|
||||
const handleUpdateRating = (rating: number) => {
|
||||
if (!detailQuery?.data) return;
|
||||
|
||||
updateRatingMutation.mutate({
|
||||
_serverId: detailQuery?.data.serverId,
|
||||
query: {
|
||||
item: [detailQuery.data],
|
||||
rating,
|
||||
},
|
||||
serverId: detailQuery.data.serverId,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -56,11 +58,11 @@ export const AlbumDetailHeader = forwardRef(
|
||||
if (!detailQuery?.data || !detailQuery?.data.userRating) return;
|
||||
|
||||
updateRatingMutation.mutate({
|
||||
_serverId: detailQuery.data.serverId,
|
||||
query: {
|
||||
item: [detailQuery.data],
|
||||
rating: 0,
|
||||
},
|
||||
serverId: detailQuery.data.serverId,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import {
|
||||
ALBUM_CARD_ROWS,
|
||||
getColumnDefs,
|
||||
TablePagination,
|
||||
VirtualGridAutoSizerContainer,
|
||||
VirtualInfiniteGrid,
|
||||
VirtualInfiniteGridRef,
|
||||
VirtualTable,
|
||||
} from '/@/renderer/components';
|
||||
import { ALBUM_CARD_ROWS } from '/@/renderer/components';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { ListDisplayType, CardRow } from '/@/renderer/types';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
@@ -40,6 +32,12 @@ import { generatePath, useNavigate } from 'react-router';
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
|
||||
import { useAlbumListContext } from '/@/renderer/features/albums/context/album-list-context';
|
||||
import {
|
||||
VirtualInfiniteGridRef,
|
||||
VirtualGridAutoSizerContainer,
|
||||
VirtualInfiniteGrid,
|
||||
} from '/@/renderer/components/virtual-grid';
|
||||
import { getColumnDefs, VirtualTable, TablePagination } from '/@/renderer/components/virtual-table';
|
||||
|
||||
interface AlbumListContentProps {
|
||||
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
||||
@@ -71,29 +69,36 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||
limit,
|
||||
startIndex,
|
||||
...filter,
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||
|
||||
if (!server) {
|
||||
return params.failCallback();
|
||||
}
|
||||
|
||||
const albumsRes = await queryClient.fetchQuery(
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
api.controller.getAlbumList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
server,
|
||||
signal,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
const albums = api.normalize.albumList(albumsRes, server);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
return params.successCallback(albumsRes?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
@@ -165,15 +170,21 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||
|
||||
const fetch = useCallback(
|
||||
async ({ skip, take }: { skip: number; take: number }) => {
|
||||
if (!server) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const query: AlbumListQuery = {
|
||||
limit: take,
|
||||
startIndex: skip,
|
||||
...filter,
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -181,13 +192,15 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||
|
||||
const albums = await queryClient.fetchQuery(queryKey, async ({ signal }) =>
|
||||
controller.getAlbumList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
server,
|
||||
signal,
|
||||
}),
|
||||
);
|
||||
|
||||
return api.normalize.albumList(albums, server);
|
||||
return albums;
|
||||
},
|
||||
[filter, queryClient, server],
|
||||
);
|
||||
@@ -268,8 +281,8 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||
navigate(generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: e.data.id }));
|
||||
};
|
||||
|
||||
const createFavoriteMutation = useCreateFavorite();
|
||||
const deleteFavoriteMutation = useDeleteFavorite();
|
||||
const createFavoriteMutation = useCreateFavorite({});
|
||||
const deleteFavoriteMutation = useDeleteFavorite({});
|
||||
|
||||
const handleFavorite = (options: {
|
||||
id: string[];
|
||||
|
||||
@@ -20,16 +20,7 @@ import {
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { AlbumListQuery, AlbumListSort, LibraryItem, SortOrder } from '/@/renderer/api/types';
|
||||
import {
|
||||
ALBUM_TABLE_COLUMNS,
|
||||
Button,
|
||||
DropdownMenu,
|
||||
MultiSelect,
|
||||
Slider,
|
||||
Switch,
|
||||
Text,
|
||||
VirtualInfiniteGridRef,
|
||||
} from '/@/renderer/components';
|
||||
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import {
|
||||
AlbumListFilter,
|
||||
@@ -43,6 +34,8 @@ import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { JellyfinAlbumFilters } from '/@/renderer/features/albums/components/jellyfin-album-filters';
|
||||
import { NavidromeAlbumFilters } from '/@/renderer/features/albums/components/navidrome-album-filters';
|
||||
import { useAlbumListContext } from '/@/renderer/features/albums/context/album-list-context';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
||||
import { ALBUM_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||
|
||||
const FILTERS = {
|
||||
jellyfin: [
|
||||
@@ -100,7 +93,7 @@ export const AlbumListHeaderFilters = ({
|
||||
const { display, filter, table, grid } = useAlbumListStore({ id, key: pageKey });
|
||||
const cq = useContainerQuery();
|
||||
|
||||
const musicFoldersQuery = useMusicFolders();
|
||||
const musicFoldersQuery = useMusicFolders({ query: null, serverId: server?.id });
|
||||
|
||||
const sortByLabel =
|
||||
(server?.type &&
|
||||
@@ -115,13 +108,15 @@ export const AlbumListHeaderFilters = ({
|
||||
limit: take,
|
||||
startIndex: skip,
|
||||
...filters,
|
||||
jfParams: {
|
||||
...filters.jfParams,
|
||||
...customFilters?.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filters.ndParams,
|
||||
...customFilters?.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filters._custom?.jellyfin,
|
||||
...customFilters?._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filters._custom?.navidrome,
|
||||
...customFilters?._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
...customFilters,
|
||||
};
|
||||
@@ -132,14 +127,16 @@ export const AlbumListHeaderFilters = ({
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
api.controller.getAlbumList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
server,
|
||||
signal,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
return api.normalize.albumList(albums, server);
|
||||
return albums;
|
||||
},
|
||||
[customFilters, queryClient, server],
|
||||
);
|
||||
@@ -157,13 +154,15 @@ export const AlbumListHeaderFilters = ({
|
||||
startIndex,
|
||||
...filters,
|
||||
...customFilters,
|
||||
jfParams: {
|
||||
...filters.jfParams,
|
||||
...customFilters?.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filters.ndParams,
|
||||
...customFilters?.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filters._custom?.jellyfin,
|
||||
...customFilters?._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filters._custom?.navidrome,
|
||||
...customFilters?._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -173,15 +172,16 @@ export const AlbumListHeaderFilters = ({
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
api.controller.getAlbumList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
server,
|
||||
signal,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
const albums = api.normalize.albumList(albumsRes, server);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
return params.successCallback(albumsRes?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
@@ -218,6 +218,7 @@ export const AlbumListHeaderFilters = ({
|
||||
handleFilterChange={handleFilterChange}
|
||||
id={id}
|
||||
pageKey={pageKey}
|
||||
serverId={server?.id}
|
||||
/>
|
||||
) : (
|
||||
<JellyfinAlbumFilters
|
||||
@@ -225,6 +226,7 @@ export const AlbumListHeaderFilters = ({
|
||||
handleFilterChange={handleFilterChange}
|
||||
id={id}
|
||||
pageKey={pageKey}
|
||||
serverId={server?.id}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -293,30 +295,32 @@ export const AlbumListHeaderFilters = ({
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
|
||||
const handlePlay = async (play: Play) => {
|
||||
if (!itemCount || itemCount === 0) return;
|
||||
if (!itemCount || itemCount === 0 || !server) return;
|
||||
|
||||
const query = {
|
||||
startIndex: 0,
|
||||
...filter,
|
||||
...customFilters,
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
...customFilters?.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
...customFilters?.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
...customFilters?._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
...customFilters?._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||
|
||||
const albumListRes = await queryClient.fetchQuery({
|
||||
queryFn: ({ signal }) => api.controller.getAlbumList({ query, server, signal }),
|
||||
queryFn: ({ signal }) =>
|
||||
api.controller.getAlbumList({ apiClientProps: { server, signal }, query }),
|
||||
queryKey,
|
||||
});
|
||||
|
||||
const albumIds =
|
||||
api.normalize.albumList(albumListRes, server).items?.map((item) => item.id) || [];
|
||||
const albumIds = albumListRes?.items?.map((a) => a.id) || [];
|
||||
|
||||
handlePlayQueueAdd?.({
|
||||
byItemType: {
|
||||
@@ -382,16 +386,16 @@ export const AlbumListHeaderFilters = ({
|
||||
const isFilterApplied = useMemo(() => {
|
||||
const isNavidromeFilterApplied =
|
||||
server?.type === ServerType.NAVIDROME &&
|
||||
filter.ndParams &&
|
||||
Object.values(filter.ndParams).some((value) => value !== undefined);
|
||||
filter?._custom.navidrome &&
|
||||
Object.values(filter._custom.navidrome).some((value) => value !== undefined);
|
||||
|
||||
const isJellyfinFilterApplied =
|
||||
server?.type === ServerType.JELLYFIN &&
|
||||
filter.jfParams &&
|
||||
Object.values(filter.jfParams).some((value) => value !== undefined);
|
||||
filter?._custom.jellyfin &&
|
||||
Object.values(filter._custom.jellyfin).some((value) => value !== undefined);
|
||||
|
||||
return isNavidromeFilterApplied || isJellyfinFilterApplied;
|
||||
}, [filter.jfParams, filter.ndParams, server?.type]);
|
||||
}, [filter._custom.jellyfin, filter._custom.navidrome, server?.type]);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
@@ -456,7 +460,7 @@ export const AlbumListHeaderFilters = ({
|
||||
</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
{musicFoldersQuery.data?.map((folder) => (
|
||||
{musicFoldersQuery.data?.items.map((folder) => (
|
||||
<DropdownMenu.Item
|
||||
key={`musicFolder-${folder.id}`}
|
||||
$isActive={filter.musicFolderId === folder.id}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { api } from '/@/renderer/api';
|
||||
import { controller } from '/@/renderer/api/controller';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { AlbumListQuery, LibraryItem } from '/@/renderer/api/types';
|
||||
import { PageHeader, SearchInput, VirtualInfiniteGridRef } from '/@/renderer/components';
|
||||
import { PageHeader, SearchInput } from '/@/renderer/components';
|
||||
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import {
|
||||
@@ -24,6 +24,7 @@ import { AlbumListHeaderFilters } from '/@/renderer/features/albums/components/a
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { useAlbumListContext } from '/@/renderer/features/albums/context/album-list-context';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
||||
|
||||
interface AlbumListHeaderProps {
|
||||
customFilters?: Partial<AlbumListFilter>;
|
||||
@@ -54,15 +55,17 @@ export const AlbumListHeader = ({
|
||||
limit: take,
|
||||
startIndex: skip,
|
||||
...filters,
|
||||
jfParams: {
|
||||
...filters.jfParams,
|
||||
...customFilters?.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filters.ndParams,
|
||||
...customFilters?.ndParams,
|
||||
},
|
||||
...customFilters,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filters._custom?.jellyfin,
|
||||
...customFilters?._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filters._custom?.navidrome,
|
||||
...customFilters?._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||
@@ -71,14 +74,16 @@ export const AlbumListHeader = ({
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
controller.getAlbumList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
server,
|
||||
signal,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
return api.normalize.albumList(albums, server);
|
||||
return albums;
|
||||
},
|
||||
[customFilters, queryClient, server],
|
||||
);
|
||||
@@ -96,13 +101,15 @@ export const AlbumListHeader = ({
|
||||
startIndex,
|
||||
...filters,
|
||||
...customFilters,
|
||||
jfParams: {
|
||||
...filters.jfParams,
|
||||
...customFilters?.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filters.ndParams,
|
||||
...customFilters?.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filters._custom?.jellyfin,
|
||||
...customFilters?._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filters._custom?.navidrome,
|
||||
...customFilters?._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -112,15 +119,16 @@ export const AlbumListHeader = ({
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
api.controller.getAlbumList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
server,
|
||||
signal,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
const albums = api.normalize.albumList(albumsRes, server);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
params.successCallback(albumsRes?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
@@ -164,24 +172,26 @@ export const AlbumListHeader = ({
|
||||
startIndex: 0,
|
||||
...filter,
|
||||
...customFilters,
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
...customFilters?.jfParams,
|
||||
},
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
...customFilters?.ndParams,
|
||||
_custom: {
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
...customFilters?._custom?.jellyfin,
|
||||
},
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
...customFilters?._custom?.navidrome,
|
||||
},
|
||||
},
|
||||
};
|
||||
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||
|
||||
const albumListRes = await queryClient.fetchQuery({
|
||||
queryFn: ({ signal }) => api.controller.getAlbumList({ query, server, signal }),
|
||||
queryFn: ({ signal }) =>
|
||||
api.controller.getAlbumList({ apiClientProps: { server, signal }, query }),
|
||||
queryKey,
|
||||
});
|
||||
|
||||
const albumIds =
|
||||
api.normalize.albumList(albumListRes, server).items?.map((item) => item.id) || [];
|
||||
const albumIds = albumListRes?.items?.map((item) => item.id) || [];
|
||||
|
||||
handlePlayQueueAdd?.({
|
||||
byItemType: {
|
||||
|
||||
@@ -12,6 +12,7 @@ interface JellyfinAlbumFiltersProps {
|
||||
handleFilterChange: (filters: AlbumListFilter) => void;
|
||||
id?: string;
|
||||
pageKey: string;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
export const JellyfinAlbumFilters = ({
|
||||
@@ -19,24 +20,25 @@ export const JellyfinAlbumFilters = ({
|
||||
handleFilterChange,
|
||||
pageKey,
|
||||
id,
|
||||
serverId,
|
||||
}: JellyfinAlbumFiltersProps) => {
|
||||
const filter = useAlbumListFilter({ id, key: pageKey });
|
||||
const { setFilter } = useListStoreActions();
|
||||
|
||||
// TODO - eventually replace with /items/filters endpoint to fetch genres and tags specific to the selected library
|
||||
const genreListQuery = useGenreList(null);
|
||||
const genreListQuery = useGenreList({ query: null, serverId });
|
||||
|
||||
const genreList = useMemo(() => {
|
||||
if (!genreListQuery?.data) return [];
|
||||
return genreListQuery.data.map((genre) => ({
|
||||
return genreListQuery.data.items.map((genre) => ({
|
||||
label: genre.name,
|
||||
value: genre.id,
|
||||
}));
|
||||
}, [genreListQuery.data]);
|
||||
|
||||
const selectedGenres = useMemo(() => {
|
||||
return filter.jfParams?.genreIds?.split(',');
|
||||
}, [filter.jfParams?.genreIds]);
|
||||
return filter._custom?.jellyfin?.genreIds?.split(',');
|
||||
}, [filter._custom?.jellyfin?.genreIds]);
|
||||
|
||||
const toggleFilters = [
|
||||
{
|
||||
@@ -44,17 +46,19 @@ export const JellyfinAlbumFilters = ({
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
includeItemTypes: 'Audio',
|
||||
isFavorite: e.currentTarget.checked ? true : undefined,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
isFavorite: e.currentTarget.checked ? true : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
}) as AlbumListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter.jfParams?.isFavorite,
|
||||
value: filter._custom?.jellyfin?.isFavorite,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -62,9 +66,12 @@ export const JellyfinAlbumFilters = ({
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
minYear: e === '' ? undefined : (e as number),
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
minYear: e === '' ? undefined : (e as number),
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
@@ -76,9 +83,12 @@ export const JellyfinAlbumFilters = ({
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
maxYear: e === '' ? undefined : (e as number),
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
maxYear: e === '' ? undefined : (e as number),
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
@@ -90,9 +100,12 @@ export const JellyfinAlbumFilters = ({
|
||||
const genreFilterString = e?.length ? e.join(',') : undefined;
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
genreIds: genreFilterString,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
genreIds: genreFilterString,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
@@ -102,17 +115,18 @@ export const JellyfinAlbumFilters = ({
|
||||
|
||||
const [albumArtistSearchTerm, setAlbumArtistSearchTerm] = useState<string>('');
|
||||
|
||||
const albumArtistListQuery = useAlbumArtistList(
|
||||
{
|
||||
const albumArtistListQuery = useAlbumArtistList({
|
||||
options: {
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
query: {
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
startIndex: 0,
|
||||
},
|
||||
{
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
);
|
||||
serverId,
|
||||
});
|
||||
|
||||
const selectableAlbumArtists = useMemo(() => {
|
||||
if (!albumArtistListQuery?.data?.items) return [];
|
||||
@@ -127,9 +141,12 @@ export const JellyfinAlbumFilters = ({
|
||||
const albumArtistFilterString = e?.length ? e.join(',') : undefined;
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
albumArtistIds: albumArtistFilterString,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
jellyfin: {
|
||||
...filter._custom?.jellyfin,
|
||||
albumArtistIds: albumArtistFilterString,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
@@ -155,21 +172,21 @@ export const JellyfinAlbumFilters = ({
|
||||
<Divider my="0.5rem" />
|
||||
<Group grow>
|
||||
<NumberInput
|
||||
defaultValue={filter.jfParams?.minYear}
|
||||
defaultValue={filter._custom?.jellyfin?.minYear}
|
||||
hideControls={false}
|
||||
label="From year"
|
||||
max={2300}
|
||||
min={1700}
|
||||
required={!!filter.jfParams?.maxYear}
|
||||
required={!!filter._custom?.jellyfin?.maxYear}
|
||||
onChange={(e) => handleMinYearFilter(e)}
|
||||
/>
|
||||
<NumberInput
|
||||
defaultValue={filter.jfParams?.maxYear}
|
||||
defaultValue={filter._custom?.jellyfin?.maxYear}
|
||||
hideControls={false}
|
||||
label="To year"
|
||||
max={2300}
|
||||
min={1700}
|
||||
required={!!filter.jfParams?.minYear}
|
||||
required={!!filter._custom?.jellyfin?.minYear}
|
||||
onChange={(e) => handleMaxYearFilter(e)}
|
||||
/>
|
||||
</Group>
|
||||
@@ -189,7 +206,7 @@ export const JellyfinAlbumFilters = ({
|
||||
clearable
|
||||
searchable
|
||||
data={selectableAlbumArtists}
|
||||
defaultValue={filter.jfParams?.albumArtistIds?.split(',')}
|
||||
defaultValue={filter._custom?.jellyfin?.albumArtistIds?.split(',')}
|
||||
disabled={disableArtistFilter}
|
||||
label="Artist"
|
||||
limit={300}
|
||||
|
||||
@@ -12,6 +12,7 @@ interface NavidromeAlbumFiltersProps {
|
||||
handleFilterChange: (filters: AlbumListFilter) => void;
|
||||
id?: string;
|
||||
pageKey: string;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
export const NavidromeAlbumFilters = ({
|
||||
@@ -19,15 +20,16 @@ export const NavidromeAlbumFilters = ({
|
||||
disableArtistFilter,
|
||||
pageKey,
|
||||
id,
|
||||
serverId,
|
||||
}: NavidromeAlbumFiltersProps) => {
|
||||
const filter = useAlbumListFilter({ id, key: pageKey });
|
||||
const { setFilter } = useListStoreActions();
|
||||
|
||||
const genreListQuery = useGenreList(null);
|
||||
const genreListQuery = useGenreList({ query: null, serverId });
|
||||
|
||||
const genreList = useMemo(() => {
|
||||
if (!genreListQuery?.data) return [];
|
||||
return genreListQuery.data.map((genre) => ({
|
||||
return genreListQuery.data.items.map((genre) => ({
|
||||
label: genre.name,
|
||||
value: genre.id,
|
||||
}));
|
||||
@@ -36,9 +38,12 @@ export const NavidromeAlbumFilters = ({
|
||||
const handleGenresFilter = debounce((e: string | null) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
genre_id: e || undefined,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
genre_id: e || undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: 'album',
|
||||
@@ -52,70 +57,89 @@ export const NavidromeAlbumFilters = ({
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
has_rating: e.currentTarget.checked ? true : undefined,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
has_rating: e.currentTarget.checked ? true : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
}) as AlbumListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter.ndParams?.has_rating,
|
||||
value: filter._custom?.navidrome?.has_rating,
|
||||
},
|
||||
{
|
||||
label: 'Is favorited',
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
console.log('e.currentTarget.checked :>> ', e.currentTarget.checked);
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: { ...filter.ndParams, starred: e.currentTarget.checked ? true : undefined },
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
starred: e.currentTarget.checked ? true : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
}) as AlbumListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter.ndParams?.starred,
|
||||
value: filter._custom?.navidrome?.starred,
|
||||
},
|
||||
{
|
||||
label: 'Is compilation',
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
compilation: e.currentTarget.checked ? true : undefined,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
compilation: e.currentTarget.checked ? true : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
}) as AlbumListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter.ndParams?.compilation,
|
||||
value: filter._custom?.navidrome?.compilation,
|
||||
},
|
||||
{
|
||||
label: 'Is recently played',
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
recently_played: e.currentTarget.checked ? true : undefined,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
recently_played: e.currentTarget.checked ? true : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
}) as AlbumListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter.ndParams?.recently_played,
|
||||
value: filter._custom?.navidrome?.recently_played,
|
||||
},
|
||||
];
|
||||
|
||||
const handleYearFilter = debounce((e: number | string) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
year: e === '' ? undefined : (e as number),
|
||||
_custom: {
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
year: e === '' ? undefined : (e as number),
|
||||
},
|
||||
...filter._custom,
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
@@ -125,18 +149,19 @@ export const NavidromeAlbumFilters = ({
|
||||
|
||||
const [albumArtistSearchTerm, setAlbumArtistSearchTerm] = useState<string>('');
|
||||
|
||||
const albumArtistListQuery = useAlbumArtistList(
|
||||
{
|
||||
const albumArtistListQuery = useAlbumArtistList({
|
||||
options: {
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
query: {
|
||||
// searchTerm: debouncedSearchTerm,
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
startIndex: 0,
|
||||
},
|
||||
{
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
);
|
||||
serverId,
|
||||
});
|
||||
|
||||
const selectableAlbumArtists = useMemo(() => {
|
||||
if (!albumArtistListQuery?.data?.items) return [];
|
||||
@@ -150,9 +175,12 @@ export const NavidromeAlbumFilters = ({
|
||||
const handleAlbumArtistFilter = (e: string | null) => {
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
artist_id: e || undefined,
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
navidrome: {
|
||||
...filter._custom?.navidrome,
|
||||
artist_id: e || undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
key: pageKey,
|
||||
@@ -177,7 +205,7 @@ export const NavidromeAlbumFilters = ({
|
||||
<Divider my="0.5rem" />
|
||||
<Group grow>
|
||||
<NumberInput
|
||||
defaultValue={filter.ndParams?.year}
|
||||
defaultValue={filter._custom?.navidrome?.year}
|
||||
hideControls={false}
|
||||
label="Year"
|
||||
max={5000}
|
||||
@@ -188,7 +216,7 @@ export const NavidromeAlbumFilters = ({
|
||||
clearable
|
||||
searchable
|
||||
data={genreList}
|
||||
defaultValue={filter.ndParams?.genre_id}
|
||||
defaultValue={filter._custom?.navidrome?.genre_id}
|
||||
label="Genre"
|
||||
onChange={handleGenresFilter}
|
||||
/>
|
||||
@@ -198,7 +226,7 @@ export const NavidromeAlbumFilters = ({
|
||||
clearable
|
||||
searchable
|
||||
data={selectableAlbumArtists}
|
||||
defaultValue={filter.ndParams?.artist_id}
|
||||
defaultValue={filter._custom?.navidrome?.artist_id}
|
||||
disabled={disableArtistFilter}
|
||||
label="Artist"
|
||||
limit={300}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AlbumDetailHeader } from '/@/renderer/features/albums/components/album-
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
|
||||
const AlbumDetailRoute = () => {
|
||||
const tableRef = useRef<AgGridReactType | null>(null);
|
||||
@@ -17,7 +18,8 @@ const AlbumDetailRoute = () => {
|
||||
const headerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { albumId } = useParams() as { albumId: string };
|
||||
const detailQuery = useAlbumDetail({ id: albumId });
|
||||
const server = useCurrentServer();
|
||||
const detailQuery = useAlbumDetail({ query: { id: albumId }, serverId: server?.id });
|
||||
const background = useFastAverageColor(detailQuery.data?.imageUrl, !detailQuery.isLoading);
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
const playButtonBehavior = usePlayButtonBehavior();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components';
|
||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||
import { AlbumListHeader } from '/@/renderer/features/albums/components/album-list-header';
|
||||
import { AlbumListContent } from '/@/renderer/features/albums/components/album-list-content';
|
||||
@@ -8,6 +7,7 @@ import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-que
|
||||
import { generatePageKey, useAlbumListFilter, useCurrentServer } from '/@/renderer/store';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { AlbumListContext } from '/@/renderer/features/albums/context/album-list-context';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
||||
|
||||
const AlbumListRoute = () => {
|
||||
const gridRef = useRef<VirtualInfiniteGridRef | null>(null);
|
||||
@@ -24,17 +24,18 @@ const AlbumListRoute = () => {
|
||||
|
||||
const albumListFilter = useAlbumListFilter({ id: albumArtistId || undefined, key: pageKey });
|
||||
|
||||
const itemCountCheck = useAlbumList(
|
||||
{
|
||||
const itemCountCheck = useAlbumList({
|
||||
options: {
|
||||
cacheTime: 1000 * 60,
|
||||
staleTime: 1000 * 60,
|
||||
},
|
||||
query: {
|
||||
limit: 1,
|
||||
startIndex: 0,
|
||||
...albumListFilter,
|
||||
},
|
||||
{
|
||||
cacheTime: 1000 * 60,
|
||||
staleTime: 1000 * 60,
|
||||
},
|
||||
);
|
||||
serverId: server?.id,
|
||||
});
|
||||
|
||||
const itemCount =
|
||||
itemCountCheck.data?.totalRecordCount === null
|
||||
|
||||
Reference in New Issue
Block a user