feishin/src/renderer/features/songs/components/song-list-content.tsx

216 lines
7.1 KiB
TypeScript
Raw Normal View History

2022-12-27 13:52:50 -08:00
import { MutableRefObject, useCallback, useMemo } from 'react';
import type {
BodyScrollEvent,
ColDef,
GridReadyEvent,
IDatasource,
PaginationChangedEvent,
RowDoubleClickedEvent,
2022-12-27 13:52:50 -08:00
} from '@ag-grid-community/core';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { Stack } from '@mantine/core';
import { useQueryClient } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import {
useCurrentServer,
2023-03-05 18:28:26 -08:00
useListStoreActions,
useSongListFilter,
2022-12-27 13:52:50 -08:00
useSongListStore,
} from '/@/renderer/store';
2023-01-05 21:59:07 -08:00
import { ListDisplayType } from '/@/renderer/types';
2022-12-27 13:52:50 -08:00
import { AnimatePresence } from 'framer-motion';
import debounce from 'lodash/debounce';
import { useHandleTableContextMenu } from '/@/renderer/features/context-menu';
2022-12-28 15:32:50 -08:00
import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
2023-01-15 20:39:43 -08:00
import { LibraryItem, QueueSong, SongListQuery } from '/@/renderer/api/types';
2023-03-05 18:28:26 -08:00
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-grid';
import { getColumnDefs, VirtualTable, TablePagination } from '/@/renderer/components/virtual-table';
2022-12-27 13:52:50 -08:00
interface SongListContentProps {
itemCount?: number;
2022-12-27 13:52:50 -08:00
tableRef: MutableRefObject<AgGridReactType | null>;
}
2023-03-05 18:28:26 -08:00
export const SongListContent = ({ itemCount, tableRef }: SongListContentProps) => {
2022-12-27 13:52:50 -08:00
const queryClient = useQueryClient();
const server = useCurrentServer();
const { id, pageKey, handlePlay } = useSongListContext();
2023-03-05 18:28:26 -08:00
const filter = useSongListFilter({ id, key: pageKey });
2023-03-09 18:09:59 -08:00
const { display, table } = useSongListStore({ id, key: pageKey });
2023-03-05 18:28:26 -08:00
const { setTable, setTablePagination } = useListStoreActions();
const playButtonBehavior = usePlayButtonBehavior();
2022-12-27 13:52:50 -08:00
2023-03-05 18:28:26 -08:00
const isPaginationEnabled = display === ListDisplayType.TABLE_PAGINATED;
2022-12-27 13:52:50 -08:00
2023-03-05 18:28:26 -08:00
const columnDefs: ColDef[] = useMemo(() => getColumnDefs(table.columns), [table.columns]);
2022-12-27 13:52:50 -08:00
const onGridReady = useCallback(
(params: GridReadyEvent) => {
const dataSource: IDatasource = {
getRows: async (params) => {
const limit = params.endRow - params.startRow;
const startIndex = params.startRow;
2023-01-15 20:39:43 -08:00
const query: SongListQuery = {
2022-12-27 13:52:50 -08:00
limit,
startIndex,
2023-03-05 18:28:26 -08:00
...filter,
2023-01-15 20:39:43 -08:00
};
const queryKey = queryKeys.songs.list(server?.id || '', query);
2022-12-27 13:52:50 -08:00
const songsRes = await queryClient.fetchQuery(
queryKey,
async ({ signal }) =>
api.controller.getSongList({
apiClientProps: {
server,
signal,
},
2023-01-15 20:39:43 -08:00
query,
}),
{ cacheTime: 1000 * 60 * 1 },
2022-12-27 13:52:50 -08:00
);
2023-05-08 03:35:23 -07:00
params.successCallback(songsRes?.items || [], songsRes?.totalRecordCount || 0);
2022-12-27 13:52:50 -08:00
},
rowCount: undefined,
};
params.api.setDatasource(dataSource);
2023-03-05 18:28:26 -08:00
params.api.ensureIndexVisible(table.scrollOffset, 'top');
2022-12-27 13:52:50 -08:00
},
2023-03-05 18:28:26 -08:00
[filter, table.scrollOffset, queryClient, server],
2022-12-27 13:52:50 -08:00
);
const onPaginationChanged = useCallback(
(event: PaginationChangedEvent) => {
if (!isPaginationEnabled || !event.api) return;
try {
// Scroll to top of page on pagination change
2023-03-05 18:28:26 -08:00
const currentPageStartIndex = table.pagination.currentPage * table.pagination.itemsPerPage;
event.api?.ensureIndexVisible(currentPageStartIndex, 'top');
} catch (err) {
console.log(err);
}
2023-03-05 18:28:26 -08:00
setTablePagination({
data: {
itemsPerPage: event.api.paginationGetPageSize(),
totalItems: event.api.paginationGetRowCount(),
totalPages: event.api.paginationGetTotalPages() + 1,
},
key: pageKey,
2022-12-27 13:52:50 -08:00
});
},
2023-03-05 18:28:26 -08:00
[
isPaginationEnabled,
pageKey,
setTablePagination,
table.pagination.currentPage,
table.pagination.itemsPerPage,
],
2022-12-27 13:52:50 -08:00
);
const handleGridSizeChange = () => {
2023-03-05 18:28:26 -08:00
if (table.autoFit) {
2022-12-27 13:52:50 -08:00
tableRef?.current?.api.sizeColumnsToFit();
}
};
2022-12-28 01:23:54 -08:00
const handleColumnChange = useCallback(() => {
2022-12-27 13:52:50 -08:00
const { columnApi } = tableRef?.current || {};
const columnsOrder = columnApi?.getAllGridColumns();
if (!columnsOrder) return;
2023-03-05 18:28:26 -08:00
const columnsInSettings = table.columns;
2022-12-27 13:52:50 -08:00
const updatedColumns = [];
for (const column of columnsOrder) {
const columnInSettings = columnsInSettings.find((c) => c.column === column.getColDef().colId);
if (columnInSettings) {
updatedColumns.push({
...columnInSettings,
2023-03-05 18:28:26 -08:00
...(!table.autoFit && {
2022-12-28 01:23:54 -08:00
width: column.getActualWidth(),
2022-12-27 13:52:50 -08:00
}),
});
}
}
2023-03-05 18:28:26 -08:00
setTable({ data: { columns: updatedColumns }, key: pageKey });
}, [tableRef, table.columns, table.autoFit, setTable, pageKey]);
2022-12-27 13:52:50 -08:00
2022-12-28 01:23:54 -08:00
const debouncedColumnChange = debounce(handleColumnChange, 200);
const handleScroll = (e: BodyScrollEvent) => {
2023-03-05 18:28:26 -08:00
const scrollOffset = Number((e.top / table.rowHeight).toFixed(0));
setTable({ data: { scrollOffset }, key: pageKey });
2022-12-28 01:23:54 -08:00
};
2022-12-27 13:52:50 -08:00
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS);
2022-12-28 15:32:50 -08:00
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
handlePlay?.({ initialSongId: e.data.id, playType: playButtonBehavior });
};
2022-12-27 13:52:50 -08:00
return (
<Stack
h="100%"
spacing={0}
>
<VirtualGridAutoSizerContainer>
<VirtualTable
// https://github.com/ag-grid/ag-grid/issues/5284
// Key is used to force remount of table when display, rowHeight, or server changes
2023-03-05 18:28:26 -08:00
key={`table-${display}-${table.rowHeight}-${server?.id}`}
2022-12-27 13:52:50 -08:00
ref={tableRef}
alwaysShowHorizontalScroll
suppressRowDrag
2023-03-05 18:28:26 -08:00
autoFitColumns={table.autoFit}
2022-12-27 13:52:50 -08:00
blockLoadDebounceMillis={200}
columnDefs={columnDefs}
2022-12-28 01:23:54 -08:00
getRowId={(data) => data.data.id}
infiniteInitialRowCount={itemCount || 100}
2022-12-27 13:52:50 -08:00
pagination={isPaginationEnabled}
paginationAutoPageSize={isPaginationEnabled}
2023-03-05 18:28:26 -08:00
paginationPageSize={table.pagination.itemsPerPage || 100}
2022-12-27 13:52:50 -08:00
rowBuffer={20}
2023-03-05 18:28:26 -08:00
rowHeight={table.rowHeight || 40}
2022-12-27 13:52:50 -08:00
rowModelType="infinite"
rowSelection="multiple"
onBodyScrollEnd={handleScroll}
2022-12-28 15:32:50 -08:00
onCellContextMenu={handleContextMenu}
2022-12-28 01:23:54 -08:00
onColumnMoved={handleColumnChange}
onColumnResized={debouncedColumnChange}
2022-12-27 13:52:50 -08:00
onGridReady={onGridReady}
onGridSizeChanged={handleGridSizeChange}
onPaginationChanged={onPaginationChanged}
onRowDoubleClicked={handleRowDoubleClick}
2022-12-27 13:52:50 -08:00
/>
</VirtualGridAutoSizerContainer>
<AnimatePresence
presenceAffectsLayout
2022-12-28 01:23:54 -08:00
initial={false}
2022-12-27 13:52:50 -08:00
mode="wait"
>
2023-03-05 18:28:26 -08:00
{display === ListDisplayType.TABLE_PAGINATED && (
2022-12-27 13:52:50 -08:00
<TablePagination
2023-03-05 18:28:26 -08:00
pageKey={pageKey}
pagination={table.pagination}
setPagination={setTablePagination}
2022-12-27 13:52:50 -08:00
tableRef={tableRef}
/>
)}
</AnimatePresence>
</Stack>
);
};