-
Notifications
You must be signed in to change notification settings - Fork 6
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
[FE] Feat/#597 댓글 기능 구현 #598
Changes from 13 commits
c3ca5d7
8d734e8
eaca140
9450d2c
3148dc0
42c9691
be2a5c0
d29a1e5
0f2589d
347cbc0
cdfaf48
2a931db
994e904
3340058
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import SingleComment from './SingleComment'; | ||
|
||
function ReplyComment({ | ||
commentList, | ||
pageTotalCommentList, | ||
depth, | ||
refetch, | ||
}: any) { | ||
if (depth === 2) return null; | ||
return ( | ||
<> | ||
{commentList.length > 0 && | ||
commentList.map((comment: string) => ( | ||
<> | ||
<SingleComment | ||
comment={comment} | ||
commentList={commentList} | ||
totalList={pageTotalCommentList} | ||
depth={depth} | ||
refetch={refetch} | ||
/> | ||
</> | ||
))} | ||
</> | ||
); | ||
} | ||
|
||
export default ReplyComment; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
import { useState } from 'react'; | ||
import styled from 'styled-components'; | ||
|
||
import { deleteApi } from '../../../apis/deleteApi'; | ||
import { postApi } from '../../../apis/postApi'; | ||
import { putApi } from '../../../apis/putApi'; | ||
import useToast from '../../../hooks/useToast'; | ||
import Text from '../Text'; | ||
import ReplyComment from './ReplyComment'; | ||
|
||
const userToken = localStorage?.getItem('userToken'); | ||
const localStorageUser = localStorage?.getItem('user'); | ||
const user = JSON.parse(localStorageUser || '{}'); | ||
// { 댓글, 댓글목록, 전체목록, depth = 0 } | ||
function SingleComment({ | ||
pinId, | ||
comment, | ||
commentList, | ||
totalList, | ||
depth = 0, | ||
refetch, | ||
}: any) { | ||
const [replyOpen, setReplyOpen] = useState(false); | ||
const [seeMore, setSeeMore] = useState(false); | ||
const [newComment, setNewComment] = useState<string>(''); | ||
const { showToast } = useToast(); | ||
const [isEditing, setIsEditing] = useState(false); | ||
const [content, setContent] = useState(comment.content); | ||
const params = new URLSearchParams(window.location.search); | ||
const pinDetail = params.get('pinDetail'); | ||
|
||
const toggleReplyOpen = () => { | ||
setReplyOpen((prev) => !prev); | ||
}; | ||
const toggleSeeMore = () => { | ||
setSeeMore((prev) => !prev); | ||
}; | ||
|
||
const replyList = commentList?.filter( | ||
(curComment: any) => curComment.parentPinCommentId === comment.id, | ||
); | ||
const replyCount = replyList.length; | ||
|
||
const onClickCommentBtn = async (e: React.MouseEvent<HTMLButtonElement>) => { | ||
e.stopPropagation(); | ||
|
||
try { | ||
// 댓글 추가 | ||
// comment 값이랑 추가 정보 body에 담아서 보내기 | ||
|
||
await postApi( | ||
`/pins/comments`, | ||
{ | ||
pinId: Number(pinDetail), | ||
content: newComment, | ||
parentPinCommentId: comment.id, | ||
Comment on lines
+57
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. parentPinCommentId로 그냥 댓글인지 대댓글인지 구분하는 것이군요! 굿~! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 |
||
}, | ||
'application/json', | ||
); | ||
await refetch(Number(pinDetail)); | ||
setReplyOpen(false); | ||
setNewComment(''); | ||
showToast('info', '댓글이 추가되었습니다.'); | ||
} catch (e) { | ||
showToast('error', '댓글을 다시 작성해주세요'); | ||
} | ||
}; | ||
|
||
const onClickDeleteBtn = async (e: React.MouseEvent<HTMLDivElement>) => { | ||
e.stopPropagation(); | ||
try { | ||
// 댓글 삭제 | ||
await deleteApi(`/pins/comments/${comment.id}`); | ||
await refetch(Number(pinDetail)); | ||
showToast('info', '댓글이 삭제되었습니다.'); | ||
} catch (e) { | ||
console.error(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요기 콘솔 발견 ! 😏 |
||
showToast('error', '댓글을 다시 작성해주세요'); | ||
} | ||
}; | ||
|
||
const onContentChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setContent(e.target.value); | ||
}; | ||
|
||
const onClickModifyConfirmBtn = async ( | ||
e: React.MouseEvent<HTMLButtonElement>, | ||
) => { | ||
e.stopPropagation(); | ||
try { | ||
// 댓글 수정 | ||
await putApi(`/pins/comments/${comment.id}`, { | ||
content, | ||
}); | ||
await refetch(Number(pinDetail)); | ||
setIsEditing(false); | ||
showToast('info', '댓글이 수정되었습니다.'); | ||
} catch (e) { | ||
console.error(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요기도 콘솔 발견 ! 👮♀️ |
||
showToast('error', '댓글을 다시 작성해주세요'); | ||
} | ||
}; | ||
|
||
const onClickModifyBtn = () => { | ||
setIsEditing((prev) => !prev); | ||
}; | ||
|
||
return ( | ||
<CommentWrapper depth={depth} key={comment.id}> | ||
<Flex> | ||
<ProfileImage | ||
src={comment.creatorImageUrl} | ||
width="40px" | ||
height="40px" | ||
/> | ||
<CommentInfo> | ||
<Writer> | ||
<Text $fontSize="default" $fontWeight="bold" color="black"> | ||
@{comment.creator} | ||
</Text> | ||
</Writer> | ||
<Content> | ||
{isEditing ? ( | ||
<Flex> | ||
<input value={content} onChange={onContentChange} /> | ||
<button type="button" onClick={onClickModifyConfirmBtn}> | ||
확인 | ||
</button> | ||
</Flex> | ||
) : ( | ||
<Text $fontSize="large" $fontWeight="normal" color="darkGray"> | ||
{comment.content} | ||
</Text> | ||
)} | ||
</Content> | ||
<div> | ||
{depth === 1 ? null : ( | ||
<button type="button" onClick={toggleReplyOpen}> | ||
답글남기기 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뭔가 '답글 쓰기'나 '답글 작성' 같이 짝수의 글자면 좋을 것 같습니다! 아이크가 마음에 드는걸로 골라주십시오 ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅇㅋ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅋㅋㅋㅋ ㅇㅋ |
||
</button> | ||
)} | ||
{replyOpen && ( | ||
<div | ||
style={{ display: 'flex', marginBottom: '20px', gap: '12px' }} | ||
> | ||
<ProfileImage | ||
src={user?.imageUrl || ''} | ||
width="40px" | ||
height="40px" | ||
/> | ||
<div style={{ width: '100%' }}> | ||
<input | ||
style={{ | ||
width: '100%', | ||
borderTop: 'none', | ||
borderLeft: 'none', | ||
borderRight: 'none', | ||
fontSize: '16px', | ||
}} | ||
value={newComment} | ||
onChange={(e: any) => setNewComment(e.target.value)} | ||
placeholder="댓글 추가" | ||
// onClick={toggleReplyOpen} | ||
/> | ||
<button | ||
style={{ | ||
marginTop: '12px', | ||
float: 'right', | ||
width: '40px', | ||
fontSize: '12px', | ||
}} | ||
type="button" | ||
onClick={onClickCommentBtn} | ||
> | ||
등록 | ||
</button> | ||
|
||
{/* {replyOpen && ( | ||
<form> | ||
<input /> | ||
</form> | ||
)} */} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요기 지워도 되는 부분인 걸까요?!!! |
||
</div> | ||
</div> | ||
)} | ||
</div> | ||
{replyCount > 0 && ( | ||
<MoreReplyButton onClick={toggleSeeMore}> | ||
{seeMore ? '\u25B2' : '\u25BC'} 답글 {replyCount}개 | ||
</MoreReplyButton> | ||
)} | ||
</CommentInfo> | ||
{comment.canChange && ( | ||
<Flex> | ||
<Text | ||
$fontSize="small" | ||
color="gray" | ||
$fontWeight="bold" | ||
onClick={onClickModifyBtn} | ||
> | ||
수정 | ||
</Text> | ||
<Text | ||
$fontSize="small" | ||
color="primary" | ||
$fontWeight="bold" | ||
onClick={onClickDeleteBtn} | ||
> | ||
삭제 | ||
</Text> | ||
</Flex> | ||
)} | ||
</Flex> | ||
|
||
{seeMore && ( | ||
<ReplyComment | ||
commentList={replyList ?? []} | ||
parentId={comment.id} | ||
pageTotalCommentList={totalList} | ||
depth={depth + 1} | ||
refetch={refetch} | ||
/> | ||
)} | ||
</CommentWrapper> | ||
); | ||
} | ||
|
||
export default SingleComment; | ||
|
||
const CommentWrapper = styled.li<{ depth: number }>` | ||
width: 100%; | ||
margin-left: ${(props) => props.depth * 20}px; | ||
list-style: none; | ||
`; | ||
|
||
const Flex = styled.div` | ||
display: flex; | ||
gap: 12px; | ||
margin-bottom: 24px; | ||
`; | ||
|
||
export const ProfileImage = styled.img` | ||
display: block; | ||
|
||
border-radius: 50%; | ||
`; | ||
|
||
const CommentInfo = styled.div``; | ||
|
||
const Writer = styled.div` | ||
white-space: nowrap; | ||
`; | ||
|
||
const Content = styled.div` | ||
overflow-wrap: anywhere; | ||
`; | ||
|
||
const MoreReplyButton = styled.button` | ||
padding: 2px; | ||
border: none; | ||
background: none; | ||
border-radius: 8px; | ||
color: black; | ||
font-weight: 600; | ||
&:hover { | ||
background: gray; | ||
cursor: pointer; | ||
} | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아직 서버 merge가 안되서 commentList 값을 console로 찍어보고 싶었으나 찍을 수 없었습니다 흑흑
curComment이 객체 형식으로 추정 되는데 타입을 만들어 적용하면 더 좋을 것 같습니닷 !!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아직 명세가 확정이 안되서 임시로 해뒀는데 반영할께여~