Skip to content

Commit

Permalink
Merge pull request #40 from chimobi-justice/edit-replies
Browse files Browse the repository at this point in the history
feat(edit): article/thread replies
  • Loading branch information
chimobi-justice authored Nov 17, 2024
2 parents 9160968 + e2b05cf commit 94f7a5d
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 35 deletions.
2 changes: 2 additions & 0 deletions src/api/endpoints/articleEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export const CREATE_ARTICLE_COMMENT_ENDPOINT = `${API_BASE_URL}/articles`;

export const DELETE_ARTICLE_COMMENT_ENDPOINT = `${API_BASE_URL}/articles`;

export const EDIT_ARTICLE_COMMENT_ENDPOINT = `${API_BASE_URL}/articles`;

export const ARTICLE_LIKE_ENDPOINT = `${API_BASE_URL}/articles`;

export const ARTICLE_DISLIKE_ENDPOINT = `${API_BASE_URL}/articles`;
Expand Down
2 changes: 2 additions & 0 deletions src/api/endpoints/threadEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export const CREATE_THREAD_COMMENT_ENDPOINT = `${API_BASE_URL}/threads`;

export const DELETE_THREAD_COMMENT_ENDPOINT = `${API_BASE_URL}/threads`;

export const EDIT_THREAD_COMMENT_ENDPOINT = `${API_BASE_URL}/threads`;

export const THREAD_LIKE_ENDPOINT = `${API_BASE_URL}/threads`;

export const THREAD_DISLIKE_ENDPOINT = `${API_BASE_URL}/threads`;
23 changes: 23 additions & 0 deletions src/hooks/article/useEditArticleComment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'

import { errorNotification, successNotification } from '@helpers/notification'
import { editArticleComment } from '@services/articles'

export const useEditArticleComment = () => {
const queryClient = useQueryClient();

const editArticlCommentMutation = useMutation({
mutationFn: editArticleComment,
onSuccess: (data) => {
successNotification(data.message);

queryClient.invalidateQueries({ queryKey: ['articles']});
queryClient.invalidateQueries({ queryKey: ['article']});
},
onError: (error: any) => {
errorNotification(error?.response?.data?.message)
}
})

return { editArticlCommentMutation }
}
23 changes: 23 additions & 0 deletions src/hooks/thread/useEditThreadComment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'

import { errorNotification, successNotification } from '@helpers/notification'
import { editThreadComment } from '@services/threads'

export const useEditThreadComment = () => {
const queryClient = useQueryClient();

const editThreadCommentMutation = useMutation({
mutationFn: editThreadComment,
onSuccess: (data) => {
successNotification(data.message);

queryClient.invalidateQueries({ queryKey: ['threads'] });
queryClient.invalidateQueries({ queryKey: ['thread'] })
},
onError: (error: any) => {
errorNotification(error?.response?.data?.message)
}
})

return { editThreadCommentMutation }
}
68 changes: 52 additions & 16 deletions src/pages/Articles/components/commentComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,70 @@ import { Avatar, Box, Divider, Flex, Text, useDisclosure } from '@chakra-ui/reac

import { IArticleComments } from 'src/types'
import { useUser } from '@context/userContext'
import { useCreateArticleComment } from '@hooks/article/useCreateArticleComment'
import { Button, ContentBlockContent, Editor, ShowLoginModal } from '@components/index'
import { useCreateArticleComment } from '@hooks/article/useCreateArticleComment'
import { useDeleteArticleComment } from '@hooks/article/useDeleteArticleComment'
import { useEditArticleComment } from '@hooks/article/useEditArticleComment'

