fix the loading state on listquery add to queue

This commit is contained in:
jeffvli
2025-12-07 02:28:16 -08:00
parent ce7e5ad7ad
commit 8841781e6b
13 changed files with 83 additions and 90 deletions
+1 -2
View File
@@ -358,7 +358,6 @@
}, },
"moveToNext": "přesunout na další", "moveToNext": "přesunout na další",
"downloadStarted": "spuštěno stahování {{count}} položek", "downloadStarted": "spuštěno stahování {{count}} položek",
"largeFetch": "tato akce se pokusí načíst {{count}} položek z vašeho serveru",
"moveItems": "přesunout položky", "moveItems": "přesunout položky",
"shuffle": "náhodně", "shuffle": "náhodně",
"shuffleAll": "vše náhodně", "shuffleAll": "vše náhodně",
@@ -913,7 +912,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "přidat položky do fronty", "title": "přidat položky do fronty",
"description": "Tento požadavek se pokusí načíst {{count}} položek ze serveru" "description": "Tato akce přidá všechny položky v aktuálně filtrovaném zobrazení"
}, },
"shuffleAll": { "shuffleAll": {
"title": "přehrát náhodně", "title": "přehrát náhodně",
+1 -2
View File
@@ -23,7 +23,6 @@
}, },
"moveToNext": "Als nächstes", "moveToNext": "Als nächstes",
"downloadStarted": "Download von {{count}} Elementen gestartet", "downloadStarted": "Download von {{count}} Elementen gestartet",
"largeFetch": "Diese Aktion wird versuchen {{count}} Elemente von Deinem Server zu laden",
"moveItems": "Elemente verschieben", "moveItems": "Elemente verschieben",
"shuffle": "Zufällig wiedergeben", "shuffle": "Zufällig wiedergeben",
"shuffleAll": "Alle zufällig wiedergeben", "shuffleAll": "Alle zufällig wiedergeben",
@@ -294,7 +293,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "Elemente der Warteschlange hinzufügen", "title": "Elemente der Warteschlange hinzufügen",
"description": "Diese Anfrage wird versuchen {{count}} Elemente vom Server abzurufen" "description": "Diese Aktion fügt alle Elemente in der aktuell gefilterten Ansicht hinzu"
}, },
"shuffleAll": { "shuffleAll": {
"title": "Zufallswiedergabe", "title": "Zufallswiedergabe",
+1 -2
View File
@@ -9,7 +9,6 @@
"downloadStarted": "started download of {{count}} items", "downloadStarted": "started download of {{count}} items",
"editPlaylist": "edit $t(entity.playlist_one)", "editPlaylist": "edit $t(entity.playlist_one)",
"goToPage": "go to page", "goToPage": "go to page",
"largeFetch": "this action will attempt to fetch {{count}} items from your server",
"moveToNext": "move to next", "moveToNext": "move to next",
"moveToBottom": "move to bottom", "moveToBottom": "move to bottom",
"moveToTop": "move to top", "moveToTop": "move to top",
@@ -297,7 +296,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "add items to the queue", "title": "add items to the queue",
"description": "This request will attempt to fetch {{count}} items from the server" "description": "This action will add all items in the current filtered view"
}, },
"addToPlaylist": { "addToPlaylist": {
"create": "create $t(entity.playlist_one) {{playlist}}", "create": "create $t(entity.playlist_one) {{playlist}}",
+1 -2
View File
@@ -358,7 +358,6 @@
}, },
"moveToNext": "pasar al siguiente", "moveToNext": "pasar al siguiente",
"downloadStarted": "Iniciada descarga de {{count}} elementos", "downloadStarted": "Iniciada descarga de {{count}} elementos",
"largeFetch": "Esta acción intentará obtener {{count}} elementos de tu servidor",
"moveItems": "Mover elementos", "moveItems": "Mover elementos",
"shuffle": "Mezclar", "shuffle": "Mezclar",
"shuffleAll": "Mezclar todo", "shuffleAll": "Mezclar todo",
@@ -813,7 +812,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "Añadir elementos a la cola", "title": "Añadir elementos a la cola",
"description": "Esta petición intentará obtener {{count}} elementos del servidor" "description": "Esta acción agregará todos los elementos en la vista filtrada actual"
}, },
"shuffleAll": { "shuffleAll": {
"title": "Reproducir aleatorio", "title": "Reproducir aleatorio",
+1 -2
View File
@@ -61,7 +61,6 @@
}, },
"moveToNext": "passer au suivant", "moveToNext": "passer au suivant",
"downloadStarted": "téléchargement de {{count}} éléments en cours", "downloadStarted": "téléchargement de {{count}} éléments en cours",
"largeFetch": "cette action tentera de récupérer {{count}} éléments depuis votre serveur.",
"moveItems": "déplacer les entrées", "moveItems": "déplacer les entrées",
"shuffle": "mélanger", "shuffle": "mélanger",
"shuffleAll": "mélanger tout", "shuffleAll": "mélanger tout",
@@ -807,7 +806,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "ajouter des entrées à la file d'attente", "title": "ajouter des entrées à la file d'attente",
"description": "cette requête tentera de récupérer {{count}} éléments depuis le serveur" "description": "Cette action ajoutera tous les éléments dans la vue filtrée actuelle"
}, },
"shuffleAll": { "shuffleAll": {
"title": "jouer aléatoirement", "title": "jouer aléatoirement",
+1 -2
View File
@@ -23,7 +23,6 @@
"addToPlaylist": "hozzáadás lejátszási listához: $t(entity.playlist_one)", "addToPlaylist": "hozzáadás lejátszási listához: $t(entity.playlist_one)",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"downloadStarted": "megkezdődött {{count}} elem letöltése", "downloadStarted": "megkezdődött {{count}} elem letöltése",
"largeFetch": "ez a művelet {{count}} hálózati kérést fog küldeni a szerverednek",
"moveItems": "elemek mozgatása", "moveItems": "elemek mozgatása",
"shuffle": "keverés", "shuffle": "keverés",
"shuffleAll": "összes keverése", "shuffleAll": "összes keverése",
@@ -325,7 +324,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "műsorlistához ad", "title": "műsorlistához ad",
"description": "Ez a kérés megpróbál letölteni {{count}} elemet a szerverről" "description": "Ez a művelet hozzáadja az összes elemet az aktuális szűrt nézetben"
}, },
"shuffleAll": { "shuffleAll": {
"title": "véletlenszerű lejátszás", "title": "véletlenszerű lejátszás",
+1 -2
View File
@@ -324,7 +324,6 @@
}, },
"moveToNext": "次", "moveToNext": "次",
"downloadStarted": "{{count}} 曲のダウンロードを開始しました", "downloadStarted": "{{count}} 曲のダウンロードを開始しました",
"largeFetch": "この操作により、サーバーに対して {{count}} 回のネットワークリクエストが送信されます",
"moveItems": "曲を移動", "moveItems": "曲を移動",
"shuffle": "シャッフル", "shuffle": "シャッフル",
"shuffleAll": "すべてシャッフル", "shuffleAll": "すべてシャッフル",
@@ -807,7 +806,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "キューにアイテムを追加する", "title": "キューにアイテムを追加する",
"description": "このリクエストはサーバーから {{count}} 個のアイテムを取得しようと試みます" "description": "このアクションは、現在のフィルターされたビュー内のすべてのアイテムを追加します"
} }
}, },
"entity": { "entity": {
+1 -2
View File
@@ -23,7 +23,6 @@
}, },
"moveToNext": "ga naar volgende", "moveToNext": "ga naar volgende",
"downloadStarted": "begonnen met downloaden van {{count}} items", "downloadStarted": "begonnen met downloaden van {{count}} items",
"largeFetch": "deze actie zal {{count}} netwerkverzoeken naar je server sturen",
"moveItems": "verplaats items", "moveItems": "verplaats items",
"shuffle": "shuffle", "shuffle": "shuffle",
"shuffleAll": "shuffle alles", "shuffleAll": "shuffle alles",
@@ -557,7 +556,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "items toevoegen aan de wachtrij", "title": "items toevoegen aan de wachtrij",
"description": "Dit verzoek zal proberen {{count}} items van de server op te halen" "description": "Deze actie voegt alle items in de huidige gefilterde weergave toe"
}, },
"shuffleAll": { "shuffleAll": {
"title": "willekeurig afspelen", "title": "willekeurig afspelen",
+1 -2
View File
@@ -23,7 +23,6 @@
}, },
"moveToNext": "przesuń na następne", "moveToNext": "przesuń na następne",
"downloadStarted": "rozpoczęto pobieranie {{count}} elementów", "downloadStarted": "rozpoczęto pobieranie {{count}} elementów",
"largeFetch": "ta akcja wykona {{count}} żądań sieciowych do twojego serwera",
"moveItems": "przenieś elementy", "moveItems": "przenieś elementy",
"shuffle": "odtwarzaj losowo", "shuffle": "odtwarzaj losowo",
"shuffleAll": "odtwarzaj wszystkie losowo", "shuffleAll": "odtwarzaj wszystkie losowo",
@@ -346,7 +345,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "dodaj elementy do kolejki", "title": "dodaj elementy do kolejki",
"description": "To żądanie spróbuje pobrać {{count}} elementów z serwera" "description": "Ta akcja doda wszystkie elementy w aktualnie przefiltrowanym widoku"
}, },
"shuffleAll": { "shuffleAll": {
"title": "odtwarzaj losowo", "title": "odtwarzaj losowo",
+1 -2
View File
@@ -778,7 +778,6 @@
"musicbrainz": "在MusicBrainz開啟" "musicbrainz": "在MusicBrainz開啟"
}, },
"downloadStarted": "已開始下載 {{count}} 項內容", "downloadStarted": "已開始下載 {{count}} 項內容",
"largeFetch": "這項操作將會嘗試從您的伺服器擷取 {{count}} 個項目",
"moveItems": "移動項目", "moveItems": "移動項目",
"shuffle": "隨機播放", "shuffle": "隨機播放",
"shuffleAll": "全部隨機播放", "shuffleAll": "全部隨機播放",
@@ -925,7 +924,7 @@
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "將項目加入佇列", "title": "將項目加入佇列",
"description": "這個請求將嘗試從伺服器擷取 {{count}} 個項目" "description": "此操作將新增目前篩選檢視中的所有項目"
}, },
"shuffleAll": { "shuffleAll": {
"title": "隨機播放", "title": "隨機播放",
+5 -1
View File
@@ -266,7 +266,11 @@ export const queryKeys: Record<
list: (serverId: string) => [serverId, 'musicFolders', 'list'] as const, list: (serverId: string) => [serverId, 'musicFolders', 'list'] as const,
}, },
player: { player: {
fetch: () => { fetch: (meta?: any) => {
if (meta) {
return ['player', 'fetch', meta] as const;
}
return ['player', 'fetch'] as const; return ['player', 'fetch'] as const;
}, },
}, },
@@ -151,62 +151,57 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const storeActions = usePlayerActions(); const storeActions = usePlayerActions();
const timeoutIds = useRef<null | Record<string, ReturnType<typeof setTimeout>>>({}); const timeoutIds = useRef<null | Record<string, ReturnType<typeof setTimeout>>>({});
const queueFetchConfirmThreshold = 100;
const [doNotShowAgain, setDoNotShowAgain] = useLocalStorage({ const [doNotShowAgain, setDoNotShowAgain] = useLocalStorage({
defaultValue: false, defaultValue: false,
key: 'large_fetch_confirmation', key: 'large_fetch_confirmation',
}); });
const confirmLargeFetch = useCallback( const confirmLargeFetch = useCallback((): Promise<boolean> => {
(itemCount: number): Promise<boolean> => { if (doNotShowAgain) {
if (doNotShowAgain) { return Promise.resolve(true);
return Promise.resolve(true); }
}
return new Promise((resolve) => { return new Promise((resolve) => {
openModal({ openModal({
children: ( children: (
<ConfirmModal <ConfirmModal
labels={{ labels={{
cancel: t('common.cancel', { postProcess: 'titleCase' }), cancel: t('common.cancel', { postProcess: 'titleCase' }),
confirm: t('common.confirm', { postProcess: 'titleCase' }), confirm: t('common.confirm', { postProcess: 'titleCase' }),
}} }}
onCancel={() => { onCancel={() => {
resolve(false); resolve(false);
closeAllModals(); closeAllModals();
}} }}
onConfirm={() => { onConfirm={() => {
resolve(true); resolve(true);
closeAllModals(); closeAllModals();
}} }}
> >
<Stack> <Stack>
<Text> <Text>
{t('action.largeFetch', { {t('form.largeFetchConfirmation.description', {
count: itemCount, postProcess: 'sentenceCase',
postProcess: 'sentenceCase', })}
})} </Text>
</Text> <Checkbox
<Checkbox label={t('common.doNotShowAgain', {
label={t('common.doNotShowAgain', { postProcess: 'sentenceCase',
postProcess: 'sentenceCase', })}
})} onChange={(event) => {
onChange={(event) => { setDoNotShowAgain(event.currentTarget.checked);
setDoNotShowAgain(event.currentTarget.checked); }}
}} />
/> </Stack>
</Stack> </ConfirmModal>
</ConfirmModal> ),
), title: t('form.largeFetchConfirmation.title', {
title: t('common.areYouSure', { postProcess: 'sentenceCase',
postProcess: 'sentenceCase', }),
}),
});
}); });
}, });
[doNotShowAgain, setDoNotShowAgain, t], }, [doNotShowAgain, setDoNotShowAgain, t]);
);
const addToQueueByData = useCallback( const addToQueueByData = useCallback(
(data: Song[], type: AddToQueueType, playSongId?: string) => { (data: Song[], type: AddToQueueType, playSongId?: string) => {
@@ -248,8 +243,8 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
type: AddToQueueType, type: AddToQueueType,
skipConfirmation?: boolean, skipConfirmation?: boolean,
) => { ) => {
if (!skipConfirmation && id.length > queueFetchConfirmThreshold) { if (!skipConfirmation) {
const confirmed = await confirmLargeFetch(id.length); const confirmed = await confirmLargeFetch();
if (!confirmed) { if (!confirmed) {
return; return;
} }
@@ -271,6 +266,11 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
exact: false, exact: false,
queryKey: getRootQueryKey(itemType, serverId), queryKey: getRootQueryKey(itemType, serverId),
}); });
queryClient.cancelQueries({
exact: false,
queryKey: queryKeys.player.fetch(),
});
}, },
title: t('player.playbackFetchInProgress', { title: t('player.playbackFetchInProgress', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
@@ -331,7 +331,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}); });
} }
}, },
[confirmLargeFetch, queueFetchConfirmThreshold, queryClient, storeActions, t], [confirmLargeFetch, queryClient, storeActions, t],
); );
const addToQueueByListQuery = useCallback( const addToQueueByListQuery = useCallback(
@@ -388,6 +388,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
serverId, serverId,
}), }),
gcTime: 0, gcTime: 0,
queryKey: queryKeys.player.fetch(),
staleTime: 0, staleTime: 0,
})) as number; })) as number;
totalCount = countResult || 0; totalCount = countResult || 0;
@@ -395,21 +396,9 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const allResults: Song[] | string[] = []; const allResults: Song[] | string[] = [];
const pageSize = 500; const pageSize = 500;
// Calculate the number of fetches needed const confirmed = await confirmLargeFetch();
let numberOfFetches = 0; if (!confirmed) {
if (itemType === LibraryItem.SONG) { return;
// For songs, the number of fetches is based on pagination
numberOfFetches = Math.ceil(totalCount / pageSize);
} else {
const paginationFetches = Math.ceil(totalCount / pageSize);
numberOfFetches = paginationFetches + totalCount;
}
if (numberOfFetches > queueFetchConfirmThreshold) {
const confirmed = await confirmLargeFetch(numberOfFetches);
if (!confirmed) {
return;
}
} }
// Start timeout only after confirmation (if needed) // Start timeout only after confirmation (if needed)
@@ -433,6 +422,11 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
exact: false, exact: false,
queryKey: getRootQueryKey(itemType, serverId), queryKey: getRootQueryKey(itemType, serverId),
}); });
queryClient.cancelQueries({
exact: false,
queryKey: queryKeys.player.fetch(),
});
}, },
title: t('player.playbackFetchInProgress', { title: t('player.playbackFetchInProgress', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
@@ -455,6 +449,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
serverId, serverId,
}), }),
gcTime: 0, gcTime: 0,
queryKey: queryKeys.player.fetch({ startIndex }),
staleTime: 0, staleTime: 0,
})) as { items: any[] }; })) as { items: any[] };
@@ -1006,6 +1001,7 @@ export async function fetchSongsByItemType(
} }
export const useIsPlayerFetching = () => { export const useIsPlayerFetching = () => {
const fetcherCount = useIsFetching({ queryKey: queryKeys.player.fetch() }); const playerFetchCount = useIsFetching({ queryKey: queryKeys.player.fetch() });
return fetcherCount > 0;
return playerFetchCount > 0;
}; };
@@ -3,7 +3,7 @@ import { CSSProperties, memo, ReactNode, useCallback } from 'react';
import styles from './library-header-bar.module.css'; import styles from './library-header-bar.module.css';
import { usePlayer } from '/@/renderer/features/player/context/player-context'; import { useIsPlayerFetching, usePlayer } from '/@/renderer/features/player/context/player-context';
import { DefaultPlayButton } from '/@/renderer/features/shared/components/play-button'; import { DefaultPlayButton } from '/@/renderer/features/shared/components/play-button';
import { PlayButtonGroup } from '/@/renderer/features/shared/components/play-button-group'; import { PlayButtonGroup } from '/@/renderer/features/shared/components/play-button-group';
import { useCurrentServerId } from '/@/renderer/store'; import { useCurrentServerId } from '/@/renderer/store';
@@ -86,10 +86,13 @@ const HeaderPlayButton = ({
}); });
}, [serverId, handlePlay]); }, [serverId, handlePlay]);
const isPlayerFetching = useIsPlayerFetching();
return ( return (
<div className={styles.playButtonContainer}> <div className={styles.playButtonContainer}>
<DefaultPlayButton <DefaultPlayButton
className={className} className={className}
loading={isPlayerFetching}
onClick={openPlayTypeModal} onClick={openPlayTypeModal}
variant={variant} variant={variant}
{...props} {...props}