diff --git a/src/apis/likes/index.ts b/src/apis/likes/index.ts index 7e16d30f..49d2235e 100644 --- a/src/apis/likes/index.ts +++ b/src/apis/likes/index.ts @@ -1,4 +1,11 @@ -import { usePostStoreLikes } from './usePostStoreLikes'; +import { useDeleteCakeLikes } from './useDeleteCakeLikes'; +import { useDeleteStoreLikes } from './useDeleteStoreLikes'; import { usePostCakeLikes } from './usePostCakeLikes'; +import { usePostStoreLikes } from './usePostStoreLikes'; -export { usePostStoreLikes, usePostCakeLikes }; +export { + usePostStoreLikes, + usePostCakeLikes, + useDeleteCakeLikes, + useDeleteStoreLikes, +}; diff --git a/src/apis/likes/useDeleteCakeLikes.ts b/src/apis/likes/useDeleteCakeLikes.ts new file mode 100644 index 00000000..4e0f36f2 --- /dev/null +++ b/src/apis/likes/useDeleteCakeLikes.ts @@ -0,0 +1,23 @@ +import { useMutation } from '@tanstack/react-query'; + +import { instance } from '@apis/instance'; + +import { END_POINT } from '@constants'; + +import { MutateResposneType } from '@types'; + +const deleteCakeLikes = async (cakeId: number): Promise => { + try { + const response = await instance.delete(END_POINT.DELETE_LIKE('cake', cakeId)); + return response.data; + } catch (error) { + console.log(error); + throw error; + } +}; + +export const useDeleteCakeLikes = () => { + return useMutation({ + mutationFn: (cakeId: number) => deleteCakeLikes(cakeId), + }); +}; diff --git a/src/apis/likes/useDeleteStoreLikes.ts b/src/apis/likes/useDeleteStoreLikes.ts new file mode 100644 index 00000000..a03f6a3d --- /dev/null +++ b/src/apis/likes/useDeleteStoreLikes.ts @@ -0,0 +1,25 @@ +import { useMutation } from '@tanstack/react-query'; + +import { instance } from '@apis/instance'; + +import { END_POINT } from '@constants'; + +import { MutateResposneType } from '@types'; + +const deleteStoreLikes = async ( + storeId: number +): Promise => { + try { + const response = await instance.delete(END_POINT.DELETE_LIKE('store', storeId)); + return response.data; + } catch (error) { + console.log(error); + throw error; + } +}; + +export const useDeleteStoreLikes = () => { + return useMutation({ + mutationFn: (storeId: number) => deleteStoreLikes(storeId), + }); +}; diff --git a/src/apis/likes/usePostCakeLikes.ts b/src/apis/likes/usePostCakeLikes.ts index 63077916..4f6c4bf8 100644 --- a/src/apis/likes/usePostCakeLikes.ts +++ b/src/apis/likes/usePostCakeLikes.ts @@ -8,7 +8,7 @@ import { MutateResposneType } from '@types'; const postCakeLikes = async (cakeId: number): Promise => { try { - const response = await instance.post(END_POINT.POST_CAKE_LIKES(cakeId)); + const response = await instance.post(END_POINT.DELETE_LIKE('cake', cakeId)); return response.data; } catch (error) { console.log(error); diff --git a/src/apis/likes/usePostStoreLikes.ts b/src/apis/likes/usePostStoreLikes.ts index f4086964..e67eda09 100644 --- a/src/apis/likes/usePostStoreLikes.ts +++ b/src/apis/likes/usePostStoreLikes.ts @@ -8,7 +8,7 @@ import { MutateResposneType } from '@types'; const postStoreLikes = async (storeId: number): Promise => { try { - const response = await instance.post(END_POINT.POST_STORE_LIKES(storeId)); + const response = await instance.post(END_POINT.POST_LIKE('store', storeId)); return response.data; } catch (error) { console.log(error); diff --git a/src/components/common/IconButton/IconButton.tsx b/src/components/common/IconButton/IconButton.tsx index ad88e3cf..73793c14 100644 --- a/src/components/common/IconButton/IconButton.tsx +++ b/src/components/common/IconButton/IconButton.tsx @@ -1,6 +1,11 @@ -import React, { ButtonHTMLAttributes } from 'react'; +import React, { ButtonHTMLAttributes, useState } from 'react'; -import { usePostCakeLikes, usePostStoreLikes } from '@apis/likes'; +import { + useDeleteCakeLikes, + useDeleteStoreLikes, + usePostCakeLikes, + usePostStoreLikes, +} from '@apis/likes'; import { IcFillLikeOff36, @@ -46,27 +51,54 @@ const IconButton = ({ count, itemId, }: IconButtonProps) => { - const { mutate: postCakeLikes } = usePostCakeLikes(); + const [localActive, setLocalActive] = useState(isActive); + const [localCount, setLocalCount] = useState(count); + const { mutate: postStoreLikes } = usePostStoreLikes(); + const { mutate: postCakeLikes } = usePostCakeLikes(); + const { mutate: deleteStoreLikes } = useDeleteStoreLikes(); + const { mutate: deleteCakeLikes } = useDeleteCakeLikes(); const handleButtonClick = (e: React.MouseEvent) => { e.stopPropagation(); + if (itemId === undefined) return; + + const isStore = buttonType === 'save24' || buttonType === 'save28'; - if (itemId !== undefined) { - if (buttonType === 'save24' || buttonType === 'save28') { - postStoreLikes(itemId); - } else { - postCakeLikes(itemId); + const optimisticUpdate = () => { + setLocalActive((prev) => !prev); + if (localCount !== undefined) { + setLocalCount(localCount + (localActive ? -1 : 1)); } + }; + + const rollbackUpdate = () => { + setLocalActive((prev) => !prev); + if (localCount !== undefined) { + setLocalCount(count); + } + }; + + optimisticUpdate(); + + if (localActive) { + (isStore ? deleteStoreLikes : deleteCakeLikes)(itemId, { + onError: () => rollbackUpdate(), + }); + } else { + (isStore ? postStoreLikes : postCakeLikes)(itemId, { + onError: () => rollbackUpdate(), + }); } }; + return ( ); diff --git a/src/constants/apis/api.ts b/src/constants/apis/api.ts index 88f26e4c..4f3518d9 100644 --- a/src/constants/apis/api.ts +++ b/src/constants/apis/api.ts @@ -16,6 +16,12 @@ export const END_POINT = { FETCH_STORE_LINK: (storeId: number) => `/api/v1/store/kakaoLink/${storeId}`, FETCH_USER: '/api/v1/user', FETCH_CAKE_RANK: '/api/v1/cake/rank', - POST_CAKE_LIKES: (cakeId: number) => `/api/v1/cake/likes/${cakeId}`, - POST_STORE_LIKES: (storeId: number) => `/api/v1/store/likes/${storeId}`, + // POST_CAKE_LIKES: (cakeId: number) => `/api/v1/cake/likes/${cakeId}`, + // POST_STORE_LIKES: (storeId: number) => `/api/v1/store/likes/${storeId}`, + // DELETE_CAKE_LIKES: (cakeId: number) => `/api/v1/cake/likes/${cakeId}`, + // DELETE_STORE_LIKES: (storeId: number) => `/api/v1/store/likes/${storeId}` + POST_LIKE: (type: 'cake' | 'store', id: number) => + `/api/v1/${type}/likes/${id}`, + DELETE_LIKE: (type: 'cake' | 'store', id: number) => + `/api/v1/${type}/likes/${id}`, } as const;