-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: solution 디테일 페이지 생성 * feat: 댓글 제출 창 기본 형태 구현 * feat: 댓글 제출 api 연결 * feat: CommentList 컴포넌트 생성 * refactor: CommentForm 스타일 파일 분리 * feat: 대댓글 제외한 댓글 리스트 기본 형태 구현 완료 * feat: 대댓글 리스팅 구현 완료 * feat: 로그인 시에만 댓글창이 보이도록 처리 * feat: 대댓글 작성 기능 구현 완료 * feat: comments GET api 연결 * feat: solution detail Route 추가 * chore: 불필요한 주석 삭제 * chore: 임시로 localhost로 설정해두었던 서버 url 수정 * feat: 댓글 제출 시 invalidate 로직 실행하도록 기능 추가 * feat: markdown preview 적용 * feat: md preview sanitize 적용 * feat: 댓글 프로필 이미지 오류 발생시 기본 이미지로 대체 * feat: 삭제된 댓글 표시 구현 * feat: 사용하지 않는 헤더 알림 기능 제거 * refactor: 댓글 관련 컴포넌트 가독성 개선 * feat: 댓글 관련 버튼 스타일링 수정 * refactor: 공통 스타일 적용 * feat: CommentItem 스타일 조정 * refactor: commentUserInfo를 생성날짜 정보를 포함시켜 CommentInfo로 수정 * test: usePathnameAt 테스트 케이스 작성 * test: usePathnameAt test 가독성 개선
- Loading branch information
1 parent
309bf43
commit 862ed9f
Showing
35 changed files
with
2,632 additions
and
160 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { Comment } from '@/types'; | ||
import { develupAPIClient } from './clients/develupClient'; | ||
import { PATH_FORMATTER } from './paths'; | ||
|
||
export const getComments = async (solutionId: number): Promise<Comment[]> => { | ||
const { data } = await develupAPIClient.get<{ data: Comment[] }>( | ||
PATH_FORMATTER.comments(solutionId), | ||
); | ||
|
||
return data; | ||
}; | ||
|
||
interface PostCommentPayload { | ||
content: string; | ||
parentCommentId?: number; | ||
} | ||
|
||
export interface PostCommentParams { | ||
solutionId: number; | ||
body: PostCommentPayload; | ||
} | ||
|
||
export interface PostCommentResponseData { | ||
id: number; | ||
solutionId: number; | ||
parentCommentId: number; | ||
content: string; | ||
member: { | ||
id: number; | ||
email: string; | ||
name: string; | ||
imageUrl: string; | ||
}; | ||
createdAt: string; | ||
} | ||
|
||
export const postComment = async ({ | ||
solutionId, | ||
body, | ||
}: PostCommentParams): Promise<PostCommentResponseData> => { | ||
const { data } = await develupAPIClient.post<{ data: PostCommentResponseData }>( | ||
PATH_FORMATTER.comments(solutionId), | ||
body, | ||
); | ||
|
||
return data; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
frontend/src/components/SolutionDetail/CommentForm/CommentForm.styled.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import styled from 'styled-components'; | ||
import MarkdownEditor from '@uiw/react-md-editor'; | ||
|
||
export const CommentForm = styled.form``; | ||
|
||
export const CommentTextArea = styled.textarea` | ||
width: 100%; | ||
padding: 1.4rem; | ||
border: 1px solid ${({ theme }) => theme.colors.grey400}; | ||
border-radius: 1rem; | ||
`; | ||
|
||
export const CommentButton = styled.button` | ||
margin-top: 1.7rem; | ||
color: ${({ theme }) => theme.colors.primary500}; | ||
${({ theme }) => theme.font.button} | ||
`; | ||
|
||
export const StartFromRight = styled.div` | ||
display: flex; | ||
flex-direction: row-reverse; | ||
`; | ||
|
||
export const MDEditor = styled(MarkdownEditor)` | ||
border: 0.05rem solid ${({ theme }) => theme.colors.grey300}; | ||
border-radius: 1rem; | ||
overflow: overlay; | ||
.cm-content { | ||
font-size: 1.6rem; | ||
padding: 1rem; | ||
} | ||
.md-editor-preview { | ||
font-size: 1.6rem; | ||
} | ||
`; |
45 changes: 45 additions & 0 deletions
45
frontend/src/components/SolutionDetail/CommentForm/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { useState } from 'react'; | ||
import usePostCommentMutation from '@/hooks/usePostCommentMutation'; | ||
import * as S from './CommentForm.styled'; | ||
import { commands } from '@uiw/react-md-editor'; | ||
|
||
interface CommentFormProps { | ||
solutionId: number; | ||
parentCommentId?: number; | ||
} | ||
|
||
export default function CommentForm({ solutionId, parentCommentId }: CommentFormProps) { | ||
const [comment, setComment] = useState(''); | ||
|
||
const resetComment = () => setComment(''); | ||
|
||
const { mutate: postCommentMutation } = usePostCommentMutation(resetComment); | ||
|
||
const onSubmitComment = (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
postCommentMutation({ solutionId, body: { content: comment, parentCommentId } }); | ||
}; | ||
|
||
return ( | ||
<S.CommentForm onSubmit={onSubmitComment}> | ||
<S.MDEditor | ||
height="fit-content" | ||
preview="edit" | ||
visibleDragbar={false} | ||
onChange={(v?: string) => setComment(v || '')} | ||
value={comment} | ||
commands={[ | ||
commands.bold, | ||
commands.italic, | ||
commands.strikethrough, | ||
commands.codeBlock, | ||
commands.image, | ||
]} | ||
extraCommands={[commands.codeEdit, commands.codeLive]} | ||
/> | ||
<S.StartFromRight> | ||
<S.CommentButton>제출</S.CommentButton> | ||
</S.StartFromRight> | ||
</S.CommentForm> | ||
); | ||
} |
28 changes: 28 additions & 0 deletions
28
frontend/src/components/SolutionDetail/CommentList/CommentItem/CommentInfo.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { UserInfo } from '@/types/user'; | ||
import * as S from '../CommentList.styled'; | ||
import type { SyntheticEvent } from 'react'; | ||
import DefaultUserIcon from '@/assets/images/default-user.png'; | ||
import { formatDateString } from '@/utils/formatDateString'; | ||
|
||
interface CommentInfoProps { | ||
member: UserInfo; | ||
createdAt: string; | ||
} | ||
|
||
export default function CommentInfo({ member, createdAt }: CommentInfoProps) { | ||
const { imageUrl, name } = member; | ||
|
||
const handleImageError = ({ target }: SyntheticEvent<HTMLImageElement>) => { | ||
if (target instanceof HTMLImageElement) { | ||
target.src = DefaultUserIcon; | ||
} | ||
}; | ||
|
||
return ( | ||
<S.CommentInfoContainer> | ||
<S.UserProfileImg src={imageUrl} onError={handleImageError} /> | ||
<S.UserName>{name}</S.UserName> | ||
<S.CommentCreatedAt>{formatDateString(createdAt)}</S.CommentCreatedAt> | ||
</S.CommentInfoContainer> | ||
); | ||
} |
18 changes: 18 additions & 0 deletions
18
frontend/src/components/SolutionDetail/CommentList/CommentItem/CommentReplyItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import * as S from '../CommentList.styled'; | ||
import type { CommentReply } from '@/types'; | ||
import CommentInfo from './CommentInfo'; | ||
|
||
interface CommentReplyItemProps { | ||
commentReply: CommentReply; | ||
} | ||
|
||
export default function CommentReplyItem({ commentReply }: CommentReplyItemProps) { | ||
const { member, content, createdAt } = commentReply; | ||
|
||
return ( | ||
<S.CommentReplyItemContainer> | ||
<CommentInfo member={member} createdAt={createdAt} /> | ||
<S.CommentContent source={content} /> | ||
</S.CommentReplyItemContainer> | ||
); | ||
} |
17 changes: 17 additions & 0 deletions
17
frontend/src/components/SolutionDetail/CommentList/CommentItem/CommentReplyList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import * as S from '../CommentList.styled'; | ||
import type { CommentReply } from '@/types'; | ||
import CommentReplyItem from './CommentReplyItem'; | ||
|
||
interface CommentReplyListProps { | ||
commentReplies: CommentReply[]; | ||
} | ||
|
||
export default function CommentReplyList({ commentReplies }: CommentReplyListProps) { | ||
return ( | ||
<S.CommentReplyListContainer> | ||
{commentReplies.map((reply) => ( | ||
<CommentReplyItem key={reply.id} commentReply={reply} /> | ||
))} | ||
</S.CommentReplyListContainer> | ||
); | ||
} |
39 changes: 39 additions & 0 deletions
39
frontend/src/components/SolutionDetail/CommentList/CommentItem/CommentReplySection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { useState } from 'react'; | ||
import CommentForm from '../../CommentForm'; | ||
import * as S from '../CommentList.styled'; | ||
import CommentReplyList from './CommentReplyList'; | ||
import type { Comment } from '@/types'; | ||
|
||
interface CommentReplySectionProps { | ||
parentComment: Comment; | ||
isLoggedIn: boolean; | ||
} | ||
|
||
export default function CommentReplySection({ | ||
parentComment, | ||
isLoggedIn, | ||
}: CommentReplySectionProps) { | ||
const { isDeleted: isParentDeleted, solutionId, id: parentId, replies } = parentComment; | ||
|
||
const [isReplyFormOpen, setIsReplyFormOpen] = useState(false); | ||
|
||
const toggleReplyFormOpen = () => { | ||
setIsReplyFormOpen((prevState) => !prevState); | ||
}; | ||
|
||
return ( | ||
<S.CommentReplySectionContainer> | ||
{isLoggedIn && !isParentDeleted && ( | ||
<> | ||
<S.ReplyWriteButton onClick={toggleReplyFormOpen}>답글</S.ReplyWriteButton> | ||
{isReplyFormOpen && ( | ||
<S.CommentReplyFormWrapper> | ||
<CommentForm solutionId={solutionId} parentCommentId={parentId} /> | ||
</S.CommentReplyFormWrapper> | ||
)} | ||
</> | ||
)} | ||
<CommentReplyList commentReplies={replies} /> | ||
</S.CommentReplySectionContainer> | ||
); | ||
} |
31 changes: 31 additions & 0 deletions
31
frontend/src/components/SolutionDetail/CommentList/CommentItem/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import type { Comment } from '@/types'; | ||
import * as S from '../CommentList.styled'; | ||
import useUserInfo from '@/hooks/useUserInfo'; | ||
import CommentInfo from './CommentInfo'; | ||
import CommentReplySection from './CommentReplySection'; | ||
|
||
interface CommentItemProps { | ||
comment: Comment; | ||
} | ||
|
||
export default function CommentItem({ comment }: CommentItemProps) { | ||
const { content, member, createdAt, isDeleted } = comment; | ||
|
||
const { data: userInfo } = useUserInfo(); | ||
|
||
return ( | ||
<S.CommentItemContainer> | ||
<S.CommentContentWrapper> | ||
{isDeleted ? ( | ||
<S.DeletedComment>삭제된 댓글입니다.</S.DeletedComment> | ||
) : ( | ||
<> | ||
<CommentInfo member={member} createdAt={createdAt} /> | ||
<S.CommentContent source={content} /> | ||
</> | ||
)} | ||
</S.CommentContentWrapper> | ||
<CommentReplySection parentComment={comment} isLoggedIn={Boolean(userInfo)} /> | ||
</S.CommentItemContainer> | ||
); | ||
} |
Oops, something went wrong.