feishin/src/renderer/features/albums/components/album-list-header.tsx

230 lines
7.4 KiB
TypeScript
Raw Normal View History

2023-02-05 22:41:47 -08:00
import type { ChangeEvent, MutableRefObject } from 'react';
2022-12-25 16:27:09 -08:00
import { useCallback } from 'react';
2022-12-28 01:44:49 -08:00
import { IDatasource } from '@ag-grid-community/core';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { Flex, Group, Stack } from '@mantine/core';
import { useQueryClient } from '@tanstack/react-query';
import debounce from 'lodash/debounce';
2022-12-25 16:27:09 -08:00
import { api } from '/@/renderer/api';
import { controller } from '/@/renderer/api/controller';
import { queryKeys } from '/@/renderer/api/query-keys';
2023-02-05 22:41:47 -08:00
import { AlbumListQuery, LibraryItem } from '/@/renderer/api/types';
2023-03-31 05:22:51 -07:00
import { PageHeader, SearchInput, VirtualInfiniteGridRef } from '/@/renderer/components';
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
2022-12-25 16:27:09 -08:00
import { useContainerQuery } from '/@/renderer/hooks';
2022-12-21 01:27:19 -08:00
import {
2022-12-25 16:27:09 -08:00
AlbumListFilter,
2022-12-21 01:27:19 -08:00
useAlbumListStore,
2022-12-25 16:27:09 -08:00
useCurrentServer,
2023-03-05 18:28:26 -08:00
useListStoreActions,
2022-12-21 01:27:19 -08:00
} from '/@/renderer/store';
2023-02-05 22:41:47 -08:00
import { ListDisplayType, Play } from '/@/renderer/types';
import { AlbumListHeaderFilters } from '/@/renderer/features/albums/components/album-list-header-filters';
import { usePlayQueueAdd } from '/@/renderer/features/player';
2023-02-05 22:41:47 -08:00
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
2023-03-09 18:09:59 -08:00
import { useAlbumListContext } from '/@/renderer/features/albums/context/album-list-context';
2022-12-21 01:27:19 -08:00
2022-12-24 20:20:17 -08:00
interface AlbumListHeaderProps {
2023-01-15 16:22:07 -08:00
customFilters?: Partial<AlbumListFilter>;
2022-12-24 20:20:17 -08:00
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
itemCount?: number;
2022-12-28 01:44:49 -08:00
tableRef: MutableRefObject<AgGridReactType | null>;
2023-01-15 16:22:07 -08:00
title?: string;
2022-12-24 20:20:17 -08:00
}
2023-01-15 16:22:07 -08:00
export const AlbumListHeader = ({
itemCount,
gridRef,
tableRef,
title,
customFilters,
}: AlbumListHeaderProps) => {
2022-12-24 20:20:17 -08:00
const queryClient = useQueryClient();
2022-12-19 15:59:14 -08:00
const server = useCurrentServer();
2023-03-05 18:28:26 -08:00
const { setFilter, setTablePagination } = useListStoreActions();
2022-12-25 02:29:00 -08:00
const cq = useContainerQuery();
2023-03-09 18:09:59 -08:00
const { id, pageKey } = useAlbumListContext();
const { filter, display } = useAlbumListStore({ id, key: pageKey });
2022-12-19 15:59:14 -08:00
2022-12-24 20:20:17 -08:00
const fetch = useCallback(
async (skip: number, take: number, filters: AlbumListFilter) => {
2023-01-15 16:22:07 -08:00
const query: AlbumListQuery = {
2022-12-24 20:20:17 -08:00
limit: take,
startIndex: skip,
...filters,
2023-01-15 16:22:07 -08:00
jfParams: {
...filters.jfParams,
...customFilters?.jfParams,
},
ndParams: {
...filters.ndParams,
...customFilters?.ndParams,
},
...customFilters,
};
const queryKey = queryKeys.albums.list(server?.id || '', query);
2022-12-24 20:20:17 -08:00
2023-01-06 03:32:35 -08:00
const albums = await queryClient.fetchQuery(
queryKey,
async ({ signal }) =>
controller.getAlbumList({
2023-01-15 16:22:07 -08:00
query,
2023-01-06 03:32:35 -08:00
server,
signal,
}),
{ cacheTime: 1000 * 60 * 1 },
2022-12-24 20:20:17 -08:00
);
return api.normalize.albumList(albums, server);
},
2023-01-15 16:22:07 -08:00
[customFilters, queryClient, server],
2022-12-24 20:20:17 -08:00
);
const handleFilterChange = useCallback(
2022-12-26 05:08:01 -08:00
async (filters: AlbumListFilter) => {
2023-03-05 18:28:26 -08:00
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
2022-12-28 01:44:49 -08:00
const dataSource: IDatasource = {
getRows: async (params) => {
const limit = params.endRow - params.startRow;
const startIndex = params.startRow;
2023-01-15 16:22:07 -08:00
const query: AlbumListQuery = {
2022-12-28 01:44:49 -08:00
limit,
startIndex,
...filters,
2023-01-15 16:22:07 -08:00
...customFilters,
jfParams: {
...filters.jfParams,
...customFilters?.jfParams,
},
ndParams: {
...filters.ndParams,
...customFilters?.ndParams,
},
};
const queryKey = queryKeys.albums.list(server?.id || '', query);
2022-12-28 01:44:49 -08:00
2023-01-06 03:32:35 -08:00
const albumsRes = await queryClient.fetchQuery(
queryKey,
async ({ signal }) =>
api.controller.getAlbumList({
2023-01-15 16:22:07 -08:00
query,
2023-01-06 03:32:35 -08:00
server,
signal,
}),
{ cacheTime: 1000 * 60 * 1 },
2022-12-28 01:44:49 -08:00
);
const albums = api.normalize.albumList(albumsRes, server);
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
2022-12-28 01:44:49 -08:00
},
rowCount: undefined,
};
tableRef.current?.api.setDatasource(dataSource);
tableRef.current?.api.purgeInfiniteCache();
tableRef.current?.api.ensureIndexVisible(0, 'top');
2023-03-05 18:28:26 -08:00
if (display === ListDisplayType.TABLE_PAGINATED) {
setTablePagination({ data: { currentPage: 0 }, key: 'album' });
2022-12-28 01:44:49 -08:00
}
} else {
gridRef.current?.scrollTo(0);
gridRef.current?.resetLoadMoreItemsCache();
2022-12-24 20:20:17 -08:00
2022-12-28 01:44:49 -08:00
// 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);
2022-12-24 20:20:17 -08:00
2022-12-28 01:44:49 -08:00
if (!data?.items) return;
gridRef.current?.setItemData(data.items);
}
2022-12-24 20:20:17 -08:00
},
2023-03-05 18:28:26 -08:00
[display, tableRef, customFilters, server, queryClient, setTablePagination, gridRef, fetch],
2022-12-24 20:20:17 -08:00
);
2022-12-21 01:27:19 -08:00
const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => {
2023-03-05 18:28:26 -08:00
const previousSearchTerm = filter.searchTerm;
const searchTerm = e.target.value === '' ? undefined : e.target.value;
2023-03-05 18:28:26 -08:00
const updatedFilters = setFilter({ data: { searchTerm }, key: 'album' }) as AlbumListFilter;
if (previousSearchTerm !== searchTerm) handleFilterChange(updatedFilters);
2022-12-21 01:27:19 -08:00
}, 500);
const handlePlayQueueAdd = usePlayQueueAdd();
2023-02-05 22:41:47 -08:00
const playButtonBehavior = usePlayButtonBehavior();
const handlePlay = async (play: Play) => {
if (!itemCount || itemCount === 0) return;
2023-01-15 16:22:07 -08:00
const query = {
startIndex: 0,
2023-03-05 18:28:26 -08:00
...filter,
2023-01-15 16:22:07 -08:00
...customFilters,
jfParams: {
2023-03-05 18:28:26 -08:00
...filter.jfParams,
2023-01-15 16:22:07 -08:00
...customFilters?.jfParams,
},
ndParams: {
2023-03-05 18:28:26 -08:00
...filter.ndParams,
2023-01-15 16:22:07 -08:00
...customFilters?.ndParams,
},
};
const queryKey = queryKeys.albums.list(server?.id || '', query);
const albumListRes = await queryClient.fetchQuery({
queryFn: ({ signal }) => api.controller.getAlbumList({ query, server, signal }),
queryKey,
});
const albumIds =
api.normalize.albumList(albumListRes, server).items?.map((item) => item.id) || [];
handlePlayQueueAdd?.({
byItemType: {
id: albumIds,
type: LibraryItem.ALBUM,
},
play,
});
};
2022-12-19 15:59:14 -08:00
return (
2023-02-05 22:41:47 -08:00
<Stack
ref={cq.ref}
spacing={0}
>
<PageHeader backgroundColor="var(--titlebar-bg)">
2022-12-21 01:27:19 -08:00
<Flex
2023-02-05 22:41:47 -08:00
justify="space-between"
w="100%"
2022-12-19 15:59:14 -08:00
>
2023-02-05 22:41:47 -08:00
<LibraryHeaderBar>
<LibraryHeaderBar.PlayButton onClick={() => handlePlay(playButtonBehavior)} />
<LibraryHeaderBar.Title>{title || 'Albums'}</LibraryHeaderBar.Title>
2023-03-31 05:22:51 -07:00
<LibraryHeaderBar.Badge isLoading={itemCount === null || itemCount === undefined}>
{itemCount}
</LibraryHeaderBar.Badge>
2023-02-05 22:41:47 -08:00
</LibraryHeaderBar>
<Group>
<SearchInput
2023-03-05 18:28:26 -08:00
defaultValue={filter.searchTerm}
2023-02-05 22:41:47 -08:00
openedWidth={cq.isMd ? 250 : cq.isSm ? 200 : 150}
onChange={handleSearch}
/>
</Group>
2022-12-26 05:08:01 -08:00
</Flex>
2023-02-05 22:41:47 -08:00
</PageHeader>
2023-03-31 05:22:51 -07:00
<FilterBar>
2023-02-05 22:41:47 -08:00
<AlbumListHeaderFilters
customFilters={customFilters}
gridRef={gridRef}
itemCount={itemCount}
tableRef={tableRef}
/>
2023-03-31 05:22:51 -07:00
</FilterBar>
2023-02-05 22:41:47 -08:00
</Stack>
2022-12-19 15:59:14 -08:00
);
};