const CommentComponent: FunctionComponent<{ comment: IArticleComments, level: number }> = ({ comment, level }) => {
const { user } = useUser();
const { createArticlCommentMutation } = useCreateArticleComment();
const { deleteArticleCommentMutation } = useDeleteArticleComment();
const { editArticlCommentMutation } = useEditArticleComment();
const { isOpen, onOpen, onClose } = useDisclosure();

const { id } = useParams();

const [showCommentInput, setShowCommentInput] = useState<string | null>(null);
const [replyContent, setReplyContent] = useState<string>('');
const [showAllReplies, setShowAllReplies] = useState<boolean>(false);
const [isEditing, setIsEditing] = useState<boolean>(false);

const handleShowCommentInput = (articleId: any) => {
const handleShowCommentInput = (articleId: string, isEditingMode: boolean = false, commentText: string) => {
if (showCommentInput === articleId) {
setShowCommentInput(null);
setReplyContent("");
setIsEditing(false);
} else {
setShowCommentInput(articleId);
setIsEditing(isEditingMode);
setReplyContent(isEditingMode ? commentText : "");
}
}

const handleCommentSubmit = (event: FormEvent<HTMLFormElement>) => {
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const payload = {
comment: replyContent,
parent_id: comment.id
}

if (replyContent.trim()) {
createArticlCommentMutation.mutate({ data: payload, id: id! })
setReplyContent("");
if (!replyContent.trim()) return;

if (isEditing) {
editArticlCommentMutation.mutate(
{ data: { comment: replyContent }, id: comment.id },
{
onSuccess: () => {
setIsEditing(false)
setShowCommentInput(null);
}
}
);
} else {
createArticlCommentMutation.mutate(
{ data: { comment: replyContent, parent_id: comment.id }, id: id! },
{
onSuccess: () => {
setReplyContent("");
setShowCommentInput(null);
}
}
)
}
}

const handleToggleReplies = () => {
setShowAllReplies(prev => !prev);
}
const handleToggleReplies = () => setShowAllReplies(prev => !prev);

const handleIsEditing = (threadId: string, commentText: string) => {
handleShowCommentInput(threadId, true, commentText)
};

return (
<Fragment>
Expand Down Expand Up @@ -99,7 +123,11 @@ const CommentComponent: FunctionComponent<{ comment: IArticleComments, level: nu
)}
</Text>

<Text fontSize="13px" cursor="pointer" onClick={() => handleShowCommentInput(comment.id)} as={"span"}>
<Text fontSize="13px" cursor="pointer" onClick={() => {
handleShowCommentInput(comment.id, false, "")
}
}
as={"span"}>
Reply
</Text>

Expand All @@ -110,6 +138,14 @@ const CommentComponent: FunctionComponent<{ comment: IArticleComments, level: nu
Delete
</Text>
)}

{comment.isOwner && (
<Text fontSize={"13px"} color={"blue.500"} cursor={"pointer"} onClick={() => {
handleIsEditing(comment.id, comment.comment);
}}>
Edit
</Text>
)}
</Flex>
</Flex>
)}
Expand All @@ -135,7 +171,7 @@ const CommentComponent: FunctionComponent<{ comment: IArticleComments, level: nu
)}

