mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-22 02:16:39 +02:00
add scan listener and query invalidation on tag editor save
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import type { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect, useSyncExternalStore } from 'react';
|
||||
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
|
||||
import { useCurrentServerId } from '/@/renderer/store';
|
||||
|
||||
const SCAN_STATUS_POLL_INTERVAL_MS = 2000;
|
||||
const SCAN_WATCH_TIMEOUT_MS = 30_000;
|
||||
|
||||
let isWatchingScan = false;
|
||||
let hasSeenScanStart = false;
|
||||
let watchTimeoutId: null | ReturnType<typeof setTimeout> = null;
|
||||
const watchListeners = new Set<() => void>();
|
||||
|
||||
const subscribeWatchingScan = (listener: () => void) => {
|
||||
watchListeners.add(listener);
|
||||
return () => {
|
||||
watchListeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
const getIsWatchingScan = () => isWatchingScan;
|
||||
|
||||
const setIsWatchingScan = (value: boolean) => {
|
||||
if (isWatchingScan === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isWatchingScan = value;
|
||||
for (const listener of watchListeners) {
|
||||
listener();
|
||||
}
|
||||
};
|
||||
|
||||
const clearWatchTimeout = () => {
|
||||
if (watchTimeoutId !== null) {
|
||||
clearTimeout(watchTimeoutId);
|
||||
watchTimeoutId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const stopWatchingScan = () => {
|
||||
clearWatchTimeout();
|
||||
hasSeenScanStart = false;
|
||||
setIsWatchingScan(false);
|
||||
};
|
||||
|
||||
const startScanWatch = () => {
|
||||
clearWatchTimeout();
|
||||
hasSeenScanStart = false;
|
||||
setIsWatchingScan(true);
|
||||
watchTimeoutId = setTimeout(() => {
|
||||
watchTimeoutId = null;
|
||||
if (isWatchingScan && !hasSeenScanStart) {
|
||||
stopWatchingScan();
|
||||
}
|
||||
}, SCAN_WATCH_TIMEOUT_MS);
|
||||
};
|
||||
|
||||
const invalidateLibraryQueriesAfterScan = (queryClient: QueryClient, serverId: string) => {
|
||||
return Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.songs.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.albums.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.albumArtists.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.artists.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.genres.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: [serverId, 'folders'] }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.musicFolders.list(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.search.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: [serverId, 'tags'] }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.roles.list(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.playlists.root(serverId) }),
|
||||
queryClient.invalidateQueries({ queryKey: [serverId, 'home'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['home'] }),
|
||||
]);
|
||||
};
|
||||
|
||||
const finishScanWatch = (queryClient: QueryClient, serverId: string) => {
|
||||
if (!hasSeenScanStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearWatchTimeout();
|
||||
hasSeenScanStart = false;
|
||||
setIsWatchingScan(false);
|
||||
invalidateLibraryQueriesAfterScan(queryClient, serverId);
|
||||
};
|
||||
|
||||
eventEmitter.on('TAG_EDITED', () => {
|
||||
startScanWatch();
|
||||
});
|
||||
|
||||
export const useScanStatus = () => {
|
||||
const serverId = useCurrentServerId();
|
||||
const queryClient = useQueryClient();
|
||||
const isWatching = useSyncExternalStore(subscribeWatchingScan, getIsWatchingScan);
|
||||
|
||||
const query = useQuery({
|
||||
...sharedQueries.scanStatus({
|
||||
options: {
|
||||
enabled: isWatching && Boolean(serverId),
|
||||
refetchInterval: SCAN_STATUS_POLL_INTERVAL_MS,
|
||||
retry: false,
|
||||
staleTime: 0,
|
||||
throwOnError: false,
|
||||
},
|
||||
query: null,
|
||||
serverId,
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isWatching || !query.isFetched || query.isFetching || !query.data || !serverId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (query.data.scanning) {
|
||||
hasSeenScanStart = true;
|
||||
clearWatchTimeout();
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep polling until the scan has actually started, then stop when it finishes.
|
||||
if (hasSeenScanStart && !query.data.scanning) {
|
||||
finishScanWatch(queryClient, serverId);
|
||||
}
|
||||
}, [isWatching, query.data, query.isFetched, query.isFetching, queryClient, serverId]);
|
||||
|
||||
return {
|
||||
...query,
|
||||
isScanning: query.data?.scanning === true,
|
||||
isWatching,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user