diff --git a/src/App.tsx b/src/App.tsx
index fe1a7c27..58e7e269 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -83,6 +83,10 @@ const App = () => {
path={navigatePath.BOOKMARK_REPORT}
element={}
/>
+ }
+ />
}
diff --git a/src/bookmarks/api/bookmark.ts b/src/bookmarks/api/bookmark.ts
index 84191dd9..8af97597 100644
--- a/src/bookmarks/api/bookmark.ts
+++ b/src/bookmarks/api/bookmark.ts
@@ -9,8 +9,6 @@ import {
useQuery,
useQueryClient,
} from '@tanstack/react-query';
-import { useNavigate } from 'react-router-dom';
-import { AxiosError } from 'axios';
import dayjs from 'dayjs';
import 'dayjs/locale/ko';
import { GET_LIKE_BOOKMARK_LIST } from './like';
@@ -790,63 +788,6 @@ export const usePUTBookmarkQuery = ({
});
};
-// 북마크 신고
-interface POSTBookmarkReportRequest {
- reporterId: number;
- reportedId: number;
- content: string;
-}
-
-const postBookmarkReportAPI = async (params: POSTBookmarkReportRequest) => {
- const { data } = await client({
- method: 'post',
- url: '/reports/bookmarks',
- data: params,
- });
-
- return data;
-};
-
-export interface POSTBookmarkReportMutation {
- reporterId: number;
-}
-
-export const usePOSTBookmarkReportMutation = ({
- reporterId,
-}: POSTBookmarkReportMutation) => {
- const queryClient = useQueryClient();
- const toast = useToast();
- const router = useNavigate();
- return useMutation(postBookmarkReportAPI, {
- onSuccess: () => {
- queryClient.invalidateQueries(
- GET_BOOKMARK_LIST(reporterId, '📖 전체', 0),
- );
- queryClient.invalidateQueries(
- GET_BOOKMARK_LIST(reporterId, '👀 읽음', 0),
- );
- queryClient.invalidateQueries(
- GET_BOOKMARK_LIST(reporterId, '🫣 읽지 않음', 0),
- );
- toast.fireToast({
- message: '신고 되었습니다',
- mode: 'SUCCESS',
- });
- router(-1);
- },
- onError: (e: AxiosError) => {
- const errorCode = e.response?.status;
- if (errorCode && errorCode === 409) {
- toast.fireToast({
- message: '이미 신고한 북마크에요',
- mode: 'DELETE',
- });
- router(-1);
- }
- },
- });
-};
-
// TODO : 추후 테스트 코드 작성
const getKeyofObject = (obj: T, value: unknown) =>
(Object.keys(obj) as (keyof T)[]).find((key) => obj[key] === value);
diff --git a/src/comment/api/Comment.ts b/src/comment/api/Comment.ts
index 2b10c6cd..29a5d553 100644
--- a/src/comment/api/Comment.ts
+++ b/src/comment/api/Comment.ts
@@ -7,8 +7,6 @@ import {
refetchAllBookmarkQuery,
} from '@/bookmarks/api/bookmark';
import useToast from '@/common-ui/Toast/hooks/useToast';
-import { useNavigate } from 'react-router-dom';
-import { AxiosError } from 'axios';
import useAuthStore from '@/store/auth';
import useBookmarkStore from '@/store/bookmark';
@@ -213,48 +211,3 @@ export const useDELETECommentQuery = ({
},
});
};
-
-// 댓글 신고
-interface POSTCommentReportRequest {
- reporterId: number;
- reportedId: number;
- content: string;
-}
-
-const postCommentReportAPI = async (params: POSTCommentReportRequest) => {
- const { data } = await client({
- method: 'post',
- url: '/reports/comments',
- data: params,
- });
-
- return data;
-};
-
-export interface POSTBookmarkReportMutation {
- reporterId: number;
-}
-
-export const usePOSTReportCommentQuery = () => {
- const toast = useToast();
- const router = useNavigate();
- return useMutation(postCommentReportAPI, {
- onSuccess: () => {
- toast.fireToast({
- message: '신고 되었습니다',
- mode: 'SUCCESS',
- });
- router(-1);
- },
- onError: (e: AxiosError) => {
- const errorCode = e.response?.status;
- if (errorCode && errorCode === 409) {
- toast.fireToast({
- message: '이미 신고한 북마크에요',
- mode: 'DELETE',
- });
- router(-1);
- }
- },
- });
-};
diff --git a/src/common-ui/Error/ApiErrorBoundary.tsx b/src/common-ui/Error/ApiErrorBoundary.tsx
index 44d815a9..c34edbfd 100644
--- a/src/common-ui/Error/ApiErrorBoundary.tsx
+++ b/src/common-ui/Error/ApiErrorBoundary.tsx
@@ -3,12 +3,17 @@ import { AxiosError } from 'axios';
import NetworkError from './NetworkError';
import { PostBridgeParams } from '@/common/service/hooks/useWebview';
-type ErrorType = 'NO_USER_INFO' | 'PRIVATE_BOOKMARK' | 'DUPLICATED_NICKNAME';
+type ErrorType =
+ | 'NO_USER_INFO'
+ | 'PRIVATE_BOOKMARK'
+ | 'DUPLICATED_NICKNAME'
+ | 'DUPLICATE_REPORT';
export const ErrorTypes: Record = {
NO_USER_INFO: 'M001',
DUPLICATED_NICKNAME: 'M002',
PRIVATE_BOOKMARK: 'B002',
+ DUPLICATE_REPORT: 'R002',
} as const;
interface CustomData {
diff --git a/src/constants/navigatePath.ts b/src/constants/navigatePath.ts
index fd29b34a..d9653971 100644
--- a/src/constants/navigatePath.ts
+++ b/src/constants/navigatePath.ts
@@ -18,6 +18,7 @@ const navigatePath = {
CATEGORY_LIST: '/category/list',
COMMENT_REPORT: '/comment/:id/report',
BOOKMARK_REPORT: '/bookmark/:id/report',
+ MEMBER_REPORT: '/member/:id/report',
COMMENT: '/comment',
LIKE_PAGE: '/likes',
INTRODUCE: '/introduce',
diff --git a/src/pages/FriendBookmarkPage.tsx b/src/pages/FriendBookmarkPage.tsx
index 57387ffd..29d1c0e1 100644
--- a/src/pages/FriendBookmarkPage.tsx
+++ b/src/pages/FriendBookmarkPage.tsx
@@ -6,7 +6,7 @@ import BookmarkUserInfo from '@/bookmarks/ui/BookmarkUserInfo';
import useCategory from '@/bookmarks/service/hooks/home/useCategory';
import useReadList from '@/bookmarks/service/hooks/home/useReadList';
import getRem from '@/utils/getRem';
-import { useParams } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import Header from '@/common-ui/Header/Header';
import TriggerBottomSheet from '@/common-ui/BottomSheet/TriggerBottomSheet';
import IconButton from '@/common/ui/IconButton';
@@ -25,6 +25,7 @@ import useBookmarkStore from '@/store/bookmark';
import SkeletonBookmarkUserInfo from '@/bookmarks/ui/SkeletonBookmarkUserInfo';
import PullToRefresh from '@/common-ui/PullToRefresh';
import useHandleRefresh from '@/common/service/hooks/useHandleRefresh';
+import { navigatePath } from '@/constants/navigatePath';
const FriendBookmarkPage = () => {
// FIRST RENDER
@@ -48,6 +49,12 @@ const FriendBookmarkPage = () => {
// USER INTERACTION
// 뒤로가기
+ // 1. 상단 more > 신고하기
+ const navigate = useNavigate();
+ const onClick_신고하기 = () => {
+ navigate(navigatePath.MEMBER_REPORT.replace(':id', String(friendId)));
+ };
+
// 1. 상단 more > 차단하기
const { mutate: postBlockMember } = usePOSTBlockMemberQuery({ memberId });
const onClick_차단하기 = () => {
@@ -82,6 +89,9 @@ const FriendBookmarkPage = () => {
as={ {}} name="more" size="s" />}
/>
+
+ 신고하기
+
{!!profileInfo?.isBlocked && (
차단해제
diff --git a/src/pages/ReportPage.tsx b/src/pages/ReportPage.tsx
index a9f9b826..d3f27c77 100644
--- a/src/pages/ReportPage.tsx
+++ b/src/pages/ReportPage.tsx
@@ -1,10 +1,9 @@
-import { usePOSTBookmarkReportMutation } from '@/bookmarks/api/bookmark';
import BookmarkReportList from '@/bookmarks/ui/Report/BookmarkReportList';
import BookmarkReportWrite from '@/bookmarks/ui/Report/BookmarkReportWrite';
-import { usePOSTReportCommentQuery } from '@/comment/api/Comment';
import BottomFixedButton from '@/common-ui/BottomFixedButton';
import Header from '@/common-ui/Header/Header';
import Text from '@/common-ui/Text';
+import { REPORT_TYPE, usePostReportMutation } from '@/report/api/report';
import useAuthStore from '@/store/auth';
import getRem from '@/utils/getRem';
import styled from '@emotion/styled';
@@ -14,7 +13,7 @@ import { useParams } from 'react-router-dom';
export type ReportMode = 'CHECK' | 'WRITE';
interface ReportPageProps {
- mode: 'BOOKMARK' | 'COMMENT';
+ mode: REPORT_TYPE;
}
const ReportPage = ({ mode }: ReportPageProps) => {
@@ -29,27 +28,20 @@ const ReportPage = ({ mode }: ReportPageProps) => {
};
// TODO : 신고하기 버튼 클릭 시 신고 API 호출
- const { mutate: reportBookmark } = usePOSTBookmarkReportMutation({
+ const { mutate: reportBookmark } = usePostReportMutation({
reporterId: memberId,
+ reportType: mode,
});
- const { mutate: reportComment } = usePOSTReportCommentQuery();
+
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const content = reportMode === 'WRITE' ? reportText : selectedReport;
- if (mode === 'BOOKMARK') {
- reportBookmark({
- reportedId: Number(id),
- reporterId: memberId,
- content,
- });
- }
- if (mode === 'COMMENT') {
- reportComment({
- reportedId: Number(id),
- reporterId: memberId,
- content,
- });
- }
+ reportBookmark({
+ reportedId: Number(id),
+ reporterId: memberId,
+ content,
+ reportType: mode,
+ });
};
const buttonDisabled =
diff --git a/src/report/api/report.ts b/src/report/api/report.ts
new file mode 100644
index 00000000..686a7f72
--- /dev/null
+++ b/src/report/api/report.ts
@@ -0,0 +1,130 @@
+import { GET_BOOKMARK_LIST } from '@/bookmarks/api/bookmark';
+import {
+ CustomAxiosError,
+ ErrorTypes,
+} from '@/common-ui/Error/ApiErrorBoundary';
+import useToast from '@/common-ui/Toast/hooks/useToast';
+import client from '@/common/service/client';
+import {
+ QueryClient,
+ useMutation,
+ useQueryClient,
+} from '@tanstack/react-query';
+import { useNavigate } from 'react-router-dom';
+
+export type REPORT_TYPE = 'BOOKMARK' | 'COMMENT' | 'MEMBER';
+
+// 북마크 신고
+interface POSTBookmarkReportRequest {
+ reporterId: number;
+ reportedId: number;
+ content: string;
+ reportType: REPORT_TYPE;
+}
+
+const postReportAPI = async (postData: POSTBookmarkReportRequest) => {
+ const { data } = await client({
+ method: 'post',
+ url: '/reports',
+ data: postData,
+ });
+
+ return data;
+};
+
+export interface POSTBookmarkReportMutation {
+ reporterId: number;
+ reportType: REPORT_TYPE;
+}
+
+export const usePostReportMutation = ({
+ reporterId,
+ reportType,
+}: POSTBookmarkReportMutation) => {
+ const queryClient = useQueryClient();
+ const { fireToast } = useToast();
+ const router = useNavigate();
+ return useMutation(
+ postReportAPI,
+ refetchReportQuery(reporterId, reportType, queryClient, fireToast, router),
+ );
+};
+
+const refetchReportQuery = (
+ reporterId: number,
+ reportType: REPORT_TYPE,
+ queryClient: QueryClient,
+ fireToast: ReturnType['fireToast'],
+ router: ReturnType,
+) => {
+ if (reportType === 'BOOKMARK') {
+ return {
+ onSuccess: () => {
+ // NOTE : 추후 어드민 페이지 개발 후 사용될 REFETCH 기능
+ queryClient.invalidateQueries(
+ GET_BOOKMARK_LIST(reporterId, '📖 전체', 0),
+ );
+ queryClient.invalidateQueries(
+ GET_BOOKMARK_LIST(reporterId, '👀 읽음', 0),
+ );
+ queryClient.invalidateQueries(
+ GET_BOOKMARK_LIST(reporterId, '🫣 읽지 않음', 0),
+ );
+ fireToast({
+ message: '신고 되었습니다',
+ mode: 'SUCCESS',
+ });
+ router(-1);
+ },
+ onError: (e: CustomAxiosError) => {
+ if (e.response?.data.code === ErrorTypes.PRIVATE_BOOKMARK) {
+ fireToast({
+ message: '이미 신고한 북마크에요',
+ mode: 'DELETE',
+ });
+ }
+ router(-1);
+ },
+ };
+ } else if (reportType === 'COMMENT') {
+ return {
+ onSuccess: () => {
+ fireToast({
+ message: '신고 되었습니다',
+ mode: 'SUCCESS',
+ });
+ router(-1);
+ },
+ onError: (e: CustomAxiosError) => {
+ if (e.response?.data.code === ErrorTypes.PRIVATE_BOOKMARK) {
+ fireToast({
+ message: '이미 신고한 댓글이에요',
+ mode: 'DELETE',
+ });
+ router(-1);
+ }
+ },
+ };
+ } else if (reportType === 'MEMBER') {
+ return {
+ onSuccess: () => {
+ fireToast({
+ message: '신고 되었습니다',
+ mode: 'SUCCESS',
+ });
+ router(-1);
+ },
+ onError: (e: CustomAxiosError) => {
+ if (e.response?.data.code === ErrorTypes.PRIVATE_BOOKMARK) {
+ fireToast({
+ message: '이미 신고한 북마크에요',
+ mode: 'DELETE',
+ });
+ router(-1);
+ }
+ },
+ };
+ } else {
+ throw new Error('reportType이 잘못되었습니다');
+ }
+};
diff --git a/src/store/toast.ts b/src/store/toast.ts
index 4e1f86e4..e2c383e1 100644
--- a/src/store/toast.ts
+++ b/src/store/toast.ts
@@ -9,6 +9,8 @@ export type ToastMessage =
| '앗! 유효하지 않은 주소에요'
| '신고 되었습니다'
| '이미 신고한 북마크에요'
+ | '이미 신고한 유저에요'
+ | '이미 신고한 댓글이에요'
| '앗! 알림 설정 기준일은 1일 이상이어야 해요'
| '차단된 사용자는 팔로우 할 수 없어요'
| '준비 중인 기능이에요'