From 672434c498f111a54d493d52076c653252cc0e6d Mon Sep 17 00:00:00 2001 From: chsua Date: Wed, 18 Oct 2023 20:22:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20(#734)=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84/=EA=B2=8C=EC=8B=9C=EB=AC=BC/=EB=8C=93=EA=B8=80?= =?UTF-8?q?=EC=9D=84=20=EC=8B=A0=EA=B3=A0=ED=95=98=EB=8A=94=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=ED=9B=85=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 fetch에서 리액트쿼리 훅으로 교체 --- frontend/src/components/ReportModal/index.tsx | 4 +- .../comment/CommentList/CommentItem/index.tsx | 55 +++---------------- .../src/hooks/query/report/useReportAction.ts | 8 ++- .../hooks/query/report/useReportContent.ts | 30 ++++++++++ .../post/PostDetailPage/PostDetail/index.tsx | 46 +++------------- 5 files changed, 58 insertions(+), 85 deletions(-) create mode 100644 frontend/src/hooks/query/report/useReportContent.ts diff --git a/frontend/src/components/ReportModal/index.tsx b/frontend/src/components/ReportModal/index.tsx index cc8a629c5..4c48b695b 100644 --- a/frontend/src/components/ReportModal/index.tsx +++ b/frontend/src/components/ReportModal/index.tsx @@ -25,7 +25,9 @@ export default function ReportModal({ const { selectedOption, handleOptionChange } = useSelect(defaultReportMessage); const handlePrimaryButtonClick = () => { - !isReportLoading && handleReportClick(selectedOption); + if (isReportLoading) return; + + handleReportClick(selectedOption); handleCancelClick(); }; diff --git a/frontend/src/components/comment/CommentList/CommentItem/index.tsx b/frontend/src/components/comment/CommentList/CommentItem/index.tsx index bff678a41..bfb47ed87 100644 --- a/frontend/src/components/comment/CommentList/CommentItem/index.tsx +++ b/frontend/src/components/comment/CommentList/CommentItem/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from 'react'; +import { useState } from 'react'; import { useParams } from 'react-router-dom'; import { Comment } from '@type/comment'; @@ -7,10 +7,8 @@ import { ReportMessage, ReportRequest } from '@type/report'; import { useToggle } from '@hooks'; -import { ToastContext } from '@hooks/context/toast'; import { useDeleteComment } from '@hooks/query/comment/useDeleteComment'; - -import { reportContent } from '@api/report'; +import { useReportContent } from '@hooks/query/report/useReportContent'; import CommentTextForm from '@components/comment/CommentList/CommentTextForm'; import DeleteModal from '@components/common/DeleteModal'; @@ -30,10 +28,6 @@ interface CommentItemProps { } export default function CommentItem({ comment, userType }: CommentItemProps) { - const [isReportCommentLoading, setIsReportCommentLoading] = useState(false); - const [isReportNicknameLoading, setIsReportNicknameLoading] = useState(false); - - const { addMessage } = useContext(ToastContext); const { isOpen, toggleComponent, closeComponent } = useToggle(); const { id, member, content, createdAt, isEdit } = comment; const [action, setAction] = useState(null); @@ -41,7 +35,8 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { const params = useParams() as { postId: string }; const postId = Number(params.postId); - const { mutate, isLoading: isCommentDeleting } = useDeleteComment(postId, id); + const { mutate: deleteComment, isLoading: isCommentDeleting } = useDeleteComment(postId, id); + const { mutate: reportContent, isLoading: isContentReporting } = useReportContent(); const handleMenuClick = (menu: CommentAction) => { closeComponent(); @@ -50,44 +45,12 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { const handleCommentReportClick = async (reason: ReportMessage) => { const reportData: ReportRequest = { type: 'COMMENT', id, reason }; - setIsReportCommentLoading(true); - - await reportContent(reportData) - .then(res => { - addMessage('댓글을 신고했습니다.'); - }) - .catch(e => { - if (e instanceof Error) { - const errorResponse = JSON.parse(e.message); - addMessage(errorResponse.message); - return; - } - addMessage('댓글 신고를 실패했습니다.'); - }) - .finally(() => { - setIsReportCommentLoading(false); - }); + reportContent(reportData); }; const handleNicknameReportClick = async (reason: ReportMessage) => { const reportData: ReportRequest = { type: 'NICKNAME', id: member.id, reason }; - setIsReportNicknameLoading(true); - - await reportContent(reportData) - .then(res => { - addMessage('작성자 닉네임을 신고했습니다.'); - }) - .catch(e => { - if (e instanceof Error) { - const errorResponse = JSON.parse(e.message); - addMessage(errorResponse.message); - return; - } - addMessage('작성자 닉네임 신고를 실패했습니다.'); - }) - .finally(() => { - setIsReportNicknameLoading(false); - }); + reportContent(reportData); }; const handleCancelClick = () => { @@ -95,7 +58,7 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { }; const handleDeleteClick = () => { - mutate(); + deleteComment(); }; const USER_TYPE = COMMENT_USER_MENU[userType]; @@ -158,7 +121,7 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { reportType="NICKNAME" handleReportClick={handleNicknameReportClick} handleCancelClick={handleCancelClick} - isReportLoading={isReportNicknameLoading} + isReportLoading={isContentReporting} /> )} {action === COMMENT_ACTION.COMMENT_REPORT && ( @@ -166,7 +129,7 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { reportType="COMMENT" handleReportClick={handleCommentReportClick} handleCancelClick={handleCancelClick} - isReportLoading={isReportCommentLoading} + isReportLoading={isContentReporting} /> )} diff --git a/frontend/src/hooks/query/report/useReportAction.ts b/frontend/src/hooks/query/report/useReportAction.ts index bf6066d19..73d2fe81f 100644 --- a/frontend/src/hooks/query/report/useReportAction.ts +++ b/frontend/src/hooks/query/report/useReportAction.ts @@ -1,13 +1,18 @@ +import { useContext } from 'react'; + import { useMutation, useQueryClient } from '@tanstack/react-query'; import { ReportActionRequest } from '@type/report'; +import { ToastContext } from '@hooks/context/toast'; + import { reportAction } from '@api/report'; import { QUERY_KEY } from '@constants/queryKey'; export const useReportAction = () => { const queryClient = useQueryClient(); + const { addMessage } = useContext(ToastContext); const { mutate, isLoading, isSuccess, isError, error } = useMutation({ mutationFn: async (reportActionData: ReportActionRequest) => @@ -16,7 +21,8 @@ export const useReportAction = () => { queryClient.invalidateQueries({ queryKey: [QUERY_KEY.REPORT] }); }, onError: () => { - window.console.error('신고 조치를 실패했습니다.'); + const message = error instanceof Error ? error.message : '투표를 실패했습니다.'; + addMessage(message); }, }); diff --git a/frontend/src/hooks/query/report/useReportContent.ts b/frontend/src/hooks/query/report/useReportContent.ts new file mode 100644 index 000000000..1d17cd164 --- /dev/null +++ b/frontend/src/hooks/query/report/useReportContent.ts @@ -0,0 +1,30 @@ +import { useContext } from 'react'; + +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { ReportRequest } from '@type/report'; + +import { ToastContext } from '@hooks/context/toast'; + +import { reportContent } from '@api/report'; + +import { QUERY_KEY } from '@constants/queryKey'; + +export const useReportContent = () => { + const queryClient = useQueryClient(); + const { addMessage } = useContext(ToastContext); + + const { mutate, isLoading } = useMutation({ + mutationFn: async (reportData: ReportRequest) => await reportContent(reportData), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.REPORT] }); + addMessage('신고를 완료하였습니다.'); + }, + onError: error => { + const message = error instanceof Error ? error.message : '개인정보 등록을 실패했습니다.'; + addMessage(message); + }, + }); + + return { mutate, isLoading }; +}; diff --git a/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx b/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx index abbab14aa..92d362c7d 100644 --- a/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx +++ b/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx @@ -1,4 +1,4 @@ -import { Suspense, useContext, useEffect, useState } from 'react'; +import { Suspense, useContext, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { PostInfo } from '@type/post'; @@ -7,8 +7,7 @@ import { ReportMessage, ReportRequest } from '@type/report'; import { AuthContext, useDeletePost, useEarlyClosePost, usePostDetail } from '@hooks'; import { ToastContext } from '@hooks/context/toast'; - -import { reportContent } from '@api/report'; +import { useReportContent } from '@hooks/query/report/useReportContent'; import ErrorBoundary from '@pages/ErrorBoundary'; @@ -30,9 +29,6 @@ import * as S from './style'; export default function PostDetail() { const navigate = useNavigate(); - const [isReportPostLoading, setIsReportPostLoading] = useState(false); - const [isReportNicknameLoading, setIsReportNicknameLoading] = useState(false); - const params = useParams() as { postId: string }; const postId = Number(params.postId); @@ -47,6 +43,7 @@ export default function PostDetail() { isLoading: isDeletePostLoading, } = useDeletePost(postId, loggedInfo.isLoggedIn); const { mutate: earlyClosePost } = useEarlyClosePost(postId); + const { mutate: reportContent, isLoading: isContentReporting } = useReportContent(); const postDataFallback = postData ?? ({} as PostInfo); @@ -81,41 +78,16 @@ export default function PostDetail() { deletePost(); }, reportPost: async (reason: ReportMessage) => { - setIsReportPostLoading(true); const reportData: ReportRequest = { type: 'POST', id: postId, reason }; - - await reportContent(reportData) - .then(res => { - addMessage('게시물을 신고했습니다.'); - }) - .catch(error => { - const message = error instanceof Error ? error.message : '게시글 신고를 실패했습니다.'; - addMessage(message); - }) - .finally(() => { - setIsReportPostLoading(false); - }); + reportContent(reportData); }, reportNickname: async (reason: ReportMessage) => { - setIsReportNicknameLoading(true); const reportData: ReportRequest = { type: 'NICKNAME', id: postDataFallback.writer.id, reason, }; - - await reportContent(reportData) - .then(res => { - addMessage('작성자 닉네임을 신고했습니다.'); - }) - .catch(error => { - const message = - error instanceof Error ? error.message : '작성자 닉네임 신고를 실패했습니다.'; - addMessage(message); - }) - .finally(() => { - setIsReportNicknameLoading(false); - }); + reportContent(reportData); }, copyPostURL: () => { const currentURL = window.location.href; @@ -146,8 +118,8 @@ export default function PostDetail() { handleEvent={{ movePage, controlPost }} isEventLoading={{ isDeletePostLoading, - isReportPostLoading, - isReportNicknameLoading, + isReportPostLoading: isContentReporting, + isReportNicknameLoading: isContentReporting, }} /> @@ -165,8 +137,8 @@ export default function PostDetail() { handleEvent={{ movePage, controlPost, openToast: addMessage }} isEventLoading={{ isDeletePostLoading, - isReportPostLoading, - isReportNicknameLoading, + isReportPostLoading: isContentReporting, + isReportNicknameLoading: isContentReporting, }} /> From 3cc49994acfecfed78523c7b68cef61910c34710 Mon Sep 17 00:00:00 2001 From: chsua Date: Wed, 18 Oct 2023 20:47:35 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20(#734)=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EB=AC=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A4=91=20=ED=95=98?= =?UTF-8?q?=EB=8B=A8=20=EB=B2=84=ED=8A=BC=EC=97=90=EC=84=9C=20action=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=ED=83=80=EC=9E=85=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PostAction 설정 --- .../src/pages/post/PostDetailPage/BottomButtonPart/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/post/PostDetailPage/BottomButtonPart/index.tsx b/frontend/src/pages/post/PostDetailPage/BottomButtonPart/index.tsx index d83eaeee2..3cae9e45b 100644 --- a/frontend/src/pages/post/PostDetailPage/BottomButtonPart/index.tsx +++ b/frontend/src/pages/post/PostDetailPage/BottomButtonPart/index.tsx @@ -2,6 +2,7 @@ import type { LoadingType } from '../types'; import { useContext, useState } from 'react'; +import { PostAction } from '@type/menu'; import { ReportMessage } from '@type/report'; import { AuthContext } from '@hooks/context/auth'; @@ -39,9 +40,9 @@ export default function BottomButtonPart({ const { moveWritePostPage, moveVoteStatisticsPage } = movePage; const { setEarlyClosePost, deletePost, reportPost, reportNickname } = controlPost; const { isDeletePostLoading, isReportPostLoading, isReportNicknameLoading } = isEventLoading; - const [action, setAction] = useState(null); + const [action, setAction] = useState(null); - const handleActionButtonClick = (action: string) => { + const handleActionButtonClick = (action: PostAction) => { if (!loggedInfo.isLoggedIn) { openToast('로그인 후에 기능을 이용해주세요.'); return; From f90f6861eb29c18dbd50e2557ad9d428891c6d0a Mon Sep 17 00:00:00 2001 From: chsua Date: Wed, 18 Oct 2023 20:48:38 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20(#734)=20=EB=AA=A8=EB=B0=94?= =?UTF-8?q?=EC=9D=BC=EC=97=90=EC=84=9C=20=EA=B2=8C=EC=8B=9C=EB=AC=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=8B=A0=EA=B3=A0=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=EC=9D=84=20=EB=88=84=EB=A5=B4=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EB=82=B4=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InnerHeaderPart.stories.tsx | 1 + .../PostDetailPage/InnerHeaderPart/index.tsx | 24 ++++++++++++------- .../post/PostDetailPage/PostDetail/index.tsx | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/InnerHeaderPart.stories.tsx b/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/InnerHeaderPart.stories.tsx index 382c71f15..ecacafc46 100644 --- a/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/InnerHeaderPart.stories.tsx +++ b/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/InnerHeaderPart.stories.tsx @@ -32,6 +32,7 @@ const handleEvent = { reportPost: (reason: string) => {}, reportNickname: (reason: string) => {}, }, + openToast: () => {}, }; export const isWriterAndIsClosedCase: Story = { diff --git a/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/index.tsx b/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/index.tsx index 0df97db95..7471619d7 100644 --- a/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/index.tsx +++ b/frontend/src/pages/post/PostDetailPage/InnerHeaderPart/index.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react'; +import { useContext, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { PostAction, MenuItem } from '@type/menu'; import { ReportMessage } from '@type/report'; -import { useToggle } from '@hooks'; +import { AuthContext, useToggle } from '@hooks'; import DeleteModal from '@components/common/DeleteModal'; import HeaderTextButton from '@components/common/HeaderTextButton'; @@ -32,6 +32,7 @@ interface PostDetailPageChildProps { reportPost: (reason: ReportMessage) => void; reportNickname: (reason: ReportMessage) => void; }; + openToast: (text: string) => void; }; isEventLoading: Record; } @@ -44,20 +45,25 @@ const menuList: MenuItem[] = [ export default function InnerHeaderPart({ isWriter, isClosed, - handleEvent: { movePage, controlPost }, + handleEvent: { movePage, controlPost, openToast }, isEventLoading, }: PostDetailPageChildProps) { const navigate = useNavigate(); - + const { loggedInfo } = useContext(AuthContext); const { moveWritePostPage, moveVoteStatisticsPage } = movePage; const { setEarlyClosePost, deletePost, reportPost, reportNickname } = controlPost; - const { isOpen, toggleComponent, closeComponent } = useToggle(); - const [action, setAction] = useState(null); - const { isDeletePostLoading, isReportNicknameLoading, isReportPostLoading } = isEventLoading; + const { isOpen: isMenuOpen, toggleComponent, closeComponent } = useToggle(); + const [action, setAction] = useState(null); const handleMenuClick = (action: PostAction) => { closeComponent(); + + if (!loggedInfo.isLoggedIn) { + openToast('로그인 후에 기능을 이용해주세요.'); + return; + } + setAction(action); }; @@ -80,12 +86,12 @@ export default function InnerHeaderPart({ {!isWriter ? ( <> 신고 - {isOpen && ( + {isMenuOpen && ( diff --git a/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx b/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx index 92d362c7d..f7cc37852 100644 --- a/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx +++ b/frontend/src/pages/post/PostDetailPage/PostDetail/index.tsx @@ -115,7 +115,7 @@ export default function PostDetail() { Date: Wed, 18 Oct 2023 22:45:14 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20(#734)=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/query/report/useReportAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/hooks/query/report/useReportAction.ts b/frontend/src/hooks/query/report/useReportAction.ts index 73d2fe81f..8537cf518 100644 --- a/frontend/src/hooks/query/report/useReportAction.ts +++ b/frontend/src/hooks/query/report/useReportAction.ts @@ -21,7 +21,7 @@ export const useReportAction = () => { queryClient.invalidateQueries({ queryKey: [QUERY_KEY.REPORT] }); }, onError: () => { - const message = error instanceof Error ? error.message : '투표를 실패했습니다.'; + const message = error instanceof Error ? error.message : '신고 조치를 실패했습니다.'; addMessage(message); }, });