mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add new album artist list
This commit is contained in:
@@ -1,51 +1,139 @@
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { lazy, Suspense } from 'react';
|
||||
|
||||
import { lazy, MutableRefObject, Suspense } from 'react';
|
||||
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid/virtual-infinite-grid';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { useListStoreByKey } from '/@/renderer/store';
|
||||
import { useAlbumArtistListFilters } from '/@/renderer/features/artists/hooks/use-album-artist-list-filters';
|
||||
import { ItemListSettings, useCurrentServer, useListSettings } from '/@/renderer/store';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { ListDisplayType } from '/@/shared/types/types';
|
||||
import { ItemListKey, ListDisplayType, ListPaginationType } from '/@/shared/types/types';
|
||||
|
||||
const AlbumArtistListGridView = lazy(() =>
|
||||
import('/@/renderer/features/artists/components/album-artist-list-grid-view').then(
|
||||
const AlbumArtistListInfiniteGrid = lazy(() =>
|
||||
import('/@/renderer/features/artists/components/album-artist-list-infinite-grid').then(
|
||||
(module) => ({
|
||||
default: module.AlbumArtistListGridView,
|
||||
default: module.AlbumArtistListInfiniteGrid,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const AlbumArtistListTableView = lazy(() =>
|
||||
import('/@/renderer/features/artists/components/album-artist-list-table-view').then(
|
||||
const AlbumArtistListPaginatedGrid = lazy(() =>
|
||||
import('/@/renderer/features/artists/components/album-artist-list-paginated-grid').then(
|
||||
(module) => ({
|
||||
default: module.AlbumArtistListTableView,
|
||||
default: module.AlbumArtistListPaginatedGrid,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
interface AlbumArtistListContentProps {
|
||||
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
|
||||
itemCount?: number;
|
||||
tableRef: MutableRefObject<AgGridReactType | null>;
|
||||
}
|
||||
const AlbumArtistListInfiniteTable = lazy(() =>
|
||||
import('/@/renderer/features/artists/components/album-artist-list-infinite-table').then(
|
||||
(module) => ({
|
||||
default: module.AlbumArtistListInfiniteTable,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export const AlbumArtistListContent = ({
|
||||
gridRef,
|
||||
itemCount,
|
||||
tableRef,
|
||||
}: AlbumArtistListContentProps) => {
|
||||
const { pageKey } = useListContext();
|
||||
const { display } = useListStoreByKey({ key: pageKey });
|
||||
const isGrid = display === ListDisplayType.CARD || display === ListDisplayType.GRID;
|
||||
const AlbumArtistListPaginatedTable = lazy(() =>
|
||||
import('/@/renderer/features/artists/components/album-artist-list-paginated-table').then(
|
||||
(module) => ({
|
||||
default: module.AlbumArtistListPaginatedTable,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export const AlbumArtistListContent = () => {
|
||||
const { display, grid, itemsPerPage, pagination, table } = useListSettings(
|
||||
ItemListKey.ALBUM_ARTIST,
|
||||
);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{isGrid ? (
|
||||
<AlbumArtistListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<AlbumArtistListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
<AlbumArtistListView
|
||||
display={display}
|
||||
grid={grid}
|
||||
itemsPerPage={itemsPerPage}
|
||||
pagination={pagination}
|
||||
table={table}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export const AlbumArtistListView = ({
|
||||
display,
|
||||
grid,
|
||||
itemsPerPage,
|
||||
pagination,
|
||||
table,
|
||||
}: ItemListSettings) => {
|
||||
const server = useCurrentServer();
|
||||
|
||||
const { query } = useAlbumArtistListFilters();
|
||||
|
||||
switch (display) {
|
||||
case ListDisplayType.GRID: {
|
||||
switch (pagination) {
|
||||
case ListPaginationType.INFINITE: {
|
||||
return (
|
||||
<AlbumArtistListInfiniteGrid
|
||||
gap={grid.itemGap}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerRow={grid.itemsPerRowEnabled ? grid.itemsPerRow : undefined}
|
||||
query={query}
|
||||
serverId={server.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ListPaginationType.PAGINATED: {
|
||||
return (
|
||||
<AlbumArtistListPaginatedGrid
|
||||
gap={grid.itemGap}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerRow={grid.itemsPerRowEnabled ? grid.itemsPerRow : undefined}
|
||||
query={query}
|
||||
serverId={server.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case ListDisplayType.TABLE: {
|
||||
switch (pagination) {
|
||||
case ListPaginationType.INFINITE: {
|
||||
return (
|
||||
<AlbumArtistListInfiniteTable
|
||||
autoFitColumns={table.autoFitColumns}
|
||||
columns={table.columns}
|
||||
enableAlternateRowColors={table.enableAlternateRowColors}
|
||||
enableHorizontalBorders={table.enableHorizontalBorders}
|
||||
enableRowHoverHighlight={table.enableRowHoverHighlight}
|
||||
enableVerticalBorders={table.enableVerticalBorders}
|
||||
itemsPerPage={itemsPerPage}
|
||||
query={query}
|
||||
serverId={server.id}
|
||||
size={table.size}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ListPaginationType.PAGINATED: {
|
||||
return (
|
||||
<AlbumArtistListPaginatedTable
|
||||
autoFitColumns={table.autoFitColumns}
|
||||
columns={table.columns}
|
||||
enableAlternateRowColors={table.enableAlternateRowColors}
|
||||
enableHorizontalBorders={table.enableHorizontalBorders}
|
||||
enableRowHoverHighlight={table.enableRowHoverHighlight}
|
||||
enableVerticalBorders={table.enableVerticalBorders}
|
||||
itemsPerPage={itemsPerPage}
|
||||
query={query}
|
||||
serverId={server.id}
|
||||
size={table.size}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,451 +1,38 @@
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
|
||||
import { IDatasource } from '@ag-grid-community/core';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { MouseEvent, MutableRefObject, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid/virtual-infinite-grid';
|
||||
import { ALBUMARTIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
|
||||
import { FolderButton } from '/@/renderer/features/shared/components/folder-button';
|
||||
import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu';
|
||||
import { MoreButton } from '/@/renderer/features/shared/components/more-button';
|
||||
import { OrderToggleButton } from '/@/renderer/features/shared/components/order-toggle-button';
|
||||
import { RefreshButton } from '/@/renderer/features/shared/components/refresh-button';
|
||||
import { ListMusicFolderDropdown } from '/@/renderer/features/shared/components/list-music-folder-dropdown';
|
||||
import { ListRefreshButton } from '/@/renderer/features/shared/components/list-refresh-button';
|
||||
import { ListSortByDropdown } from '/@/renderer/features/shared/components/list-sort-by-dropdown';
|
||||
import { ListSortOrderToggleButton } from '/@/renderer/features/shared/components/list-sort-order-toggle-button';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import {
|
||||
AlbumArtistListFilter,
|
||||
PersistedTableColumn,
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
useListStoreByKey,
|
||||
} from '/@/renderer/store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import {
|
||||
AlbumArtistListQuery,
|
||||
AlbumArtistListSort,
|
||||
LibraryItem,
|
||||
ServerType,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ListDisplayType } from '/@/shared/types/types';
|
||||
import { AlbumArtistListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-types';
|
||||
import { ItemListKey } from '/@/shared/types/types';
|
||||
|
||||
const FILTERS = {
|
||||
jellyfin: [
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.album', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.ALBUM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.DURATION,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RANDOM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RECENTLY_ADDED,
|
||||
},
|
||||
// { defaultOrder: SortOrder.DESC, name: 'Release Date', value: AlbumArtistListSort.RELEASE_DATE },
|
||||
],
|
||||
navidrome: [
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.ALBUM_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.FAVORITED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.mostPlayed', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.PLAY_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RATING,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.songCount', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.SONG_COUNT,
|
||||
},
|
||||
],
|
||||
subsonic: [
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.ALBUM_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.FAVORITED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RATING,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
interface AlbumArtistListHeaderFiltersProps {
|
||||
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
|
||||
tableRef: MutableRefObject<AgGridReactType | null>;
|
||||
}
|
||||
|
||||
export const AlbumArtistListHeaderFilters = ({
|
||||
gridRef,
|
||||
tableRef,
|
||||
}: AlbumArtistListHeaderFiltersProps) => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const server = useCurrentServer();
|
||||
const { pageKey } = useListContext();
|
||||
const { display, filter, grid, table } = useListStoreByKey<AlbumArtistListQuery>({
|
||||
key: pageKey,
|
||||
});
|
||||
const { setDisplayType, setFilter, setGrid, setTable, setTablePagination } =
|
||||
useListStoreActions();
|
||||
export const AlbumArtistListHeaderFilters = () => {
|
||||
const cq = useContainerQuery();
|
||||
|
||||
const isGrid = display === ListDisplayType.CARD || display === ListDisplayType.GRID;
|
||||
const musicFoldersQuery = useQuery(
|
||||
sharedQueries.musicFolders({ query: null, serverId: server?.id }),
|
||||
);
|
||||
|
||||
const sortByLabel =
|
||||
(server?.type &&
|
||||
FILTERS[server.type as keyof typeof FILTERS].find((f) => f.value === filter.sortBy)
|
||||
?.name) ||
|
||||
t('common.unknown', { postProcess: 'titleCase' });
|
||||
|
||||
const handleItemSize = (e: number) => {
|
||||
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
|
||||
setTable({ data: { rowHeight: e }, key: pageKey });
|
||||
} else {
|
||||
setGrid({ data: { itemSize: e }, key: pageKey });
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemGap = (e: number) => {
|
||||
setGrid({ data: { itemGap: e }, key: pageKey });
|
||||
};
|
||||
|
||||
const debouncedHandleItemSize = debounce(handleItemSize, 20);
|
||||
|
||||
const fetch = useCallback(
|
||||
async (startIndex: number, limit: number, filters: AlbumArtistListFilter) => {
|
||||
const queryKey = queryKeys.albumArtists.list(server?.id || '', {
|
||||
limit,
|
||||
startIndex,
|
||||
...filters,
|
||||
});
|
||||
|
||||
const albums = await queryClient.fetchQuery({
|
||||
gcTime: 1000 * 60 * 1,
|
||||
queryFn: async ({ signal }) =>
|
||||
api.controller.getAlbumArtistList({
|
||||
apiClientProps: {
|
||||
serverId: server?.id || '',
|
||||
signal,
|
||||
},
|
||||
query: {
|
||||
limit,
|
||||
startIndex,
|
||||
...filters,
|
||||
},
|
||||
}),
|
||||
queryKey,
|
||||
});
|
||||
|
||||
return albums;
|
||||
},
|
||||
[queryClient, server],
|
||||
);
|
||||
|
||||
const handleFilterChange = useCallback(
|
||||
async (filters: AlbumArtistListFilter) => {
|
||||
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
|
||||
const dataSource: IDatasource = {
|
||||
getRows: async (params) => {
|
||||
const limit = params.endRow - params.startRow;
|
||||
const startIndex = params.startRow;
|
||||
|
||||
const queryKey = queryKeys.albumArtists.list(server?.id || '', {
|
||||
limit,
|
||||
startIndex,
|
||||
...filters,
|
||||
});
|
||||
|
||||
const albumArtistsRes = await queryClient.fetchQuery({
|
||||
gcTime: 1000 * 60 * 1,
|
||||
queryFn: async ({ signal }) =>
|
||||
api.controller.getAlbumArtistList({
|
||||
apiClientProps: {
|
||||
serverId: server?.id || '',
|
||||
signal,
|
||||
},
|
||||
query: {
|
||||
limit,
|
||||
startIndex,
|
||||
...filters,
|
||||
},
|
||||
}),
|
||||
queryKey,
|
||||
});
|
||||
|
||||
params.successCallback(
|
||||
albumArtistsRes?.items || [],
|
||||
albumArtistsRes?.totalRecordCount || 0,
|
||||
);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
tableRef.current?.api.setDatasource(dataSource);
|
||||
tableRef.current?.api.purgeInfiniteCache();
|
||||
tableRef.current?.api.ensureIndexVisible(0, 'top');
|
||||
|
||||
if (display === ListDisplayType.TABLE_PAGINATED) {
|
||||
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
|
||||
}
|
||||
} else {
|
||||
gridRef.current?.scrollTo(0);
|
||||
gridRef.current?.resetLoadMoreItemsCache();
|
||||
|
||||
// Refetching within the virtualized grid may be inconsistent due to it refetching
|
||||
// using an outdated set of filters. To avoid this, we fetch using the updated filters
|
||||
// and then set the grid's data here.
|
||||
const data = await fetch(0, 200, filters);
|
||||
|
||||
if (!data?.items) return;
|
||||
gridRef.current?.setItemData(data.items);
|
||||
}
|
||||
},
|
||||
[display, tableRef, server, queryClient, setTablePagination, pageKey, gridRef, fetch],
|
||||
);
|
||||
|
||||
const handleSetSortBy = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (!e.currentTarget?.value || !server?.type) return;
|
||||
|
||||
const sortOrder = FILTERS[server.type as keyof typeof FILTERS].find(
|
||||
(f) => f.value === e.currentTarget.value,
|
||||
)?.defaultOrder;
|
||||
|
||||
const updatedFilters = setFilter({
|
||||
data: {
|
||||
sortBy: e.currentTarget.value as AlbumArtistListSort,
|
||||
sortOrder: sortOrder || SortOrder.ASC,
|
||||
},
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
key: pageKey,
|
||||
}) as AlbumArtistListFilter;
|
||||
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
[handleFilterChange, pageKey, server?.type, setFilter],
|
||||
);
|
||||
|
||||
const handleSetMusicFolder = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (!e.currentTarget?.value) return;
|
||||
|
||||
let updatedFilters: AlbumArtistListFilter | null = null;
|
||||
if (e.currentTarget.value === String(filter.musicFolderId)) {
|
||||
updatedFilters = setFilter({
|
||||
data: { musicFolderId: undefined },
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
key: pageKey,
|
||||
}) as AlbumArtistListFilter;
|
||||
} else {
|
||||
updatedFilters = setFilter({
|
||||
data: { musicFolderId: e.currentTarget.value },
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
key: pageKey,
|
||||
}) as AlbumArtistListFilter;
|
||||
}
|
||||
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
[filter.musicFolderId, handleFilterChange, setFilter, pageKey],
|
||||
);
|
||||
|
||||
const handleToggleSortOrder = useCallback(() => {
|
||||
const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||
const updatedFilters = setFilter({
|
||||
data: { sortOrder: newSortOrder },
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
key: pageKey,
|
||||
}) as AlbumArtistListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
}, [filter.sortOrder, handleFilterChange, pageKey, setFilter]);
|
||||
|
||||
const handleSetViewType = useCallback(
|
||||
(displayType: ListDisplayType) => {
|
||||
setDisplayType({ data: displayType, key: pageKey });
|
||||
},
|
||||
[pageKey, setDisplayType],
|
||||
);
|
||||
|
||||
const handleTableColumns = (values: string[]) => {
|
||||
const existingColumns = table.columns;
|
||||
|
||||
if (values.length === 0) {
|
||||
return setTable({
|
||||
data: {
|
||||
columns: [],
|
||||
},
|
||||
key: pageKey,
|
||||
});
|
||||
}
|
||||
|
||||
// If adding a column
|
||||
if (values.length > existingColumns.length) {
|
||||
const newColumn = {
|
||||
column: values[values.length - 1],
|
||||
width: 100,
|
||||
} as PersistedTableColumn;
|
||||
|
||||
setTable({ data: { columns: [...existingColumns, newColumn] }, key: pageKey });
|
||||
} else {
|
||||
// If removing a column
|
||||
const removed = existingColumns.filter((column) => !values.includes(column.column));
|
||||
const newColumns = existingColumns.filter((column) => !removed.includes(column));
|
||||
|
||||
setTable({ data: { columns: newColumns }, key: pageKey });
|
||||
}
|
||||
|
||||
return tableRef.current?.api.sizeColumnsToFit();
|
||||
};
|
||||
|
||||
const handleAutoFitColumns = (autoFitColumns: boolean) => {
|
||||
setTable({ data: { autoFit: autoFitColumns }, key: pageKey });
|
||||
|
||||
if (autoFitColumns) {
|
||||
tableRef.current?.api.sizeColumnsToFit();
|
||||
}
|
||||
};
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.albumArtists.list(server?.id || '') });
|
||||
handleFilterChange(filter);
|
||||
}, [filter, handleFilterChange, queryClient, server?.id]);
|
||||
|
||||
const isFolderFilterApplied = useMemo(() => {
|
||||
return filter.musicFolderId !== undefined;
|
||||
}, [filter.musicFolderId]);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
{FILTERS[server?.type as keyof typeof FILTERS].map((f) => (
|
||||
<DropdownMenu.Item
|
||||
isSelected={f.value === filter.sortBy}
|
||||
key={`filter-${f.name}`}
|
||||
onClick={handleSetSortBy}
|
||||
value={f.value}
|
||||
>
|
||||
{f.name}
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<ListSortByDropdown
|
||||
defaultSortByValue={AlbumArtistListSort.NAME}
|
||||
itemType={LibraryItem.ALBUM_ARTIST}
|
||||
listKey={ItemListKey.ALBUM_ARTIST}
|
||||
/>
|
||||
<Divider orientation="vertical" />
|
||||
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
|
||||
{server?.type === ServerType.JELLYFIN && (
|
||||
<>
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<FolderButton isActive={!!isFolderFilterApplied} />
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
{musicFoldersQuery.data?.items.map((folder) => (
|
||||
<DropdownMenu.Item
|
||||
isSelected={filter.musicFolderId === folder.id}
|
||||
key={`musicFolder-${folder.id}`}
|
||||
onClick={handleSetMusicFolder}
|
||||
value={folder.id}
|
||||
>
|
||||
{folder.name}
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
)}
|
||||
<RefreshButton onClick={handleRefresh} />
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<MoreButton />
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
<DropdownMenu.Item
|
||||
leftSection={<Icon icon="refresh" />}
|
||||
onClick={handleRefresh}
|
||||
>
|
||||
{t('common.refresh', {
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<ListSortOrderToggleButton
|
||||
defaultSortOrder={SortOrder.ASC}
|
||||
listKey={ItemListKey.ALBUM_ARTIST}
|
||||
/>
|
||||
<ListMusicFolderDropdown listKey={ItemListKey.ALBUM_ARTIST} />
|
||||
<ListRefreshButton listKey={ItemListKey.ALBUM_ARTIST} />
|
||||
</Group>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ListConfigMenu
|
||||
autoFitColumns={table.autoFit}
|
||||
disabledViewTypes={[ListDisplayType.LIST]}
|
||||
displayType={display}
|
||||
itemGap={grid?.itemGap || 0}
|
||||
itemSize={isGrid ? grid?.itemSize || 0 : table.rowHeight}
|
||||
onChangeAutoFitColumns={handleAutoFitColumns}
|
||||
onChangeDisplayType={handleSetViewType}
|
||||
onChangeItemGap={handleItemGap}
|
||||
onChangeItemSize={debouncedHandleItemSize}
|
||||
onChangeTableColumns={handleTableColumns}
|
||||
tableColumns={table?.columns.map((column) => column.column)}
|
||||
listKey={ItemListKey.ALBUM_ARTIST}
|
||||
tableColumnsData={ALBUMARTIST_TABLE_COLUMNS}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
@@ -1,72 +1,40 @@
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import type { ChangeEvent, MutableRefObject } from 'react';
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { PageHeader } from '/@/renderer/components/page-header/page-header';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid/virtual-infinite-grid';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { AlbumArtistListHeaderFilters } from '/@/renderer/features/artists/components/album-artist-list-header-filters';
|
||||
import { FilterBar } from '/@/renderer/features/shared/components/filter-bar';
|
||||
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
|
||||
import { SearchInput } from '/@/renderer/features/shared/components/search-input';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { useDisplayRefresh } from '/@/renderer/hooks/use-display-refresh';
|
||||
import { AlbumArtistListFilter, useCurrentServer } from '/@/renderer/store';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
import { ListSearchInput } from '/@/renderer/features/shared/components/list-search-input';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { AlbumArtistListQuery, LibraryItem } from '/@/shared/types/domain-types';
|
||||
|
||||
interface AlbumArtistListHeaderProps {
|
||||
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
|
||||
itemCount?: number;
|
||||
tableRef: MutableRefObject<AgGridReactType | null>;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const AlbumArtistListHeader = ({
|
||||
gridRef,
|
||||
itemCount,
|
||||
tableRef,
|
||||
}: AlbumArtistListHeaderProps) => {
|
||||
export const AlbumArtistListHeader = ({ title }: AlbumArtistListHeaderProps) => {
|
||||
const { t } = useTranslation();
|
||||
const server = useCurrentServer();
|
||||
const cq = useContainerQuery();
|
||||
|
||||
const { filter, refresh, search } = useDisplayRefresh<AlbumArtistListQuery>({
|
||||
gridRef,
|
||||
itemCount,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
server,
|
||||
tableRef,
|
||||
});
|
||||
|
||||
const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = search(e) as AlbumArtistListFilter;
|
||||
refresh(updatedFilters);
|
||||
}, 500);
|
||||
const { itemCount } = useListContext();
|
||||
const pageTitle = title || t('page.albumArtistList.title', { postProcess: 'titleCase' });
|
||||
|
||||
return (
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>
|
||||
{t('page.albumArtistList.title', { postProcess: 'titleCase' })}
|
||||
</LibraryHeaderBar.Title>
|
||||
<LibraryHeaderBar.Badge
|
||||
isLoading={itemCount === null || itemCount === undefined}
|
||||
>
|
||||
{itemCount}
|
||||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
<Stack gap={0}>
|
||||
<PageHeader backgroundColor="var(--theme-colors-background)">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.PlayButton />
|
||||
<LibraryHeaderBar.Title>{pageTitle}</LibraryHeaderBar.Title>
|
||||
<LibraryHeaderBar.Badge isLoading={!itemCount}>
|
||||
{itemCount}
|
||||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<ListSearchInput />
|
||||
</Group>
|
||||
</PageHeader>
|
||||
<FilterBar>
|
||||
<AlbumArtistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
|
||||
<AlbumArtistListHeaderFilters />
|
||||
</FilterBar>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { useItemListInfiniteLoader } from '/@/renderer/components/item-list/helpers/item-list-infinite-loader';
|
||||
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
||||
import { ItemGridList } from '/@/renderer/components/item-list/item-grid-list/item-grid-list';
|
||||
import { ItemListGridComponentProps } from '/@/renderer/components/item-list/types';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import {
|
||||
AlbumArtistListQuery,
|
||||
AlbumArtistListSort,
|
||||
LibraryItem,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ItemListKey } from '/@/shared/types/types';
|
||||
|
||||
interface AlbumArtistListInfiniteGridProps extends ItemListGridComponentProps<AlbumArtistListQuery> {}
|
||||
|
||||
export const AlbumArtistListInfiniteGrid = forwardRef<any, AlbumArtistListInfiniteGridProps>(
|
||||
(
|
||||
{
|
||||
gap = 'md',
|
||||
itemsPerPage = 100,
|
||||
itemsPerRow,
|
||||
query = {
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
saveScrollOffset = true,
|
||||
serverId,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const listCountQuery = artistsQueries.albumArtistListCount({
|
||||
query: { ...query },
|
||||
serverId: serverId,
|
||||
}) as UseSuspenseQueryOptions<number, Error, number, readonly unknown[]>;
|
||||
|
||||
const listQueryFn = api.controller.getAlbumArtistList;
|
||||
|
||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
||||
eventKey: ItemListKey.ALBUM_ARTIST,
|
||||
itemsPerPage,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
listCountQuery,
|
||||
listQueryFn,
|
||||
query,
|
||||
serverId,
|
||||
});
|
||||
|
||||
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({
|
||||
enabled: saveScrollOffset,
|
||||
});
|
||||
|
||||
return (
|
||||
<ItemGridList
|
||||
data={data}
|
||||
gap={gap}
|
||||
initialTop={scrollOffset ?? 0}
|
||||
itemsPerRow={itemsPerRow}
|
||||
itemType={LibraryItem.ALBUM_ARTIST}
|
||||
onRangeChanged={onRangeChanged}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,87 @@
|
||||
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { useItemListInfiniteLoader } from '/@/renderer/components/item-list/helpers/item-list-infinite-loader';
|
||||
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
||||
import { ItemTableList } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { ItemListTableComponentProps } from '/@/renderer/components/item-list/types';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import {
|
||||
AlbumArtistListQuery,
|
||||
AlbumArtistListSort,
|
||||
LibraryItem,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ItemListKey } from '/@/shared/types/types';
|
||||
|
||||
interface AlbumArtistListInfiniteTableProps
|
||||
extends ItemListTableComponentProps<AlbumArtistListQuery> {}
|
||||
|
||||
export const AlbumArtistListInfiniteTable = forwardRef<any, AlbumArtistListInfiniteTableProps>(
|
||||
(
|
||||
{
|
||||
autoFitColumns = false,
|
||||
columns,
|
||||
enableAlternateRowColors = false,
|
||||
enableHorizontalBorders = false,
|
||||
enableRowHoverHighlight = true,
|
||||
enableSelection = true,
|
||||
enableVerticalBorders = false,
|
||||
itemsPerPage = 100,
|
||||
query = {
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
saveScrollOffset = true,
|
||||
serverId,
|
||||
size = 'default',
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const listCountQuery = artistsQueries.albumArtistListCount({
|
||||
query: { ...query },
|
||||
serverId: serverId,
|
||||
}) as UseSuspenseQueryOptions<number, Error, number, readonly unknown[]>;
|
||||
|
||||
const listQueryFn = api.controller.getAlbumArtistList;
|
||||
|
||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
||||
eventKey: ItemListKey.ALBUM_ARTIST,
|
||||
itemsPerPage,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
listCountQuery,
|
||||
listQueryFn,
|
||||
query,
|
||||
serverId,
|
||||
});
|
||||
|
||||
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({
|
||||
enabled: saveScrollOffset,
|
||||
});
|
||||
|
||||
return (
|
||||
<ItemTableList
|
||||
autoFitColumns={autoFitColumns}
|
||||
CellComponent={ItemTableListColumn}
|
||||
columns={columns}
|
||||
data={data}
|
||||
enableAlternateRowColors={enableAlternateRowColors}
|
||||
enableHorizontalBorders={enableHorizontalBorders}
|
||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||
enableSelection={enableSelection}
|
||||
enableVerticalBorders={enableVerticalBorders}
|
||||
initialTop={{
|
||||
to: scrollOffset ?? 0,
|
||||
type: 'offset',
|
||||
}}
|
||||
itemType={LibraryItem.ALBUM_ARTIST}
|
||||
onRangeChanged={onRangeChanged}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
ref={ref}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,84 @@
|
||||
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { useItemListPaginatedLoader } from '/@/renderer/components/item-list/helpers/item-list-paginated-loader';
|
||||
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
||||
import { ItemGridList } from '/@/renderer/components/item-list/item-grid-list/item-grid-list';
|
||||
import { ItemListWithPagination } from '/@/renderer/components/item-list/item-list-pagination/item-list-pagination';
|
||||
import { useItemListPagination } from '/@/renderer/components/item-list/item-list-pagination/use-item-list-pagination';
|
||||
import { ItemListGridComponentProps } from '/@/renderer/components/item-list/types';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import {
|
||||
AlbumArtistListQuery,
|
||||
AlbumArtistListSort,
|
||||
LibraryItem,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
|
||||
interface AlbumArtistListPaginatedGridProps
|
||||
extends ItemListGridComponentProps<AlbumArtistListQuery> {}
|
||||
|
||||
export const AlbumArtistListPaginatedGrid = forwardRef<any, AlbumArtistListPaginatedGridProps>(
|
||||
(
|
||||
{
|
||||
gap = 'md',
|
||||
itemsPerPage = 100,
|
||||
itemsPerRow,
|
||||
query = {
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
saveScrollOffset = true,
|
||||
serverId,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const listCountQuery = artistsQueries.albumArtistListCount({
|
||||
query: { ...query },
|
||||
serverId: serverId,
|
||||
}) as UseSuspenseQueryOptions<number, Error, number, readonly unknown[]>;
|
||||
|
||||
const listQueryFn = api.controller.getAlbumArtistList;
|
||||
|
||||
const { currentPage, onChange } = useItemListPagination();
|
||||
|
||||
const { data, pageCount, totalItemCount } = useItemListPaginatedLoader({
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
listCountQuery,
|
||||
listQueryFn,
|
||||
query,
|
||||
serverId,
|
||||
});
|
||||
|
||||
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({
|
||||
enabled: saveScrollOffset,
|
||||
});
|
||||
|
||||
return (
|
||||
<ItemListWithPagination
|
||||
currentPage={currentPage}
|
||||
itemsPerPage={itemsPerPage}
|
||||
onChange={onChange}
|
||||
pageCount={pageCount}
|
||||
totalItemCount={totalItemCount}
|
||||
>
|
||||
<ItemGridList
|
||||
currentPage={currentPage}
|
||||
data={data || []}
|
||||
gap={gap}
|
||||
initialTop={{
|
||||
to: scrollOffset ?? 0,
|
||||
type: 'offset',
|
||||
}}
|
||||
itemsPerRow={itemsPerRow}
|
||||
itemType={LibraryItem.ALBUM_ARTIST}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
ref={ref}
|
||||
/>
|
||||
</ItemListWithPagination>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,98 @@
|
||||
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { useItemListPaginatedLoader } from '/@/renderer/components/item-list/helpers/item-list-paginated-loader';
|
||||
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
||||
import { ItemListWithPagination } from '/@/renderer/components/item-list/item-list-pagination/item-list-pagination';
|
||||
import { useItemListPagination } from '/@/renderer/components/item-list/item-list-pagination/use-item-list-pagination';
|
||||
import { ItemTableList } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { ItemListTableComponentProps } from '/@/renderer/components/item-list/types';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import {
|
||||
AlbumArtistListQuery,
|
||||
AlbumArtistListSort,
|
||||
LibraryItem,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
|
||||
interface AlbumArtistListPaginatedTableProps
|
||||
extends ItemListTableComponentProps<AlbumArtistListQuery> {}
|
||||
|
||||
export const AlbumArtistListPaginatedTable = forwardRef<any, AlbumArtistListPaginatedTableProps>(
|
||||
(
|
||||
{
|
||||
autoFitColumns = false,
|
||||
columns,
|
||||
enableAlternateRowColors = false,
|
||||
enableHorizontalBorders = false,
|
||||
enableRowHoverHighlight = true,
|
||||
enableSelection = true,
|
||||
enableVerticalBorders = false,
|
||||
itemsPerPage = 100,
|
||||
query = {
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
saveScrollOffset = true,
|
||||
serverId,
|
||||
size = 'default',
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const listCountQuery = artistsQueries.albumArtistListCount({
|
||||
query: { ...query },
|
||||
serverId: serverId,
|
||||
}) as UseSuspenseQueryOptions<number, Error, number, readonly unknown[]>;
|
||||
|
||||
const listQueryFn = api.controller.getAlbumArtistList;
|
||||
|
||||
const { currentPage, onChange } = useItemListPagination();
|
||||
|
||||
const { data, pageCount, totalItemCount } = useItemListPaginatedLoader({
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
listCountQuery,
|
||||
listQueryFn,
|
||||
query,
|
||||
serverId,
|
||||
});
|
||||
|
||||
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({
|
||||
enabled: saveScrollOffset,
|
||||
});
|
||||
|
||||
return (
|
||||
<ItemListWithPagination
|
||||
currentPage={currentPage}
|
||||
itemsPerPage={itemsPerPage}
|
||||
onChange={onChange}
|
||||
pageCount={pageCount}
|
||||
totalItemCount={totalItemCount}
|
||||
>
|
||||
<ItemTableList
|
||||
autoFitColumns={autoFitColumns}
|
||||
CellComponent={ItemTableListColumn}
|
||||
columns={columns}
|
||||
currentPage={currentPage}
|
||||
data={data || []}
|
||||
enableAlternateRowColors={enableAlternateRowColors}
|
||||
enableHorizontalBorders={enableHorizontalBorders}
|
||||
enableRowHoverHighlight={enableRowHoverHighlight}
|
||||
enableSelection={enableSelection}
|
||||
enableVerticalBorders={enableVerticalBorders}
|
||||
initialTop={{
|
||||
to: scrollOffset ?? 0,
|
||||
type: 'offset',
|
||||
}}
|
||||
itemType={LibraryItem.ALBUM_ARTIST}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
ref={ref}
|
||||
size={size}
|
||||
/>
|
||||
</ItemListWithPagination>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useMusicFolderIdFilter } from '/@/renderer/features/shared/hooks/use-music-folder-id-filter';
|
||||
import { useSearchTermFilter } from '/@/renderer/features/shared/hooks/use-search-term-filter';
|
||||
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
|
||||
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
import { AlbumArtistListSort } from '/@/shared/types/domain-types';
|
||||
import { ItemListKey } from '/@/shared/types/types';
|
||||
|
||||
export const useAlbumArtistListFilters = () => {
|
||||
const { sortBy } = useSortByFilter<AlbumArtistListSort>(null, ItemListKey.ALBUM_ARTIST);
|
||||
|
||||
const { sortOrder } = useSortOrderFilter(null, ItemListKey.ALBUM_ARTIST);
|
||||
|
||||
const { musicFolderId } = useMusicFolderIdFilter(null, ItemListKey.ALBUM_ARTIST);
|
||||
|
||||
const { searchTerm, setSearchTerm } = useSearchTermFilter('');
|
||||
|
||||
const query = {
|
||||
[FILTER_KEYS.SHARED.MUSIC_FOLDER_ID]: musicFolderId ?? undefined,
|
||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: searchTerm ?? undefined,
|
||||
[FILTER_KEYS.SHARED.SORT_BY]: sortBy ?? undefined,
|
||||
[FILTER_KEYS.SHARED.SORT_ORDER]: sortOrder ?? undefined,
|
||||
};
|
||||
|
||||
return {
|
||||
query,
|
||||
setSearchTerm,
|
||||
};
|
||||
};
|
||||
@@ -1,56 +1,30 @@
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { ListContext } from '/@/renderer/context/list-context';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { AlbumArtistListContent } from '/@/renderer/features/artists/components/album-artist-list-content';
|
||||
import { AlbumArtistListHeader } from '/@/renderer/features/artists/components/album-artist-list-header';
|
||||
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
|
||||
import { useCurrentServer } from '/@/renderer/store/auth.store';
|
||||
import { useListFilterByKey } from '/@/renderer/store/list.store';
|
||||
import { AlbumArtistListQuery, LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { ItemListKey } from '/@/shared/types/types';
|
||||
|
||||
const AlbumArtistListRoute = () => {
|
||||
const gridRef = useRef<null>(null);
|
||||
const tableRef = useRef<AgGridReactType | null>(null);
|
||||
const pageKey = LibraryItem.ALBUM_ARTIST;
|
||||
const server = useCurrentServer();
|
||||
const pageKey = ItemListKey.ALBUM_ARTIST;
|
||||
|
||||
const albumArtistListFilter = useListFilterByKey<AlbumArtistListQuery>({ key: pageKey });
|
||||
|
||||
const itemCountCheck = useQuery(
|
||||
artistsQueries.albumArtistListCount({
|
||||
options: {
|
||||
gcTime: 1000 * 60,
|
||||
},
|
||||
query: albumArtistListFilter,
|
||||
serverId: server?.id,
|
||||
}),
|
||||
);
|
||||
|
||||
const itemCount = itemCountCheck.data === null ? undefined : itemCountCheck.data;
|
||||
const [itemCount, setItemCount] = useState<number | undefined>(undefined);
|
||||
|
||||
const providerValue = useMemo(() => {
|
||||
return {
|
||||
id: undefined,
|
||||
itemCount,
|
||||
pageKey,
|
||||
setItemCount,
|
||||
};
|
||||
}, [pageKey]);
|
||||
}, [itemCount, pageKey, setItemCount]);
|
||||
|
||||
return (
|
||||
<AnimatedPage>
|
||||
<ListContext.Provider value={providerValue}>
|
||||
<AlbumArtistListHeader
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
{/* <AlbumArtistListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/> */}
|
||||
<AlbumArtistListHeader />
|
||||
<AlbumArtistListContent />
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCurrentServer } from '/@/renderer/store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
|
||||
import {
|
||||
AlbumArtistListSort,
|
||||
AlbumListSort,
|
||||
LibraryItem,
|
||||
ServerType,
|
||||
@@ -357,7 +358,94 @@ const SONG_LIST_FILTERS: Partial<
|
||||
],
|
||||
};
|
||||
|
||||
const ALBUM_ARTIST_LIST_FILTERS: Partial<
|
||||
Record<ServerType, Array<{ defaultOrder: SortOrder; name: string; value: string }>>
|
||||
> = {
|
||||
[ServerType.JELLYFIN]: [
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.album', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.ALBUM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.DURATION,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RANDOM,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RECENTLY_ADDED,
|
||||
},
|
||||
],
|
||||
[ServerType.NAVIDROME]: [
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.ALBUM_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.FAVORITED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.mostPlayed', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.PLAY_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RATING,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.songCount', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.SONG_COUNT,
|
||||
},
|
||||
],
|
||||
[ServerType.SUBSONIC]: [
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.ALBUM_COUNT,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.FAVORITED,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.ASC,
|
||||
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.NAME,
|
||||
},
|
||||
{
|
||||
defaultOrder: SortOrder.DESC,
|
||||
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
|
||||
value: AlbumArtistListSort.RATING,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const FILTERS: Partial<Record<LibraryItem, any>> = {
|
||||
[LibraryItem.ALBUM]: ALBUM_LIST_FILTERS,
|
||||
[LibraryItem.ALBUM_ARTIST]: ALBUM_ARTIST_LIST_FILTERS,
|
||||
[LibraryItem.SONG]: SONG_LIST_FILTERS,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user