mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
refactor playlist route state
This commit is contained in:
@@ -13,6 +13,7 @@ interface ListContextProps {
|
|||||||
isSmartPlaylist?: boolean;
|
isSmartPlaylist?: boolean;
|
||||||
itemCount?: number;
|
itemCount?: number;
|
||||||
listData?: unknown[];
|
listData?: unknown[];
|
||||||
|
listKey?: ItemListKey;
|
||||||
mode?: 'edit' | 'view';
|
mode?: 'edit' | 'view';
|
||||||
pageKey: ItemListKey | string;
|
pageKey: ItemListKey | string;
|
||||||
setDisplayMode?: (displayMode: ListDisplayMode) => void;
|
setDisplayMode?: (displayMode: ListDisplayMode) => void;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { useListContext } from '/@/renderer/context/list-context';
|
|||||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||||
import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-api';
|
import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-api';
|
||||||
|
import { usePlaylistTrackList } from '/@/renderer/features/playlists/hooks/use-playlist-track-list';
|
||||||
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
|
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
|
||||||
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
||||||
import { useCurrentServer, useGeneralSettings, useListSettings } from '/@/renderer/store';
|
import { useCurrentServer, useGeneralSettings, useListSettings } from '/@/renderer/store';
|
||||||
@@ -70,7 +71,6 @@ const PlaylistDetailSongListGrid = lazy(() =>
|
|||||||
export const PlaylistDetailSongListContent = () => {
|
export const PlaylistDetailSongListContent = () => {
|
||||||
const { playlistId } = useParams() as { playlistId: string };
|
const { playlistId } = useParams() as { playlistId: string };
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const { displayMode, setItemCount } = useListContext();
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const playlistSongsQuery = useSuspenseQuery(
|
const playlistSongsQuery = useSuspenseQuery(
|
||||||
@@ -82,18 +82,12 @@ export const PlaylistDetailSongListContent = () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
displayMode !== LibraryItem.ALBUM &&
|
|
||||||
playlistSongsQuery.data?.totalRecordCount != null
|
|
||||||
) {
|
|
||||||
setItemCount?.(playlistSongsQuery.data.totalRecordCount);
|
|
||||||
}
|
|
||||||
}, [displayMode, playlistSongsQuery.data?.totalRecordCount, setItemCount]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleRefresh = async (payload: { key: string }) => {
|
const handleRefresh = async (payload: { key: string }) => {
|
||||||
if (payload.key !== ItemListKey.PLAYLIST_SONG) {
|
if (
|
||||||
|
payload.key !== ItemListKey.PLAYLIST_SONG &&
|
||||||
|
payload.key !== ItemListKey.PLAYLIST_ALBUM
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +107,7 @@ export const PlaylistDetailSongListContent = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
eventEmitter.off('ITEM_LIST_REFRESH', handleRefresh);
|
eventEmitter.off('ITEM_LIST_REFRESH', handleRefresh);
|
||||||
};
|
};
|
||||||
}, [playlistId, queryClient, server.id]);
|
}, [playlistId, queryClient, server?.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<Spinner container />}>
|
<Suspense fallback={<Spinner container />}>
|
||||||
@@ -124,7 +118,13 @@ export const PlaylistDetailSongListContent = () => {
|
|||||||
|
|
||||||
export type OverridePlaylistSongListQuery = Omit<Partial<PlaylistSongListQuery>, 'id'>;
|
export type OverridePlaylistSongListQuery = Omit<Partial<PlaylistSongListQuery>, 'id'>;
|
||||||
|
|
||||||
export const PlaylistDetailSongListView = ({ data }: { data: PlaylistSongListResponse }) => {
|
interface PlaylistDetailSongListViewProps {
|
||||||
|
data: PlaylistSongListResponse;
|
||||||
|
/** When provided, table/grid use this instead of computing from data (avoids duplicate filter/sort). */
|
||||||
|
items?: Song[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PlaylistDetailSongListView = ({ data, items }: PlaylistDetailSongListViewProps) => {
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const { display, itemsPerPage, pagination, table } = useListSettings(ItemListKey.PLAYLIST_SONG);
|
const { display, itemsPerPage, pagination, table } = useListSettings(ItemListKey.PLAYLIST_SONG);
|
||||||
const { currentPage, onChange: onPageChange } = useItemListPagination();
|
const { currentPage, onChange: onPageChange } = useItemListPagination();
|
||||||
@@ -141,7 +141,12 @@ export const PlaylistDetailSongListView = ({ data }: { data: PlaylistSongListRes
|
|||||||
switch (display) {
|
switch (display) {
|
||||||
case ListDisplayType.GRID: {
|
case ListDisplayType.GRID: {
|
||||||
return (
|
return (
|
||||||
<PlaylistDetailSongListGrid data={data} serverId={server.id} {...paginationProps} />
|
<PlaylistDetailSongListGrid
|
||||||
|
data={data}
|
||||||
|
items={items}
|
||||||
|
serverId={server.id}
|
||||||
|
{...paginationProps}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ListDisplayType.TABLE: {
|
case ListDisplayType.TABLE: {
|
||||||
@@ -155,6 +160,7 @@ export const PlaylistDetailSongListView = ({ data }: { data: PlaylistSongListRes
|
|||||||
enableHorizontalBorders={table.enableHorizontalBorders}
|
enableHorizontalBorders={table.enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={table.enableRowHoverHighlight}
|
enableRowHoverHighlight={table.enableRowHoverHighlight}
|
||||||
enableVerticalBorders={table.enableVerticalBorders}
|
enableVerticalBorders={table.enableVerticalBorders}
|
||||||
|
items={items}
|
||||||
serverId={server.id}
|
serverId={server.id}
|
||||||
size={table.size}
|
size={table.size}
|
||||||
{...paginationProps}
|
{...paginationProps}
|
||||||
@@ -363,7 +369,7 @@ export function playlistSongsToAlbums(songs: Song[]): PlaylistAlbumRow[] {
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlaylistDetailAlbumList = ({ data }: { data: PlaylistSongListResponse }) => {
|
export const PlaylistDetailAlbumView = ({ data }: { data: PlaylistSongListResponse }) => {
|
||||||
const player = usePlayer();
|
const player = usePlayer();
|
||||||
const { setItemCount, setListData } = useListContext();
|
const { setItemCount, setListData } = useListContext();
|
||||||
const { detail, display, grid, itemsPerPage, pagination, table } = useListSettings(
|
const { detail, display, grid, itemsPerPage, pagination, table } = useListSettings(
|
||||||
@@ -528,23 +534,33 @@ const PlaylistDetailAlbumList = ({ data }: { data: PlaylistSongListResponse }) =
|
|||||||
return renderAlbumList();
|
return renderAlbumList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlaylistDetailSongList = ({ data }: { data: PlaylistSongListResponse }) => {
|
/** Track view: view mode uses centralized list derivation; edit mode uses local reorder state. */
|
||||||
const { displayMode, isSmartPlaylist, mode } = useListContext();
|
const PlaylistDetailTrackView = ({ data }: { data: PlaylistSongListResponse }) => {
|
||||||
|
const { isSmartPlaylist, mode } = useListContext();
|
||||||
if (displayMode === LibraryItem.ALBUM) {
|
|
||||||
return <PlaylistDetailAlbumList data={data} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSmartPlaylist) {
|
if (isSmartPlaylist) {
|
||||||
return <PlaylistDetailSongListView data={data} />;
|
return <PlaylistDetailTrackViewContent data={data} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (mode) {
|
if (mode === 'edit') {
|
||||||
case 'edit':
|
|
||||||
return <PlaylistDetailSongListEdit data={data} />;
|
return <PlaylistDetailSongListEdit data={data} />;
|
||||||
case 'view':
|
|
||||||
return <PlaylistDetailSongListView data={data} />;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return <PlaylistDetailTrackViewContent data={data} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Uses usePlaylistTrackList once and passes derived items to the list view. */
|
||||||
|
const PlaylistDetailTrackViewContent = ({ data }: { data: PlaylistSongListResponse }) => {
|
||||||
|
const { sortedAndFilteredSongs } = usePlaylistTrackList(data);
|
||||||
|
return <PlaylistDetailSongListView data={data} items={sortedAndFilteredSongs} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlaylistDetailSongList = ({ data }: { data: PlaylistSongListResponse }) => {
|
||||||
|
const { displayMode } = useListContext();
|
||||||
|
|
||||||
|
if (displayMode === LibraryItem.ALBUM) {
|
||||||
|
return <PlaylistDetailAlbumView data={data} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <PlaylistDetailTrackView data={data} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
LibraryItem,
|
LibraryItem,
|
||||||
PlaylistSongListQuery,
|
PlaylistSongListQuery,
|
||||||
PlaylistSongListResponse,
|
PlaylistSongListResponse,
|
||||||
|
Song,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -23,36 +24,44 @@ interface PlaylistDetailSongListGridProps
|
|||||||
extends Omit<ItemListGridComponentProps<PlaylistSongListQuery>, 'query'> {
|
extends Omit<ItemListGridComponentProps<PlaylistSongListQuery>, 'query'> {
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
data: PlaylistSongListResponse;
|
data: PlaylistSongListResponse;
|
||||||
|
items?: Song[];
|
||||||
itemsPerPage?: number;
|
itemsPerPage?: number;
|
||||||
onPageChange?: (page: number) => void;
|
onPageChange?: (page: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlaylistDetailSongListGrid = forwardRef<any, PlaylistDetailSongListGridProps>(
|
export const PlaylistDetailSongListGrid = forwardRef<any, PlaylistDetailSongListGridProps>(
|
||||||
({ currentPage, data, itemsPerPage, onPageChange, saveScrollOffset = true }) => {
|
({
|
||||||
|
currentPage,
|
||||||
|
data,
|
||||||
|
items: itemsProp,
|
||||||
|
itemsPerPage,
|
||||||
|
onPageChange,
|
||||||
|
saveScrollOffset = true,
|
||||||
|
}) => {
|
||||||
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({
|
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({
|
||||||
enabled: saveScrollOffset,
|
enabled: saveScrollOffset,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { searchTerm } = useSearchTermFilter();
|
const { searchTerm } = useSearchTermFilter();
|
||||||
const { query } = usePlaylistSongListFilters();
|
const { query } = usePlaylistSongListFilters();
|
||||||
const { setListData } = useListContext();
|
|
||||||
|
|
||||||
const songData = useMemo(() => {
|
|
||||||
let items = data?.items || [];
|
|
||||||
|
|
||||||
|
const songDataFromData = useMemo(() => {
|
||||||
|
let list = data?.items || [];
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
items = searchLibraryItems(items, searchTerm, LibraryItem.SONG);
|
list = searchLibraryItems(list, searchTerm, LibraryItem.SONG);
|
||||||
return items;
|
return list;
|
||||||
}
|
}
|
||||||
|
return sortSongList(list, query.sortBy, query.sortOrder);
|
||||||
return sortSongList(items, query.sortBy, query.sortOrder);
|
|
||||||
}, [data?.items, searchTerm, query.sortBy, query.sortOrder]);
|
}, [data?.items, searchTerm, query.sortBy, query.sortOrder]);
|
||||||
|
|
||||||
|
const { setListData } = useListContext();
|
||||||
|
const songData = itemsProp ?? songDataFromData;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (setListData) {
|
if (itemsProp == null && setListData) {
|
||||||
setListData(songData);
|
setListData(songDataFromData);
|
||||||
}
|
}
|
||||||
}, [songData, setListData]);
|
}, [itemsProp, songDataFromData, setListData]);
|
||||||
|
|
||||||
const gridProps = useListSettings(ItemListKey.PLAYLIST_SONG).grid;
|
const gridProps = useListSettings(ItemListKey.PLAYLIST_SONG).grid;
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -46,7 +46,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
|||||||
isSmartPlaylist,
|
isSmartPlaylist,
|
||||||
}: PlaylistDetailSongListHeaderFiltersProps) => {
|
}: PlaylistDetailSongListHeaderFiltersProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { mode, setMode } = useListContext();
|
const { listKey: listKeyFromContext, mode, setMode } = useListContext();
|
||||||
const { playlistId } = useParams() as { playlistId: string };
|
const { playlistId } = useParams() as { playlistId: string };
|
||||||
const playlistTarget = usePlaylistTarget();
|
const playlistTarget = usePlaylistTarget();
|
||||||
const { setPlaylistBehavior } = useSettingsStoreActions();
|
const { setPlaylistBehavior } = useSettingsStoreActions();
|
||||||
@@ -66,8 +66,12 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAlbumMode = playlistTarget === PlaylistTarget.ALBUM;
|
const listKey =
|
||||||
const listKey = isAlbumMode ? ItemListKey.PLAYLIST_ALBUM : ItemListKey.PLAYLIST_SONG;
|
listKeyFromContext ??
|
||||||
|
(playlistTarget === PlaylistTarget.ALBUM
|
||||||
|
? ItemListKey.PLAYLIST_ALBUM
|
||||||
|
: ItemListKey.PLAYLIST_SONG);
|
||||||
|
const isAlbumMode = listKey === ItemListKey.PLAYLIST_ALBUM;
|
||||||
const toggleChoice = isAlbumMode
|
const toggleChoice = isAlbumMode
|
||||||
? t('entity.album', { count: 2, postProcess: 'titleCase' })
|
? t('entity.album', { count: 2, postProcess: 'titleCase' })
|
||||||
: t('entity.track', { count: 2, postProcess: 'titleCase' });
|
: t('entity.track', { count: 2, postProcess: 'titleCase' });
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ interface PlaylistDetailSongListTableProps
|
|||||||
extends Omit<ItemListTableComponentProps<PlaylistSongListQuery>, 'query'> {
|
extends Omit<ItemListTableComponentProps<PlaylistSongListQuery>, 'query'> {
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
data: PlaylistSongListResponse;
|
data: PlaylistSongListResponse;
|
||||||
|
items?: Song[];
|
||||||
itemsPerPage?: number;
|
itemsPerPage?: number;
|
||||||
onPageChange?: (page: number) => void;
|
onPageChange?: (page: number) => void;
|
||||||
}
|
}
|
||||||
@@ -44,6 +45,7 @@ export const PlaylistDetailSongListTable = forwardRef<any, PlaylistDetailSongLis
|
|||||||
enableRowHoverHighlight = true,
|
enableRowHoverHighlight = true,
|
||||||
enableSelection = true,
|
enableSelection = true,
|
||||||
enableVerticalBorders = false,
|
enableVerticalBorders = false,
|
||||||
|
items: itemsProp,
|
||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
saveScrollOffset = true,
|
saveScrollOffset = true,
|
||||||
@@ -65,24 +67,24 @@ export const PlaylistDetailSongListTable = forwardRef<any, PlaylistDetailSongLis
|
|||||||
|
|
||||||
const { searchTerm } = useSearchTermFilter();
|
const { searchTerm } = useSearchTermFilter();
|
||||||
const { query } = usePlaylistSongListFilters();
|
const { query } = usePlaylistSongListFilters();
|
||||||
const { setListData } = useListContext();
|
|
||||||
|
|
||||||
const songData = useMemo(() => {
|
|
||||||
let items = data?.items || [];
|
|
||||||
|
|
||||||
|
const songDataFromData = useMemo(() => {
|
||||||
|
let list = data?.items || [];
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
items = searchLibraryItems(items, searchTerm, LibraryItem.SONG);
|
list = searchLibraryItems(list, searchTerm, LibraryItem.SONG);
|
||||||
return items;
|
return list;
|
||||||
}
|
}
|
||||||
|
return sortSongList(list, query.sortBy, query.sortOrder);
|
||||||
return sortSongList(items, query.sortBy, query.sortOrder);
|
|
||||||
}, [data?.items, searchTerm, query.sortBy, query.sortOrder]);
|
}, [data?.items, searchTerm, query.sortBy, query.sortOrder]);
|
||||||
|
|
||||||
|
const { setListData } = useListContext();
|
||||||
|
const songData = itemsProp ?? songDataFromData;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (setListData) {
|
if (itemsProp == null && setListData) {
|
||||||
setListData(songData);
|
setListData(songDataFromData);
|
||||||
}
|
}
|
||||||
}, [songData, setListData]);
|
}, [itemsProp, songDataFromData, setListData]);
|
||||||
|
|
||||||
const player = usePlayer();
|
const player = usePlayer();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useListContext } from '/@/renderer/context/list-context';
|
||||||
|
import { usePlaylistSongListFilters } from '/@/renderer/features/playlists/hooks/use-playlist-song-list-filters';
|
||||||
|
import { useSearchTermFilter } from '/@/renderer/features/shared/hooks/use-search-term-filter';
|
||||||
|
import { searchLibraryItems } from '/@/renderer/features/shared/utils';
|
||||||
|
import { sortSongList } from '/@/shared/api/utils';
|
||||||
|
import { LibraryItem, PlaylistSongListResponse, Song } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
|
export function usePlaylistTrackList(data: PlaylistSongListResponse | undefined): {
|
||||||
|
sortedAndFilteredSongs: Song[];
|
||||||
|
totalCount: number;
|
||||||
|
} {
|
||||||
|
const { setItemCount, setListData } = useListContext();
|
||||||
|
const { searchTerm } = useSearchTermFilter();
|
||||||
|
const { query } = usePlaylistSongListFilters();
|
||||||
|
|
||||||
|
const sortedAndFilteredSongs = useMemo(() => {
|
||||||
|
const raw = data?.items ?? [];
|
||||||
|
|
||||||
|
if (searchTerm) {
|
||||||
|
return searchLibraryItems(raw, searchTerm, LibraryItem.SONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortSongList(raw, query.sortBy, query.sortOrder);
|
||||||
|
}, [data?.items, searchTerm, query.sortBy, query.sortOrder]);
|
||||||
|
|
||||||
|
const totalCount = sortedAndFilteredSongs.length;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setListData?.(sortedAndFilteredSongs);
|
||||||
|
setItemCount?.(totalCount);
|
||||||
|
}, [sortedAndFilteredSongs, totalCount, setListData, setItemCount]);
|
||||||
|
|
||||||
|
return { sortedAndFilteredSongs, totalCount };
|
||||||
|
}
|
||||||
@@ -402,6 +402,8 @@ const PlaylistDetailSongListRoute = () => {
|
|||||||
const playlistTarget = usePlaylistTarget();
|
const playlistTarget = usePlaylistTarget();
|
||||||
const displayMode: LibraryItem.ALBUM | LibraryItem.SONG =
|
const displayMode: LibraryItem.ALBUM | LibraryItem.SONG =
|
||||||
playlistTarget === PlaylistTarget.ALBUM ? LibraryItem.ALBUM : LibraryItem.SONG;
|
playlistTarget === PlaylistTarget.ALBUM ? LibraryItem.ALBUM : LibraryItem.SONG;
|
||||||
|
const listKey =
|
||||||
|
displayMode === LibraryItem.ALBUM ? ItemListKey.PLAYLIST_ALBUM : ItemListKey.PLAYLIST_SONG;
|
||||||
|
|
||||||
const [itemCount, setItemCount] = useState<number | undefined>(undefined);
|
const [itemCount, setItemCount] = useState<number | undefined>(undefined);
|
||||||
const [listData, setListData] = useState<unknown[]>([]);
|
const [listData, setListData] = useState<unknown[]>([]);
|
||||||
@@ -415,13 +417,14 @@ const PlaylistDetailSongListRoute = () => {
|
|||||||
isSmartPlaylist,
|
isSmartPlaylist,
|
||||||
itemCount,
|
itemCount,
|
||||||
listData,
|
listData,
|
||||||
|
listKey,
|
||||||
mode,
|
mode,
|
||||||
pageKey: ItemListKey.PLAYLIST_SONG,
|
pageKey: listKey,
|
||||||
setItemCount,
|
setItemCount,
|
||||||
setListData,
|
setListData,
|
||||||
setMode,
|
setMode,
|
||||||
};
|
};
|
||||||
}, [playlistId, isSmartPlaylist, displayMode, itemCount, listData, mode]);
|
}, [playlistId, isSmartPlaylist, displayMode, listKey, itemCount, listData, mode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedPage key={`playlist-detail-songList-${playlistId}`}>
|
<AnimatedPage key={`playlist-detail-songList-${playlistId}`}>
|
||||||
|
|||||||
Reference in New Issue
Block a user