feishin/src/renderer/features/playlists/components/playlist-detail-song-list-content.tsx

268 lines
8.6 KiB
TypeScript
Raw Normal View History

2022-12-31 18:03:26 -08:00
import { MutableRefObject, useCallback, useMemo } from 'react';
import type {
BodyScrollEvent,
ColDef,
2023-01-01 13:58:05 -08:00
GridReadyEvent,
IDatasource,
PaginationChangedEvent,
2022-12-31 18:03:26 -08:00
RowDoubleClickedEvent,
} from '@ag-grid-community/core';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
2023-01-01 13:58:05 -08:00
import {
useCurrentServer,
usePlaylistDetailStore,
usePlaylistDetailTablePagination,
useSetPlaylistDetailTable,
useSetPlaylistDetailTablePagination,
} from '/@/renderer/store';
2023-01-05 21:59:07 -08:00
import { ListDisplayType } from '/@/renderer/types';
2023-01-01 13:58:05 -08:00
import { useQueryClient } from '@tanstack/react-query';
import { AnimatePresence } from 'framer-motion';
2022-12-31 18:03:26 -08:00
import debounce from 'lodash/debounce';
import { useHandleTableContextMenu } from '/@/renderer/features/context-menu';
import {
PLAYLIST_SONG_CONTEXT_MENU_ITEMS,
SMART_PLAYLIST_SONG_CONTEXT_MENU_ITEMS,
} from '/@/renderer/features/context-menu/context-menu-items';
2022-12-31 18:03:26 -08:00
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
2023-01-05 21:59:07 -08:00
import {
LibraryItem,
PlaylistSongListQuery,
QueueSong,
SongListSort,
SortOrder,
} from '/@/renderer/api/types';
2022-12-31 18:03:26 -08:00
import { usePlaylistSongList } from '/@/renderer/features/playlists/queries/playlist-song-list-query';
import { useParams } from 'react-router';
2022-12-31 19:26:58 -08:00
import { usePlayQueueAdd } from '/@/renderer/features/player';
2023-01-01 13:58:05 -08:00
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-grid';
import { getColumnDefs, VirtualTable, TablePagination } from '/@/renderer/components/virtual-table';
2022-12-31 18:03:26 -08:00
interface PlaylistDetailContentProps {
tableRef: MutableRefObject<AgGridReactType | null>;
}
2023-01-01 13:58:05 -08:00
export const PlaylistDetailSongListContent = ({ tableRef }: PlaylistDetailContentProps) => {
2022-12-31 18:03:26 -08:00
const { playlistId } = useParams() as { playlistId: string };
2023-01-01 13:58:05 -08:00
const queryClient = useQueryClient();
2022-12-31 18:03:26 -08:00
const server = useCurrentServer();
2023-01-01 13:58:05 -08:00
const page = usePlaylistDetailStore();
const filters: Partial<PlaylistSongListQuery> = useMemo(() => {
return {
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
};
}, [page?.table.id, playlistId]);
const detailQuery = usePlaylistDetail({ query: { id: playlistId }, serverId: server?.id });
2023-01-01 13:58:05 -08:00
const p = usePlaylistDetailTablePagination(playlistId);
const pagination = {
currentPage: p?.currentPage || 0,
itemsPerPage: p?.itemsPerPage || 100,
scrollOffset: p?.scrollOffset || 0,
totalItems: p?.totalItems || 1,
totalPages: p?.totalPages || 1,
};
2022-12-31 18:03:26 -08:00
2023-01-01 13:58:05 -08:00
const setPagination = useSetPlaylistDetailTablePagination();
const setTable = useSetPlaylistDetailTable();
2022-12-31 19:26:58 -08:00
const handlePlayQueueAdd = usePlayQueueAdd();
2022-12-31 18:03:26 -08:00
const playButtonBehavior = usePlayButtonBehavior();
2023-01-01 13:58:05 -08:00
const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED;
2022-12-31 18:03:26 -08:00
2023-01-01 13:58:05 -08:00
const checkPlaylistList = usePlaylistSongList({
query: {
id: playlistId,
limit: 1,
startIndex: 0,
},
serverId: server?.id,
2022-12-31 18:03:26 -08:00
});
const columnDefs: ColDef[] = useMemo(
() => getColumnDefs(page.table.columns),
[page.table.columns],
);
2023-01-01 13:58:05 -08:00
const onGridReady = useCallback(
(params: GridReadyEvent) => {
const dataSource: IDatasource = {
getRows: async (params) => {
const limit = params.endRow - params.startRow;
const startIndex = params.startRow;
const queryKey = queryKeys.playlists.songList(server?.id || '', playlistId, {
id: playlistId,
limit,
startIndex,
...filters,
});
if (!server) return;
const songsRes = await queryClient.fetchQuery(
queryKey,
async ({ signal }) =>
api.controller.getPlaylistSongList({
apiClientProps: {
server,
signal,
},
query: {
id: playlistId,
limit,
startIndex,
...filters,
},
}),
{ cacheTime: 1000 * 60 * 1 },
2023-01-01 13:58:05 -08:00
);
params.successCallback(songsRes?.items || [], songsRes?.totalRecordCount || 0);
2023-01-01 13:58:05 -08:00
},
rowCount: undefined,
};
params.api.setDatasource(dataSource);
params.api?.ensureIndexVisible(pagination.scrollOffset, 'top');
2023-01-01 13:58:05 -08:00
},
[filters, pagination.scrollOffset, playlistId, queryClient, server],
);
const handleGridSizeChange = () => {
if (page.table.autoFit) {
tableRef?.current?.api.sizeColumnsToFit();
}
};
2023-01-01 13:58:05 -08:00
const onPaginationChanged = useCallback(
(event: PaginationChangedEvent) => {
if (!isPaginationEnabled || !event.api) return;
try {
// Scroll to top of page on pagination change
const currentPageStartIndex = pagination.currentPage * pagination.itemsPerPage;
event.api?.ensureIndexVisible(currentPageStartIndex, 'top');
} catch (err) {
console.log(err);
}
2023-01-01 13:58:05 -08:00
setPagination(playlistId, {
itemsPerPage: event.api.paginationGetPageSize(),
totalItems: event.api.paginationGetRowCount(),
totalPages: event.api.paginationGetTotalPages() + 1,
});
},
[
isPaginationEnabled,
pagination.currentPage,
pagination.itemsPerPage,
playlistId,
setPagination,
],
);
2022-12-31 18:03:26 -08:00
const handleColumnChange = useCallback(() => {
const { columnApi } = tableRef?.current || {};
const columnsOrder = columnApi?.getAllGridColumns();
if (!columnsOrder) return;
const columnsInSettings = page.table.columns;
const updatedColumns = [];
for (const column of columnsOrder) {
const columnInSettings = columnsInSettings.find((c) => c.column === column.getColDef().colId);
if (columnInSettings) {
updatedColumns.push({
...columnInSettings,
...(!page.table.autoFit && {
width: column.getActualWidth(),
}),
});
}
}
setTable({ columns: updatedColumns });
}, [page.table.autoFit, page.table.columns, setTable, tableRef]);
const debouncedColumnChange = debounce(handleColumnChange, 200);
const handleScroll = (e: BodyScrollEvent) => {
const scrollOffset = Number((e.top / page.table.rowHeight).toFixed(0));
2023-01-01 13:58:05 -08:00
setPagination(playlistId, { scrollOffset });
2022-12-31 18:03:26 -08:00
};
const contextMenuItems = useMemo(() => {
if (detailQuery?.data?.rules) {
return SMART_PLAYLIST_SONG_CONTEXT_MENU_ITEMS;
}
return PLAYLIST_SONG_CONTEXT_MENU_ITEMS;
}, [detailQuery?.data?.rules]);
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, contextMenuItems, {
playlistId,
tableRef,
});
2022-12-31 18:03:26 -08:00
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
2022-12-31 19:26:58 -08:00
handlePlayQueueAdd?.({
2022-12-31 18:03:26 -08:00
byData: [e.data],
2023-05-20 14:55:08 -07:00
playType: playButtonBehavior,
2022-12-31 18:03:26 -08:00
});
};
return (
<>
<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
key={`table-${page.display}-${page.table.rowHeight}-${server?.id}`}
ref={tableRef}
alwaysShowHorizontalScroll
autoFitColumns={page.table.autoFit}
columnDefs={columnDefs}
getRowId={(data) => data.data.uniqueId}
infiniteInitialRowCount={checkPlaylistList.data?.totalRecordCount || 100}
pagination={isPaginationEnabled}
paginationAutoPageSize={isPaginationEnabled}
paginationPageSize={pagination.itemsPerPage || 100}
rowHeight={page.table.rowHeight || 40}
rowModelType="infinite"
onBodyScrollEnd={handleScroll}
onCellContextMenu={handleContextMenu}
onColumnMoved={handleColumnChange}
onColumnResized={debouncedColumnChange}
onGridReady={onGridReady}
onGridSizeChanged={handleGridSizeChange}
onPaginationChanged={onPaginationChanged}
onRowDoubleClicked={handleRowDoubleClick}
/>
</VirtualGridAutoSizerContainer>
{isPaginationEnabled && (
<AnimatePresence
presenceAffectsLayout
initial={false}
mode="wait"
>
{page.display === ListDisplayType.TABLE_PAGINATED && (
<TablePagination
2023-03-05 18:47:24 -08:00
pageKey={playlistId}
pagination={pagination}
setIdPagination={setPagination}
tableRef={tableRef}
/>
)}
</AnimatePresence>
)}
</>
2022-12-31 18:03:26 -08:00
);
};