mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-22 20:07:42 +02:00
Remove default playlist page
This commit is contained in:
@@ -275,7 +275,7 @@ export const PLAYLIST_CARD_ROWS: { [key: string]: CardRow<Playlist> } = {
|
|||||||
name: {
|
name: {
|
||||||
property: 'name',
|
property: 'name',
|
||||||
route: {
|
route: {
|
||||||
route: AppRoute.PLAYLISTS_DETAIL,
|
route: AppRoute.PLAYLISTS_DETAIL_SONGS,
|
||||||
slugs: [{ idProperty: 'id', slugProperty: 'playlistId' }],
|
slugs: [{ idProperty: 'id', slugProperty: 'playlistId' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -385,7 +385,9 @@ export const useVirtualTable = <TFilter>({
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case LibraryItem.PLAYLIST:
|
case LibraryItem.PLAYLIST:
|
||||||
navigate(generatePath(AppRoute.PLAYLISTS_DETAIL, { playlistId: e.data.id }));
|
navigate(
|
||||||
|
generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: e.data.id }),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,254 +0,0 @@
|
|||||||
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 { useTranslation } from 'react-i18next';
|
|
||||||
import { RiMoreFill } from 'react-icons/ri';
|
|
||||||
import { generatePath, useNavigate, useParams } from 'react-router';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { useListStoreByKey } from '../../../store/list.store';
|
|
||||||
import { LibraryItem, QueueSong } from '/@/renderer/api/types';
|
|
||||||
import { Button, ConfirmModal, DropdownMenu, MotionGroup, toast } from '/@/renderer/components';
|
|
||||||
import { getColumnDefs, VirtualTable } from '/@/renderer/components/virtual-table';
|
|
||||||
import { useCurrentSongRowStyles } from '/@/renderer/components/virtual-table/hooks/use-current-song-row-styles';
|
|
||||||
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';
|
|
||||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
|
||||||
import { openUpdatePlaylistModal } from '/@/renderer/features/playlists/components/update-playlist-form';
|
|
||||||
import { useDeletePlaylist } from '/@/renderer/features/playlists/mutations/delete-playlist-mutation';
|
|
||||||
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
|
||||||
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 } from '/@/renderer/store';
|
|
||||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
|
||||||
import { Play } from '/@/renderer/types';
|
|
||||||
|
|
||||||
const ContentContainer = styled.div`
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 1rem 2rem 5rem;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.ag-theme-alpine-dark {
|
|
||||||
--ag-header-background-color: rgb(0 0 0 / 0%) !important;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface PlaylistDetailContentProps {
|
|
||||||
tableRef: MutableRefObject<AgGridReactType | null>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PlaylistDetailContent = ({ tableRef }: PlaylistDetailContentProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { playlistId } = useParams() as { playlistId: string };
|
|
||||||
const { table } = useListStoreByKey({ key: LibraryItem.SONG });
|
|
||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
|
||||||
const server = useCurrentServer();
|
|
||||||
const detailQuery = usePlaylistDetail({ query: { id: playlistId }, serverId: server?.id });
|
|
||||||
const playButtonBehavior = usePlayButtonBehavior();
|
|
||||||
|
|
||||||
const playlistSongsQueryInfinite = usePlaylistSongListInfinite({
|
|
||||||
options: {
|
|
||||||
cacheTime: 0,
|
|
||||||
keepPreviousData: false,
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
id: playlistId,
|
|
||||||
limit: 50,
|
|
||||||
startIndex: 0,
|
|
||||||
},
|
|
||||||
serverId: server?.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
|
||||||
playlistSongsQueryInfinite.fetchNextPage();
|
|
||||||
};
|
|
||||||
|
|
||||||
const columnDefs: ColDef[] = useMemo(
|
|
||||||
() =>
|
|
||||||
getColumnDefs(table.columns).filter((c) => c.colId !== 'album' && c.colId !== 'artist'),
|
|
||||||
[table.columns],
|
|
||||||
);
|
|
||||||
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
const playlistSongData = useMemo(
|
|
||||||
() => playlistSongsQueryInfinite.data?.pages.flatMap((p) => p?.items),
|
|
||||||
[playlistSongsQueryInfinite.data?.pages],
|
|
||||||
);
|
|
||||||
|
|
||||||
const deletePlaylistMutation = useDeletePlaylist({});
|
|
||||||
|
|
||||||
const handleDeletePlaylist = () => {
|
|
||||||
deletePlaylistMutation.mutate(
|
|
||||||
{ query: { id: playlistId }, serverId: server?.id },
|
|
||||||
{
|
|
||||||
onError: (err) => {
|
|
||||||
toast.error({
|
|
||||||
message: err.message,
|
|
||||||
title: t('error.genericError', { postProcess: 'sentenceCase' }),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
closeAllModals();
|
|
||||||
navigate(AppRoute.PLAYLISTS);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openDeletePlaylist = () => {
|
|
||||||
openModal({
|
|
||||||
children: (
|
|
||||||
<ConfirmModal
|
|
||||||
loading={deletePlaylistMutation.isLoading}
|
|
||||||
onConfirm={handleDeletePlaylist}
|
|
||||||
>
|
|
||||||
Are you sure you want to delete this playlist?
|
|
||||||
</ConfirmModal>
|
|
||||||
),
|
|
||||||
title: t('form.deletePlaylist.title', { postProcess: 'sentenceCase' }),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePlay = (playType?: Play) => {
|
|
||||||
handlePlayQueueAdd?.({
|
|
||||||
byItemType: {
|
|
||||||
id: [playlistId],
|
|
||||||
type: LibraryItem.PLAYLIST,
|
|
||||||
},
|
|
||||||
playType: playType || playButtonBehavior,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
|
|
||||||
if (!e.data) return;
|
|
||||||
|
|
||||||
handlePlayQueueAdd?.({
|
|
||||||
byItemType: {
|
|
||||||
id: [playlistId],
|
|
||||||
type: LibraryItem.PLAYLIST,
|
|
||||||
},
|
|
||||||
initialSongId: e.data.id,
|
|
||||||
playType: playButtonBehavior,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
|
|
||||||
|
|
||||||
const loadMoreRef = useRef<HTMLButtonElement | null>(null);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContentContainer>
|
|
||||||
<Group
|
|
||||||
p="1rem"
|
|
||||||
position="apart"
|
|
||||||
>
|
|
||||||
<Group>
|
|
||||||
<PlayButton onClick={() => handlePlay()} />
|
|
||||||
<DropdownMenu position="bottom-start">
|
|
||||||
<DropdownMenu.Target>
|
|
||||||
<Button
|
|
||||||
compact
|
|
||||||
variant="subtle"
|
|
||||||
>
|
|
||||||
<RiMoreFill size={20} />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenu.Target>
|
|
||||||
<DropdownMenu.Dropdown>
|
|
||||||
{PLAY_TYPES.filter((type) => type.play !== playButtonBehavior).map(
|
|
||||||
(type) => (
|
|
||||||
<DropdownMenu.Item
|
|
||||||
key={`playtype-${type.play}`}
|
|
||||||
onClick={() => handlePlay(type.play)}
|
|
||||||
>
|
|
||||||
{type.label}
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
<DropdownMenu.Divider />
|
|
||||||
<DropdownMenu.Item
|
|
||||||
onClick={() => {
|
|
||||||
if (!detailQuery.data || !server) return;
|
|
||||||
openUpdatePlaylistModal({ playlist: detailQuery.data, server });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Edit playlist
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item onClick={openDeletePlaylist}>
|
|
||||||
Delete playlist
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Dropdown>
|
|
||||||
</DropdownMenu>
|
|
||||||
<Button
|
|
||||||
compact
|
|
||||||
uppercase
|
|
||||||
component={Link}
|
|
||||||
to={generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId })}
|
|
||||||
variant="subtle"
|
|
||||||
>
|
|
||||||
View full playlist
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
<Box>
|
|
||||||
<VirtualTable
|
|
||||||
ref={tableRef}
|
|
||||||
autoFitColumns
|
|
||||||
autoHeight
|
|
||||||
deselectOnClickOutside
|
|
||||||
stickyHeader
|
|
||||||
suppressCellFocus
|
|
||||||
suppressHorizontalScroll
|
|
||||||
suppressLoadingOverlay
|
|
||||||
suppressRowDrag
|
|
||||||
columnDefs={columnDefs}
|
|
||||||
getRowId={(data) => {
|
|
||||||
// It's possible that there are duplicate song ids in a playlist
|
|
||||||
return `${data.data.id}-${data.data.pageIndex}`;
|
|
||||||
}}
|
|
||||||
rowClassRules={rowClassRules}
|
|
||||||
rowData={playlistSongData}
|
|
||||||
rowHeight={60}
|
|
||||||
rowSelection="multiple"
|
|
||||||
onCellContextMenu={handleContextMenu}
|
|
||||||
onRowDoubleClicked={handleRowDoubleClick}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<MotionGroup
|
|
||||||
p="2rem"
|
|
||||||
position="center"
|
|
||||||
onViewportEnter={handleLoadMore}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
ref={loadMoreRef}
|
|
||||||
compact
|
|
||||||
disabled={!playlistSongsQueryInfinite.hasNextPage}
|
|
||||||
loading={playlistSongsQueryInfinite.isFetchingNextPage}
|
|
||||||
variant="subtle"
|
|
||||||
onClick={handleLoadMore}
|
|
||||||
>
|
|
||||||
{playlistSongsQueryInfinite.hasNextPage ? 'Load more' : 'End of playlist'}
|
|
||||||
</Button>
|
|
||||||
</MotionGroup>
|
|
||||||
</ContentContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import { forwardRef, Fragment, Ref } from 'react';
|
|
||||||
import { Group, Stack } from '@mantine/core';
|
|
||||||
import { useParams } from 'react-router';
|
|
||||||
import { Badge, Text } from '/@/renderer/components';
|
|
||||||
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
|
||||||
import { LibraryHeader } from '/@/renderer/features/shared';
|
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
|
||||||
import { formatDurationString } from '/@/renderer/utils';
|
|
||||||
import { LibraryItem } from '/@/renderer/api/types';
|
|
||||||
import { useCurrentServer } from '../../../store/auth.store';
|
|
||||||
|
|
||||||
interface PlaylistDetailHeaderProps {
|
|
||||||
background: string;
|
|
||||||
imagePlaceholderUrl?: string | null;
|
|
||||||
imageUrl?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PlaylistDetailHeader = forwardRef(
|
|
||||||
(
|
|
||||||
{ background, imageUrl, imagePlaceholderUrl }: PlaylistDetailHeaderProps,
|
|
||||||
ref: Ref<HTMLDivElement>,
|
|
||||||
) => {
|
|
||||||
const { playlistId } = useParams() as { playlistId: string };
|
|
||||||
const server = useCurrentServer();
|
|
||||||
const detailQuery = usePlaylistDetail({ query: { id: playlistId }, serverId: server?.id });
|
|
||||||
|
|
||||||
const metadataItems = [
|
|
||||||
{
|
|
||||||
id: 'songCount',
|
|
||||||
secondary: false,
|
|
||||||
value: `${detailQuery?.data?.songCount || 0} songs`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'duration',
|
|
||||||
secondary: true,
|
|
||||||
value:
|
|
||||||
detailQuery?.data?.duration && formatDurationString(detailQuery.data.duration),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const isSmartPlaylist = detailQuery?.data?.rules;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack>
|
|
||||||
<LibraryHeader
|
|
||||||
ref={ref}
|
|
||||||
background={background}
|
|
||||||
imagePlaceholderUrl={imagePlaceholderUrl}
|
|
||||||
imageUrl={imageUrl}
|
|
||||||
item={{ route: AppRoute.PLAYLISTS, type: LibraryItem.PLAYLIST }}
|
|
||||||
title={detailQuery?.data?.name || ''}
|
|
||||||
>
|
|
||||||
<Stack>
|
|
||||||
<Group spacing="sm">
|
|
||||||
{metadataItems.map((item, index) => (
|
|
||||||
<Fragment key={`item-${item.id}-${index}`}>
|
|
||||||
{index > 0 && <Text $noSelect>•</Text>}
|
|
||||||
<Text $secondary={item.secondary}>{item.value}</Text>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
{isSmartPlaylist && (
|
|
||||||
<>
|
|
||||||
<Text $noSelect>•</Text>
|
|
||||||
<Badge
|
|
||||||
radius="sm"
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
Smart Playlist
|
|
||||||
</Badge>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Group>
|
|
||||||
<Text lineClamp={3}>{detailQuery?.data?.description}</Text>
|
|
||||||
</Stack>
|
|
||||||
</LibraryHeader>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { QueryKey, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { MutableRefObject, useCallback, useMemo } from 'react';
|
import { MutableRefObject, useCallback, useMemo } from 'react';
|
||||||
|
import { QueryKey, useQueryClient } from '@tanstack/react-query';
|
||||||
import AutoSizer, { Size } from 'react-virtualized-auto-sizer';
|
import AutoSizer, { Size } from 'react-virtualized-auto-sizer';
|
||||||
import { ListOnScrollProps } from 'react-window';
|
import { ListOnScrollProps } from 'react-window';
|
||||||
import { useListContext } from '../../../context/list-context';
|
import { useListContext } from '../../../context/list-context';
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
|
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useCurrentServer, useGeneralSettings, useListStoreByKey } from '/@/renderer/store';
|
import { useCurrentServer, useListStoreByKey } from '/@/renderer/store';
|
||||||
import { CardRow, ListDisplayType } from '/@/renderer/types';
|
import { CardRow, ListDisplayType } from '/@/renderer/types';
|
||||||
|
|
||||||
interface PlaylistListGridViewProps {
|
interface PlaylistListGridViewProps {
|
||||||
@@ -37,7 +37,6 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||||
const { display, grid, filter } = useListStoreByKey({ key: pageKey });
|
const { display, grid, filter } = useListStoreByKey({ key: pageKey });
|
||||||
const { setGrid } = useListStoreActions();
|
const { setGrid } = useListStoreActions();
|
||||||
const { defaultFullPlaylist } = useGeneralSettings();
|
|
||||||
|
|
||||||
const createFavoriteMutation = useCreateFavorite({});
|
const createFavoriteMutation = useCreateFavorite({});
|
||||||
const deleteFavoriteMutation = useDeleteFavorite({});
|
const deleteFavoriteMutation = useDeleteFavorite({});
|
||||||
@@ -68,9 +67,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cardRows = useMemo(() => {
|
const cardRows = useMemo(() => {
|
||||||
const rows: CardRow<Playlist>[] = defaultFullPlaylist
|
const rows: CardRow<Playlist>[] = [PLAYLIST_CARD_ROWS.name];
|
||||||
? [PLAYLIST_CARD_ROWS.nameFull]
|
|
||||||
: [PLAYLIST_CARD_ROWS.name];
|
|
||||||
|
|
||||||
switch (filter.sortBy) {
|
switch (filter.sortBy) {
|
||||||
case PlaylistListSort.DURATION:
|
case PlaylistListSort.DURATION:
|
||||||
@@ -93,7 +90,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
}, [defaultFullPlaylist, filter.sortBy]);
|
}, [filter.sortBy]);
|
||||||
|
|
||||||
const handleGridScroll = useCallback(
|
const handleGridScroll = useCallback(
|
||||||
(e: ListOnScrollProps) => {
|
(e: ListOnScrollProps) => {
|
||||||
@@ -187,9 +184,7 @@ export const PlaylistListGridView = ({ gridRef, itemCount }: PlaylistListGridVie
|
|||||||
loading={itemCount === undefined || itemCount === null}
|
loading={itemCount === undefined || itemCount === null}
|
||||||
minimumBatchSize={40}
|
minimumBatchSize={40}
|
||||||
route={{
|
route={{
|
||||||
route: defaultFullPlaylist
|
route: AppRoute.PLAYLISTS_DETAIL_SONGS,
|
||||||
? AppRoute.PLAYLISTS_DETAIL_SONGS
|
|
||||||
: AppRoute.PLAYLISTS_DETAIL,
|
|
||||||
slugs: [{ idProperty: 'id', slugProperty: 'playlistId' }],
|
slugs: [{ idProperty: 'id', slugProperty: 'playlistId' }],
|
||||||
}}
|
}}
|
||||||
width={width}
|
width={width}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { VirtualTable } from '/@/renderer/components/virtual-table';
|
|||||||
import { 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 { PLAYLIST_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
|
|
||||||
interface PlaylistListTableViewProps {
|
interface PlaylistListTableViewProps {
|
||||||
itemCount?: number;
|
itemCount?: number;
|
||||||
@@ -18,16 +18,11 @@ interface PlaylistListTableViewProps {
|
|||||||
export const PlaylistListTableView = ({ tableRef, itemCount }: PlaylistListTableViewProps) => {
|
export const PlaylistListTableView = ({ tableRef, itemCount }: PlaylistListTableViewProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const { defaultFullPlaylist } = useGeneralSettings();
|
|
||||||
const pageKey = 'playlist';
|
const pageKey = 'playlist';
|
||||||
|
|
||||||
const handleRowDoubleClick = (e: RowDoubleClickedEvent) => {
|
const handleRowDoubleClick = (e: RowDoubleClickedEvent) => {
|
||||||
if (!e.data) return;
|
if (!e.data) return;
|
||||||
if (defaultFullPlaylist) {
|
navigate(generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: e.data.id }));
|
||||||
navigate(generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: e.data.id }));
|
|
||||||
} else {
|
|
||||||
navigate(generatePath(AppRoute.PLAYLISTS_DETAIL, { playlistId: e.data.id }));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableProps = useVirtualTable({
|
const tableProps = useVirtualTable({
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useQuery, useInfiniteQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { api } from '/@/renderer/api';
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import type { PlaylistSongListQuery, PlaylistSongListResponse } from '/@/renderer/api/types';
|
import type { PlaylistSongListQuery } from '/@/renderer/api/types';
|
||||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||||
import { getServerById } from '/@/renderer/store';
|
import { getServerById } from '/@/renderer/store';
|
||||||
import { api } from '/@/renderer/api';
|
|
||||||
|
|
||||||
export const usePlaylistSongList = (args: QueryHookArgs<PlaylistSongListQuery>) => {
|
export const usePlaylistSongList = (args: QueryHookArgs<PlaylistSongListQuery>) => {
|
||||||
const { options, query, serverId } = args || {};
|
const { options, query, serverId } = args || {};
|
||||||
@@ -23,31 +23,31 @@ export const usePlaylistSongList = (args: QueryHookArgs<PlaylistSongListQuery>)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePlaylistSongListInfinite = (args: QueryHookArgs<PlaylistSongListQuery>) => {
|
// export const usePlaylistSongListInfinite = (args: QueryHookArgs<PlaylistSongListQuery>) => {
|
||||||
const { options, query, serverId } = args || {};
|
// const { options, query, serverId } = args || {};
|
||||||
const server = getServerById(serverId);
|
// const server = getServerById(serverId);
|
||||||
|
|
||||||
return useInfiniteQuery({
|
// return useInfiniteQuery({
|
||||||
enabled: !!server,
|
// enabled: !!server,
|
||||||
getNextPageParam: (lastPage: PlaylistSongListResponse | undefined, pages) => {
|
// getNextPageParam: (lastPage: PlaylistSongListResponse | undefined, pages) => {
|
||||||
if (!lastPage?.items) return undefined;
|
// if (!lastPage?.items) return undefined;
|
||||||
if (lastPage?.items?.length >= (query?.limit || 50)) {
|
// if (lastPage?.items?.length >= (query?.limit || 50)) {
|
||||||
return pages?.length;
|
// return pages?.length;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return undefined;
|
// return undefined;
|
||||||
},
|
// },
|
||||||
queryFn: ({ pageParam = 0, signal }) => {
|
// queryFn: ({ pageParam = 0, signal }) => {
|
||||||
return api.controller.getPlaylistSongList({
|
// return api.controller.getPlaylistSongList({
|
||||||
apiClientProps: { server, signal },
|
// apiClientProps: { server, signal },
|
||||||
query: {
|
// query: {
|
||||||
...query,
|
// ...query,
|
||||||
limit: query.limit || 50,
|
// limit: query.limit || 50,
|
||||||
startIndex: pageParam * (query.limit || 50),
|
// startIndex: pageParam * (query.limit || 50),
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
queryKey: queryKeys.playlists.detailSongList(server?.id || '', query.id, query),
|
// queryKey: queryKeys.playlists.detailSongList(server?.id || '', query.id, query),
|
||||||
...options,
|
// ...options,
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
|
|||||||
@@ -246,28 +246,6 @@ export const ControlSettings = () => {
|
|||||||
isHidden: !isElectron(),
|
isHidden: !isElectron(),
|
||||||
title: t('setting.savePlayQueue', { postProcess: 'sentenceCase' }),
|
title: t('setting.savePlayQueue', { postProcess: 'sentenceCase' }),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
control: (
|
|
||||||
<Switch
|
|
||||||
aria-label="Go to playlist songs page by default"
|
|
||||||
defaultChecked={settings.defaultFullPlaylist}
|
|
||||||
onChange={(e) =>
|
|
||||||
setSettings({
|
|
||||||
general: {
|
|
||||||
...settings,
|
|
||||||
defaultFullPlaylist: e.currentTarget.checked,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
description: t('setting.skipPlaylistPage', {
|
|
||||||
context: 'description',
|
|
||||||
postProcess: 'sentenceCase',
|
|
||||||
}),
|
|
||||||
isHidden: false,
|
|
||||||
title: t('setting.skipPlaylistPage', { postProcess: 'sentenceCase' }),
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SettingsSection options={controlOptions} />;
|
return <SettingsSection options={controlOptions} />;
|
||||||
|
|||||||
@@ -14,16 +14,12 @@ import { Play } from '/@/renderer/types';
|
|||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
||||||
import { useHideScrollbar } from '/@/renderer/hooks';
|
import { useHideScrollbar } from '/@/renderer/hooks';
|
||||||
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
|
|
||||||
const PlaylistRow = ({ index, data, style }: ListChildComponentProps) => {
|
const PlaylistRow = ({ index, data, style }: ListChildComponentProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const path = data?.items[index].id
|
const path = data?.items[index].id
|
||||||
? data.defaultFullPlaylist
|
? generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: data.items[index].id })
|
||||||
? generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: data.items[index].id })
|
|
||||||
: generatePath(AppRoute.PLAYLISTS_DETAIL, {
|
|
||||||
playlistId: data?.items[index].id,
|
|
||||||
})
|
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -120,7 +116,6 @@ const PlaylistRow = ({ index, data, style }: ListChildComponentProps) => {
|
|||||||
export const SidebarPlaylistList = () => {
|
export const SidebarPlaylistList = () => {
|
||||||
const { isScrollbarHidden, hideScrollbarElementProps } = useHideScrollbar(0);
|
const { isScrollbarHidden, hideScrollbarElementProps } = useHideScrollbar(0);
|
||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||||
const { defaultFullPlaylist } = useGeneralSettings();
|
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
|
|
||||||
const playlistsQuery = usePlaylistList({
|
const playlistsQuery = usePlaylistList({
|
||||||
@@ -154,11 +149,10 @@ export const SidebarPlaylistList = () => {
|
|||||||
|
|
||||||
const memoizedItemData = useMemo(() => {
|
const memoizedItemData = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
defaultFullPlaylist,
|
|
||||||
handlePlay: handlePlayPlaylist,
|
handlePlay: handlePlayPlaylist,
|
||||||
items: playlistsQuery?.data?.items,
|
items: playlistsQuery?.data?.items,
|
||||||
};
|
};
|
||||||
}, [playlistsQuery?.data?.items, defaultFullPlaylist, handlePlayPlaylist]);
|
}, [playlistsQuery?.data?.items, handlePlayPlaylist]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ const AlbumListRoute = lazy(() => import('/@/renderer/features/albums/routes/alb
|
|||||||
|
|
||||||
const SongListRoute = lazy(() => import('/@/renderer/features/songs/routes/song-list-route'));
|
const SongListRoute = lazy(() => import('/@/renderer/features/songs/routes/song-list-route'));
|
||||||
|
|
||||||
const PlaylistDetailRoute = lazy(
|
|
||||||
() => import('/@/renderer/features/playlists/routes/playlist-detail-route'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const PlaylistDetailSongListRoute = lazy(
|
const PlaylistDetailSongListRoute = lazy(
|
||||||
() => import('/@/renderer/features/playlists/routes/playlist-detail-song-list-route'),
|
() => import('/@/renderer/features/playlists/routes/playlist-detail-song-list-route'),
|
||||||
);
|
);
|
||||||
@@ -136,11 +132,6 @@ export const AppRouter = () => {
|
|||||||
errorElement={<RouteErrorBoundary />}
|
errorElement={<RouteErrorBoundary />}
|
||||||
path={AppRoute.PLAYLISTS}
|
path={AppRoute.PLAYLISTS}
|
||||||
/>
|
/>
|
||||||
<Route
|
|
||||||
element={<PlaylistDetailRoute />}
|
|
||||||
errorElement={<RouteErrorBoundary />}
|
|
||||||
path={AppRoute.PLAYLISTS_DETAIL}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
element={<PlaylistDetailSongListRoute />}
|
element={<PlaylistDetailSongListRoute />}
|
||||||
errorElement={<RouteErrorBoundary />}
|
errorElement={<RouteErrorBoundary />}
|
||||||
|
|||||||
Reference in New Issue
Block a user