From d17f30f5e6f4955b3db503c89f126509ee4288e6 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sun, 8 Jan 2023 00:52:53 -0800 Subject: [PATCH] Add favorite handler to grid cards --- .../virtual-grid/grid-card/default-card.tsx | 2 ++ .../grid-card/grid-card-controls.tsx | 17 +++++++++-- .../virtual-grid/grid-card/index.tsx | 2 ++ .../virtual-grid/grid-card/poster-card.tsx | 2 ++ .../virtual-grid/virtual-grid-wrapper.tsx | 5 ++++ .../virtual-grid/virtual-infinite-grid.tsx | 3 ++ .../virtual-table/cells/favorite-cell.tsx | 24 +++++++++++++++- .../albums/components/album-list-content.tsx | 28 +++++++++++++++++++ .../mutations/create-favorite-mutation.ts | 13 +++++++-- .../mutations/delete-favorite-mutation.ts | 13 +++++++-- src/renderer/store/album-list-data.store.ts | 11 ++++++++ src/renderer/types.ts | 1 + 12 files changed, 114 insertions(+), 7 deletions(-) diff --git a/src/renderer/components/virtual-grid/grid-card/default-card.tsx b/src/renderer/components/virtual-grid/grid-card/default-card.tsx index a91da7b3..1c419de9 100644 --- a/src/renderer/components/virtual-grid/grid-card/default-card.tsx +++ b/src/renderer/components/virtual-grid/grid-card/default-card.tsx @@ -101,6 +101,7 @@ interface BaseGridCardProps { columnIndex: number; controls: { cardRows: CardRow[]; + handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void; handlePlayQueueAdd: (options: PlayQueueAddOptions) => void; itemType: LibraryItem; playButtonBehavior: Play; @@ -179,6 +180,7 @@ export const DefaultCard = ({ )} void; handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void; itemData: any; itemType: LibraryItem; @@ -138,6 +140,17 @@ export const GridCardControls = ({ }); }; + const handleFavorites = async (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + handleFavorite?.({ + id: [itemData.id], + isFavorite: itemData.userFavorite, + itemType, + }); + }; + return ( {/* */} @@ -148,13 +161,13 @@ export const GridCardControls = ({ - {itemData?.isFavorite ? ( + {itemData?.userFavorite ? ( ) : ( []; + handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void; handlePlayQueueAdd: (options: PlayQueueAddOptions) => void; itemType: LibraryItem; playButtonBehavior: Play; @@ -173,6 +174,7 @@ export const PosterCard = ({ )} ({ cardRows, columnCount, display, + handleFavorite, handlePlayQueueAdd, itemCount, itemData, @@ -50,6 +52,7 @@ export const VirtualGridWrapper = ({ columnCount, rowCount, initialScrollOffset, + handleFavorite, handlePlayQueueAdd, itemData, route, @@ -59,6 +62,7 @@ export const VirtualGridWrapper = ({ cardRows: CardRow[]; columnCount: number; display: ListDisplayType; + handleFavorite?: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void; handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void; itemData: any[]; itemGap: number; @@ -81,6 +85,7 @@ export const VirtualGridWrapper = ({ itemWidth, route, handlePlayQueueAdd, + handleFavorite, ); const memoizedOnScroll = createScrollHandler(onScroll); diff --git a/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx b/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx index 1906d21a..f20322c2 100644 --- a/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx +++ b/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx @@ -17,6 +17,7 @@ interface VirtualGridProps extends Omit[]; display?: ListDisplayType; fetchFn: (options: { columnCount: number; skip: number; take: number }) => Promise; + handleFavorite?: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void; handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void; itemData: any[]; itemGap: number; @@ -54,6 +55,7 @@ export const VirtualInfiniteGrid = forwardRef( fetchFn, loading, initialScrollOffset, + handleFavorite, height, width, }: VirtualGridProps, @@ -146,6 +148,7 @@ export const VirtualInfiniteGrid = forwardRef( cardRows={cardRows} columnCount={columnCount} display={display || ListDisplayType.CARD} + handleFavorite={handleFavorite} handlePlayQueueAdd={handlePlayQueueAdd} height={height} initialScrollOffset={initialScrollOffset} diff --git a/src/renderer/components/virtual-table/cells/favorite-cell.tsx b/src/renderer/components/virtual-table/cells/favorite-cell.tsx index 1954ff17..403c5348 100644 --- a/src/renderer/components/virtual-table/cells/favorite-cell.tsx +++ b/src/renderer/components/virtual-table/cells/favorite-cell.tsx @@ -6,21 +6,43 @@ import { useMutation } from '@tanstack/react-query'; import { HTTPError } from 'ky'; import { api } from '/@/renderer/api'; import { RawFavoriteResponse, FavoriteArgs, LibraryItem } from '/@/renderer/api/types'; -import { useCurrentServer, useSetQueueFavorite } from '/@/renderer/store'; +import { + useCurrentServer, + useSetAlbumListItemDataById, + useSetQueueFavorite, +} from '/@/renderer/store'; const useCreateFavorite = () => { const server = useCurrentServer(); + const setAlbumListData = useSetAlbumListItemDataById(); return useMutation, null>({ mutationFn: (args) => api.controller.createFavorite({ ...args, server }), + onSuccess: (_data, variables) => { + 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 }); + } + } + }, }); }; const useDeleteFavorite = () => { const server = useCurrentServer(); + const setAlbumListData = useSetAlbumListItemDataById(); return useMutation, null>({ mutationFn: (args) => api.controller.deleteFavorite({ ...args, server }), + onSuccess: (_data, variables) => { + 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 }); + } + } + }, }); }; diff --git a/src/renderer/features/albums/components/album-list-content.tsx b/src/renderer/features/albums/components/album-list-content.tsx index 80829656..ec1be5ab 100644 --- a/src/renderer/features/albums/components/album-list-content.tsx +++ b/src/renderer/features/albums/components/album-list-content.tsx @@ -41,6 +41,7 @@ import { useHandleTableContextMenu } from '/@/renderer/features/context-menu'; import { ALBUM_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items'; import { generatePath, useNavigate } from 'react-router'; import { usePlayQueueAdd } from '/@/renderer/features/player'; +import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared'; interface AlbumListContentProps { gridRef: MutableRefObject; @@ -265,6 +266,32 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont navigate(generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: e.data.id })); }; + const createFavoriteMutation = useCreateFavorite(); + const deleteFavoriteMutation = useDeleteFavorite(); + + const handleFavorite = (options: { + id: string[]; + isFavorite: boolean; + itemType: LibraryItem; + }) => { + const { id, itemType, isFavorite } = options; + if (isFavorite) { + deleteFavoriteMutation.mutate({ + query: { + id, + type: itemType, + }, + }); + } else { + createFavoriteMutation.mutate({ + query: { + id, + type: itemType, + }, + }); + } + }; + return ( <> @@ -278,6 +305,7 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont cardRows={cardRows} display={page.display || ListDisplayType.CARD} fetchFn={fetch} + handleFavorite={handleFavorite} handlePlayQueueAdd={handlePlayQueueAdd} height={height} initialScrollOffset={page?.grid.scrollOffset || 0} diff --git a/src/renderer/features/shared/mutations/create-favorite-mutation.ts b/src/renderer/features/shared/mutations/create-favorite-mutation.ts index 2be51614..5372f11f 100644 --- a/src/renderer/features/shared/mutations/create-favorite-mutation.ts +++ b/src/renderer/features/shared/mutations/create-favorite-mutation.ts @@ -1,15 +1,24 @@ import { useMutation } from '@tanstack/react-query'; import { HTTPError } from 'ky'; import { api } from '/@/renderer/api'; -import { FavoriteArgs, RawFavoriteResponse } from '/@/renderer/api/types'; +import { FavoriteArgs, LibraryItem, RawFavoriteResponse } from '/@/renderer/api/types'; import { MutationOptions } from '/@/renderer/lib/react-query'; -import { useCurrentServer } from '/@/renderer/store'; +import { useCurrentServer, useSetAlbumListItemDataById } from '/@/renderer/store'; export const useCreateFavorite = (options?: MutationOptions) => { const server = useCurrentServer(); + const setAlbumListData = useSetAlbumListItemDataById(); return useMutation, null>({ mutationFn: (args) => api.controller.createFavorite({ ...args, server }), + onSuccess: (_data, variables) => { + 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 }); + } + } + }, ...options, }); }; diff --git a/src/renderer/features/shared/mutations/delete-favorite-mutation.ts b/src/renderer/features/shared/mutations/delete-favorite-mutation.ts index b739f948..3b11d03a 100644 --- a/src/renderer/features/shared/mutations/delete-favorite-mutation.ts +++ b/src/renderer/features/shared/mutations/delete-favorite-mutation.ts @@ -1,15 +1,24 @@ import { useMutation } from '@tanstack/react-query'; import { HTTPError } from 'ky'; import { api } from '/@/renderer/api'; -import { FavoriteArgs, RawFavoriteResponse } from '/@/renderer/api/types'; +import { FavoriteArgs, LibraryItem, RawFavoriteResponse } from '/@/renderer/api/types'; import { MutationOptions } from '/@/renderer/lib/react-query'; -import { useCurrentServer } from '/@/renderer/store'; +import { useCurrentServer, useSetAlbumListItemDataById } from '/@/renderer/store'; export const useDeleteFavorite = (options?: MutationOptions) => { const server = useCurrentServer(); + const setAlbumListData = useSetAlbumListItemDataById(); return useMutation, null>({ mutationFn: (args) => api.controller.deleteFavorite({ ...args, server }), + onSuccess: (_data, variables) => { + 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 }); + } + } + }, ...options, }); }; diff --git a/src/renderer/store/album-list-data.store.ts b/src/renderer/store/album-list-data.store.ts index f7b52ca6..9a418b07 100644 --- a/src/renderer/store/album-list-data.store.ts +++ b/src/renderer/store/album-list-data.store.ts @@ -9,6 +9,7 @@ export interface AlbumListDataState { export interface AlbumListDataSlice extends AlbumListDataState { actions: { setItemData: (data: any[]) => void; + setItemDataById: (id: string, data: any) => void; }; } @@ -21,6 +22,13 @@ export const useAlbumListDataStore = create()( state.itemData = data; }); }, + setItemDataById: (id, data) => { + set((state) => { + const index = state.itemData.findIndex((item) => item?.id === id); + if (index === -1) return; + state.itemData[index] = { ...state.itemData[index], ...data }; + }); + }, }, itemData: [], })), @@ -34,3 +42,6 @@ export const useAlbumListItemData = () => useAlbumListDataStore((state) => { return { itemData: state.itemData, setItemData: state.actions.setItemData }; }); + +export const useSetAlbumListItemDataById = () => + useAlbumListDataStore((state) => state.actions.setItemDataById); diff --git a/src/renderer/types.ts b/src/renderer/types.ts index 476cf54c..b2941b2d 100644 --- a/src/renderer/types.ts +++ b/src/renderer/types.ts @@ -162,6 +162,7 @@ export type GridCardData = { cardRows: CardRow[]; columnCount: number; display: ListDisplayType; + handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void; handlePlayQueueAdd: (options: PlayQueueAddOptions) => void; itemCount: number; itemData: any[];