Add query cancellation for play queue requests
- Opens a notification after 2s to allow for manual cancellation of in-progress query
This commit is contained in:
parent
652c4a1f81
commit
292737d53c
2 changed files with 65 additions and 2 deletions
|
@ -1001,3 +1001,7 @@ export type SearchResponse = {
|
||||||
albums: Album[];
|
albums: Album[];
|
||||||
songs: Song[];
|
songs: Song[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const instanceOfCancellationError = (error: any) => {
|
||||||
|
return 'revert' in error;
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/store';
|
import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/store';
|
||||||
import { usePlayerType } from '/@/renderer/store/settings.store';
|
import { usePlayerType } from '/@/renderer/store/settings.store';
|
||||||
|
@ -6,7 +6,13 @@ import { PlayQueueAddOptions, Play, PlaybackType } from '/@/renderer/types';
|
||||||
import { toast } from '/@/renderer/components/toast/index';
|
import { toast } from '/@/renderer/components/toast/index';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { nanoid } from 'nanoid/non-secure';
|
import { nanoid } from 'nanoid/non-secure';
|
||||||
import { LibraryItem, QueueSong, Song, SongListResponse } from '/@/renderer/api/types';
|
import {
|
||||||
|
LibraryItem,
|
||||||
|
QueueSong,
|
||||||
|
Song,
|
||||||
|
SongListResponse,
|
||||||
|
instanceOfCancellationError,
|
||||||
|
} from '/@/renderer/api/types';
|
||||||
import {
|
import {
|
||||||
getPlaylistSongsById,
|
getPlaylistSongsById,
|
||||||
getSongById,
|
getSongById,
|
||||||
|
@ -14,6 +20,31 @@ import {
|
||||||
getAlbumArtistSongsById,
|
getAlbumArtistSongsById,
|
||||||
getSongsByQuery,
|
getSongsByQuery,
|
||||||
} from '/@/renderer/features/player/utils';
|
} from '/@/renderer/features/player/utils';
|
||||||
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
|
|
||||||
|
const getRootQueryKey = (itemType: LibraryItem, serverId: string) => {
|
||||||
|
let queryKey;
|
||||||
|
|
||||||
|
switch (itemType) {
|
||||||
|
case LibraryItem.ALBUM:
|
||||||
|
queryKey = queryKeys.songs.list(serverId);
|
||||||
|
break;
|
||||||
|
case LibraryItem.ALBUM_ARTIST:
|
||||||
|
queryKey = queryKeys.songs.list(serverId);
|
||||||
|
break;
|
||||||
|
case LibraryItem.PLAYLIST:
|
||||||
|
queryKey = queryKeys.playlists.songList(serverId);
|
||||||
|
break;
|
||||||
|
case LibraryItem.SONG:
|
||||||
|
queryKey = queryKeys.songs.list(serverId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
queryKey = queryKeys.songs.list(serverId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryKey;
|
||||||
|
};
|
||||||
|
|
||||||
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
||||||
const utils = isElectron() ? window.electron.utils : null;
|
const utils = isElectron() ? window.electron.utils : null;
|
||||||
|
@ -26,6 +57,7 @@ export const useHandlePlayQueueAdd = () => {
|
||||||
const playerType = usePlayerType();
|
const playerType = usePlayerType();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const { play } = usePlayerControls();
|
const { play } = usePlayerControls();
|
||||||
|
const timeoutIds = useRef<Record<string, ReturnType<typeof setTimeout>> | null>({});
|
||||||
|
|
||||||
const handlePlayQueueAdd = useCallback(
|
const handlePlayQueueAdd = useCallback(
|
||||||
async (options: PlayQueueAddOptions) => {
|
async (options: PlayQueueAddOptions) => {
|
||||||
|
@ -38,6 +70,25 @@ export const useHandlePlayQueueAdd = () => {
|
||||||
let songList: SongListResponse | undefined;
|
let songList: SongListResponse | undefined;
|
||||||
const { type: itemType, id } = byItemType;
|
const { type: itemType, id } = byItemType;
|
||||||
|
|
||||||
|
const fetchId = nanoid();
|
||||||
|
timeoutIds.current = {
|
||||||
|
...timeoutIds.current,
|
||||||
|
[fetchId]: setTimeout(() => {
|
||||||
|
toast.info({
|
||||||
|
autoClose: false,
|
||||||
|
id: fetchId,
|
||||||
|
message: 'This is taking a while... close the notification to cancel the request',
|
||||||
|
onClose: () => {
|
||||||
|
queryClient.cancelQueries({
|
||||||
|
exact: false,
|
||||||
|
queryKey: getRootQueryKey(itemType, server?.id),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
title: 'Adding to queue',
|
||||||
|
});
|
||||||
|
}, 2000),
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (itemType === LibraryItem.PLAYLIST) {
|
if (itemType === LibraryItem.PLAYLIST) {
|
||||||
songList = await getPlaylistSongsById({ id: id?.[0], query, queryClient, server });
|
songList = await getPlaylistSongsById({ id: id?.[0], query, queryClient, server });
|
||||||
|
@ -50,7 +101,15 @@ export const useHandlePlayQueueAdd = () => {
|
||||||
} else {
|
} else {
|
||||||
songList = await getSongById({ id: id?.[0], queryClient, server });
|
songList = await getSongById({ id: id?.[0], queryClient, server });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearTimeout(timeoutIds.current[fetchId] as ReturnType<typeof setTimeout>);
|
||||||
|
delete timeoutIds.current[fetchId];
|
||||||
|
toast.hide(fetchId);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
if (instanceOfCancellationError(err)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return toast.error({
|
return toast.error({
|
||||||
message: err.message,
|
message: err.message,
|
||||||
title: 'Play queue add failed',
|
title: 'Play queue add failed',
|
||||||
|
|
Reference in a new issue