From f0354e8dc96c34e2746f37b131a04476ce85961e Mon Sep 17 00:00:00 2001 From: Benjamin Lefebvre Date: Thu, 29 May 2025 10:53:24 -0400 Subject: [PATCH 1/8] Fix backend routes --- .../src/modules/comment/routes/comment.routes.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/modules/comment/routes/comment.routes.ts b/backend/src/modules/comment/routes/comment.routes.ts index 9314dfd..8cccfc0 100644 --- a/backend/src/modules/comment/routes/comment.routes.ts +++ b/backend/src/modules/comment/routes/comment.routes.ts @@ -3,16 +3,16 @@ import { updateCommentController, createCommentController, deleteCommentController, - getCommentsByPostController, - getCommentByIdController, deletePostController + getCommentsByContentController, + getCommentByIdController } from '../controllers/comment.controller'; +import { authenticateToken } from '../../../shared/middleware/auth'; const router = Router(); -router.post('/comments/:post_id', createCommentController); -router.get('/comments/:post_id/', getCommentsByPostController); -router.get('/comments/:post_id/:comment_id', getCommentByIdController); -router.put('/comments/:post_id/:comment_id/:user_id', updateCommentController); -router.delete('/post/:post_id/:user_id', deletePostController); -router.delete('/comments/:post_id/:comment_id/:user_id', deleteCommentController); +router.post('/:contentId', authenticateToken, createCommentController, ); +router.get("/content/:contentId", authenticateToken, getCommentsByContentController); +router.get("/:commentId", authenticateToken, getCommentByIdController); +router.put("/:commentId", authenticateToken, updateCommentController); +router.delete("/:commentId", authenticateToken, deleteCommentController); export default router; From a3ffbb97f0a64b13d5034238a58615478f0ab3a5 Mon Sep 17 00:00:00 2001 From: Benjamin Lefebvre Date: Thu, 29 May 2025 10:54:04 -0400 Subject: [PATCH 2/8] Content comment remove request removed since it should be managed by the backend when the content deleted --- frontend/src/pages/content/ContentView.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/pages/content/ContentView.tsx b/frontend/src/pages/content/ContentView.tsx index 3849b20..054b08b 100644 --- a/frontend/src/pages/content/ContentView.tsx +++ b/frontend/src/pages/content/ContentView.tsx @@ -202,11 +202,8 @@ export default function ContentView() { if (localStorage.getItem("userUID") === content?.creatorUID) { try { - // Delete comments - const user_id = content?.creatorUID; - await axios.delete(`${apiURL}/comment/post/${content.uid}/${user_id}`); - // Delete content + const user_id = user?.uid; const content_id = content?.uid; await axios({ method: "delete", From 064bb0a2091e07fb2053fb4f1e30c0a041c8f4eb Mon Sep 17 00:00:00 2001 From: Benjamin Lefebvre Date: Thu, 29 May 2025 10:55:40 -0400 Subject: [PATCH 3/8] Comment Service implementation --- .../src/components/content/CommentList.tsx | 175 ++++++++---------- frontend/src/services/CommentService.ts | 118 ++++++++++++ 2 files changed, 193 insertions(+), 100 deletions(-) create mode 100644 frontend/src/services/CommentService.ts diff --git a/frontend/src/components/content/CommentList.tsx b/frontend/src/components/content/CommentList.tsx index 0e8ac4c..b97ed96 100644 --- a/frontend/src/components/content/CommentList.tsx +++ b/frontend/src/components/content/CommentList.tsx @@ -13,9 +13,9 @@ import { apiURL } from "../../scripts/api"; import { Content } from "../../models/Content"; import { User } from "../../models/User"; import { Comment } from "../../models/Comment"; +import CommentService from "../../services/CommentService"; export default function CommentList({ - content, user, }: { content: Content; @@ -23,129 +23,102 @@ export default function CommentList({ }) { const [comments, setComments] = useState([]); const [newComment, setNewComment] = useState(""); - const [editingCommentId, setEditingCommentId] = useState(null); - const [editingCommentText, setEditingCommentText] = useState(""); + + const [selectedCommentEdit, setSelectedCommentEdit] = + useState(null); + const [numComments, setNumComments] = useState(0); const [loading, setLoading] = useState(true); - const postId = useParams().id; + const contentId = useParams().id as string; const editTextareaRef = useRef(null); const navigate = useNavigate(); - async function refreshComments() { - setLoading(true); - try { - const response = await axios.get(`${apiURL}/comment/comments/${postId}`); - - if (response.data) { - console.log("refresh response: ", response.data); - - let commentArray: Comment[] = []; - if (Array.isArray(response.data)) { - console.log("is array: ", response.data); - const responseArray = response.data; - responseArray.forEach((c) => { - commentArray.push(Object.values(c) as unknown as Comment); - }); - } else { - console.log("is NOT array: ", response.data); - commentArray = Object.values(response.data) as unknown as Comment[]; - } - - console.log("refreshComments Response.data: ", commentArray); - setComments(commentArray); - setNumComments(comments.length); - if (Array.isArray(commentArray)) { - setNumComments(commentArray.length); - } else if (commentArray) { - setNumComments(1); - } else { - setNumComments(0); - } - } - } catch (error) { - console.error("Error fetching comments:", error); - } finally { - setLoading(false); - } - } - useEffect(() => { refreshComments(); - }, [postId]); + }, [contentId]); useEffect(() => { if (editTextareaRef.current) { editTextareaRef.current.style.height = "auto"; editTextareaRef.current.style.height = `${editTextareaRef.current.scrollHeight}px`; } - }, [editingCommentText]); + }, [selectedCommentEdit]); + + const refreshComments = async () => { + const commentsResult = await CommentService.getPostComments(contentId); + + if (commentsResult instanceof Error) { + console.error(commentsResult.message); + setLoading(false); + return; + } + setComments(commentsResult); + setNumComments(commentsResult.length); + setLoading(false); + }; const handleAddComment = async (e: React.FormEvent) => { e.preventDefault(); if (!newComment) return; if (!user.uid) navigate(`../authentication/login`); - try { - await axios.post(`${apiURL}/comment/comments/${postId}`, { - owner_id: user.uid, - text: newComment, - }); - - if (content && content.creatorUID != user.uid) { - try { - await axios.post(`${apiURL}/notifications/create`, { - userId: content?.creatorUID, - notification: { - userId: user.uid, - username: user?.username, - type: "comment", - textPreview: `"${ - newComment && newComment.length > 30 - ? newComment.substring(0, 30) + "..." - : newComment - }"!`, - contentId: content.uid, - timestamp: Date.now(), - read: false, - }, - }); - } catch (error) { - console.error(`Error sending notifications: ${error}`); - } - } - - await refreshComments(); - setNewComment(""); - } catch (error) { - console.error("Error adding comment:", error); + + // Add comment to the backend + const comment = await CommentService.publishComment( + contentId, + newComment, + user.uid + ); + + // Check if the comment was added successfully + if (comment instanceof Error) { + console.error(comment.message); + return; } + + setNewComment(""); }; const handleEditComment = async (e: React.FormEvent) => { e.preventDefault(); - if (!editingCommentText) return; + if (!selectedCommentEdit) return; if (!user.uid) navigate(`../authentication/login`); - try { - await axios.put( - `${apiURL}/comment/comments/${postId}/${editingCommentId}/${user.uid}`, - { - text: editingCommentText, - } - ); - await refreshComments(); - setEditingCommentId(null); - setEditingCommentText(""); - } catch (error) { - console.error("Error editing comment:", error); + + // If comment is empty, delete it + if (selectedCommentEdit.text === "") { + await handleDeleteComment(selectedCommentEdit.comment_id); + setSelectedCommentEdit(null); + return; } + + // Update comment in the backend + const comment = await CommentService.updateComment( + selectedCommentEdit.comment_id, + selectedCommentEdit.text + ); + + // Check if the comment was updated successfully + if (comment instanceof Error) { + console.error(comment.message); + return; + } + + // Update comments list to reflect the changes + const updatedComments = comments.map((comment) => + comment.comment_id === selectedCommentEdit.comment_id + ? { ...comment, text: selectedCommentEdit.text } + : comment + ); + setComments(updatedComments); + setNumComments(updatedComments.length); + setNewComment(""); + setSelectedCommentEdit(null); }; const handleDeleteComment = async (id: string) => { if (!user.uid) navigate(`../authentication/login`); try { - await axios.delete( - `${apiURL}/comment/comments/${postId}/${id}/${user.uid}` - ); + await axios.delete(`${apiURL}/comment/${id}/`); } catch (error) { console.error("Error deleting comment:", error); } @@ -187,17 +160,17 @@ export default function CommentList({ {/************ EDIT COMMENT ************/} - {editingCommentId != null && ( + {selectedCommentEdit != null && ( <>
setEditingCommentId(null)} + onClick={() => setSelectedCommentEdit(null)} >
{ - setEditingCommentId(null); + setSelectedCommentEdit(null); }} className='close-button' style={{ cursor: "pointer" }} @@ -210,9 +183,12 @@ export default function CommentList({