Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

게시글, 댓글, 닉네임 신고 API fetch 함수 구현 HTML5 해결 #296

Merged
merged 26 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
58a1e31
feat: (#251) html5으로 인식되도록 코드 추가
chsua Aug 8, 2023
8b3a912
feat: (#167) 신고하는 api 생성
chsua Aug 8, 2023
bf6bf6a
feat: (#167) 신고용 모달 생성 및 기존 코멘트모달 리팩토링
chsua Aug 8, 2023
32cfca9
refactor: 공통으로 사용되는 메뉴 컴포넌트 이름 수정 및 common으로 이동
chsua Aug 8, 2023
4d40a19
refactor: 신고모달 생성에 따른 기존 댓글신고, 댓글 작성자 신고 모달 대체
chsua Aug 8, 2023
eb73008
refactor: postMenu를 처음 commentMenu으로 되돌리기
chsua Aug 8, 2023
617cb22
feat: 공용으로 사용할 수 있는 메뉴 컴포넌트 제작
chsua Aug 8, 2023
099f073
test: 공용으로 사용할 수 있는 메뉴 컴포넌트 테스트
chsua Aug 8, 2023
3b1ed66
feat: (#167) 게시물 신고 ui 수정 및 api 연동
chsua Aug 8, 2023
a487115
test: (#167) 신고 기능 추가에 따라 컴포넌트가 수정되어 테스트 코드도 수정
chsua Aug 8, 2023
c379888
fix: then이 catch보다 늦게 있어 오류 발생해도 then이 실행되는 오류 수정
chsua Aug 8, 2023
a5fae3f
fix: (#167) 모달창에서 버튼을 클릭해도 모달이 닫히지 않는 오류 수정
chsua Aug 8, 2023
28ec3b2
feat: 게시물 삭제 조건 변경 및 예외처리 시 api 통신하지 않도록 수정
chsua Aug 8, 2023
3d2da36
test: (#167) 게시물 삭제/신고 msw 생성 및 테스트
chsua Aug 8, 2023
f1145fe
refactor: alert창 오타 수정
chsua Aug 8, 2023
e05e810
test: 댓글 가지고 오는 api msw 중지
chsua Aug 8, 2023
1ee2b3e
fix: (#167) 모달창에서 버튼을 클릭해도 모달이 닫히지 않는 오류 수정
chsua Aug 8, 2023
25ef888
feat: (#167) 댓글 신고/댓글 작성자 닉네임 신고기능 생성
chsua Aug 8, 2023
e29520a
feat: (#167) 게시글 액션 타입 제한 적용
chsua Aug 8, 2023
09a8d60
test: (#167) 게시글 메뉴 컴포넌트 타입 제한에 따른 테스트 변경
chsua Aug 8, 2023
a984e28
test: (#167) api 테스트 통과를 위해 댓글 msw 복구
chsua Aug 8, 2023
b4ec217
refactor: DeleteModal과 동일한 CommentDeleteModal 삭제하기
chsua Aug 8, 2023
2b66396
refactor: (#167) 불필요한 타입 정의 정리
chsua Aug 9, 2023
a962913
refactor: (#167) 스타일 컨벤션 지키기
chsua Aug 9, 2023
c63314b
refactor: (#167) 삭제 모달의 삭제 대상 타입 정의 및 상수화
chsua Aug 9, 2023
c62d12e
fix: (#167) 댓글 신고 시 request 데이터 타입 오지정 수정
chsua Aug 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!DOCTYPE html>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍👍

<html lang="ko">
<head>
<meta charset="UTF-8" />
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/api/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@utils/fetch';

const BASE_URL = process.env.VOTOGETHER_BASE_URL;
const MOCK_URL = process.env.VOTOGETHER_MOCKING_URL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍


export const transformPostResponse = (post: PostInfoResponse): PostInfo => {
return {
Expand Down Expand Up @@ -68,7 +69,7 @@ export const editPost = async (postId: number, updatedPost: FormData) => {
};

export const removePost = async (postId: number) => {
return await deleteFetch(`${BASE_URL}/posts/${postId}`);
return await deleteFetch(`${MOCK_URL}/posts/${postId}`);
};

export const setEarlyClosePost = async (postId: number) => {
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/api/report.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ReportRequest } from '@type/report';

import { postFetch } from '@utils/fetch';

const BASE_URL = process.env.VOTOGETHER_MOCKING_URL;

export const reportContent = async (reportData: ReportRequest) => {
return await postFetch(`${BASE_URL}/report`, reportData);
};
28 changes: 28 additions & 0 deletions frontend/src/components/ReportModal/ReportModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from '@storybook/react';

import ReportModal from '.';

const meta: Meta<typeof ReportModal> = {
component: ReportModal,
};

export default meta;
type Story = StoryObj<typeof ReportModal>;

export const Nickname: Story = {
render: () => (
<ReportModal reportType="NICKNAME" handleCancelClick={() => {}} handleReportClick={() => {}} />
),
};

export const Comment: Story = {
render: () => (
<ReportModal reportType="COMMENT" handleCancelClick={() => {}} handleReportClick={() => {}} />
),
};

export const Post: Story = {
render: () => (
<ReportModal reportType="POST" handleCancelClick={() => {}} handleReportClick={() => {}} />
),
};
16 changes: 16 additions & 0 deletions frontend/src/components/ReportModal/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ReportInfo, ReportType } from '@type/report';

export const REPORT_MESSAGE = {
BEHAVIOR: '부적절한 언행/혐오/차별적 표현이 포함되어있습니다.',
SPAMMING: '도배성 내용이 포함되어있습니다.',
ADVERTISING: '광고성 내용이 포함되어있습니다.',
SENSUALITY: '음란성 내용이 포함되어 있습니다.',
SPECIFIC_PERSON: '특정인이 거론되어있습니다.',
PRIVACY: '개인정보가 포함되어있습니다.',
};

export const REPORT_TYPE: Record<ReportType, ReportInfo> = {
POST: { name: '게시글 신고', reportMessageList: REPORT_MESSAGE },
COMMENT: { name: '댓글 신고', reportMessageList: REPORT_MESSAGE },
NICKNAME: { name: '닉네임 신고', reportMessageList: REPORT_MESSAGE },
};
50 changes: 50 additions & 0 deletions frontend/src/components/ReportModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ReportType } from '@type/report';

import { useSelect } from '@hooks/useSelect';

import Select from '@components/common/Select';
import TwoButtonModal from '@components/common/TwoButtonModal';

import { REPORT_TYPE } from './constants';

interface UserReportModalProps {
reportType: ReportType;
handleCancelClick: () => void;
handleReportClick: (reason: string) => void;
}

export default function ReportModal({
reportType,
handleCancelClick,
handleReportClick,
}: UserReportModalProps) {
const { name, reportMessageList } = REPORT_TYPE[reportType];
const defaultReportMessage = Object.keys(reportMessageList)[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reportMessageList.BEHAVIOR 대신 keys 함수를 사용하시는 이유가 궁금합니다. '첫번째 인덱스의 값을 가져오겠다' 라는 의미를 위해서인지..?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 맞습니다. 현재 해당 모달은 key에 string으로 필수적으로 들어가는 key값이 없습니다.
때문에 기본값으로 설정할 key이 없어 index의 첫번째 값을 기본값으로 두었습니다.

const { selectedOption, handleOptionChange } = useSelect(defaultReportMessage);

const handlePrimaryButtonClick = () => {
handleReportClick(selectedOption);
handleCancelClick();
};

return (
<TwoButtonModal
title={name}
primaryButton={{
text: '신고',
handleClick: handlePrimaryButtonClick,
}}
secondaryButton={{
text: '취소',
handleClick: handleCancelClick,
}}
>
<Select
aria-label={`${name} 방법 선택`}
optionList={reportMessageList}
handleOptionChange={handleOptionChange}
selectedOption={reportMessageList[selectedOption]}
/>
</TwoButtonModal>
);
}

This file was deleted.

29 changes: 0 additions & 29 deletions frontend/src/components/comment/CommentDeleteModal/index.tsx

This file was deleted.

39 changes: 33 additions & 6 deletions frontend/src/components/comment/CommentList/CommentItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { useState } from 'react';
import { useParams } from 'react-router-dom';

import { Comment } from '@type/comment';
import { ReportRequest } from '@type/report';

import { useDeleteComment } from '@hooks/query/comment/useDeleteComment';
import { useToggle } from '@hooks/useToggle';

import CommentDeleteModal from '@components/comment/CommentDeleteModal';
import { reportContent } from '@api/report';

import CommentTextForm from '@components/comment/CommentList/CommentTextForm';
import CommentReportModal from '@components/report/CommentReportModal';
import UserReportModal from '@components/report/UserReportModal';
import DeleteModal from '@components/common/DeleteModal';
import ReportModal from '@components/ReportModal';

import ellipsis from '@assets/ellipsis-horizontal.svg';

Expand Down Expand Up @@ -39,6 +41,22 @@ export default function CommentItem({ comment, userType }: CommentItemProps) {
setAction(menu);
};

const handleCommentReportClick = async (reason: string) => {
const reportData = { type: 'POST', id: postId, reason } as ReportRequest;

await reportContent(reportData)
.then(res => alert('댓글을 신고했습니다.'))
.catch(error => alert('댓글 신고가 실패했습니다.'));
};

const handleNicknameReportClick = async (reason: string) => {
const reportData = { type: 'NICKNAME', id: member.id, reason } as ReportRequest;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

단언 대신 선언 어떠신가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 선언을 하지 못한다고 생각했는데 생각해보니 가능하네요! 감사합니다!


await reportContent(reportData)
.then(res => alert('작성자 닉네임을 신고했습니다.'))
.catch(error => alert('작성자 닉네임 신고가 실패했습니다.'));
};

const handleCancelClick = () => {
setAction(null);
};
Expand Down Expand Up @@ -84,16 +102,25 @@ export default function CommentItem({ comment, userType }: CommentItemProps) {
<S.Description>{content}</S.Description>
)}
{action === COMMENT_ACTION.DELETE && (
<CommentDeleteModal
<DeleteModal
target="댓글"
handleCancelClick={handleCancelClick}
handleDeleteClick={handleDeleteClick}
/>
)}
{action === COMMENT_ACTION.USER_REPORT && (
<UserReportModal handleCancelClick={handleCancelClick} />
<ReportModal
reportType="NICKNAME"
handleReportClick={handleNicknameReportClick}
handleCancelClick={handleCancelClick}
/>
)}
{action === COMMENT_ACTION.COMMENT_REPORT && (
<CommentReportModal handleCancelClick={handleCancelClick} />
<ReportModal
reportType="COMMENT"
handleReportClick={handleCommentReportClick}
handleCancelClick={handleCancelClick}
/>
)}
</S.Container>
);
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/components/common/DeleteModal/DeleteModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/react';

import DeleteModal from '.';

const meta: Meta<typeof DeleteModal> = {
component: DeleteModal,
};

export default meta;
type Story = StoryObj<typeof DeleteModal>;

export const Post: Story = {
render: () => (
<DeleteModal target="게시물" handleCancelClick={() => {}} handleDeleteClick={() => {}} />
),
};

export const User: Story = {
render: () => (
<DeleteModal target="계정" handleCancelClick={() => {}} handleDeleteClick={() => {}} />
),
};
36 changes: 36 additions & 0 deletions frontend/src/components/common/DeleteModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import TwoButtonModal from '../../common/TwoButtonModal';

import * as S from './style';

interface DeleteModalProps {
target: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string 대신 타입의 유니온으로 구체해봐도 좋겠네요!

handleCancelClick: () => void;
handleDeleteClick: () => void;
}

export default function DeleteModal({
target,
handleCancelClick,
handleDeleteClick,
}: DeleteModalProps) {
const handlePrimaryButtonClick = () => {
handleDeleteClick();
handleCancelClick();
};

return (
<TwoButtonModal
title={`${target} 삭제하기`}
primaryButton={{
text: '삭제',
handleClick: handlePrimaryButtonClick,
}}
secondaryButton={{
text: '취소',
handleClick: handleCancelClick,
}}
>
<S.Description>{`${target}을(를) 삭제하시겠습니까?\n${target}은(는) 삭제되고 취소할 수 없습니다.`}</S.Description>
</TwoButtonModal>
);
}
21 changes: 21 additions & 0 deletions frontend/src/components/common/PostMenu/PostMenu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Meta, StoryObj } from '@storybook/react';

import { PostMenuItem } from '@type/menu';

import PostMenu from '.';

const meta: Meta<typeof PostMenu> = {
component: PostMenu,
};

export default meta;
type Story = StoryObj<typeof PostMenu>;

const menuList: PostMenuItem[] = [
{ color: 'black', content: '닉네임 신고', action: 'NICKNAME_REPORT' },
{ color: 'black', content: '게시글 신고', action: 'POST_REPORT' },
];

export const Default: Story = {
render: () => <PostMenu menuList={menuList} handleMenuClick={() => {}} />,
};
30 changes: 30 additions & 0 deletions frontend/src/components/common/PostMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MouseEvent } from 'react';

import { PostAction, PostMenuItem } from '@type/menu';

import * as S from './style';

interface PostMenuProps {
menuList: PostMenuItem[];
handleMenuClick: (menu: PostAction) => void;
}

export default function PostMenu({ menuList, handleMenuClick }: PostMenuProps) {
return (
<S.Container>
{menuList.map(({ content, color, action }) => (
<S.Menu
key={content}
type="button"
$color={color}
onClick={(event: MouseEvent) => {
event.stopPropagation();
handleMenuClick(action);
}}
>
{content}
</S.Menu>
))}
</S.Container>
);
}
Loading