Skip to content

Commit 7f4499c

Browse files
committed
feat: edit comment or reply and updat ui immediately
1 parent 2f3b5cc commit 7f4499c

File tree

10 files changed

+211
-9
lines changed

10 files changed

+211
-9
lines changed

app/api/update-comment/route.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import CommentModel from "@/models/Comment"
2+
import { connectDB } from "@/utils/database"
3+
import { CommentProps } from "@/types/props"
4+
5+
const PUT = async (req: Request) => {
6+
try {
7+
const { id, commentId, comment } = await req.json()
8+
9+
await connectDB()
10+
11+
const commentToEdit = await CommentModel.findOne({ _id: id })
12+
13+
if (!commentToEdit) {
14+
// user is editing a reply
15+
const parentComment = await CommentModel.findOne(
16+
{ '_id': commentId }
17+
)
18+
19+
if (!parentComment) {
20+
return new Response(JSON.stringify({ error: 'Comment not found!' }))
21+
}
22+
23+
const reply = parentComment.replies.find((reply: CommentProps) => reply._id == id)
24+
25+
if (!reply) {
26+
return new Response(JSON.stringify({ error: 'Reply not found!' }))
27+
}
28+
29+
reply.comment = comment
30+
31+
const updated = await CommentModel.updateOne(
32+
{ _id: commentId },
33+
{ $set: { replies: parentComment.replies } }
34+
)
35+
36+
if (!updated.matchedCount) return new Response(JSON.stringify({ error: 'Could not update comment. Please try again.' }))
37+
38+
return new Response(JSON.stringify({ success: 'Commment updated successfully.' }))
39+
}
40+
41+
commentToEdit.comment = comment
42+
await commentToEdit.save()
43+
return new Response(JSON.stringify({ success: 'Comment updated successfully!' }))
44+
45+
} catch (error) {
46+
new Response(JSON.stringify({ error: 'Something went wrong! Please try again.' }))
47+
}
48+
}
49+
50+
export { PUT }

app/layout.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Metadata } from 'next'
44
import UserProvider from '@/providers/UserProvider'
55
import ReplyProvider from '@/providers/ReplyProvider'
66
import DeletedCommentProvider from '@/providers/DeletedCommentProvider'
7+
import EditCommentProvider from '@/providers/EditCommentProvider'
78

