Update playlist list implementation
This commit is contained in:
parent
8b4a2d1ac0
commit
85964bfded
8 changed files with 77 additions and 130 deletions
|
@ -1,8 +1,8 @@
|
|||
import { MutableRefObject, useMemo, useRef } from 'react';
|
||||
import { ColDef, RowDoubleClickedEvent } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Box, Group } from '@mantine/core';
|
||||
import { closeAllModals, openModal } from '@mantine/modals';
|
||||
import { MutableRefObject, useMemo, useRef } from 'react';
|
||||
import { RiMoreFill } from 'react-icons/ri';
|
||||
import { generatePath, useNavigate, useParams } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
@ -27,9 +27,10 @@ import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playli
|
|||
import { usePlaylistSongListInfinite } from '/@/renderer/features/playlists/queries/playlist-song-list-query';
|
||||
import { PlayButton, PLAY_TYPES } from '/@/renderer/features/shared';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer, useSongListStore } from '/@/renderer/store';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { Play } from '/@/renderer/types';
|
||||
import { useListStoreByKey } from '../../../store/list.store';
|
||||
|
||||
const ContentContainer = styled.div`
|
||||
position: relative;
|
||||
|
@ -54,7 +55,7 @@ interface PlaylistDetailContentProps {
|
|||
export const PlaylistDetailContent = ({ tableRef }: PlaylistDetailContentProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { playlistId } = useParams() as { playlistId: string };
|
||||
const page = useSongListStore();
|
||||
const { table } = useListStoreByKey({ key: LibraryItem.SONG });
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
const server = useCurrentServer();
|
||||
const detailQuery = usePlaylistDetail({ query: { id: playlistId }, serverId: server?.id });
|
||||
|
@ -79,10 +80,8 @@ export const PlaylistDetailContent = ({ tableRef }: PlaylistDetailContentProps)
|
|||
|
||||
const columnDefs: ColDef[] = useMemo(
|
||||
() =>
|
||||
getColumnDefs(page.table.columns).filter(
|
||||
(c) => c.colId !== 'album' && c.colId !== 'artist',
|
||||
),
|
||||
[page.table.columns],
|
||||
getColumnDefs(table.columns).filter((c) => c.colId !== 'album' && c.colId !== 'artist'),
|
||||
[table.columns],
|
||||
);
|
||||
|
||||
const contextMenuItems = useMemo(() => {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { lazy, MutableRefObject, Suspense } from 'react';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Spinner } from '/@/renderer/components';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
||||
import { usePlaylistListStore } from '/@/renderer/store';
|
||||
import { ListDisplayType } from '/@/renderer/types';
|
||||
import { useListContext } from '../../../context/list-context';
|
||||
import { useListStoreByKey } from '../../../store/list.store';
|
||||
|
||||
const PlaylistListTableView = lazy(() =>
|
||||
import('/@/renderer/features/playlists/components/playlist-list-table-view').then((module) => ({
|
||||
|
@ -24,7 +25,8 @@ interface PlaylistListContentProps {
|
|||
}
|
||||
|
||||
export const PlaylistListContent = ({ gridRef, tableRef, itemCount }: PlaylistListContentProps) => {
|
||||
const { display } = usePlaylistListStore();
|
||||
const { pageKey } = useListContext();
|
||||
const { display } = useListStoreByKey({ key: pageKey });
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
|
|
|
@ -2,7 +2,8 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||
import { MutableRefObject, useCallback, useMemo } from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { ListOnScrollProps } from 'react-window';
|
||||
import { usePlaylistGridStore, usePlaylistStoreActions } from '../../../store/playlist.store';
|
||||
import { useListContext } from '../../../context/list-context';
|
||||
import { useListStoreActions } from '../../../store/list.store';
|
||||
import { controller } from '/@/renderer/api/controller';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, Playlist, PlaylistListQuery, PlaylistListSort } from '/@/renderer/api/types';
|
||||
|
@ -15,7 +16,7 @@ import {
|
|||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer, useGeneralSettings, usePlaylistListStore } from '/@/renderer/store';
|
||||
import { useCurrentServer, useGeneralSettings, useListStoreByKey } from '/@/renderer/store';
|
||||
import { CardRow, ListDisplayType } from '/@/renderer/types';
|
||||
|
||||
interface PlaylistListGridViewProps {
|
||||
|
@ -24,13 +25,12 @@ interface PlaylistListGridViewProps {
|
|||
}
|
||||
|
||||
export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridViewProps) => {
|
||||
const { pageKey } = useListContext();
|
||||
const queryClient = useQueryClient();
|
||||
const server = useCurrentServer();
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
const { display } = usePlaylistListStore();
|
||||
const grid = usePlaylistGridStore();
|
||||
const { setGrid } = usePlaylistStoreActions();
|
||||
const page = usePlaylistListStore();
|
||||
const { display, grid, filter } = useListStoreByKey({ key: pageKey });
|
||||
const { setGrid } = useListStoreActions();
|
||||
const { defaultFullPlaylist } = useGeneralSettings();
|
||||
|
||||
const createFavoriteMutation = useCreateFavorite({});
|
||||
|
@ -66,7 +66,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||
? [PLAYLIST_CARD_ROWS.nameFull]
|
||||
: [PLAYLIST_CARD_ROWS.name];
|
||||
|
||||
switch (page.filter.sortBy) {
|
||||
switch (filter.sortBy) {
|
||||
case PlaylistListSort.DURATION:
|
||||
rows.push(PLAYLIST_CARD_ROWS.duration);
|
||||
break;
|
||||
|
@ -87,13 +87,13 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||
}
|
||||
|
||||
return rows;
|
||||
}, [defaultFullPlaylist, page.filter.sortBy]);
|
||||
}, [defaultFullPlaylist, filter.sortBy]);
|
||||
|
||||
const handleGridScroll = useCallback(
|
||||
(e: ListOnScrollProps) => {
|
||||
setGrid({ data: { scrollOffset: e.scrollOffset } });
|
||||
setGrid({ data: { scrollOffset: e.scrollOffset }, key: pageKey });
|
||||
},
|
||||
[setGrid],
|
||||
[pageKey, setGrid],
|
||||
);
|
||||
|
||||
const fetch = useCallback(
|
||||
|
@ -105,7 +105,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||
const query: PlaylistListQuery = {
|
||||
limit: take,
|
||||
startIndex: skip,
|
||||
...page.filter,
|
||||
...filter,
|
||||
_custom: {},
|
||||
};
|
||||
|
||||
|
@ -123,7 +123,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||
|
||||
return playlists;
|
||||
},
|
||||
[page.filter, queryClient, server],
|
||||
[filter, queryClient, server],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -4,6 +4,8 @@ import { Divider, Flex, Group, Stack } from '@mantine/core';
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { ChangeEvent, MouseEvent, MutableRefObject, useCallback } from 'react';
|
||||
import { RiMoreFill, RiRefreshLine, RiSettings3Fill } from 'react-icons/ri';
|
||||
import { useListContext } from '../../../context/list-context';
|
||||
import { useListStoreByKey } from '../../../store/list.store';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, PlaylistListQuery, PlaylistListSort, SortOrder } from '/@/renderer/api/types';
|
||||
|
@ -12,12 +14,7 @@ import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
|||
import { PLAYLIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||
import { OrderToggleButton } from '/@/renderer/features/shared';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import {
|
||||
PlaylistListFilter,
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
usePlaylistListStore,
|
||||
} from '/@/renderer/store';
|
||||
import { PlaylistListFilter, useCurrentServer, useListStoreActions } from '/@/renderer/store';
|
||||
import { ListDisplayType, TableColumn } from '/@/renderer/types';
|
||||
|
||||
const FILTERS = {
|
||||
|
@ -45,12 +42,12 @@ export const PlaylistListHeaderFilters = ({
|
|||
gridRef,
|
||||
tableRef,
|
||||
}: PlaylistListHeaderFiltersProps) => {
|
||||
const pageKey = 'playlist';
|
||||
const { pageKey } = useListContext();
|
||||
const queryClient = useQueryClient();
|
||||
const server = useCurrentServer();
|
||||
const { setFilter, setTable, setTablePagination, setGrid, setDisplayType } =
|
||||
useListStoreActions();
|
||||
const { display, filter, table, grid } = usePlaylistListStore({ key: pageKey });
|
||||
const { display, filter, table, grid } = useListStoreByKey({ key: pageKey });
|
||||
const cq = useContainerQuery();
|
||||
|
||||
const isGrid = display === ListDisplayType.CARD || display === ListDisplayType.POSTER;
|
||||
|
@ -146,7 +143,17 @@ export const PlaylistListHeaderFilters = ({
|
|||
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
|
||||
}
|
||||
},
|
||||
[isGrid, gridRef, fetch, filter, tableRef, setTablePagination, server, queryClient],
|
||||
[
|
||||
isGrid,
|
||||
gridRef,
|
||||
fetch,
|
||||
filter,
|
||||
tableRef,
|
||||
setTablePagination,
|
||||
pageKey,
|
||||
server,
|
||||
queryClient,
|
||||
],
|
||||
);
|
||||
|
||||
const handleSetSortBy = useCallback(
|
||||
|
@ -168,7 +175,7 @@ export const PlaylistListHeaderFilters = ({
|
|||
|
||||
handleFilterChange(updatedFilters);
|
||||
},
|
||||
[handleFilterChange, server?.type, setFilter],
|
||||
[handleFilterChange, pageKey, server?.type, setFilter],
|
||||
);
|
||||
|
||||
const handleToggleSortOrder = useCallback(() => {
|
||||
|
@ -179,14 +186,14 @@ export const PlaylistListHeaderFilters = ({
|
|||
key: pageKey,
|
||||
}) as PlaylistListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
}, [filter.sortOrder, handleFilterChange, setFilter]);
|
||||
}, [filter.sortOrder, handleFilterChange, pageKey, setFilter]);
|
||||
|
||||
const handleSetViewType = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (!e.currentTarget?.value) return;
|
||||
setDisplayType({ data: e.currentTarget.value as ListDisplayType, key: pageKey });
|
||||
},
|
||||
[setDisplayType],
|
||||
[pageKey, setDisplayType],
|
||||
);
|
||||
|
||||
const handleTableColumns = (values: TableColumn[]) => {
|
||||
|
|
|
@ -8,18 +8,14 @@ import { CreatePlaylistForm } from '/@/renderer/features/playlists/components/cr
|
|||
import { PlaylistListHeaderFilters } from '/@/renderer/features/playlists/components/playlist-list-header-filters';
|
||||
import { LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import {
|
||||
PlaylistListFilter,
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
usePlaylistListFilter,
|
||||
usePlaylistListStore,
|
||||
} from '/@/renderer/store';
|
||||
import { PlaylistListFilter, useCurrentServer, useListStoreActions } from '/@/renderer/store';
|
||||
import { ListDisplayType, ServerType } from '/@/renderer/types';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { RiFileAddFill } from 'react-icons/ri';
|
||||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { useListFilterRefresh } from '../../../hooks/use-list-filter-refresh';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { useListStoreByKey } from '../../../store/list.store';
|
||||
|
||||
interface PlaylistListHeaderProps {
|
||||
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
||||
|
@ -28,12 +24,11 @@ interface PlaylistListHeaderProps {
|
|||
}
|
||||
|
||||
export const PlaylistListHeader = ({ itemCount, tableRef, gridRef }: PlaylistListHeaderProps) => {
|
||||
const pageKey = 'playlist';
|
||||
const { pageKey } = useListContext();
|
||||
const cq = useContainerQuery();
|
||||
const server = useCurrentServer();
|
||||
const { setFilter, setTablePagination } = useListStoreActions();
|
||||
const filter = usePlaylistListFilter({ key: pageKey });
|
||||
const { display } = usePlaylistListStore({ key: pageKey });
|
||||
const { display, filter } = useListStoreByKey({ key: pageKey });
|
||||
|
||||
const handleCreatePlaylistModal = () => {
|
||||
openModal({
|
||||
|
|
|
@ -1,25 +1,13 @@
|
|||
import { RowDoubleClickedEvent } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { useCallback } from 'react';
|
||||
import { generatePath, useNavigate } from 'react-router';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, PlaylistListQuery, PlaylistListResponse } from '/@/renderer/api/types';
|
||||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-grid';
|
||||
import { VirtualTable } from '/@/renderer/components/virtual-table';
|
||||
import {
|
||||
AgGridFetchFn,
|
||||
useVirtualTable,
|
||||
} from '/@/renderer/components/virtual-table/hooks/use-virtual-table';
|
||||
import { useVirtualTable } from '/@/renderer/components/virtual-table/hooks/use-virtual-table';
|
||||
import { PLAYLIST_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import {
|
||||
useCurrentServer,
|
||||
useGeneralSettings,
|
||||
useListStoreActions,
|
||||
usePlaylistListFilter,
|
||||
usePlaylistListStore,
|
||||
} from '/@/renderer/store';
|
||||
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
|
||||
|
||||
interface PlaylistListTableViewProps {
|
||||
itemCount?: number;
|
||||
|
@ -30,36 +18,7 @@ export const PlaylistListTableView = ({ tableRef, itemCount }: PlaylistListTable
|
|||
const navigate = useNavigate();
|
||||
const server = useCurrentServer();
|
||||
const { defaultFullPlaylist } = useGeneralSettings();
|
||||
const { setTable, setTablePagination } = useListStoreActions();
|
||||
const pageKey = 'playlist';
|
||||
const filter = usePlaylistListFilter({ key: pageKey });
|
||||
const listProperties = usePlaylistListStore({ key: pageKey });
|
||||
|
||||
console.log('listProperties :>> ', listProperties);
|
||||
|
||||
const fetchFn: AgGridFetchFn<
|
||||
PlaylistListResponse,
|
||||
Omit<PlaylistListQuery, 'startIndex'>
|
||||
> = useCallback(
|
||||
async ({ filter, limit, startIndex }, signal) => {
|
||||
const res = api.controller.getPlaylistList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query: {
|
||||
...filter,
|
||||
limit,
|
||||
sortBy: filter.sortBy,
|
||||
sortOrder: filter.sortOrder,
|
||||
startIndex,
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
},
|
||||
[server],
|
||||
);
|
||||
|
||||
const handleRowDoubleClick = (e: RowDoubleClickedEvent) => {
|
||||
if (!e.data) return;
|
||||
|
@ -70,25 +29,14 @@ export const PlaylistListTableView = ({ tableRef, itemCount }: PlaylistListTable
|
|||
}
|
||||
};
|
||||
|
||||
const tableProps = useVirtualTable<PlaylistListResponse, Omit<PlaylistListQuery, 'startIndex'>>(
|
||||
{
|
||||
contextMenu: PLAYLIST_CONTEXT_MENU_ITEMS,
|
||||
fetch: {
|
||||
filter,
|
||||
fn: fetchFn,
|
||||
itemCount,
|
||||
queryKey: queryKeys.playlists.list,
|
||||
server,
|
||||
},
|
||||
itemCount,
|
||||
itemType: LibraryItem.PLAYLIST,
|
||||
pageKey,
|
||||
properties: listProperties,
|
||||
setTable,
|
||||
setTablePagination,
|
||||
tableRef,
|
||||
},
|
||||
);
|
||||
const tableProps = useVirtualTable({
|
||||
contextMenu: PLAYLIST_CONTEXT_MENU_ITEMS,
|
||||
itemCount,
|
||||
itemType: LibraryItem.PLAYLIST,
|
||||
pageKey,
|
||||
server,
|
||||
tableRef,
|
||||
});
|
||||
|
||||
return (
|
||||
<VirtualGridAutoSizerContainer>
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { useRef } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { PlaylistListSort, SortOrder } from '/@/renderer/api/types';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
||||
import { ListContext } from '/@/renderer/context/list-context';
|
||||
import { PlaylistListContent } from '/@/renderer/features/playlists/components/playlist-list-content';
|
||||
import { PlaylistListHeader } from '/@/renderer/features/playlists/components/playlist-list-header';
|
||||
import { usePlaylistList } from '/@/renderer/features/playlists/queries/playlist-list-query';
|
||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||
import { useCurrentServer, usePlaylistListFilter } from '/@/renderer/store';
|
||||
import { useCurrentServer, useListStoreByKey } from '/@/renderer/store';
|
||||
|
||||
const PlaylistListRoute = () => {
|
||||
const gridRef = useRef<VirtualInfiniteGridRef | null>(null);
|
||||
const tableRef = useRef<AgGridReactType | null>(null);
|
||||
const server = useCurrentServer();
|
||||
|
||||
const playlistListFilter = usePlaylistListFilter({ key: 'playlist' });
|
||||
const { playlistId } = useParams();
|
||||
const pageKey = 'playlist';
|
||||
const { filter } = useListStoreByKey({ key: pageKey });
|
||||
|
||||
const itemCountCheck = usePlaylistList({
|
||||
options: {
|
||||
|
@ -21,7 +24,7 @@ const PlaylistListRoute = () => {
|
|||
staleTime: 1000 * 60 * 60 * 2,
|
||||
},
|
||||
query: {
|
||||
...playlistListFilter,
|
||||
...filter,
|
||||
limit: 1,
|
||||
sortBy: PlaylistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
|
@ -37,16 +40,18 @@ const PlaylistListRoute = () => {
|
|||
|
||||
return (
|
||||
<AnimatedPage>
|
||||
<PlaylistListHeader
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<PlaylistListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<ListContext.Provider value={{ id: playlistId, pageKey }}>
|
||||
<PlaylistListHeader
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<PlaylistListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -624,13 +624,4 @@ export const useAlbumArtistListFilter = (args: { id?: string; key?: string }) =>
|
|||
}) as AlbumArtistListFilter;
|
||||
}, shallow);
|
||||
|
||||
export const usePlaylistListFilter = (args: { id?: string; key?: string }) =>
|
||||
useListStore((state) => {
|
||||
return state._actions.getFilter({
|
||||
id: args.id,
|
||||
itemType: LibraryItem.PLAYLIST,
|
||||
key: args.key,
|
||||
}) as PlaylistListFilter;
|
||||
}, shallow);
|
||||
|
||||
export const useListDetail = (key: string) => useListStore((state) => state.detail[key], shallow);
|
||||
|
|
Reference in a new issue