diff --git a/src/apis/bookmark.ts b/src/apis/bookmark.ts index 9e34e15..ed271b7 100644 --- a/src/apis/bookmark.ts +++ b/src/apis/bookmark.ts @@ -321,7 +321,7 @@ export const useBookmarkMoveCategory = () => { { bookMarkMovingDtos: { originCategoryId: number | null; - movingCategoryId: number; + movingCategoryId: number | null; bookMarkId: number; }[]; } diff --git a/src/components/FilterBox/EditMode/hooks/useHandleMoveCategory.ts b/src/components/FilterBox/EditMode/hooks/useHandleMoveCategory.ts new file mode 100644 index 0000000..5579afb --- /dev/null +++ b/src/components/FilterBox/EditMode/hooks/useHandleMoveCategory.ts @@ -0,0 +1,132 @@ +import { usePathname, useRouter } from 'next/navigation'; + +import { useBookmarkMoveCategory } from '@/apis/bookmark'; +import useEditModeStore, { MovingBookmarkDto } from '@/stores/editModeStore'; +import useToastStore from '@/stores/toastStore'; + +// 실제 API 함수 경로로 수정 + +const useHandleMoveCategory = () => { + const router = useRouter(); + const pathname = usePathname(); + + const { addToast } = useToastStore(); + + const { mutate: moveCategory } = useBookmarkMoveCategory(); + // Zustand 스토어에서 필요한 상태와 함수 가져오기 + const { + selectedBookmarks, + setMovedBookmarks, + resetSelectedBookmarks, + setEditMode, + setIsOpenCategoryOptions, + } = useEditModeStore((state) => ({ + selectedBookmarks: state.selectedBookmarks, + setMovedBookmarks: state.setMovedBookmarks, + resetSelectedBookmarks: state.resetSelectedBookmarks, + setEditMode: state.setEditMode, + setIsOpenCategoryOptions: state.setIsOpenCategoryOptions, + })); + + // DTO 생성 함수 + const createMovingBookmarksDtos = ( + bookmarks: typeof selectedBookmarks, + categoryId: number, + ): MovingBookmarkDto[] => { + return bookmarks.map((bookmark) => ({ + originCategoryId: bookmark.categoryDtos.length ? bookmark.categoryDtos[0].categoryId : null, + bookMarkId: bookmark.bookmarkId, + movingCategoryId: categoryId, + })); + }; + + // 복구 DTO 생성 함수 + const createReverseDtos = (dtos: MovingBookmarkDto[]): MovingBookmarkDto[] => { + return dtos.map((dto) => ({ + originCategoryId: dto.movingCategoryId, + bookMarkId: dto.bookMarkId, + movingCategoryId: dto.originCategoryId, + })); + }; + + // 복구 핸들러 함수 + const handleRestore = (reverseDtos: MovingBookmarkDto[]) => { + moveCategory( + { + bookMarkMovingDtos: reverseDtos, + }, + { + onSuccess: () => { + addToast({ + message: '북마크 이동을 복구했어요.', + type: 'default', + }); + + setMovedBookmarks(reverseDtos.map((dto) => dto.bookMarkId)); + + if (reverseDtos.length > 0) { + router.replace(`${pathname}/?tab=${reverseDtos[0].movingCategoryId}`); + } + }, + onError: () => { + addToast({ + message: '복구에 실패했어요.', + type: 'error', + }); + }, + }, + ); + }; + + // 성공 시 처리 함수 + const handleMoveSuccess = (movingBookmarksDtos: MovingBookmarkDto[]) => { + setMovedBookmarks(selectedBookmarks.map((bookmark) => bookmark.bookmarkId)); + + addToast({ + message: '북마크를 이동했어요.', + type: 'default', + clickText: '복구하기', + onClick: () => { + const reverseDtos = createReverseDtos(movingBookmarksDtos); + + handleRestore(reverseDtos); + }, + }); + + resetSelectedBookmarks(); + setEditMode(false); + + if (movingBookmarksDtos.length > 0) { + router.replace(`${pathname}/?tab=${movingBookmarksDtos[0].movingCategoryId}`); + } + }; + + // 에러 핸들러 함수 + const handleMoveError = () => { + addToast({ + message: '북마크 이동에 실패했어요.', + type: 'error', + }); + }; + + // 메인 이동 핸들러 함수 + const handleMoveCategory = (categoryId: number) => { + const movingBookmarksDtos = createMovingBookmarksDtos(selectedBookmarks, categoryId); + + moveCategory( + { + bookMarkMovingDtos: movingBookmarksDtos, + }, + { + onSuccess: () => handleMoveSuccess(movingBookmarksDtos), + onError: handleMoveError, + }, + ); + + setIsOpenCategoryOptions(false); + }; + + return { handleMoveCategory }; +}; + +export default useHandleMoveCategory; diff --git a/src/components/FilterBox/EditMode/index.tsx b/src/components/FilterBox/EditMode/index.tsx index 75f285c..a33318c 100644 --- a/src/components/FilterBox/EditMode/index.tsx +++ b/src/components/FilterBox/EditMode/index.tsx @@ -1,12 +1,6 @@ -import { usePathname, useRouter } from 'next/navigation'; import { useRef, useState } from 'react'; -import { - fetchBookmarkReadCount, - useBookmarkDelete, - useBookmarkMoveCategory, - useBookmarkRestore, -} from '@/apis/bookmark'; +import { fetchBookmarkReadCount, useBookmarkDelete, useBookmarkRestore } from '@/apis/bookmark'; import { useCategoryList } from '@/apis/category'; import { Button } from '@/components/common/Button'; import Divider from '@/components/common/Divider'; @@ -18,32 +12,29 @@ import { cn } from '@/lib/utils'; import useEditModeStore from '@/stores/editModeStore'; import useToastStore from '@/stores/toastStore'; +import useHandleMoveCategory from './hooks/useHandleMoveCategory'; + interface Props { handleEditMode: () => void; } const EditMode = ({ handleEditMode }: Props) => { - const router = useRouter(); - const pathname = usePathname(); - const { selectedBookmarks, getSelectedBookmarksLength, resetEditMode, resetSelectedBookmarks, - setEditMode, setDeletedBookmarks, - setMovedBookmarks, movedBookmarks, deletedBookmarks, } = useEditModeStore(); const { addToast } = useToastStore(); const { data: categoryData } = useCategoryList(null); - const { mutate: moveCategory } = useBookmarkMoveCategory(); + const { mutate: mutateBookmarkDelete } = useBookmarkDelete(); const { mutate: mutateBookmarkRestore } = useBookmarkRestore(); - + const { handleMoveCategory } = useHandleMoveCategory(); const [isOpenCategoryOptions, setIsOpenCategoryOptions] = useState(false); const moveDivRef = useRef(null); @@ -69,44 +60,6 @@ const EditMode = ({ handleEditMode }: Props) => { }); }; - const handleMoveCategory = (categoryId: number) => { - const movingBookmarksDtos = selectedBookmarks.map((bookmark) => { - return { - originCategoryId: bookmark.categoryDtos.length ? bookmark.categoryDtos[0].categoryId : null, - bookMarkId: bookmark.bookmarkId, - movingCategoryId: categoryId, - }; - }); - - moveCategory( - { - bookMarkMovingDtos: movingBookmarksDtos, - }, - { - onSuccess: () => { - setMovedBookmarks(selectedBookmarks.map((bookmark) => bookmark.bookmarkId)); - addToast({ - message: '북마크를 이동했어요.', - type: 'default', - clickText: '복구하기', - onClick: () => { - handleRestoreBookmarks({ - targetIds: selectedBookmarks.map((bookmark) => bookmark.bookmarkId), - }); - }, - }); - - resetSelectedBookmarks(); - setEditMode(false); - - router.replace(`${pathname}/?tab=${categoryId}`); - }, - }, - ); - - setIsOpenCategoryOptions(false); - }; - const handleDeleteBookmarks = () => { const bookmarkIds = [...selectedBookmarks.map((bookmark) => bookmark.bookmarkId)]; diff --git a/src/stores/editModeStore.ts b/src/stores/editModeStore.ts index a0ac45b..4f46fdd 100644 --- a/src/stores/editModeStore.ts +++ b/src/stores/editModeStore.ts @@ -5,31 +5,33 @@ export type CategoryDtoType = { categoryName: string; }; +type Bookmark = { + bookmarkId: number; + url: string; + categoryDtos: CategoryDtoType[]; +}; + +export type MovingBookmarkDto = { + originCategoryId: number | null; + bookMarkId: number; + movingCategoryId: number | null; +}; + type Store = { isEditMode: boolean; setEditMode: (isEditMode: boolean) => void; - selectedBookmarks: Array<{ - bookmarkId: number; - url: string; - categoryDtos: CategoryDtoType[]; - }>; + selectedBookmarks: Bookmark[]; movedBookmarks: number[]; deletedBookmarks: number[]; isSelectedBookmark: (bookmarkId: number) => boolean; - setSelectedBookmarks: ({ - bookmarkId, - url, - categoryDtos, - }: { - bookmarkId: number; - url: string; - categoryDtos: CategoryDtoType[]; - }) => void; + setSelectedBookmarks: (bookmark: Bookmark) => void; resetEditMode: () => void; resetSelectedBookmarks: () => void; getSelectedBookmarksLength: () => number; setMovedBookmarks: (bookmarkIds: number[]) => void; setDeletedBookmarks: (bookmarkIds: number[]) => void; + isOpenCategoryOptions: boolean; + setIsOpenCategoryOptions: (isOpen: boolean) => void; }; const useEditModeStore = create((set, get) => ({ @@ -37,58 +39,60 @@ const useEditModeStore = create((set, get) => ({ selectedBookmarks: [], movedBookmarks: [], deletedBookmarks: [], + isOpenCategoryOptions: false, setEditMode: (isEditMode: boolean) => { - set(() => ({ - isEditMode, - })); + set({ isEditMode }); }, - // @desc: 선택된 북마크 추가 - setSelectedBookmarks: ({ bookmarkId, url, categoryDtos }) => { - const isAlreadyExist = get().selectedBookmarks.some((item) => item.bookmarkId === bookmarkId); + setSelectedBookmarks: (bookmark: Bookmark) => { + const isAlreadyExist = get().selectedBookmarks.some( + (item) => item.bookmarkId === bookmark.bookmarkId, + ); - set((state) => ({ - // @desc: toggle 되게끔 + set({ selectedBookmarks: isAlreadyExist - ? state.selectedBookmarks.filter((item) => item.bookmarkId !== bookmarkId) - : [...state.selectedBookmarks, { bookmarkId, url, categoryDtos }], - })); + ? get().selectedBookmarks.filter((item) => item.bookmarkId !== bookmark.bookmarkId) + : [...get().selectedBookmarks, bookmark], + }); }, setMovedBookmarks: (bookmarkIds: number[]) => { - set((state) => ({ - movedBookmarks: [...state.movedBookmarks, ...bookmarkIds], - })); + set({ + movedBookmarks: [...get().movedBookmarks, ...bookmarkIds], + }); }, setDeletedBookmarks: (bookmarkIds: number[]) => { - set((state) => ({ - deletedBookmarks: [...state.deletedBookmarks, ...bookmarkIds], - })); + set({ + deletedBookmarks: [...get().deletedBookmarks, ...bookmarkIds], + }); }, isSelectedBookmark: (bookmarkId: number) => { return get().selectedBookmarks.some((item) => item.bookmarkId === bookmarkId); }, - // @desc: 선택된 북마크 개수 반환 getSelectedBookmarksLength: () => { return get().selectedBookmarks.length; }, - resetSelectedBookmarks: () => - set(() => ({ - selectedBookmarks: [], - })), + resetSelectedBookmarks: () => { + set({ selectedBookmarks: [] }); + }, - // @desc: edit mode store 초기화 - resetEditMode: () => - set(() => ({ + resetEditMode: () => { + set({ selectedBookmarks: [], movedBookmarks: [], deletedBookmarks: [], - })), + isEditMode: false, + }); + }, + + setIsOpenCategoryOptions: (isOpen: boolean) => { + set({ isOpenCategoryOptions: isOpen }); + }, })); export default useEditModeStore;