mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-30 06:16:33 +02:00
Add album view for playlists (#1700)
* update client side song ordering to include album order * add compact styling to LibraryHeader * move search button to top right of LibraryHeader
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { useGridRows } from '/@/renderer/components/item-list/helpers/use-grid-rows';
|
||||
import { useItemListColumnReorder } from '/@/renderer/components/item-list/helpers/use-item-list-column-reorder';
|
||||
import { useItemListColumnResize } from '/@/renderer/components/item-list/helpers/use-item-list-column-resize';
|
||||
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
||||
import { ItemDetailList } from '/@/renderer/components/item-list/item-detail-list/item-detail-list';
|
||||
import { ItemGridList } from '/@/renderer/components/item-list/item-grid-list/item-grid-list';
|
||||
import { ItemListWithPagination } from '/@/renderer/components/item-list/item-list-pagination/item-list-pagination';
|
||||
import { useItemListPagination } from '/@/renderer/components/item-list/item-list-pagination/use-item-list-pagination';
|
||||
import { ItemTableList } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { DefaultItemControlProps, ItemControls } from '/@/renderer/components/item-list/types';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||
import { usePlaylistSongListFilters } from '/@/renderer/features/playlists/hooks/use-playlist-song-list-filters';
|
||||
import { type PlaylistAlbumRow, playlistSongsToAlbums } from '/@/renderer/features/playlists/utils';
|
||||
import { useSearchTermFilter } from '/@/renderer/features/shared/hooks/use-search-term-filter';
|
||||
import { searchLibraryItems } from '/@/renderer/features/shared/utils';
|
||||
import { useGeneralSettings, useListSettings } from '/@/renderer/store';
|
||||
import { sortSongList } from '/@/shared/api/utils';
|
||||
import {
|
||||
LibraryItem,
|
||||
PlaylistSongListResponse,
|
||||
SongListSort,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ItemListKey, ListDisplayType, ListPaginationType, Play } from '/@/shared/types/types';
|
||||
|
||||
export const PlaylistDetailAlbumView = ({ data }: { data: PlaylistSongListResponse }) => {
|
||||
const player = usePlayer();
|
||||
const { setItemCount, setListData } = useListContext();
|
||||
const { detail, display, grid, itemsPerPage, pagination, table } = useListSettings(
|
||||
ItemListKey.PLAYLIST_ALBUM,
|
||||
);
|
||||
const { enableGridMultiSelect } = useGeneralSettings();
|
||||
const { currentPage, onChange: onPageChange } = useItemListPagination();
|
||||
const { searchTerm } = useSearchTermFilter();
|
||||
const { query } = usePlaylistSongListFilters();
|
||||
|
||||
const sortedAlbums = useMemo(() => {
|
||||
let songs = data?.items ?? [];
|
||||
if (searchTerm?.trim()) {
|
||||
songs = searchLibraryItems(songs, searchTerm, LibraryItem.SONG);
|
||||
}
|
||||
const sortedSongs = sortSongList(
|
||||
songs,
|
||||
(query.sortBy as SongListSort) ?? SongListSort.ID,
|
||||
(query.sortOrder as SortOrder) ?? SortOrder.ASC,
|
||||
);
|
||||
return playlistSongsToAlbums(sortedSongs);
|
||||
}, [data?.items, searchTerm, query.sortBy, query.sortOrder]);
|
||||
|
||||
const isPaginated = pagination === ListPaginationType.PAGINATED;
|
||||
const totalAlbumCount = sortedAlbums.length;
|
||||
const albumPageCount = Math.max(1, Math.ceil(totalAlbumCount / itemsPerPage));
|
||||
const paginatedAlbums = useMemo(() => {
|
||||
if (!isPaginated) return sortedAlbums;
|
||||
const start = currentPage * itemsPerPage;
|
||||
return sortedAlbums.slice(start, start + itemsPerPage);
|
||||
}, [isPaginated, currentPage, itemsPerPage, sortedAlbums]);
|
||||
const albumsToRender = isPaginated ? paginatedAlbums : sortedAlbums;
|
||||
|
||||
const playlistSongs = useMemo(() => data?.items ?? [], [data?.items]);
|
||||
|
||||
const albumControlOverrides = useMemo<Partial<ItemControls>>(() => {
|
||||
return {
|
||||
onPlay: ({
|
||||
item,
|
||||
itemType,
|
||||
playType,
|
||||
}: DefaultItemControlProps & { playType: Play }) => {
|
||||
if (!item) return;
|
||||
const rowSongs = (item as PlaylistAlbumRow)._playlistSongs;
|
||||
if (itemType === LibraryItem.ALBUM && rowSongs?.length) {
|
||||
player.addToQueueByData(rowSongs, playType);
|
||||
return;
|
||||
}
|
||||
player.addToQueueByFetch(item._serverId, [item.id], itemType, playType);
|
||||
},
|
||||
};
|
||||
}, [player]);
|
||||
|
||||
useEffect(() => {
|
||||
setItemCount?.(totalAlbumCount);
|
||||
}, [setItemCount, totalAlbumCount]);
|
||||
|
||||
useEffect(() => {
|
||||
setListData?.(data?.items ?? []);
|
||||
}, [data?.items, setListData]);
|
||||
|
||||
const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({ enabled: true });
|
||||
const { handleColumnReordered } = useItemListColumnReorder({
|
||||
itemListKey: ItemListKey.PLAYLIST_ALBUM,
|
||||
});
|
||||
const { handleColumnResized } = useItemListColumnResize({
|
||||
itemListKey: ItemListKey.PLAYLIST_ALBUM,
|
||||
});
|
||||
const { handleColumnReordered: handleDetailColumnReordered } = useItemListColumnReorder({
|
||||
itemListKey: ItemListKey.PLAYLIST_ALBUM,
|
||||
tableKey: 'detail',
|
||||
});
|
||||
const { handleColumnResized: handleDetailColumnResized } = useItemListColumnResize({
|
||||
itemListKey: ItemListKey.PLAYLIST_ALBUM,
|
||||
tableKey: 'detail',
|
||||
});
|
||||
const rows = useGridRows(LibraryItem.ALBUM, ItemListKey.PLAYLIST_ALBUM, grid.size);
|
||||
|
||||
const renderAlbumList = () => {
|
||||
switch (display) {
|
||||
case ListDisplayType.DETAIL:
|
||||
return (
|
||||
<ItemDetailList
|
||||
enableHeader={detail?.enableHeader}
|
||||
items={albumsToRender}
|
||||
listKey={ItemListKey.PLAYLIST_ALBUM}
|
||||
onColumnReordered={handleDetailColumnReordered}
|
||||
onColumnResized={handleDetailColumnResized}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
onSongRowDoubleClick={({ internalState, item }) => {
|
||||
if (playlistSongs.length === 0) return;
|
||||
internalState?.setSelected([item]);
|
||||
player.addToQueueByData(playlistSongs, Play.NOW, item.id);
|
||||
}}
|
||||
overrideControls={albumControlOverrides}
|
||||
scrollOffset={scrollOffset ?? 0}
|
||||
songsByAlbumId={{}}
|
||||
tableId="album-detail"
|
||||
/>
|
||||
);
|
||||
case ListDisplayType.GRID:
|
||||
return (
|
||||
<ItemGridList
|
||||
data={albumsToRender}
|
||||
enableExpansion
|
||||
enableMultiSelect={enableGridMultiSelect}
|
||||
gap={grid.itemGap}
|
||||
initialTop={{
|
||||
to: scrollOffset ?? 0,
|
||||
type: 'offset',
|
||||
}}
|
||||
itemsPerRow={grid.itemsPerRowEnabled ? grid.itemsPerRow : undefined}
|
||||
itemType={LibraryItem.ALBUM}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
overrideControls={albumControlOverrides}
|
||||
rows={rows}
|
||||
size={grid.size}
|
||||
/>
|
||||
);
|
||||
case ListDisplayType.TABLE:
|
||||
return (
|
||||
<ItemTableList
|
||||
autoFitColumns={table.autoFitColumns}
|
||||
CellComponent={ItemTableListColumn}
|
||||
columns={table.columns}
|
||||
data={albumsToRender}
|
||||
enableAlternateRowColors={table.enableAlternateRowColors}
|
||||
enableHeader={table.enableHeader}
|
||||
enableHorizontalBorders={table.enableHorizontalBorders}
|
||||
enableRowHoverHighlight={table.enableRowHoverHighlight}
|
||||
enableSelection
|
||||
enableVerticalBorders={table.enableVerticalBorders}
|
||||
initialTop={{
|
||||
to: scrollOffset ?? 0,
|
||||
type: 'offset',
|
||||
}}
|
||||
itemType={LibraryItem.ALBUM}
|
||||
onColumnReordered={handleColumnReordered}
|
||||
onColumnResized={handleColumnResized}
|
||||
onScrollEnd={handleOnScrollEnd}
|
||||
overrideControls={albumControlOverrides}
|
||||
size={table.size}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
if (isPaginated) {
|
||||
return (
|
||||
<ItemListWithPagination
|
||||
currentPage={currentPage}
|
||||
itemsPerPage={itemsPerPage}
|
||||
onChange={onPageChange}
|
||||
pageCount={albumPageCount}
|
||||
totalItemCount={totalAlbumCount}
|
||||
>
|
||||
{renderAlbumList()}
|
||||
</ItemListWithPagination>
|
||||
);
|
||||
}
|
||||
|
||||
return renderAlbumList();
|
||||
};
|
||||
Reference in New Issue
Block a user