89
export const metadata: Metadata = {
910
title: 'Comments | CoderSuresh',
@@ -23,9 +24,11 @@ const RootLayout = ({
2324
<body className='md:w-[740px] md:mx-auto mx-3 md:my-10'>
2425
<UserProvider>
2526
<DeletedCommentProvider>
26-
<ReplyProvider>
27-
{children}
28-
</ReplyProvider>
27+
<EditCommentProvider>
28+
<ReplyProvider>
29+
{children}
30+
</ReplyProvider>
31+
</EditCommentProvider>
2932
</DeletedCommentProvider>
3033
</UserProvider>
3134
</body>

app/page.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import { ReplyContext } from '@/context/ReplyContext'
99
import { CommentProps } from '@/types/props'
1010
import AddReply from '@/components/AddReply'
1111
import { CommentContext } from '@/context/CommentContext'
12-
import {removeDeletedReply} from '@/utils/removeDeleted/removeDeletedReply'
12+
import { removeDeletedReply } from '@/utils/removeDeleted/removeDeletedReply'
1313
import { removeDeletedComment } from '@/utils/removeDeleted/removeDeletedComment'
14+
import { EditCommentContext } from '@/context/EditCommentContext'
1415

1516
const Home = () => {
1617

1718
const { values, setValues } = React.useContext(UserContext)
1819
const { reply } = React.useContext(ReplyContext)
1920
const { deletedCommentValues, setDeletedCommentValues } = React.useContext(CommentContext)
21+
const { editCommentValues, setEditCommentValues } = React.useContext(EditCommentContext)
2022
const router = useRouter()
2123

2224
const [comments, setComments] = React.useState<CommentProps[]>([])
@@ -50,7 +52,7 @@ const Home = () => {
5052
setLoading(false)
5153

5254
if (data.error) {
53-
console.log(data.error)
55+
alert(data.error)
5456
return
5557
}
5658

@@ -62,6 +64,7 @@ const Home = () => {
6264
fetchComments()
6365
}, [])
6466

67+
// updated ui when a comment or reply is deleted
6568
React.useEffect(() => {
6669
if (deletedCommentValues.isDeleted) {
6770

@@ -78,6 +81,33 @@ const Home = () => {
7881
}
7982
}, [deletedCommentValues])
8083

84+
// update ui when a comment or reply is updated by the author
85+
React.useEffect(() => {
86+
if (editCommentValues.editedComment) {
87+
88+
const commentIndex = comments.findIndex(comment => comment._id === editCommentValues.commentId)
89+
90+
if (commentIndex != -1) {
91+
const updatedComments = [...comments]
92+
93+
if (editCommentValues.isReply) {
94+
const replies = updatedComments[commentIndex].replies
95+
96+
replies?.map((reply: CommentProps) => {
97+
if (reply._id === editCommentValues.id) {
98+
reply.comment = editCommentValues.editedComment
99+
}
100+
})
101+
102+
} else {
103+
updatedComments[commentIndex].comment = editCommentValues.editedComment
104+
}
105+
106+
setComments(updatedComments)
107+
}
108+
}
109+
}, [editCommentValues])
110+
81111
const addReplyToComment = (commentId: string, newReply: CommentProps) => {
82112
setComments((prevComments: any) => [
83113
...prevComments.map((comment: CommentProps) => {

components/CommentBody.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import CommentCardBtns from './CommentCardBtns'
44
import { CommentProps } from '@/types/props'
55
import { UserContext } from '@/context/UserContext'
66
import { formatDistanceToNow } from 'date-fns'
7+
import { EditCommentContext } from '@/context/EditCommentContext'
8+
import EditComment from './EditComment'
79

810
const CommentBody = ({ _id, author, commentID, userId, replyTo, comment, createdAt, loading }: CommentProps) => {
911

1012
const { values } = React.useContext(UserContext)
13+
const { editCommentValues } = React.useContext(EditCommentContext)
1114

1215
const renderTime = (createdAt: Date) => {
1316
const timeAgo = formatDistanceToNow(new Date(createdAt), {
@@ -53,7 +56,7 @@ const CommentBody = ({ _id, author, commentID, userId, replyTo, comment, created
5356
</div>
5457
</div>
5558

56-
<CommentCardBtns _id={_id} commentID={commentID || _id} userId={userId} author={author} />
59+
<CommentCardBtns _id={_id} isReply={commentID ? true : false} commentID={commentID || _id} userId={userId} author={author} />
5760

5861
</div>
5962

@@ -67,7 +70,12 @@ const CommentBody = ({ _id, author, commentID, userId, replyTo, comment, created
6770
<span className='font-medium text-moderate-blue'>@{replyTo} </span>
6871
}
6972
{
70-
comment
73+
editCommentValues.editComment && editCommentValues.id === _id
74+
?
75+
// only reply has commentID field in DB.
76+
<EditComment comment={comment} isReply={commentID ? true : false} commentID={commentID || _id} _id={_id} />
77+
:
78+
comment
7179
}
7280
</div>
7381
</div>

components/CommentCardBtns.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import { ReplyContext } from '@/context/ReplyContext'
44
import { UserContext } from '@/context/UserContext'
55
import { CommentContext } from '@/context/CommentContext'
66
import Modal from './Modal'
7+
import { EditCommentContext } from '@/context/EditCommentContext'
78

8-
const CommentCardBtns = ({ _id, commentID, userId, author }: CommentProps) => {
9+
const CommentCardBtns = ({ _id, commentID, isReply, userId, author }: CommentProps) => {
910

1011
const { reply, setReply } = React.useContext(ReplyContext)
1112
const { values } = React.useContext(UserContext)
1213
const { setDeletedCommentValues } = React.useContext(CommentContext)
14+
const { setEditCommentValues } = React.useContext(EditCommentContext)
1315

1416
const [isAuthor, setIsAuthor] = React.useState(false)
1517
const [loading, setLoading] = React.useState(true)
@@ -80,6 +82,16 @@ const CommentCardBtns = ({ _id, commentID, userId, author }: CommentProps) => {
8082
})
8183
}
8284

85+
const editComment = () => {
86+
setEditCommentValues({
87+
editComment: true,
88+
id: _id!,
89+
commentId: commentID!,
90+
editedComment: '',
91+
isReply: isReply || false,
92+
})
93+
}
94+
8395
return (
8496
<div className='sm:static absolute bottom-8 right-5'>
8597

@@ -113,7 +125,7 @@ const CommentCardBtns = ({ _id, commentID, userId, author }: CommentProps) => {
113125
</button>
114126
}
115127

116-
<button className='flex items-center gap-x-2 hover:opacity-50 text-sm font-medium text-moderate-blue'>
128+
<button onClick={() => editComment()} className='flex items-center gap-x-2 hover:opacity-50 text-sm font-medium text-moderate-blue'>
117129
<i className='fas fa-pencil text-xs' />
118130
<span>Edit</span>
119131
</button>

components/EditComment.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react'
2+
import { CommentProps } from '@/types/props'
3+
import { EditCommentContext } from '@/context/EditCommentContext'
4+
5+
const EditComment = ({ comment, commentID, isReply, _id }: CommentProps) => {
6+
7+
const [commenting, setCommenting] = React.useState(false)
8+
const [localComment, setLocalComment] = React.useState(comment)
9+
10+
const { editCommentValues, setEditCommentValues } = React.useContext(EditCommentContext)
11+
12+
const updateComment = async (e: React.FormEvent<HTMLFormElement>) => {
13+
e.preventDefault()
14+
setCommenting(true)
15+
16+
const res = await fetch('/api/update-comment', {
17+
method: 'PUT',
18+
body: JSON.stringify({ 'comment': localComment, 'id': _id, 'commentId': commentID })
19+
})
20+
21+
const data = await res.json()
22+
23+
setCommenting(false)
24+
25+
if (data.error) {
26+
alert(data.error)
27+
return
28+
}
29+
30+
console.log(commentID, isReply)
31+
32+
setEditCommentValues({
33+
...editCommentValues,
34+
editComment: false,
35+
editedComment: localComment!,
36+
})
37+
}
38+
39+
return (
40+
<form action="#" onSubmit={updateComment} className='flex flex-1 flex-col items-start gap-5'>
41+
<textarea
42+
required
43+
value={localComment}
44+
onChange={(e) => setLocalComment(e.currentTarget.value)}
45+
placeholder='Add a reply...'
46+
name="comment" id="comment"
47+
className='sm:mb-0 mb-[60px] placeholder-grayish-blue
48+
resize-none border rounded-md w-full h-24 py-3 px-5 focus:border-moderate-blue focus:outline-none
49+
'
50+
/>
51+
<button
52+
className='bg-moderate-blue self-end text-white px-5 py-2 uppercase rounded-md font-medium hover:opacity-50
53+
sm:static absolute bottom-5 right-5'
54+
>
55+
{commenting ? 'Updating' : 'Update'}
56+
</button>
57+
</form>
58+
)
59+
}
60+
61+
export default EditComment

context/EditCommentContext.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
import { EditCommentContextType } from '@/types/EditCommentContextType'
3+
4+
const EditCommentContext = React.createContext({
5+
editCommentValues: {} as EditCommentContextType,
6+
setEditCommentValues: (editCommentValues: EditCommentContextType) => { }
7+
})
8+
9+
export { EditCommentContext }

providers/EditCommentProvider.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use client'
2+
import React from 'react'
3+
import { EditCommentContext } from '@/context/EditCommentContext'
4+
import { ChildrenProps } from '@/types/props'
5+
import { EditCommentContextType } from '@/types/EditCommentContextType'
6+
7+
const EditCommentProvider = ({ children }: ChildrenProps) => {
8+
9+
const [editCommentValues, setEditCommentValues] = React.useState<EditCommentContextType>({
10+
editComment: false,
11+
id: '',
12+
})
13+
14+
return (
15+
<EditCommentContext.Provider value={{ editCommentValues, setEditCommentValues }}>
16+
{children}
17+
</EditCommentContext.Provider>
18+
)
19+
}
20+
21+
export default EditCommentProvider

types/EditCommentContextType.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type EditCommentContextType = {
2+
editComment: boolean,
3+
editedComment: string,
4+
id: string,
5+
isReply: boolean,
6+
commentId: string,
7+
}

types/props.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type CommentProps = {
1111
createdAt?: Date,
1212
loading?: boolean,
1313
replies?: [],
14+
isReply?: boolean,
1415
score?: number,
1516
replyTo?: string,
1617
};

0 commit comments

Comments
 (0)