{showCommentInput === comment.id && (
<form onSubmit={handleCommentSubmit}>
<form onSubmit={handleSubmit}>
<Box height={"160px"} mb={"40px"}>
<Editor
content={replyContent}
Expand All @@ -153,7 +189,7 @@ const CommentComponent: FunctionComponent<{ comment: IArticleComments, level: nu
fontWeight={"semibold"}
rounded="sm"
isDisabled={!replyContent}
isloading={createArticlCommentMutation.isPending}
isloading={isEditing ? editArticlCommentMutation.isPending : createArticlCommentMutation.isPending}
>
Submit
</Button>
Expand Down
72 changes: 53 additions & 19 deletions src/pages/Threads/components/commentComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,67 @@ import { IThreadComments } from 'src/types'
import { useUser } from '@context/userContext';
import { Button, ContentBlockContent, Editor, ShowLoginModal } from '@components/index'
import { useCreateThreadComment } from '@hooks/thread/useCreateThreadComment'
import { useDeleteThreadComment } from '@hooks/thread/useDeleteThreadComment';
import { useDeleteThreadComment } from '@hooks/thread/useDeleteThreadComment'
import { useEditThreadComment } from '@hooks/thread/useEditThreadComment'

const CommentComponent: FunctionComponent<{ comment: IThreadComments, level: number }> = ({ comment, level }) => {
const { user } = useUser();
const { createThreadCommentMutation } = useCreateThreadComment();
const { deleteThreadCommentMutation } = useDeleteThreadComment();
const { editThreadCommentMutation } = useEditThreadComment();
const { id } = useParams();
const { isOpen, onOpen, onClose } = useDisclosure();

const [showCommentInput, setShowCommentInput] = useState<string | null>(null);
const [replyContent, setReplyContent] = useState<string>('');
const [showAllReplies, setShowAllReplies] = useState<boolean>(false);
const [isEditing, setIsEditing] = useState<boolean>(false);

const handleShowCommentInput = (threadId: any) => {
const handleShowCommentInput = (threadId: string, isEditingMode: boolean = false, commentText: string) => {
if (showCommentInput === threadId) {
setShowCommentInput(null);
setReplyContent("");
setIsEditing(false);
} else {
setShowCommentInput(threadId);
setIsEditing(isEditingMode);
setReplyContent(isEditingMode ? commentText : "");
}
}

const handleCommentSubmit = (event: FormEvent<HTMLFormElement>) => {
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const payload = {
comment: replyContent,
parent_id: comment.id
};

if (replyContent.trim()) {
createThreadCommentMutation.mutate({ data: payload, id: id! });
setReplyContent("");
if (!replyContent.trim()) return;

if (isEditing) {
editThreadCommentMutation.mutate(
{ data: { comment: replyContent }, id: comment.id },
{
onSuccess: () => {
setIsEditing(false)
setShowCommentInput(null);
}
}
);
} else {
createThreadCommentMutation.mutate(
{ data: { comment: replyContent, parent_id: comment.id }, id: id! },
{
onSuccess: () => {
setReplyContent("");
setShowCommentInput(null);
}
}
)
}
}

const handleToggleReplies = () => {
setShowAllReplies(prev => !prev);
}
const handleToggleReplies = () => setShowAllReplies(prev => !prev);

const handleIsEditing = (threadId: string, commentText: string) => {
handleShowCommentInput(threadId, true, commentText)
};

return (
<Fragment>
Expand Down Expand Up @@ -76,7 +100,11 @@ const CommentComponent: FunctionComponent<{ comment: IThreadComments, level: num
)}
</Text>

<Text fontSize="13px" cursor="pointer" onClick={() => handleShowCommentInput(comment.id)} as={"span"}>
<Text fontSize="13px" cursor="pointer" onClick={() => {
handleShowCommentInput(comment.id, false, "")
}
}
as={"span"}>
Reply
</Text>
</Flex>
Expand All @@ -88,6 +116,14 @@ const CommentComponent: FunctionComponent<{ comment: IThreadComments, level: num
Delete
</Text>
)}

{comment.isOwner && (
<Text fontSize={"13px"} color={"blue.500"} cursor={"pointer"} onClick={() => {
handleIsEditing(comment.id, comment.comment);
}}>
Edit
</Text>
)}
</Flex>
)}

Expand All @@ -112,12 +148,12 @@ const CommentComponent: FunctionComponent<{ comment: IThreadComments, level: num
)}

{showCommentInput === comment.id && (
<form onSubmit={handleCommentSubmit}>
<form onSubmit={handleSubmit}>
<Box height={"160px"} mb={"25px"}>
<Editor
content={replyContent}
setContent={setReplyContent}
placeholder="Write your reply..."
placeholder={isEditing ? 'Edit your reply...' : 'Write your reply...'}
/>
</Box>

Expand All @@ -130,7 +166,7 @@ const CommentComponent: FunctionComponent<{ comment: IThreadComments, level: num
fontWeight={"semibold"}
rounded="sm"
isDisabled={!replyContent}
isloading={createThreadCommentMutation.isPending}
isloading={isEditing ? editThreadCommentMutation.isPending : createThreadCommentMutation.isPending}
>
Submit
</Button>
Expand All @@ -157,6 +193,4 @@ const CommentComponent: FunctionComponent<{ comment: IThreadComments, level: num
);
};



export default CommentComponent;
5 changes: 5 additions & 0 deletions src/services/articles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
DELETE_ARTICLE_COMMENT_ENDPOINT,
DELETE_ARTICLE_ENDPOINT,
DELETE_SAVE_ARTICLE_ENDPOINT,
EDIT_ARTICLE_COMMENT_ENDPOINT,
EDIT_ARTICLE_ENDPOINT,
GET_ALL_ARTICLES_ENDPOINT,
GET_PAGINATED_ARTICLES_ENDPOINT,
Expand Down Expand Up @@ -36,6 +37,10 @@ export const createArticleComment = async ({ data, id }: { data: any, id: string
return await handleResponse<MessageResponse>(axiosInstance.post(`${CREATE_ARTICLE_COMMENT_ENDPOINT}/${id}/comments`, data));
}

export const editArticleComment = async ({ data, id }: { data: any, id: string }) => {
return await handleResponse<MessageResponse>(axiosInstance.patch(`${EDIT_ARTICLE_COMMENT_ENDPOINT}/${id}/comments`, data));
}

export const deleteArticleComment = async (id: string) => {
return await handleResponse<MessageResponse>(axiosInstance.delete(`${DELETE_ARTICLE_COMMENT_ENDPOINT}/comments/${id}`));
}
Expand Down
5 changes: 5 additions & 0 deletions src/services/threads/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
THREAD_LIKE_ENDPOINT,
GET_PAGINATED_THREADS_ENDPOINT,
DELETE_THREAD_COMMENT_ENDPOINT,
EDIT_THREAD_COMMENT_ENDPOINT,
} from '@api/index'
import { handleResponse, getWithPagination } from '@utils/apiHelpers'
import { IThreadRequest, IThreadResponse, MessageResponse, Thread } from 'src/types'
Expand All @@ -30,6 +31,10 @@ export const createThreadComment = async ({ data, id }: { data: any, id: string
return await handleResponse<MessageResponse>(axiosInstance.post(`${CREATE_THREAD_COMMENT_ENDPOINT}/${id}/comments`, data));
}

export const editThreadComment = async ({ data, id }: { data: any, id: string }) => {
return await handleResponse<MessageResponse>(axiosInstance.patch(`${EDIT_THREAD_COMMENT_ENDPOINT}/${id}/comments`, data));
}

export const deleteThreadComment = async (id: string) => {
return await handleResponse<MessageResponse>(axiosInstance.delete(`${DELETE_THREAD_COMMENT_ENDPOINT}/comments/${id}`));
}
Expand Down

0 comments on commit 94f7a5d

Please sign in to comment.