Handle queue all songs by double click (#67)

This commit is contained in:
jeffvli 2023-05-20 21:31:00 -07:00
parent 93530008a9
commit 51c2731b07
11 changed files with 129 additions and 55 deletions

View file

@ -1,5 +1,5 @@
import { MutableRefObject, useCallback, useMemo } from 'react';
import { Button, Text } from '/@/renderer/components';
import { Button } from '/@/renderer/components';
import { ColDef, RowDoubleClickedEvent, RowHeightParams, RowNode } from '@ag-grid-community/core';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { Box, Group, Stack } from '@mantine/core';
@ -213,16 +213,17 @@ export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS);
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
if (!e.data || e.node.isFullWidthCell()) return;
const rowData: QueueSong[] = [];
e.api.forEachNode((node) => {
if (!node.data) return;
if (!node.data || node.isFullWidthCell()) return;
rowData.push(node.data);
});
handlePlayQueueAdd?.({
byData: rowData,
initialSongId: e.data.id,
playType: playButtonBehavior,
});
};

View file

@ -256,9 +256,11 @@ export const AlbumArtistDetailContent = () => {
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS);
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
if (!e.data || !topSongsQuery?.data) return;
handlePlayQueueAdd?.({
byData: [e.data],
byData: topSongsQuery?.data?.items || [],
initialSongId: e.data.id,
playType: playButtonBehavior,
});
};

View file

