Add view artist discography
This commit is contained in:
parent
67523f1e7b
commit
5614ad54f2
7 changed files with 338 additions and 155 deletions
|
@ -10,12 +10,12 @@ import {
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { ListDisplayType, CardRow } from '/@/renderer/types';
|
import { ListDisplayType, CardRow } from '/@/renderer/types';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { MutableRefObject, useCallback, useMemo } from 'react';
|
import { MutableRefObject, useCallback, useMemo, useState } from 'react';
|
||||||
import { ListOnScrollProps } from 'react-window';
|
import { ListOnScrollProps } from 'react-window';
|
||||||
import { api } from '/@/renderer/api';
|
import { api } from '/@/renderer/api';
|
||||||
import { controller } from '/@/renderer/api/controller';
|
import { controller } from '/@/renderer/api/controller';
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { Album, AlbumListSort, LibraryItem } from '/@/renderer/api/types';
|
import { Album, AlbumListQuery, AlbumListSort, LibraryItem } from '/@/renderer/api/types';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import {
|
import {
|
||||||
useCurrentServer,
|
useCurrentServer,
|
||||||
|
@ -25,6 +25,7 @@ import {
|
||||||
useSetAlbumTable,
|
useSetAlbumTable,
|
||||||
useSetAlbumTablePagination,
|
useSetAlbumTablePagination,
|
||||||
useAlbumListItemData,
|
useAlbumListItemData,
|
||||||
|
AlbumListFilter,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
import {
|
import {
|
||||||
|
@ -44,12 +45,18 @@ import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
|
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
|
||||||
|
|
||||||
interface AlbumListContentProps {
|
interface AlbumListContentProps {
|
||||||
|
customFilters?: Partial<AlbumListFilter>;
|
||||||
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
||||||
itemCount?: number;
|
itemCount?: number;
|
||||||
tableRef: MutableRefObject<AgGridReactType | null>;
|
tableRef: MutableRefObject<AgGridReactType | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListContentProps) => {
|
export const AlbumListContent = ({
|
||||||
|
customFilters,
|
||||||
|
itemCount,
|
||||||
|
gridRef,
|
||||||
|
tableRef,
|
||||||
|
}: AlbumListContentProps) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
|
@ -58,6 +65,7 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||||
|
|
||||||
const { itemData, setItemData } = useAlbumListItemData();
|
const { itemData, setItemData } = useAlbumListItemData();
|
||||||
|
const [localItemData, setLocalItemData] = useState<any[]>([]);
|
||||||
|
|
||||||
const pagination = useAlbumTablePagination();
|
const pagination = useAlbumTablePagination();
|
||||||
const setPagination = useSetAlbumTablePagination();
|
const setPagination = useSetAlbumTablePagination();
|
||||||
|
@ -77,21 +85,28 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
const limit = params.endRow - params.startRow;
|
const limit = params.endRow - params.startRow;
|
||||||
const startIndex = params.startRow;
|
const startIndex = params.startRow;
|
||||||
|
|
||||||
const queryKey = queryKeys.albums.list(server?.id || '', {
|
const query: AlbumListQuery = {
|
||||||
limit,
|
limit,
|
||||||
startIndex,
|
startIndex,
|
||||||
...page.filter,
|
...page.filter,
|
||||||
});
|
...customFilters,
|
||||||
|
jfParams: {
|
||||||
|
...page.filter.jfParams,
|
||||||
|
...customFilters?.jfParams,
|
||||||
|
},
|
||||||
|
ndParams: {
|
||||||
|
...page.filter.ndParams,
|
||||||
|
...customFilters?.ndParams,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||||
|
|
||||||
const albumsRes = await queryClient.fetchQuery(
|
const albumsRes = await queryClient.fetchQuery(
|
||||||
queryKey,
|
queryKey,
|
||||||
async ({ signal }) =>
|
async ({ signal }) =>
|
||||||
api.controller.getAlbumList({
|
api.controller.getAlbumList({
|
||||||
query: {
|
query,
|
||||||
limit,
|
|
||||||
startIndex,
|
|
||||||
...page.filter,
|
|
||||||
},
|
|
||||||
server,
|
server,
|
||||||
signal,
|
signal,
|
||||||
}),
|
}),
|
||||||
|
@ -104,9 +119,12 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
rowCount: undefined,
|
rowCount: undefined,
|
||||||
};
|
};
|
||||||
params.api.setDatasource(dataSource);
|
params.api.setDatasource(dataSource);
|
||||||
params.api.ensureIndexVisible(page.table.scrollOffset || 0, 'top');
|
|
||||||
|
if (!customFilters) {
|
||||||
|
params.api.ensureIndexVisible(page.table.scrollOffset || 0, 'top');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[page.filter, page.table.scrollOffset, queryClient, server],
|
[customFilters, page.filter, page.table.scrollOffset, queryClient, server],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTablePaginationChanged = useCallback(
|
const onTablePaginationChanged = useCallback(
|
||||||
|
@ -153,25 +171,33 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
const debouncedTableColumnChange = debounce(handleTableColumnChange, 200);
|
const debouncedTableColumnChange = debounce(handleTableColumnChange, 200);
|
||||||
|
|
||||||
const handleTableScroll = (e: BodyScrollEvent) => {
|
const handleTableScroll = (e: BodyScrollEvent) => {
|
||||||
|
if (customFilters) return;
|
||||||
const scrollOffset = Number((e.top / page.table.rowHeight).toFixed(0));
|
const scrollOffset = Number((e.top / page.table.rowHeight).toFixed(0));
|
||||||
setTable({ scrollOffset });
|
setTable({ scrollOffset });
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetch = useCallback(
|
const fetch = useCallback(
|
||||||
async ({ skip, take }: { skip: number; take: number }) => {
|
async ({ skip, take }: { skip: number; take: number }) => {
|
||||||
const queryKey = queryKeys.albums.list(server?.id || '', {
|
const query: AlbumListQuery = {
|
||||||
limit: take,
|
limit: take,
|
||||||
startIndex: skip,
|
startIndex: skip,
|
||||||
...page.filter,
|
...page.filter,
|
||||||
});
|
...customFilters,
|
||||||
|
jfParams: {
|
||||||
|
...page.filter.jfParams,
|
||||||
|
...customFilters?.jfParams,
|
||||||
|
},
|
||||||
|
ndParams: {
|
||||||
|
...page.filter.ndParams,
|
||||||
|
...customFilters?.ndParams,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||||
|
|
||||||
const albums = await queryClient.fetchQuery(queryKey, async ({ signal }) =>
|
const albums = await queryClient.fetchQuery(queryKey, async ({ signal }) =>
|
||||||
controller.getAlbumList({
|
controller.getAlbumList({
|
||||||
query: {
|
query,
|
||||||
limit: take,
|
|
||||||
startIndex: skip,
|
|
||||||
...page.filter,
|
|
||||||
},
|
|
||||||
server,
|
server,
|
||||||
signal,
|
signal,
|
||||||
}),
|
}),
|
||||||
|
@ -179,11 +205,12 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
|
|
||||||
return api.normalize.albumList(albums, server);
|
return api.normalize.albumList(albums, server);
|
||||||
},
|
},
|
||||||
[page.filter, queryClient, server],
|
[customFilters, page.filter, queryClient, server],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleGridScroll = useCallback(
|
const handleGridScroll = useCallback(
|
||||||
(e: ListOnScrollProps) => {
|
(e: ListOnScrollProps) => {
|
||||||
|
if (customFilters) return;
|
||||||
setPage({
|
setPage({
|
||||||
list: {
|
list: {
|
||||||
...page,
|
...page,
|
||||||
|
@ -194,7 +221,7 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[page, setPage],
|
[customFilters, page, setPage],
|
||||||
);
|
);
|
||||||
|
|
||||||
const cardRows = useMemo(() => {
|
const cardRows = useMemo(() => {
|
||||||
|
@ -308,9 +335,9 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
handleFavorite={handleFavorite}
|
handleFavorite={handleFavorite}
|
||||||
handlePlayQueueAdd={handlePlayQueueAdd}
|
handlePlayQueueAdd={handlePlayQueueAdd}
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={page?.grid.scrollOffset || 0}
|
initialScrollOffset={customFilters ? 0 : page?.grid.scrollOffset || 0}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
itemData={itemData}
|
itemData={customFilters ? localItemData : itemData}
|
||||||
itemGap={20}
|
itemGap={20}
|
||||||
itemSize={150 + page.grid?.size}
|
itemSize={150 + page.grid?.size}
|
||||||
itemType={LibraryItem.ALBUM}
|
itemType={LibraryItem.ALBUM}
|
||||||
|
@ -320,7 +347,7 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
|
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
|
||||||
slugs: [{ idProperty: 'id', slugProperty: 'albumId' }],
|
slugs: [{ idProperty: 'id', slugProperty: 'albumId' }],
|
||||||
}}
|
}}
|
||||||
setItemData={setItemData}
|
setItemData={customFilters ? setLocalItemData : setItemData}
|
||||||
width={width}
|
width={width}
|
||||||
onScroll={handleGridScroll}
|
onScroll={handleGridScroll}
|
||||||
/>
|
/>
|
||||||
|
@ -334,6 +361,7 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont
|
||||||
key={`table-${page.display}-${page.table.rowHeight}-${server?.id}`}
|
key={`table-${page.display}-${page.table.rowHeight}-${server?.id}`}
|
||||||
ref={tableRef}
|
ref={tableRef}
|
||||||
alwaysShowHorizontalScroll
|
alwaysShowHorizontalScroll
|
||||||
|
suppressRowDrag
|
||||||
autoFitColumns={page.table.autoFit}
|
autoFitColumns={page.table.autoFit}
|
||||||
blockLoadDebounceMillis={200}
|
blockLoadDebounceMillis={200}
|
||||||
columnDefs={columnDefs}
|
columnDefs={columnDefs}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useCallback } from 'react';
|
||||||
import { IDatasource } from '@ag-grid-community/core';
|
import { IDatasource } from '@ag-grid-community/core';
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
import { Flex, Group, Stack } from '@mantine/core';
|
import { Flex, Group, Stack } from '@mantine/core';
|
||||||
|
import { openModal } from '@mantine/modals';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import {
|
import {
|
||||||
|
@ -17,7 +18,13 @@ import styled from 'styled-components';
|
||||||
import { api } from '/@/renderer/api';
|
import { api } from '/@/renderer/api';
|
||||||
import { controller } from '/@/renderer/api/controller';
|
import { controller } from '/@/renderer/api/controller';
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { AlbumListSort, LibraryItem, ServerType, SortOrder } from '/@/renderer/api/types';
|
import {
|
||||||
|
AlbumListQuery,
|
||||||
|
AlbumListSort,
|
||||||
|
LibraryItem,
|
||||||
|
ServerType,
|
||||||
|
SortOrder,
|
||||||
|
} from '/@/renderer/api/types';
|
||||||
import {
|
import {
|
||||||
ALBUM_TABLE_COLUMNS,
|
ALBUM_TABLE_COLUMNS,
|
||||||
Badge,
|
Badge,
|
||||||
|
@ -25,7 +32,6 @@ import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
Popover,
|
|
||||||
SearchInput,
|
SearchInput,
|
||||||
Slider,
|
Slider,
|
||||||
SpinnerIcon,
|
SpinnerIcon,
|
||||||
|
@ -92,12 +98,20 @@ const HeaderItems = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface AlbumListHeaderProps {
|
interface AlbumListHeaderProps {
|
||||||
|
customFilters?: Partial<AlbumListFilter>;
|
||||||
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
||||||
itemCount?: number;
|
itemCount?: number;
|
||||||
tableRef: MutableRefObject<AgGridReactType | null>;
|
tableRef: MutableRefObject<AgGridReactType | null>;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeaderProps) => {
|
export const AlbumListHeader = ({
|
||||||
|
itemCount,
|
||||||
|
gridRef,
|
||||||
|
tableRef,
|
||||||
|
title,
|
||||||
|
customFilters,
|
||||||
|
}: AlbumListHeaderProps) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const setPage = useSetAlbumStore();
|
const setPage = useSetAlbumStore();
|
||||||
|
@ -131,21 +145,28 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
|
|
||||||
const fetch = useCallback(
|
const fetch = useCallback(
|
||||||
async (skip: number, take: number, filters: AlbumListFilter) => {
|
async (skip: number, take: number, filters: AlbumListFilter) => {
|
||||||
const queryKey = queryKeys.albums.list(server?.id || '', {
|
const query: AlbumListQuery = {
|
||||||
limit: take,
|
limit: take,
|
||||||
startIndex: skip,
|
startIndex: skip,
|
||||||
...filters,
|
...filters,
|
||||||
});
|
jfParams: {
|
||||||
|
...filters.jfParams,
|
||||||
|
...customFilters?.jfParams,
|
||||||
|
},
|
||||||
|
ndParams: {
|
||||||
|
...filters.ndParams,
|
||||||
|
...customFilters?.ndParams,
|
||||||
|
},
|
||||||
|
...customFilters,
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||||
|
|
||||||
const albums = await queryClient.fetchQuery(
|
const albums = await queryClient.fetchQuery(
|
||||||
queryKey,
|
queryKey,
|
||||||
async ({ signal }) =>
|
async ({ signal }) =>
|
||||||
controller.getAlbumList({
|
controller.getAlbumList({
|
||||||
query: {
|
query,
|
||||||
limit: take,
|
|
||||||
startIndex: skip,
|
|
||||||
...filters,
|
|
||||||
},
|
|
||||||
server,
|
server,
|
||||||
signal,
|
signal,
|
||||||
}),
|
}),
|
||||||
|
@ -154,7 +175,7 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
|
|
||||||
return api.normalize.albumList(albums, server);
|
return api.normalize.albumList(albums, server);
|
||||||
},
|
},
|
||||||
[queryClient, server],
|
[customFilters, queryClient, server],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFilterChange = useCallback(
|
const handleFilterChange = useCallback(
|
||||||
|
@ -168,21 +189,28 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
const limit = params.endRow - params.startRow;
|
const limit = params.endRow - params.startRow;
|
||||||
const startIndex = params.startRow;
|
const startIndex = params.startRow;
|
||||||
|
|
||||||
const queryKey = queryKeys.albums.list(server?.id || '', {
|
const query: AlbumListQuery = {
|
||||||
limit,
|
limit,
|
||||||
startIndex,
|
startIndex,
|
||||||
...filters,
|
...filters,
|
||||||
});
|
...customFilters,
|
||||||
|
jfParams: {
|
||||||
|
...filters.jfParams,
|
||||||
|
...customFilters?.jfParams,
|
||||||
|
},
|
||||||
|
ndParams: {
|
||||||
|
...filters.ndParams,
|
||||||
|
...customFilters?.ndParams,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||||
|
|
||||||
const albumsRes = await queryClient.fetchQuery(
|
const albumsRes = await queryClient.fetchQuery(
|
||||||
queryKey,
|
queryKey,
|
||||||
async ({ signal }) =>
|
async ({ signal }) =>
|
||||||
api.controller.getAlbumList({
|
api.controller.getAlbumList({
|
||||||
query: {
|
query,
|
||||||
limit,
|
|
||||||
startIndex,
|
|
||||||
...filters,
|
|
||||||
},
|
|
||||||
server,
|
server,
|
||||||
signal,
|
signal,
|
||||||
}),
|
}),
|
||||||
|
@ -214,9 +242,30 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
gridRef.current?.setItemData(data.items);
|
gridRef.current?.setItemData(data.items);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[page.display, tableRef, setPagination, server, queryClient, gridRef, fetch],
|
[page.display, tableRef, customFilters, server, queryClient, setPagination, gridRef, fetch],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleOpenFiltersModal = () => {
|
||||||
|
openModal({
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
{server?.type === ServerType.NAVIDROME ? (
|
||||||
|
<NavidromeAlbumFilters
|
||||||
|
disableArtistFilter={!!customFilters}
|
||||||
|
handleFilterChange={handleFilterChange}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<JellyfinAlbumFilters
|
||||||
|
disableArtistFilter={!!customFilters}
|
||||||
|
handleFilterChange={handleFilterChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
title: 'Album Filters',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleRefresh = useCallback(() => {
|
const handleRefresh = useCallback(() => {
|
||||||
queryClient.invalidateQueries(queryKeys.albums.list(server?.id || ''));
|
queryClient.invalidateQueries(queryKeys.albums.list(server?.id || ''));
|
||||||
handleFilterChange(filters);
|
handleFilterChange(filters);
|
||||||
|
@ -315,7 +364,19 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
const handlePlay = async (play: Play) => {
|
const handlePlay = async (play: Play) => {
|
||||||
if (!itemCount || itemCount === 0) return;
|
if (!itemCount || itemCount === 0) return;
|
||||||
|
|
||||||
const query = { startIndex: 0, ...filters };
|
const query = {
|
||||||
|
startIndex: 0,
|
||||||
|
...filters,
|
||||||
|
...customFilters,
|
||||||
|
jfParams: {
|
||||||
|
...filters.jfParams,
|
||||||
|
...customFilters?.jfParams,
|
||||||
|
},
|
||||||
|
ndParams: {
|
||||||
|
...filters.ndParams,
|
||||||
|
...customFilters?.ndParams,
|
||||||
|
},
|
||||||
|
};
|
||||||
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
const queryKey = queryKeys.albums.list(server?.id || '', query);
|
||||||
|
|
||||||
const albumListRes = await queryClient.fetchQuery({
|
const albumListRes = await queryClient.fetchQuery({
|
||||||
|
@ -355,9 +416,11 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
<Group noWrap>
|
<Group noWrap>
|
||||||
<TextTitle
|
<TextTitle
|
||||||
fw="bold"
|
fw="bold"
|
||||||
|
maw="20vw"
|
||||||
order={3}
|
order={3}
|
||||||
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
Albums
|
{title || 'Albums'}
|
||||||
</TextTitle>
|
</TextTitle>
|
||||||
<Badge
|
<Badge
|
||||||
radius="xl"
|
radius="xl"
|
||||||
|
@ -509,24 +572,14 @@ export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeade
|
||||||
</DropdownMenu.Dropdown>
|
</DropdownMenu.Dropdown>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
)}
|
)}
|
||||||
<Popover position="bottom-start">
|
<Button
|
||||||
<Popover.Target>
|
compact
|
||||||
<Button
|
fw="600"
|
||||||
compact
|
variant="subtle"
|
||||||
fw="600"
|
onClick={handleOpenFiltersModal}
|
||||||
variant="subtle"
|
>
|
||||||
>
|
{cq.isMd ? 'Filters' : <RiFilter3Line size={15} />}
|
||||||
{cq.isMd ? 'Filters' : <RiFilter3Line size={15} />}
|
</Button>
|
||||||
</Button>
|
|
||||||
</Popover.Target>
|
|
||||||
<Popover.Dropdown>
|
|
||||||
{server?.type === ServerType.NAVIDROME ? (
|
|
||||||
<NavidromeAlbumFilters handleFilterChange={handleFilterChange} />
|
|
||||||
) : (
|
|
||||||
<JellyfinAlbumFilters handleFilterChange={handleFilterChange} />
|
|
||||||
)}
|
|
||||||
</Popover.Dropdown>
|
|
||||||
</Popover>
|
|
||||||
<DropdownMenu position="bottom-start">
|
<DropdownMenu position="bottom-start">
|
||||||
<DropdownMenu.Target>
|
<DropdownMenu.Target>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
import { ChangeEvent, useMemo } from 'react';
|
import { ChangeEvent, useMemo, useState } from 'react';
|
||||||
import { Divider, Group, Stack } from '@mantine/core';
|
import { Divider, Group, Stack } from '@mantine/core';
|
||||||
import { MultiSelect, NumberInput, Switch, Text } from '/@/renderer/components';
|
import { MultiSelect, NumberInput, SpinnerIcon, Switch, Text } from '/@/renderer/components';
|
||||||
import { AlbumListFilter, useAlbumListStore, useSetAlbumFilters } from '/@/renderer/store';
|
import { AlbumListFilter, useAlbumListStore, useSetAlbumFilters } from '/@/renderer/store';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { useGenreList } from '/@/renderer/features/genres';
|
import { useGenreList } from '/@/renderer/features/genres';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
import { AlbumArtistListSort, SortOrder } from '/@/renderer/api/types';
|
||||||
|
import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query';
|
||||||
|
|
||||||
interface JellyfinAlbumFiltersProps {
|
interface JellyfinAlbumFiltersProps {
|
||||||
|
disableArtistFilter?: boolean;
|
||||||
handleFilterChange: (filters: AlbumListFilter) => void;
|
handleFilterChange: (filters: AlbumListFilter) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const JellyfinAlbumFilters = ({ handleFilterChange }: JellyfinAlbumFiltersProps) => {
|
export const JellyfinAlbumFilters = ({
|
||||||
|
disableArtistFilter,
|
||||||
|
handleFilterChange,
|
||||||
|
}: JellyfinAlbumFiltersProps) => {
|
||||||
const { filter } = useAlbumListStore();
|
const { filter } = useAlbumListStore();
|
||||||
const setFilters = useSetAlbumFilters();
|
const setFilters = useSetAlbumFilters();
|
||||||
|
|
||||||
|
@ -74,46 +81,33 @@ export const JellyfinAlbumFilters = ({ handleFilterChange }: JellyfinAlbumFilter
|
||||||
handleFilterChange(updatedFilters);
|
handleFilterChange(updatedFilters);
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
||||||
|
const [albumArtistSearchTerm, setAlbumArtistSearchTerm] = useState<string>('');
|
||||||
|
const [debouncedSearchTerm] = useDebouncedValue(albumArtistSearchTerm, 200);
|
||||||
|
|
||||||
|
const albumArtistListQuery = useAlbumArtistList(
|
||||||
|
{
|
||||||
|
limit: 300,
|
||||||
|
searchTerm: debouncedSearchTerm,
|
||||||
|
sortBy: AlbumArtistListSort.NAME,
|
||||||
|
sortOrder: SortOrder.ASC,
|
||||||
|
startIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: debouncedSearchTerm ? debouncedSearchTerm !== '' : false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectableAlbumArtists = useMemo(() => {
|
||||||
|
if (!albumArtistListQuery?.data?.items) return [];
|
||||||
|
|
||||||
|
return albumArtistListQuery?.data?.items?.map((artist) => ({
|
||||||
|
label: artist.name,
|
||||||
|
value: artist.id,
|
||||||
|
}));
|
||||||
|
}, [albumArtistListQuery?.data?.items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="0.8rem">
|
<Stack p="0.8rem">
|
||||||
<Group position="apart">
|
|
||||||
<Text>Year range</Text>
|
|
||||||
<Group>
|
|
||||||
<NumberInput
|
|
||||||
required
|
|
||||||
hideControls={false}
|
|
||||||
max={2300}
|
|
||||||
min={1700}
|
|
||||||
value={filter.jfParams?.minYear}
|
|
||||||
width={80}
|
|
||||||
onChange={handleMinYearFilter}
|
|
||||||
/>
|
|
||||||
<NumberInput
|
|
||||||
hideControls={false}
|
|
||||||
max={2300}
|
|
||||||
min={1700}
|
|
||||||
value={filter.jfParams?.maxYear}
|
|
||||||
width={80}
|
|
||||||
onChange={handleMaxYearFilter}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
<Divider my="0.5rem" />
|
|
||||||
<Group
|
|
||||||
position="apart"
|
|
||||||
spacing={20}
|
|
||||||
>
|
|
||||||
<Text>Genres</Text>
|
|
||||||
<MultiSelect
|
|
||||||
clearable
|
|
||||||
searchable
|
|
||||||
data={genreList}
|
|
||||||
defaultValue={selectedGenres}
|
|
||||||
width={250}
|
|
||||||
onChange={handleGenresFilter}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
<Divider my="0.5rem" />
|
|
||||||
{toggleFilters.map((filter) => (
|
{toggleFilters.map((filter) => (
|
||||||
<Group
|
<Group
|
||||||
key={`nd-filter-${filter.label}`}
|
key={`nd-filter-${filter.label}`}
|
||||||
|
@ -127,14 +121,52 @@ export const JellyfinAlbumFilters = ({ handleFilterChange }: JellyfinAlbumFilter
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
))}
|
))}
|
||||||
{/* <Divider my="0.5rem" />
|
<Divider my="0.5rem" />
|
||||||
<Stack>
|
<Group grow>
|
||||||
<Text>Tags</Text>
|
<NumberInput
|
||||||
<MultiSelect
|
hideControls={false}
|
||||||
disabled
|
label="From year"
|
||||||
data={[]}
|
max={2300}
|
||||||
|
min={1700}
|
||||||
|
required={!!filter.jfParams?.maxYear}
|
||||||
|
value={filter.jfParams?.minYear}
|
||||||
|
onChange={handleMinYearFilter}
|
||||||
/>
|
/>
|
||||||
</Stack> */}
|
<NumberInput
|
||||||
|
hideControls={false}
|
||||||
|
label="To year"
|
||||||
|
max={2300}
|
||||||
|
min={1700}
|
||||||
|
required={!!filter.jfParams?.minYear}
|
||||||
|
value={filter.jfParams?.maxYear}
|
||||||
|
onChange={handleMaxYearFilter}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
<Group grow>
|
||||||
|
<MultiSelect
|
||||||
|
clearable
|
||||||
|
searchable
|
||||||
|
data={genreList}
|
||||||
|
defaultValue={selectedGenres}
|
||||||
|
label="Genres"
|
||||||
|
onChange={handleGenresFilter}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group grow>
|
||||||
|
<MultiSelect
|
||||||
|
clearable
|
||||||
|
searchable
|
||||||
|
data={selectableAlbumArtists}
|
||||||
|
disabled={disableArtistFilter}
|
||||||
|
label="Artist"
|
||||||
|
limit={300}
|
||||||
|
placeholder="Type to search for an artist"
|
||||||
|
rightSection={albumArtistListQuery.isFetching ? <SpinnerIcon /> : undefined}
|
||||||
|
searchValue={albumArtistSearchTerm}
|
||||||
|
onSearchChange={setAlbumArtistSearchTerm}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
import { ChangeEvent, useMemo } from 'react';
|
import { ChangeEvent, useMemo, useState } from 'react';
|
||||||
import { Divider, Group, Stack } from '@mantine/core';
|
import { Divider, Group, Stack } from '@mantine/core';
|
||||||
import { NumberInput, Switch, Text, Select } from '/@/renderer/components';
|
import { NumberInput, Switch, Text, Select, SpinnerIcon } from '/@/renderer/components';
|
||||||
import { AlbumListFilter, useAlbumListStore, useSetAlbumFilters } from '/@/renderer/store';
|
import { AlbumListFilter, useAlbumListStore, useSetAlbumFilters } from '/@/renderer/store';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { useGenreList } from '/@/renderer/features/genres';
|
import { useGenreList } from '/@/renderer/features/genres';
|
||||||
|
import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query';
|
||||||
|
import { AlbumArtistListSort, SortOrder } from '/@/renderer/api/types';
|
||||||
|
|
||||||
interface NavidromeAlbumFiltersProps {
|
interface NavidromeAlbumFiltersProps {
|
||||||
|
disableArtistFilter?: boolean;
|
||||||
handleFilterChange: (filters: AlbumListFilter) => void;
|
handleFilterChange: (filters: AlbumListFilter) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NavidromeAlbumFilters = ({ handleFilterChange }: NavidromeAlbumFiltersProps) => {
|
export const NavidromeAlbumFilters = ({
|
||||||
|
handleFilterChange,
|
||||||
|
disableArtistFilter,
|
||||||
|
}: NavidromeAlbumFiltersProps) => {
|
||||||
const { filter } = useAlbumListStore();
|
const { filter } = useAlbumListStore();
|
||||||
const setFilters = useSetAlbumFilters();
|
const setFilters = useSetAlbumFilters();
|
||||||
|
|
||||||
|
@ -89,35 +96,33 @@ export const NavidromeAlbumFilters = ({ handleFilterChange }: NavidromeAlbumFilt
|
||||||
handleFilterChange(updatedFilters);
|
handleFilterChange(updatedFilters);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
const [albumArtistSearchTerm, setAlbumArtistSearchTerm] = useState<string>('');
|
||||||
|
const [debouncedSearchTerm] = useDebouncedValue(albumArtistSearchTerm, 200);
|
||||||
|
|
||||||
|
const albumArtistListQuery = useAlbumArtistList(
|
||||||
|
{
|
||||||
|
limit: 300,
|
||||||
|
searchTerm: debouncedSearchTerm,
|
||||||
|
sortBy: AlbumArtistListSort.NAME,
|
||||||
|
sortOrder: SortOrder.ASC,
|
||||||
|
startIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: debouncedSearchTerm ? debouncedSearchTerm !== '' : false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectableAlbumArtists = useMemo(() => {
|
||||||
|
if (!albumArtistListQuery?.data?.items) return [];
|
||||||
|
|
||||||
|
return albumArtistListQuery?.data?.items?.map((artist) => ({
|
||||||
|
label: artist.name,
|
||||||
|
value: artist.id,
|
||||||
|
}));
|
||||||
|
}, [albumArtistListQuery?.data?.items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="0.8rem">
|
<Stack p="0.8rem">
|
||||||
<Group position="apart">
|
|
||||||
<Text>Year</Text>
|
|
||||||
<NumberInput
|
|
||||||
hideControls={false}
|
|
||||||
max={5000}
|
|
||||||
min={0}
|
|
||||||
value={filter.ndParams?.year}
|
|
||||||
width={80}
|
|
||||||
onChange={handleYearFilter}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
<Divider my="0.5rem" />
|
|
||||||
<Group
|
|
||||||
position="apart"
|
|
||||||
spacing={20}
|
|
||||||
>
|
|
||||||
<Text>Genre</Text>
|
|
||||||
<Select
|
|
||||||
clearable
|
|
||||||
searchable
|
|
||||||
data={genreList}
|
|
||||||
defaultValue={filter.ndParams?.genre_id}
|
|
||||||
width={150}
|
|
||||||
onChange={handleGenresFilter}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
<Divider my="0.5rem" />
|
|
||||||
{toggleFilters.map((filter) => (
|
{toggleFilters.map((filter) => (
|
||||||
<Group
|
<Group
|
||||||
key={`nd-filter-${filter.label}`}
|
key={`nd-filter-${filter.label}`}
|
||||||
|
@ -130,6 +135,39 @@ export const NavidromeAlbumFilters = ({ handleFilterChange }: NavidromeAlbumFilt
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
))}
|
))}
|
||||||
|
<Divider my="0.5rem" />
|
||||||
|
<Group grow>
|
||||||
|
<NumberInput
|
||||||
|
hideControls={false}
|
||||||
|
label="Year"
|
||||||
|
max={5000}
|
||||||
|
min={0}
|
||||||
|
value={filter.ndParams?.year}
|
||||||
|
onChange={handleYearFilter}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
clearable
|
||||||
|
searchable
|
||||||
|
data={genreList}
|
||||||
|
defaultValue={filter.ndParams?.genre_id}
|
||||||
|
label="Genre"
|
||||||
|
onChange={handleGenresFilter}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
<Group grow>
|
||||||
|
<Select
|
||||||
|
clearable
|
||||||
|
searchable
|
||||||
|
data={selectableAlbumArtists}
|
||||||
|
disabled={disableArtistFilter}
|
||||||
|
label="Artist"
|
||||||
|
limit={300}
|
||||||
|
placeholder="Type to search for an artist"
|
||||||
|
rightSection={albumArtistListQuery.isFetching ? <SpinnerIcon /> : undefined}
|
||||||
|
searchValue={albumArtistSearchTerm}
|
||||||
|
onSearchChange={setAlbumArtistSearchTerm}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,18 +5,49 @@ import { AlbumListContent } from '/@/renderer/features/albums/components/album-l
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query';
|
import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query';
|
||||||
import { useAlbumListFilters } from '/@/renderer/store';
|
import { useAlbumListFilters, useCurrentServer } from '/@/renderer/store';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
import { AlbumListQuery, ServerType } from '/@/renderer/api/types';
|
||||||
|
|
||||||
const AlbumListRoute = () => {
|
const AlbumListRoute = () => {
|
||||||
const gridRef = useRef<VirtualInfiniteGridRef | null>(null);
|
const gridRef = useRef<VirtualInfiniteGridRef | null>(null);
|
||||||
const tableRef = useRef<AgGridReactType | null>(null);
|
const tableRef = useRef<AgGridReactType | null>(null);
|
||||||
const filters = useAlbumListFilters();
|
const filters = useAlbumListFilters();
|
||||||
|
const server = useCurrentServer();
|
||||||
|
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const customFilters: Partial<AlbumListQuery> | undefined = searchParams.get('artistId')
|
||||||
|
? {
|
||||||
|
jfParams:
|
||||||
|
server?.type === ServerType.JELLYFIN
|
||||||
|
? {
|
||||||
|
artistIds: searchParams.get('artistId') as string,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
ndParams:
|
||||||
|
server?.type === ServerType.NAVIDROME
|
||||||
|
? {
|
||||||
|
artist_id: searchParams.get('artistId') as string,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const itemCountCheck = useAlbumList(
|
const itemCountCheck = useAlbumList(
|
||||||
{
|
{
|
||||||
limit: 1,
|
limit: 1,
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
...filters,
|
...filters,
|
||||||
|
...customFilters,
|
||||||
|
jfParams: {
|
||||||
|
...filters.jfParams,
|
||||||
|
...customFilters?.jfParams,
|
||||||
|
},
|
||||||
|
ndParams: {
|
||||||
|
...filters.ndParams,
|
||||||
|
...customFilters?.ndParams,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cacheTime: 1000 * 60 * 60 * 2,
|
cacheTime: 1000 * 60 * 60 * 2,
|
||||||
|
@ -33,11 +64,14 @@ const AlbumListRoute = () => {
|
||||||
<AnimatedPage>
|
<AnimatedPage>
|
||||||
<VirtualGridContainer>
|
<VirtualGridContainer>
|
||||||
<AlbumListHeader
|
<AlbumListHeader
|
||||||
|
customFilters={customFilters}
|
||||||
gridRef={gridRef}
|
gridRef={gridRef}
|
||||||
itemCount={itemCount}
|
itemCount={itemCount}
|
||||||
tableRef={tableRef}
|
tableRef={tableRef}
|
||||||
|
title={searchParams.get('artistName') || undefined}
|
||||||
/>
|
/>
|
||||||
<AlbumListContent
|
<AlbumListContent
|
||||||
|
customFilters={customFilters}
|
||||||
gridRef={gridRef}
|
gridRef={gridRef}
|
||||||
itemCount={itemCount}
|
itemCount={itemCount}
|
||||||
tableRef={tableRef}
|
tableRef={tableRef}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { Box, Group, Stack } from '@mantine/core';
|
||||||
import { RiArrowDownSLine, RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
|
import { RiArrowDownSLine, RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
|
||||||
import { generatePath, useParams } from 'react-router';
|
import { generatePath, useParams } from 'react-router';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
import { Link } from 'react-router-dom';
|
import { createSearchParams, Link } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useContainerQuery } from '/@/renderer/hooks';
|
import { useContainerQuery } from '/@/renderer/hooks';
|
||||||
|
@ -66,6 +66,13 @@ export const AlbumArtistDetailContent = () => {
|
||||||
|
|
||||||
const detailQuery = useAlbumArtistDetail({ id: albumArtistId });
|
const detailQuery = useAlbumArtistDetail({ id: albumArtistId });
|
||||||
|
|
||||||
|
const artistDiscographyLink = `${generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY, {
|
||||||
|
albumArtistId,
|
||||||
|
})}?${createSearchParams({
|
||||||
|
artistId: albumArtistId,
|
||||||
|
artistName: detailQuery?.data?.name || '',
|
||||||
|
})}`;
|
||||||
|
|
||||||
const recentAlbumsQuery = useAlbumList({
|
const recentAlbumsQuery = useAlbumList({
|
||||||
jfParams: server?.type === ServerType.JELLYFIN ? { artistIds: albumArtistId } : undefined,
|
jfParams: server?.type === ServerType.JELLYFIN ? { artistIds: albumArtistId } : undefined,
|
||||||
limit: itemsPerPage,
|
limit: itemsPerPage,
|
||||||
|
@ -146,9 +153,7 @@ export const AlbumArtistDetailContent = () => {
|
||||||
compact
|
compact
|
||||||
uppercase
|
uppercase
|
||||||
component={Link}
|
component={Link}
|
||||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY, {
|
to={artistDiscographyLink}
|
||||||
albumArtistId,
|
|
||||||
})}
|
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
>
|
>
|
||||||
View discography
|
View discography
|
||||||
|
@ -283,9 +288,7 @@ export const AlbumArtistDetailContent = () => {
|
||||||
compact
|
compact
|
||||||
uppercase
|
uppercase
|
||||||
component={Link}
|
component={Link}
|
||||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY, {
|
to={artistDiscographyLink}
|
||||||
albumArtistId,
|
|
||||||
})}
|
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
>
|
>
|
||||||
View discography
|
View discography
|
||||||
|
@ -302,7 +305,6 @@ export const AlbumArtistDetailContent = () => {
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{showGenres && (
|
{showGenres && (
|
||||||
<Box component="section">
|
<Box component="section">
|
||||||
<Group>
|
<Group>
|
||||||
|
|
|
@ -58,10 +58,6 @@ const AlbumArtistDetailTopSongsListRoute = lazy(
|
||||||
() => import('../features/artists/routes/album-artist-detail-top-songs-list-route'),
|
() => import('../features/artists/routes/album-artist-detail-top-songs-list-route'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const AlbumArtistDetailDiscographyRoute = lazy(
|
|
||||||
() => import('../features/artists/routes/album-artist-detail-discography-route'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const AlbumDetailRoute = lazy(
|
const AlbumDetailRoute = lazy(
|
||||||
() => import('/@/renderer/features/albums/routes/album-detail-route'),
|
() => import('/@/renderer/features/albums/routes/album-detail-route'),
|
||||||
);
|
);
|
||||||
|
@ -141,7 +137,7 @@ export const AppRouter = () => {
|
||||||
element={<AlbumArtistDetailRoute />}
|
element={<AlbumArtistDetailRoute />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
element={<AlbumArtistDetailDiscographyRoute />}
|
element={<AlbumListRoute />}
|
||||||
path={AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY}
|
path={AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_DISCOGRAPHY}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
|
|
Reference in a new issue