diff --git a/src/renderer/features/shared/components/library-header.tsx b/src/renderer/features/shared/components/library-header.tsx index 603fd9ba6..e18134f90 100644 --- a/src/renderer/features/shared/components/library-header.tsx +++ b/src/renderer/features/shared/components/library-header.tsx @@ -10,6 +10,9 @@ import { WidePlayButton, WideShuffleButton, } from '/@/renderer/features/shared/components/play-button'; +import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation'; +import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation'; +import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-rating-mutation'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Center } from '/@/shared/components/center/center'; import { Group } from '/@/shared/components/group/group'; @@ -149,6 +152,10 @@ export const LibraryHeaderMenu = ({ onShuffle, rating, }: LibraryHeaderMenuProps) => { + const isMutatingRating = useIsMutatingRating(); + const isMutatingCreateFavorite = useIsMutatingCreateFavorite(); + const isMutatingDeleteFavorite = useIsMutatingDeleteFavorite(); + return (
@@ -156,12 +163,20 @@ export const LibraryHeaderMenu = ({ {onShuffle && } - {onRating && } + {onRating && ( + + )} {onFavorite && ( { const { options } = args || {}; const queryClient = useQueryClient(); - const setAlbumListData = useSetAlbumListItemDataById(); - // const setQueueFavorite = useSetQueueFavorite(); + const setFavoriteEvent = useFavoriteEvent(); return useMutation({ @@ -32,6 +26,7 @@ export const useCreateFavorite = (args: MutationHookArgs) => { apiClientProps: { serverId: args.apiClientProps.serverId }, }); }, + mutationKey: createFavoriteQueryKey, onError: (_error, variables) => { eventEmitter.emit('USER_FAVORITE', { favorite: false, @@ -51,56 +46,61 @@ export const useCreateFavorite = (args: MutationHookArgs) => { return null; }, onSuccess: (_data, variables) => { - const { apiClientProps } = variables; - const serverId = apiClientProps.serverId; - - if (!serverId) return; - - for (const id of variables.query.id) { - // Set the userFavorite property to true for the album in the album list data store - if (variables.query.type === LibraryItem.ALBUM) { - setAlbumListData(id, { userFavorite: true }); - } - } - if (variables.query.type === LibraryItem.SONG) { - remote?.updateFavorite(true, serverId, variables.query.id); + remote?.updateFavorite(true, variables.apiClientProps.serverId, variables.query.id); // setQueueFavorite(variables.query.id, true); setFavoriteEvent(variables.query.id, true); } - // We only need to set if we're already on the album detail page - if (variables.query.type === LibraryItem.ALBUM && variables.query.id.length === 1) { - const queryKey = queryKeys.albums.detail(serverId, { id: variables.query.id[0] }); - const previous = queryClient.getQueryData(queryKey); - - if (previous) { - queryClient.setQueryData(queryKey, { - ...previous, - userFavorite: true, + switch (variables.query.type) { + case LibraryItem.ALBUM: { + const queryKey = queryKeys.albums.detail(variables.apiClientProps.serverId); + queryClient.invalidateQueries({ + exact: false, + queryKey, }); + + break; } - } + case LibraryItem.ALBUM_ARTIST: { + const queryKey = queryKeys.albumArtists.detail( + variables.apiClientProps.serverId, + ); - // We only need to set if we're already on the album detail page - if ( - variables.query.type === LibraryItem.ALBUM_ARTIST && - variables.query.id.length === 1 - ) { - const queryKey = queryKeys.albumArtists.detail(serverId, { - id: variables.query.id[0], - }); - - const previous = queryClient.getQueryData(queryKey); - - if (previous) { - queryClient.setQueryData(queryKey, { - ...previous, - userFavorite: true, + queryClient.invalidateQueries({ + exact: false, + queryKey, }); + + break; + } + case LibraryItem.ARTIST: { + const queryKey = queryKeys.artists.detail(variables.apiClientProps.serverId); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; + } + case LibraryItem.SONG: { + const queryKey = queryKeys.songs.detail(variables.apiClientProps.serverId); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; } } }, ...options, }); }; + +export const useIsMutatingCreateFavorite = () => { + const mutatingCount = useIsMutating({ mutationKey: createFavoriteQueryKey }); + return mutatingCount > 0; +}; diff --git a/src/renderer/features/shared/mutations/delete-favorite-mutation.ts b/src/renderer/features/shared/mutations/delete-favorite-mutation.ts index 02cc80ee0..087dcea6f 100644 --- a/src/renderer/features/shared/mutations/delete-favorite-mutation.ts +++ b/src/renderer/features/shared/mutations/delete-favorite-mutation.ts @@ -1,4 +1,4 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useIsMutating, useMutation, useQueryClient } from '@tanstack/react-query'; import { AxiosError } from 'axios'; import isElectron from 'is-electron'; @@ -6,25 +6,17 @@ import { api } from '/@/renderer/api'; import { queryKeys } from '/@/renderer/api/query-keys'; import { eventEmitter } from '/@/renderer/events/event-emitter'; import { MutationHookArgs } from '/@/renderer/lib/react-query'; -import { useSetAlbumListItemDataById } from '/@/renderer/store'; import { useFavoriteEvent } from '/@/renderer/store/event.store'; -import { - AlbumArtistDetailResponse, - AlbumDetailResponse, - FavoriteArgs, - FavoriteResponse, - LibraryItem, -} from '/@/shared/types/domain-types'; +import { FavoriteArgs, FavoriteResponse, LibraryItem } from '/@/shared/types/domain-types'; const remote = isElectron() ? window.api.remote : null; +const deleteFavoriteQueryKey = ['set-favorite', false]; + export const useDeleteFavorite = (args: MutationHookArgs) => { const { options } = args || {}; const queryClient = useQueryClient(); - const setAlbumListData = useSetAlbumListItemDataById(); - // const setQueueFavorite = useSetQueueFavorite(); const setFavoriteEvent = useFavoriteEvent(); - return useMutation({ mutationFn: (args) => { return api.controller.deleteFavorite({ @@ -32,6 +24,7 @@ export const useDeleteFavorite = (args: MutationHookArgs) => { apiClientProps: { serverId: args.apiClientProps.serverId }, }); }, + mutationKey: deleteFavoriteQueryKey, onError: (_error, variables) => { eventEmitter.emit('USER_FAVORITE', { favorite: true, @@ -51,55 +44,64 @@ export const useDeleteFavorite = (args: MutationHookArgs) => { return null; }, onSuccess: (_data, variables) => { - const { apiClientProps } = variables; - const serverId = apiClientProps.serverId; - - for (const id of variables.query.id) { - // Set the userFavorite property to false for the album in the album list data store - if (variables.query.type === LibraryItem.ALBUM) { - setAlbumListData(id, { userFavorite: false }); - } - } - if (variables.query.type === LibraryItem.SONG) { - remote?.updateFavorite(false, serverId, variables.query.id); - // setQueueFavorite(variables.query.id, false); + remote?.updateFavorite( + false, + variables.apiClientProps.serverId, + variables.query.id, + ); setFavoriteEvent(variables.query.id, false); } - // We only need to set if we're already on the album detail page - if (variables.query.type === LibraryItem.ALBUM && variables.query.id.length === 1) { - const queryKey = queryKeys.albums.detail(serverId, { - id: variables.query.id[0], - }); - const previous = queryClient.getQueryData(queryKey); - - if (previous) { - queryClient.setQueryData(queryKey, { - ...previous, - userFavorite: false, + switch (variables.query.type) { + case LibraryItem.ALBUM: { + const queryKey = queryKeys.albums.detail(variables.apiClientProps.serverId); + queryClient.invalidateQueries({ + exact: false, + queryKey, }); + + break; } - } + case LibraryItem.ALBUM_ARTIST: { + const queryKey = queryKeys.albumArtists.detail( + variables.apiClientProps.serverId, + ); - // We only need to set if we're already on the album artist detail page - if ( - variables.query.type === LibraryItem.ALBUM_ARTIST && - variables.query.id.length === 1 - ) { - const queryKey = queryKeys.albumArtists.detail(serverId, { - id: variables.query.id[0], - }); - const previous = queryClient.getQueryData(queryKey); - - if (previous) { - queryClient.setQueryData(queryKey, { - ...previous, - userFavorite: false, + queryClient.invalidateQueries({ + exact: false, + queryKey, }); + + break; + } + case LibraryItem.ARTIST: { + const queryKey = queryKeys.artists.detail(variables.apiClientProps.serverId); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; + } + case LibraryItem.SONG: { + const queryKey = queryKeys.songs.detail(variables.apiClientProps.serverId); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; } } }, ...options, }); }; + +export const useIsMutatingDeleteFavorite = () => { + const mutatingCount = useIsMutating({ mutationKey: deleteFavoriteQueryKey }); + return mutatingCount > 0; +}; diff --git a/src/renderer/features/shared/mutations/set-rating-mutation.ts b/src/renderer/features/shared/mutations/set-rating-mutation.ts index 309dec7f0..71b336bfe 100644 --- a/src/renderer/features/shared/mutations/set-rating-mutation.ts +++ b/src/renderer/features/shared/mutations/set-rating-mutation.ts @@ -1,13 +1,17 @@ -import { useMutation } from '@tanstack/react-query'; +import { useIsMutating, useMutation, useQueryClient } from '@tanstack/react-query'; import { AxiosError } from 'axios'; import { api } from '/@/renderer/api'; +import { queryKeys } from '/@/renderer/api/query-keys'; import { eventEmitter } from '/@/renderer/events/event-emitter'; import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { LibraryItem, RatingResponse, SetRatingArgs } from '/@/shared/types/domain-types'; +const setRatingQueryKey = ['set-rating']; + export const useSetRating = (args: MutationHookArgs) => { const { options } = args || {}; + const queryClient = useQueryClient(); return useMutation< RatingResponse, @@ -21,7 +25,8 @@ export const useSetRating = (args: MutationHookArgs) => { apiClientProps: { serverId: args.apiClientProps.serverId }, }); }, - onSuccess: (_data, variables) => { + mutationKey: setRatingQueryKey, + onError: (_error, variables) => { eventEmitter.emit('USER_RATING', { id: variables.query.id, itemType: variables.query.type, @@ -29,6 +34,66 @@ export const useSetRating = (args: MutationHookArgs) => { serverId: variables.apiClientProps.serverId, }); }, + onMutate: (variables) => { + eventEmitter.emit('USER_RATING', { + id: variables.query.id, + itemType: variables.query.type, + rating: variables.query.rating, + serverId: variables.apiClientProps.serverId, + }); + + return { previous: undefined }; + }, + onSuccess: (_data, variables) => { + switch (variables.query.type) { + case LibraryItem.ALBUM: { + const queryKey = queryKeys.albums.detail(variables.apiClientProps.serverId); + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; + } + case LibraryItem.ALBUM_ARTIST: { + const queryKey = queryKeys.albumArtists.detail( + variables.apiClientProps.serverId, + ); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; + } + case LibraryItem.ARTIST: { + const queryKey = queryKeys.artists.detail(variables.apiClientProps.serverId); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; + } + case LibraryItem.SONG: { + const queryKey = queryKeys.songs.detail(variables.apiClientProps.serverId); + + queryClient.invalidateQueries({ + exact: false, + queryKey, + }); + + break; + } + } + }, ...options, }); }; + +export const useIsMutatingRating = () => { + const mutatingCount = useIsMutating({ mutationKey: setRatingQueryKey }); + return mutatingCount > 0; +}; diff --git a/src/renderer/lib/react-query.ts b/src/renderer/lib/react-query.ts index 08e3f4cdb..13b964d12 100644 --- a/src/renderer/lib/react-query.ts +++ b/src/renderer/lib/react-query.ts @@ -20,14 +20,13 @@ const queryCache = new QueryCache({ const queryConfig: DefaultOptions = { mutations: { - gcTime: 1000 * 20, // 20 seconds retry: process.env.NODE_ENV === 'production', }, queries: { gcTime: 1000 * 5, // 5 seconds refetchOnWindowFocus: false, retry: process.env.NODE_ENV === 'production', - staleTime: 1000 * 5, // 5 seconds + staleTime: 0, // 5 seconds throwOnError: (error: any) => { return error?.response?.status >= 500; },