@ -34,8 +34,16 @@ export const AlbumArtistDetailTopSongsListContent = ({
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
const rowData: QueueSong[] = [];
e.api.forEachNode((node) => {
if (!node.data) return;
rowData.push(node.data);
});
handlePlayQueueAdd?.({
byData: [e.data],
byData: rowData,
initialSongId: e.data.id,
playType: playButtonBehavior,
});
};

View file

@ -12,6 +12,7 @@ import {
getSongById,
getAlbumSongsById,
getAlbumArtistSongsById,
getSongsByQuery,
} from '/@/renderer/features/player/utils';
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
@ -44,6 +45,8 @@ export const useHandlePlayQueueAdd = () => {
songList = await getAlbumSongsById({ id, query, queryClient, server });
} else if (itemType === LibraryItem.ALBUM_ARTIST) {
songList = await getAlbumArtistSongsById({ id, query, queryClient, server });
} else if (itemType === LibraryItem.SONG) {
songList = await getSongsByQuery({ query, queryClient, server });
} else {
songList = await getSongById({ id: id?.[0], queryClient, server });
}
@ -61,8 +64,6 @@ export const useHandlePlayQueueAdd = () => {
if (!songs) return toast.warn({ message: 'No songs found' });
// const index = initialIndex || initial songs.findIndex((song) => song.id === initialSongId);
if (initialIndex) {
initialSongIndex = initialIndex;
} else if (initialSongId) {

View file

@ -124,6 +124,41 @@ export const getAlbumArtistSongsById = async (args: {
return res;
};
export const getSongsByQuery = async (args: {
query?: Partial<SongListQuery>;
queryClient: QueryClient;
server: ServerListItem;
}) => {
const { queryClient, server, query } = args;
const queryFilter: SongListQuery = {
sortBy: SongListSort.ALBUM,
sortOrder: SortOrder.ASC,
startIndex: 0,
...query,
};
const queryKey = queryKeys.songs.list(server?.id, queryFilter);
const res = await queryClient.fetchQuery(
queryKey,
async ({ signal }) =>
api.controller.getSongList({
apiClientProps: {
server,
signal,
},
query: queryFilter,
}),
{
cacheTime: 1000 * 60,
staleTime: 1000 * 60,
},
);
return res;
};
export const getSongById = async (args: {
id: string;
queryClient: QueryClient;

View file

@ -201,8 +201,13 @@ export const PlaylistDetailContent = ({ tableRef }: PlaylistDetailContentProps)
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
handlePlayQueueAdd?.({
byData: [e.data],
byItemType: {
id: [playlistId],
type: LibraryItem.PLAYLIST,
},
initialSongId: e.data.id,
playType: playButtonBehavior,
});
};

View file

@ -25,7 +25,6 @@ import { useHandleTableContextMenu } from '/@/renderer/features/context-menu';
import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { LibraryItem, QueueSong, SongListQuery } from '/@/renderer/api/types';
import { usePlayQueueAdd } from '/@/renderer/features/player';
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-grid';
import { getColumnDefs, VirtualTable, TablePagination } from '/@/renderer/components/virtual-table';
@ -39,12 +38,11 @@ export const SongListContent = ({ itemCount, tableRef }: SongListContentProps) =
const queryClient = useQueryClient();
const server = useCurrentServer();
const { id, pageKey } = useSongListContext();
const { id, pageKey, handlePlay } = useSongListContext();
const filter = useSongListFilter({ id, key: pageKey });
const { display, table } = useSongListStore({ id, key: pageKey });
const { setTable, setTablePagination } = useListStoreActions();
const handlePlayQueueAdd = usePlayQueueAdd();
const playButtonBehavior = usePlayButtonBehavior();
const isPaginationEnabled = display === ListDisplayType.TABLE_PAGINATED;
@ -160,10 +158,7 @@ export const SongListContent = ({ itemCount, tableRef }: SongListContentProps) =
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
handlePlayQueueAdd?.({
byData: [e.data],
playType: playButtonBehavior,
});
handlePlay?.({ initialSongId: e.data.id, playType: playButtonBehavior });
};
return (

View file

@ -1,5 +1,6 @@
import { useCallback, useMemo, ChangeEvent, MutableRefObject, MouseEvent } from 'react';
import { IDatasource } from '@ag-grid-community/core';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { Flex, Group, Stack } from '@mantine/core';
import { openModal } from '@mantine/modals';
import {
@ -24,7 +25,6 @@ import { JellyfinSongFilters } from '/@/renderer/features/songs/components/jelly
import { NavidromeSongFilters } from '/@/renderer/features/songs/components/navidrome-song-filters';
import { useContainerQuery } from '/@/renderer/hooks';
import { queryClient } from '/@/renderer/lib/react-query';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import {
SongListFilter,
useCurrentServer,
@ -265,13 +265,25 @@ export const SongListHeaderFilters = ({
if (!itemCount || itemCount === 0) return;
const query: SongListQuery = { startIndex: 0, ...filter, ...customFilters };
if (id) {
handlePlayQueueAdd?.({
byItemType: {
id: query,
id: [id],
type: LibraryItem.ALBUM_ARTIST,
},
playType,
query,
});
} else {
handlePlayQueueAdd?.({
byItemType: {
id: [],
type: LibraryItem.SONG,
},
playType,
query,
});
}
};
const handleOpenFiltersModal = () => {

View file

@ -5,9 +5,8 @@ import debounce from 'lodash/debounce';
import { ChangeEvent, MutableRefObject, useCallback } from 'react';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { LibraryItem, SongListQuery } from '/@/renderer/api/types';
import { SongListQuery } from '/@/renderer/api/types';
import { PageHeader, SearchInput } from '/@/renderer/components';
import { usePlayQueueAdd } from '/@/renderer/features/player';
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
import { SongListHeaderFilters } from '/@/renderer/features/songs/components/song-list-header-filters';
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
@ -20,26 +19,18 @@ import {
useSongListFilter,
} from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { Play } from '/@/renderer/types';
interface SongListHeaderProps {
customFilters?: Partial<SongListFilter>;
itemCount?: number;
tableRef: MutableRefObject<AgGridReactType | null>;
title?: string;
}
export const SongListHeader = ({
customFilters,
title,
itemCount,
tableRef,
}: SongListHeaderProps) => {
export const SongListHeader = ({ title, itemCount, tableRef }: SongListHeaderProps) => {
const server = useCurrentServer();
const { id, pageKey } = useSongListContext();
const { id, pageKey, handlePlay } = useSongListContext();
const filter = useSongListFilter({ id, key: pageKey });
const { setFilter, setTablePagination } = useListStoreActions();
const handlePlayQueueAdd = usePlayQueueAdd();
const cq = useContainerQuery();
const handleFilterChange = useCallback(
@ -55,7 +46,6 @@ export const SongListHeader = ({
limit,
startIndex,
...pageFilters,
...customFilters,
};
const queryKey = queryKeys.songs.list(server?.id || '', query);
@ -82,7 +72,7 @@ export const SongListHeader = ({
tableRef.current?.api.ensureIndexVisible(0, 'top');
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
},
[customFilters, filter, pageKey, server, setTablePagination, tableRef],
[filter, pageKey, server, setTablePagination, tableRef],
);
const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => {
@ -94,19 +84,6 @@ export const SongListHeader = ({
const playButtonBehavior = usePlayButtonBehavior();
const handlePlay = async (playType: Play) => {
if (!itemCount || itemCount === 0) return;
const query: SongListQuery = { startIndex: 0, ...filter, ...customFilters };
handlePlayQueueAdd?.({
byItemType: {
id: query,
type: LibraryItem.SONG,
},
playType,
});
};
return (
<Stack
ref={cq.ref}
@ -118,7 +95,9 @@ export const SongListHeader = ({
w="100%"
>
<LibraryHeaderBar>
<LibraryHeaderBar.PlayButton onClick={() => handlePlay(playButtonBehavior)} />
<LibraryHeaderBar.PlayButton
onClick={() => handlePlay?.({ playType: playButtonBehavior })}
/>
<LibraryHeaderBar.Title>{title || 'Tracks'}</LibraryHeaderBar.Title>
<LibraryHeaderBar.Badge isLoading={itemCount === null || itemCount === undefined}>
{itemCount}
@ -135,7 +114,6 @@ export const SongListHeader = ({
</PageHeader>
<FilterBar>
<SongListHeaderFilters
customFilters={customFilters}
itemCount={itemCount}
tableRef={tableRef}
/>

View file

@ -1,7 +1,9 @@
import { createContext, useContext } from 'react';
import { ListKey } from '/@/renderer/store';
import { Play } from '/@/renderer/types';
interface SongListContextProps {
handlePlay?: (args: { initialSongId?: string; playType: Play }) => void;
id?: string;
pageKey: ListKey;
}

View file

@ -1,12 +1,15 @@
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { useRef } from 'react';
import { useCallback, useRef } from 'react';
import { useParams, useSearchParams } from 'react-router-dom';
import { SongListQuery, LibraryItem } from '/@/renderer/api/types';
import { usePlayQueueAdd } from '/@/renderer/features/player';
import { AnimatedPage } from '/@/renderer/features/shared';
import { SongListContent } from '/@/renderer/features/songs/components/song-list-content';
import { SongListHeader } from '/@/renderer/features/songs/components/song-list-header';
import { SongListContext } from '/@/renderer/features/songs/context/song-list-context';
import { useSongList } from '/@/renderer/features/songs/queries/song-list-query';
import { generatePageKey, useCurrentServer, useSongListFilter } from '/@/renderer/store';
import { Play } from '/@/renderer/types';
const TrackListRoute = () => {
const tableRef = useRef<AgGridReactType | null>(null);
@ -18,6 +21,7 @@ const TrackListRoute = () => {
albumArtistId ? `${albumArtistId}_${server?.id}` : undefined,
);
const handlePlayQueueAdd = usePlayQueueAdd();
const songListFilter = useSongListFilter({ id: albumArtistId, key: pageKey });
const itemCountCheck = useSongList({
options: {
@ -37,9 +41,40 @@ const TrackListRoute = () => {
? undefined
: itemCountCheck.data?.totalRecordCount;
const handlePlay = useCallback(
async (args: { initialSongId?: string; playType: Play }) => {
if (!itemCount || itemCount === 0) return;
const { initialSongId, playType } = args;
const query: SongListQuery = { startIndex: 0, ...songListFilter };
if (albumArtistId) {
handlePlayQueueAdd?.({
byItemType: {
id: [albumArtistId],
type: LibraryItem.ALBUM_ARTIST,
},
initialSongId,
playType,
query,
});
} else {
handlePlayQueueAdd?.({
byItemType: {
id: [],
type: LibraryItem.SONG,
},
initialSongId,
playType,
query,
});
}
},
[albumArtistId, handlePlayQueueAdd, itemCount, songListFilter],
);
return (
<AnimatedPage>
<SongListContext.Provider value={{ id: albumArtistId, pageKey }}>
<SongListContext.Provider value={{ handlePlay, id: albumArtistId, pageKey }}>
<SongListHeader
itemCount={itemCount}
tableRef={tableRef}