Last active
March 1, 2023 10:04
-
-
Save florianwalther-private/892900c654c2de774e37e417a34ce2df to your computer and use it in GitHub Desktop.
Updated Comment component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UserProfileLink from "@/components/UserProfileLink"; | |
import { formatRelativeDate } from "@/helpers/utils"; | |
import { useAuthenticatedUser } from "@/hooks/useAuthenticatedUser"; | |
import { Comment as CommentModel } from "@/models/comment"; | |
import { NotFoundError } from "@/network/http-errors"; | |
import { AppContext } from "@/pages/_app"; | |
import { useContext, useState } from "react"; | |
import { Button } from "react-bootstrap"; | |
import CreateCommentBox from "./CreateCommentBox"; | |
import EditCommentBox from "./EditCommentBox"; | |
import * as BlogApi from "@/network/api/blog"; | |
interface CommentProps { | |
comment: CommentModel, | |
onReplyCreated: (reply: CommentModel) => void, | |
onCommentUpdated: (updatedComment: CommentModel) => void, | |
onCommentDeleted: (comment: CommentModel) => void, | |
} | |
const Comment = ({ comment, onReplyCreated, onCommentUpdated, onCommentDeleted }: CommentProps) => { | |
const { user } = useAuthenticatedUser(); | |
const appContext = useContext(AppContext); | |
const [showEditBox, setShowEditBox] = useState(false); | |
const [showReplyBox, setShowReplyBox] = useState(false); | |
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false); | |
const [deleteInProgress, setDeleteInProgress] = useState(false); | |
async function deleteComment() { | |
try { | |
setDeleteInProgress(true); | |
await BlogApi.deleteComment(comment._id); | |
onCommentDeleted(comment); | |
} catch (error) { | |
console.error(error); | |
if (error instanceof NotFoundError) { | |
onCommentDeleted(comment); | |
} else { | |
alert(error); | |
} | |
} finally { | |
setDeleteInProgress(false); | |
} | |
} | |
function handleReplyClick() { | |
if (user) { | |
setShowReplyBox(true); | |
} else { | |
appContext.showLoginModal(); | |
} | |
} | |
function handleEditClick() { | |
setShowEditBox(true); | |
setShowDeleteConfirmation(false); | |
} | |
function handleReplyCreated(newReply: CommentModel) { | |
onReplyCreated(newReply); | |
setShowReplyBox(false); | |
} | |
function handleCommentUpdated(updatedComment: CommentModel) { | |
onCommentUpdated(updatedComment); | |
setShowEditBox(false); | |
} | |
return ( | |
<div className="w-100"> | |
<hr /> | |
{showEditBox | |
? <EditCommentBox | |
comment={comment} | |
onCommentUpdated={handleCommentUpdated} | |
onCancel={() => setShowEditBox(false)} | |
/> | |
: <CommentLayout | |
comment={comment} | |
onReplyClicked={handleReplyClick} | |
onEditClicked={handleEditClick} | |
onDeleteClicked={() => setShowDeleteConfirmation(true)} | |
/>} | |
{showDeleteConfirmation && | |
<DeleteConfirmationSection | |
deleteDisabled={deleteInProgress} | |
onDeleteConfirmed={deleteComment} | |
onCancel={() => setShowDeleteConfirmation(false)} | |
/> | |
} | |
{showReplyBox && | |
<CreateCommentBox | |
blogPostId={comment.blogPostId} | |
title="Write a reply" | |
defaultText={comment.parentCommentId ? `@${comment.author.username} ` : ""} | |
onCommentCreated={handleReplyCreated} | |
parentCommentId={comment.parentCommentId || comment._id} | |
onCancel={() => setShowReplyBox(false)} | |
showCancel | |
/> | |
} | |
</div> | |
); | |
} | |
export default Comment; | |
interface CommentLayoutProps { | |
comment: CommentModel, | |
onReplyClicked: () => void, | |
onEditClicked: () => void, | |
onDeleteClicked: () => void, | |
} | |
const CommentLayout = ({ comment, onReplyClicked, onEditClicked, onDeleteClicked }: CommentLayoutProps) => { | |
const { user } = useAuthenticatedUser(); | |
return ( | |
<div> | |
<div className="mb-2">{comment.text}</div> | |
<div className="d-flex gap-2 align-items-center"> | |
<UserProfileLink user={comment.author} /> | |
{formatRelativeDate(comment.createdAt)} | |
{comment.updatedAt > comment.createdAt && <span>(Edited)</span>} | |
</div> | |
<div className="mt-1 d-flex gap-2"> | |
<Button | |
variant="link" | |
onClick={onReplyClicked}> | |
<small>Reply</small> | |
</Button> | |
{user?._id === comment.author._id && | |
<> | |
<Button | |
variant="link" | |
onClick={onEditClicked}> | |
<small>Edit</small> | |
</Button> | |
<Button | |
variant="link" | |
onClick={onDeleteClicked}> | |
<small>Delete</small> | |
</Button> | |
</> | |
} | |
</div> | |
</div> | |
); | |
} | |
interface DeleteConfirmationSectionProps { | |
deleteDisabled: boolean, | |
onDeleteConfirmed: () => void, | |
onCancel: () => void, | |
} | |
const DeleteConfirmationSection = ({ deleteDisabled: disableDelete, onDeleteConfirmed, onCancel }: DeleteConfirmationSectionProps) => { | |
return ( | |
<div> | |
<p className="text-danger">Do you really want to delete this comment?</p> | |
<Button | |
variant="danger" | |
className="me-2" | |
onClick={onDeleteConfirmed} | |
disabled={disableDelete}> | |
Delete | |
</Button> | |
<Button | |
variant="outline-danger" | |
onClick={onCancel}> | |
Cancel | |
</Button> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment