| 
1 |  | -import {faker} from "@faker-js/faker/locale/ko";  | 
2 | 1 | import {dateToKorean} from "../../common/util/date.tsx";  | 
3 |  | -import {MdOutlineAddComment, MdOutlineComment} from "react-icons/md";  | 
 | 2 | +import {MdOutlineAddComment, MdOutlineComment, MdOutlinePerson} from "react-icons/md";  | 
4 | 3 | import {ChangeEvent, useState} from "react";  | 
5 | 4 | import CommentTextField from "./CommentTextField.tsx";  | 
 | 5 | +import {CommentType} from "../../common/types/comment.tsx";  | 
 | 6 | +import {LoadMoreButton} from "../common/FillButton.tsx";  | 
 | 7 | +import {useSelector} from "react-redux";  | 
 | 8 | +import {RootState} from "../../store.tsx";  | 
 | 9 | +import {TextButton} from "../common/TextButton.tsx";  | 
 | 10 | +import {deleteComment} from "../../common/apis/comment.tsx";  | 
 | 11 | +import {useNavigate} from "react-router-dom";  | 
6 | 12 | 
 
  | 
7 |  | -function CommentCard({depth = 0}: { depth?: number }) {  | 
 | 13 | +function CommentCard({comment, handleLoadMoreSubComment, handleCommentSubmit, pageSize, depth = 0}: {  | 
 | 14 | +    comment: CommentType,  | 
 | 15 | +    handleLoadMoreSubComment: (page: number, parentId: string) => void,  | 
 | 16 | +    handleCommentSubmit: (commentId: string | null, content: string, taggedUsername: string | null, originalComment: CommentType | null) => void,  | 
 | 17 | +    pageSize: number,  | 
 | 18 | +    depth?: number,  | 
 | 19 | +}) {  | 
 | 20 | + | 
 | 21 | +    const navigate = useNavigate();  | 
 | 22 | +    const loginState = useSelector((state: RootState) => state.loginSlice);  | 
8 | 23 | 
 
  | 
9 |  | -    const [openComment, setOpenComment] = useState(depth !== 0);  | 
10 | 24 |     const [commentInput, setCommentInput] = useState("");  | 
 | 25 | +    const [editCommentInput, setEditCommentInput] = useState("");  | 
11 | 26 |     const [showTextField, setShowTextField] = useState(false);  | 
 | 27 | +    const [showEditTextField, setShowEditTextField] = useState(false);  | 
 | 28 | +    const [page, setPage] = useState(0);  | 
12 | 29 | 
 
  | 
13 | 30 |     const handleCommentInput = (e: ChangeEvent<HTMLTextAreaElement>) => {  | 
14 | 31 |         setCommentInput(e.currentTarget.value);  | 
15 | 32 |     }  | 
16 | 33 |     const handleShowTextField = () => {  | 
17 | 34 |         setShowTextField(prev => !prev);  | 
18 | 35 |     }  | 
19 |  | -    const handleSubmit = (commentId: string | undefined) => {  | 
20 |  | -        confirm("댓글을 등록하시겠습니까?");  | 
21 |  | -        alert("등록되었습니다.");  | 
22 |  | -        console.log(commentId);  | 
 | 36 | +    const handleOpenTextField = () => {  | 
 | 37 | +        setCommentInput("");  | 
 | 38 | +        setShowTextField(true);  | 
 | 39 | +    }  | 
 | 40 | + | 
 | 41 | +    const handleShowEdit = () => {  | 
 | 42 | +        setEditCommentInput(comment.content);  | 
 | 43 | +        setShowEditTextField(prev => !prev);  | 
 | 44 | +    }  | 
 | 45 | +    const handleEditCommentInput = (e: ChangeEvent<HTMLTextAreaElement>) => {  | 
 | 46 | +        setEditCommentInput(e.currentTarget.value);  | 
 | 47 | +    }  | 
 | 48 | + | 
 | 49 | +    const handleDeleteComment = () => {  | 
 | 50 | +        if (!confirm("댓글을 삭제하시겠습니까?")) {  | 
 | 51 | +            return;  | 
 | 52 | +        }  | 
 | 53 | + | 
 | 54 | +        deleteComment(comment.id)  | 
 | 55 | +            .then(() => {  | 
 | 56 | +                alert("삭제되었습니다.");  | 
 | 57 | +                navigate(0);  | 
 | 58 | +            })  | 
 | 59 | +            .catch((error) => alert(error.response.data.message));  | 
23 | 60 |     }  | 
24 | 61 | 
 
  | 
 | 62 | +    const taggedUsername = depth > 0 ? comment.member.username : null;  | 
 | 63 | + | 
 | 64 | + | 
25 | 65 |     return (  | 
26 | 66 |         <div className={`flex ${depth === 0 && "pb-4"}`}>  | 
27 | 67 |             {depth !== 0 && <div className="w-8"/>}  | 
28 | 68 |             <div className="flex-1 flex flex-col justify-center mt-4 text-sm">  | 
29 | 69 |                 <div className="flex flex-col gap-y-2 border rounded-2xl border-gray-300 px-4 pt-4 pb-2">  | 
30 | 70 |                     <div className="flex items-center gap-x-2">  | 
31 |  | -                        <img className="size-6 rounded-full hover:cursor-pointer"  | 
32 |  | -                             src={faker.image.avatar()} alt="user_image"/>  | 
 | 71 | +                        {comment.member.profileUrl  | 
 | 72 | +                            ? <img className="size-6 rounded-full hover:cursor-pointer"  | 
 | 73 | +                                   src={comment.member.profileUrl} alt="user_image"/>  | 
 | 74 | +                            : <MdOutlinePerson className="size-5 text-gray-600"/>}  | 
33 | 75 |                         <p className="font-bold">  | 
34 |  | -                            {faker.animal.cow()}  | 
 | 76 | +                            {comment.member.username}  | 
35 | 77 |                         </p>  | 
36 | 78 |                         <p className="text-gray-400 text-sm">  | 
37 |  | -                            {dateToKorean(faker.date.anytime())}  | 
 | 79 | +                            {dateToKorean(comment.createdAt)}  | 
38 | 80 |                         </p>  | 
39 | 81 |                     </div>  | 
40 |  | -                    <p className="mt-4 text-gray-900">  | 
41 |  | -                        {faker.lorem.paragraph()}  | 
 | 82 | +                    <p className="mt-4 text-gray-900 flex items-center gap-x-2">  | 
 | 83 | +                        {!showEditTextField &&  | 
 | 84 | +                            comment.taggedUsername &&  | 
 | 85 | +                            <span className="text-lime-700">  | 
 | 86 | +                            @{comment.taggedUsername}  | 
 | 87 | +                        </span>}  | 
 | 88 | +                        {showEditTextField  | 
 | 89 | +                            ? <CommentTextField  | 
 | 90 | +                                value={editCommentInput}  | 
 | 91 | +                                onChange={handleEditCommentInput}  | 
 | 92 | +                                handleSubmit={handleCommentSubmit}  | 
 | 93 | +                                taggedUsername={comment.taggedUsername}  | 
 | 94 | +                                originalComment={comment}  | 
 | 95 | +                                handleShowTextField={handleShowEdit}/>  | 
 | 96 | +                            : <div>{comment.content}</div>}  | 
42 | 97 |                     </p>  | 
43 | 98 |                     <div className="flex justify-between items-center">  | 
44 | 99 |                         {depth === 0 &&  | 
45 |  | -                        <button  | 
46 |  | -                            className="w-fit py-2 flex justify-center items-center gap-x-2 text-gray-600 hover:cursor-pointer rounded-md hover:brightness-120"  | 
47 |  | -                            onClick={() => setOpenComment(prev => !prev)}>  | 
48 |  | -                            <MdOutlineComment className="text-gray-600 size-4"/>  | 
49 |  | -                            {!openComment ? "답글 (10)" : "답글 닫기"}  | 
50 |  | -                        </button>}  | 
 | 100 | +                            <button  | 
 | 101 | +                                className="w-fit py-2 flex justify-center items-center gap-x-2 text-gray-600 hover:cursor-pointer rounded-md hover:brightness-120"  | 
 | 102 | +                                onClick={() => {  | 
 | 103 | +                                    handleLoadMoreSubComment(page, comment.id);  | 
 | 104 | +                                    setPage(page + 1);  | 
 | 105 | +                                }}>  | 
 | 106 | +                                <MdOutlineComment className="text-gray-600 size-4"/>  | 
 | 107 | +                                답글 ({comment.replyCount})  | 
 | 108 | +                            </button>}  | 
51 | 109 |                         <div/>  | 
52 |  | -                        <button  | 
53 |  | -                            className="w-fit py-2 flex justify-center items-center gap-x-2 text-gray-600 hover:cursor-pointer rounded-md hover:brightness-120"  | 
54 |  | -                            onClick={() => setShowTextField(true)}>  | 
55 |  | -                            <MdOutlineAddComment className="text-gray-600 size-4"/>  | 
56 |  | -                            댓글 작성하기  | 
57 |  | -                        </button>  | 
 | 110 | +                        {!showEditTextField &&  | 
 | 111 | +                            <div className="flex items-center gap-x-4 text-gray-600">  | 
 | 112 | +                                {comment.member.username === loginState.username &&  | 
 | 113 | +                                    <div className="flex items-center">  | 
 | 114 | +                                        <TextButton text={"수정"} onClick={handleShowEdit}/>  | 
 | 115 | +                                        <TextButton text={"삭제"} onClick={handleDeleteComment}/>  | 
 | 116 | +                                    </div>  | 
 | 117 | +                                }  | 
 | 118 | +                                <button  | 
 | 119 | +                                    className="w-fit py-2 flex justify-center items-center gap-x-2 hover:cursor-pointer rounded-md hover:brightness-120"  | 
 | 120 | +                                    onClick={handleOpenTextField}>  | 
 | 121 | +                                    <MdOutlineAddComment className="size-4"/>  | 
 | 122 | +                                    댓글 작성하기  | 
 | 123 | +                                </button>  | 
 | 124 | +                            </div>  | 
 | 125 | +                        }  | 
58 | 126 |                     </div>  | 
59 | 127 |                 </div>  | 
60 |  | -                <div className="ml-8">  | 
 | 128 | +                <div>  | 
61 | 129 |                     {showTextField &&  | 
62 | 130 |                         <CommentTextField  | 
63 | 131 |                             value={commentInput}  | 
64 | 132 |                             onChange={handleCommentInput}  | 
65 |  | -                            handleSubmit={handleSubmit}  | 
66 |  | -                            commentId={faker.number.int().toString()}  | 
 | 133 | +                            handleSubmit={handleCommentSubmit}  | 
 | 134 | +                            commentId={comment.id}  | 
 | 135 | +                            taggedUsername={taggedUsername}  | 
67 | 136 |                             handleShowTextField={handleShowTextField}/>}  | 
68 | 137 |                 </div>  | 
69 |  | -                {openComment &&  | 
 | 138 | +                {comment.subComments &&  | 
70 | 139 |                     <div>  | 
71 |  | -                        {Array.from({length: 2}).map(() =>  | 
 | 140 | +                        {comment.subComments.map((comment) =>  | 
72 | 141 |                             (depth <= 2)  | 
73 |  | -                                ? <CommentCard key={faker.animal.cow()} depth={depth + 1}/>  | 
 | 142 | +                                ? <CommentCard  | 
 | 143 | +                                    key={comment.id}  | 
 | 144 | +                                    comment={comment}  | 
 | 145 | +                                    handleLoadMoreSubComment={handleLoadMoreSubComment}  | 
 | 146 | +                                    handleCommentSubmit={handleCommentSubmit}  | 
 | 147 | +                                    pageSize={pageSize}  | 
 | 148 | +                                    depth={depth + 1}/>  | 
74 | 149 |                                 : null)}  | 
75 |  | -                    </div>  | 
76 |  | -                }  | 
 | 150 | +                        {page !== 0 && (page) * 10 < comment.replyCount &&  | 
 | 151 | +                            <LoadMoreButton  | 
 | 152 | +                                addStyle="mt-2 ml-8 w-[calc(100%-36px)] !bg-gray-400"  | 
 | 153 | +                                onClick={() => {  | 
 | 154 | +                                    handleLoadMoreSubComment(page, comment.id);  | 
 | 155 | +                                    setPage(page + 1);  | 
 | 156 | +                                }}/>}  | 
 | 157 | +                    </div>}  | 
77 | 158 |             </div>  | 
78 | 159 |         </div>  | 
79 | 160 |     );  | 
 | 
0 commit comments