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:
jeffvli 2023-05-20 23:13:20 -07:00
parent 652c4a1f81
commit 292737d53c
2 changed files with 65 additions and 2 deletions

View file

@ -1001,3 +1001,7 @@ export type SearchResponse = {
albums: Album[];
songs: Song[];
};
export const instanceOfCancellationError = (error: any) => {
return 'revert' in error;
};

View file

@ -1,4 +1,4 @@
import { useCallback } from 'react';
import { useCallback, useRef } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/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 isElectron from 'is-electron';
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 {
getPlaylistSongsById,
getSongById,
@ -14,6 +20,31 @@ import {
getAlbumArtistSongsById,
getSongsByQuery,
} 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 utils = isElectron() ? window.electron.utils : null;
@ -26,6 +57,7 @@ export const useHandlePlayQueueAdd = () => {
const playerType = usePlayerType();
const server = useCurrentServer();
const { play } = usePlayerControls();
const timeoutIds = useRef<Record<string, ReturnType<typeof setTimeout>> | null>({});
const handlePlayQueueAdd = useCallback(
async (options: PlayQueueAddOptions) => {
@ -38,6 +70,25 @@ export const useHandlePlayQueueAdd = () => {
let songList: SongListResponse | undefined;
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 {
if (itemType === LibraryItem.PLAYLIST) {
songList = await getPlaylistSongsById({ id: id?.[0], query, queryClient, server });
@ -50,7 +101,15 @@ export const useHandlePlayQueueAdd = () => {
} else {
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) {
if (instanceOfCancellationError(err)) {
return null;
}
return toast.error({
message: err.message,
title: 'Play queue add failed',