diff --git a/src/app/(home)/_components/FloatingBtn/FloatingBtn.tsx b/src/app/(home)/_components/FloatingBtn/FloatingBtn.tsx index ec2c2da..5d3f7cd 100644 --- a/src/app/(home)/_components/FloatingBtn/FloatingBtn.tsx +++ b/src/app/(home)/_components/FloatingBtn/FloatingBtn.tsx @@ -37,42 +37,31 @@ const FloatingBtn = ({ setIsLoginModalOpened(true); } }; + + const updateQueryData = (bookmarked: boolean) => + queryClient.setQueryData( + ["shuffledContents", searchParams], + (oldData: InfiniteData<{ content: IContentData[] }, unknown>) => { + const updatedPages = oldData.pages.map((page) => { + const updatedContent = page.content.map((item) => { + if (item.id === contentId) { + return { ...item, bookmarked }; // 새로운 객체 반환 + } + return item; // 원본 객체 반환 + }); + return { ...page, content: updatedContent }; // 새로운 페이지 객체 반환 + }); + return { ...oldData, pages: updatedPages }; // 새로운 oldData 객체 반환 + } + ); const closeSaveModal = (isSaved: boolean | null) => { setIsSaveModalOpened(false); if (isSaved === true) { setIsSavedContent(true); - queryClient.setQueryData( - ["shuffledContents", searchParams], - (oldData: InfiniteData<{ content: IContentData[] }, unknown>) => { - const updatedPages = oldData.pages.map((page) => { - const updatedContent = page.content.map((item) => { - if (item.id === contentId) { - return { ...item, bookmarked: true }; // 새로운 객체 반환 - } - return item; // 원본 객체 반환 - }); - return { ...page, content: updatedContent }; // 새로운 페이지 객체 반환 - }); - return { ...oldData, pages: updatedPages }; // 새로운 oldData 객체 반환 - } - ); + updateQueryData(true); } else if (isSaved === false) { setIsSavedContent(false); - queryClient.setQueryData( - ["shuffledContents", searchParams], - (oldData: InfiniteData<{ content: IContentData[] }, unknown>) => { - const updatedPages = oldData.pages.map((page) => { - const updatedContent = page.content.map((item) => { - if (item.id === contentId) { - return { ...item, bookmarked: false }; // 새로운 객체 반환 - } - return item; // 원본 객체 반환 - }); - return { ...page, content: updatedContent }; // 새로운 페이지 객체 반환 - }); - return { ...oldData, pages: updatedPages }; // 새로운 oldData 객체 반환 - } - ); + updateQueryData(false); } }; diff --git a/src/app/_components/NewGroupModal/NewGroupModal.tsx b/src/app/_components/NewGroupModal/NewGroupModal.tsx index 066e949..e602bb8 100644 --- a/src/app/_components/NewGroupModal/NewGroupModal.tsx +++ b/src/app/_components/NewGroupModal/NewGroupModal.tsx @@ -3,25 +3,31 @@ import Image from "next/image"; import Check_White from "../../../../public/assets/check_white.svg"; import { useState } from "react"; import { createGroup } from "../../_utils/api"; +import queryClient from "@/app/_utils/queryClient"; +import { useMutation } from "@tanstack/react-query"; const NewGroup = ({ closeNewGroupModal, }: { closeNewGroupModal: () => void; }) => { + const localTokenData = localStorage.getItem("tokenData"); + if (localTokenData === null) throw new Error("Token is not found"); + const tokenData = JSON.parse(localTokenData); + const [groupName, setGroupName] = useState(""); - const handleCreateGroup = async () => { - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData !== null) { - if (groupName === "") { - alert("그룹 이름을 입력해주세요."); - return; - } - const tokenData = JSON.parse(localTokenData); - await createGroup(groupName, tokenData.accessToken); - closeNewGroupModal(); + const { mutate: createNewGroup } = useMutation({ + mutationFn: () => createGroup(groupName, tokenData.accessToken), + onSuccess: () => + queryClient.invalidateQueries({ queryKey: ["groupListData"] }), + }); + + const handleCreateGroup = () => { + if (groupName !== "") { + createNewGroup(); } + closeNewGroupModal(); }; return (
diff --git a/src/app/_components/SaveContentModal/SaveContentModal.tsx b/src/app/_components/SaveContentModal/SaveContentModal.tsx index d46cc7c..324c9a1 100644 --- a/src/app/_components/SaveContentModal/SaveContentModal.tsx +++ b/src/app/_components/SaveContentModal/SaveContentModal.tsx @@ -11,6 +11,7 @@ import { saveContentToGroup, } from "../../_utils/api"; import { IGroup } from "../../index"; +import { useMutation, useQuery } from "@tanstack/react-query"; const SaveContent = ({ closeSaveModal, @@ -21,13 +22,65 @@ const SaveContent = ({ openNewGroupModal: () => void; contentId: number; }) => { - const [groupListData, setGroupListData] = useState<{ - content: IGroup[]; - } | null>(null); + const localTokenData = localStorage.getItem("tokenData"); + if (localTokenData === null) throw new Error("Token is not found"); + const tokenData = JSON.parse(localTokenData); + + const { data: groupListData } = useQuery<{ content: IGroup[] }>({ + queryKey: ["groupListData"], + queryFn: () => getGroupList(tokenData.accessToken), + enabled: !!tokenData, + }); + + const { data: containedGroupList, refetch: refetchContainedGroupList } = + useQuery({ + queryKey: ["containedGroupList", contentId], + queryFn: () => + getContainedGroupList(contentId.toString(), tokenData.accessToken), + enabled: !!groupListData, + }); + + const { mutate: saveContent } = useMutation({ + mutationFn: ({ + index, + contentId, + }: { + index: number; + contentId: number; + }) => { + if (groupListData === undefined) + throw new Error("GroupListData is not found"); + return saveContentToGroup( + groupListData.content[index].name, + contentId, + tokenData.accessToken + ); + }, + onSuccess: () => refetchContainedGroupList(), + }); + + const { mutate: deleteContent } = useMutation({ + mutationFn: ({ + index, + contentId, + }: { + index: number; + contentId: number; + }) => { + if (groupListData === undefined) + throw new Error("GroupListData is not found"); + return deleteContentInGroup( + groupListData.content[index].name, + contentId, + tokenData.accessToken + ); + }, + onSuccess: () => refetchContainedGroupList(), + }); + const [checkList, setCheckList] = useState( new Array(groupListData?.content.length).fill(false) ); - const [containedGroupList, setContainedGroupList] = useState([]); const handleGroupClick = (index: number) => { const updatedCheckList = [...checkList]; @@ -36,38 +89,18 @@ const SaveContent = ({ }; const handleSaveGroupClick = async () => { - const queryString = window.location.search; - const urlParams = new URLSearchParams(queryString); - const contentId = urlParams.get("id"); - - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData === null) throw new Error("Token is not found"); - const tokenData = JSON.parse(localTokenData); - let isSaved = false; checkList.forEach((v, index) => { if (v === true) { - if (groupListData === null) - throw new Error("GroupListData is not found"); // 그룹 추가 - if (containedGroupList[index] === false) { - saveContentToGroup( - groupListData.content[index].name, - contentId, - tokenData.accessToken - ); + if (containedGroupList.content[index].contains === false) { + saveContent({ index, contentId }); } isSaved = true; } else { - if (containedGroupList[index] === true) { - if (groupListData === null) - throw new Error("GroupListData is not found"); - // 그룹 삭제 - deleteContentInGroup( - groupListData.content[index].name, - contentId, - tokenData.accessToken - ); + // 그룹 삭제 + if (containedGroupList.content[index].contains === true) { + deleteContent({ index, contentId }); } } }); @@ -75,33 +108,14 @@ const SaveContent = ({ }; useEffect(() => { - const getGroupListData = async () => { - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData !== null) { - const tokenData = JSON.parse(localTokenData); - const groupListData = await getGroupList(tokenData.accessToken); - setGroupListData(groupListData); - setCheckList(new Array(groupListData.content.length).fill(false)); - getContainedGroupListData(); - } - }; - const getContainedGroupListData = async () => { - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData === null) throw new Error("Token is not found"); - const tokenData = JSON.parse(localTokenData); - const containedGroupList = await getContainedGroupList( - contentId.toString(), - tokenData.accessToken - ); - + if (containedGroupList) { const updatedCheckList = containedGroupList.content.map( (group: { name: string; contains: boolean }) => group.contains ); - setContainedGroupList(updatedCheckList); + setCheckList(updatedCheckList); - }; - getGroupListData(); - }, []); + } + }, [containedGroupList]); return (
closeSaveModal(null)}> diff --git a/src/app/_utils/api.ts b/src/app/_utils/api.ts index 72a4280..30d77a9 100644 --- a/src/app/_utils/api.ts +++ b/src/app/_utils/api.ts @@ -276,7 +276,7 @@ const saveContentToGroup_Deprecated = async ( const saveContentToGroup = async ( groupName: string | null, - contentId: string | null, + contentId: number | null, access_token: string ) => { fetchUrl.pathname = `/api/bookmark/v1/groups/${groupName}/contents/${contentId}`; @@ -338,7 +338,7 @@ const deleteGroup = async (groupName: string, access_token: string) => { const deleteContentInGroup = async ( groupName: string, - contentId: string | null, + contentId: number | null, access_token: string ) => { fetchUrl.pathname = `/api/bookmark/v1/groups/${groupName}/contents/${contentId}`; diff --git a/src/app/groupContents/[id]/page.tsx b/src/app/groupContents/[id]/page.tsx index 0220ec2..7fc7129 100644 --- a/src/app/groupContents/[id]/page.tsx +++ b/src/app/groupContents/[id]/page.tsx @@ -37,11 +37,7 @@ const GroupContents = ({ params }: { params: { id: string } }) => { const localTokenData = localStorage.getItem("tokenData"); if (localTokenData === null) throw new Error("Token is not found"); const tokenData = JSON.parse(localTokenData); - await deleteContentInGroup( - params.id, - contentId.toString(), - tokenData.accessToken - ); + await deleteContentInGroup(params.id, contentId, tokenData.accessToken); const newContentsData: { content: IContentData[] } = await getContentListInGroup(params.id, tokenData.accessToken); diff --git a/src/app/profile/page.module.css b/src/app/profile/page.module.css index a54146c..0fb371c 100644 --- a/src/app/profile/page.module.css +++ b/src/app/profile/page.module.css @@ -110,6 +110,7 @@ top: 20px; border-radius: 5px; padding: 5px; + z-index: 3; background-color: #fff; box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.1); } diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 26fa76d..4f2b83f 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -4,48 +4,58 @@ import LoginModal from "../_components/LoginModal/LoginModal"; import { deleteGroup, getGroupList } from "../_utils/api"; import { MouseEvent, useEffect, useState } from "react"; import { useRouter } from "next/navigation"; -import CheckToken from "../_utils/CheckToken"; import { IGroup } from ".."; import no_image from "@/../public/assets/no_image.svg"; import dots from "@/../public/assets/dots.svg"; import plus_gray from "@/../public/assets/plus_gray.svg"; import Image from "next/image"; import NewGroupModal from "../_components/NewGroupModal/NewGroupModal"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { createPortal } from "react-dom"; +import queryClient from "../_utils/queryClient"; const Profile = () => { const router = useRouter(); - const [groupListData, setGroupListData] = useState<{ + + const localTokenData = localStorage.getItem("tokenData"); + if (localTokenData === null) throw new Error("Token is not found"); + const tokenData = JSON.parse(localTokenData); + + const { data: groupListData, isSuccess: isListDataFetched } = useQuery<{ content: IGroup[]; - } | null>(null); + }>({ + queryKey: ["groupListData"], + queryFn: () => getGroupList(tokenData.accessToken), + enabled: !!tokenData, + }); + + const { mutate: deleteGroupFn } = useMutation({ + mutationFn: ({ groupName }: { groupName: string }) => + deleteGroup(groupName, tokenData.accessToken), + onSuccess: () => + queryClient.invalidateQueries({ queryKey: ["groupListData"] }), + }); + const [isLoginModalOpened, setIsLoginModalOpened] = useState(false); const [isDotMenuOpened, setIsDotMenuOpened] = useState([]); const [isNewGroupModalOpened, setIsNewGroupModalOpened] = useState(false); useEffect(() => { - const getGroupListData = async () => { - const isLogin = await CheckToken(); - if (isLogin) { - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData !== null) { - const tokenData = JSON.parse(localTokenData); - const groupListData = await getGroupList(tokenData.accessToken); - setGroupListData(groupListData); - } - } else { - setIsLoginModalOpened(true); - } - }; - getGroupListData(); - }, []); + if (isListDataFetched) { + setIsDotMenuOpened(new Array(groupListData.content.length).fill(false)); + } + }, [isListDataFetched]); - const closeLoginModal = () => { - setIsLoginModalOpened(false); - router.push("/"); - }; const handleDotMenu = (e: MouseEvent, index: number) => { e.stopPropagation(); - const newIsDotMenuOpened = [...isDotMenuOpened]; - newIsDotMenuOpened[index] = !newIsDotMenuOpened[index]; + const newIsDotMenuOpened = [...isDotMenuOpened].map((value, idx) => { + if (index === idx) { + value = !value; + } else { + value = false; + } + return value; + }); setIsDotMenuOpened(newIsDotMenuOpened); }; const handleRouteToGroupContents = (name: string) => { @@ -55,32 +65,13 @@ const Profile = () => { const newIsDotMenuOpened = new Array(isDotMenuOpened.length).fill(false); setIsDotMenuOpened(newIsDotMenuOpened); }; - const handleDeleteGroup = async (e: MouseEvent, name: string) => { - //name만 뺀 새로운 groupListData를 만들어서 setGroupListData - if (groupListData !== null) { - const newGroupListData = groupListData.content.filter( - (content) => content.name !== name - ); - setGroupListData({ content: newGroupListData }); - } - + const handleDeleteGroup = (e: MouseEvent, name: string) => { e.stopPropagation(); - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData !== null) { - const tokenData = JSON.parse(localTokenData); - await deleteGroup(name, tokenData.accessToken); - const groupListData = await getGroupList(tokenData.accessToken); - setGroupListData(groupListData); - } + deleteGroupFn({ groupName: name }); + handleCloseDotMenu(); }; - const closeNewGroupModal = async () => { + const closeNewGroupModal = () => { setIsNewGroupModalOpened(false); - const localTokenData = localStorage.getItem("tokenData"); - if (localTokenData !== null) { - const tokenData = JSON.parse(localTokenData); - const groupListData = await getGroupList(tokenData.accessToken); - setGroupListData(groupListData); - } }; return ( <> @@ -144,9 +135,11 @@ const Profile = () => {

새 그룹

- {isNewGroupModalOpened && ( - - )} + {isNewGroupModalOpened && + createPortal( + , + document.body + )}