Skip to content

Instantly share code, notes, and snippets.

@florianwalther-private
Created February 25, 2023 09:44
Show Gist options
  • Save florianwalther-private/7811f6639abd6178bde9a2fba3da4116 to your computer and use it in GitHub Desktop.
Save florianwalther-private/7811f6639abd6178bde9a2fba3da4116 to your computer and use it in GitHub Desktop.
Question about organizing code into JSX variables
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: () => 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();
} catch (error) {
console.error(error);
if (error instanceof NotFoundError) {
onCommentDeleted();
} else {
alert(error);
}
} finally {
setDeleteInProgress(false);
}
}
const commentLayout =
<CommentLayout
comment={comment}
onReplyClicked={() => {
if (user) {
setShowReplyBox(true);
} else {
appContext.showLoginModal();
}
}}
onEditClicked={() => {
setShowEditBox(true);
setShowDeleteConfirmation(false);
}}
onDeleteClicked={() => setShowDeleteConfirmation(true)}
/>
const editCommentBox =
<EditCommentBox
comment={comment}
onCommentUpdated={(updatedComment) => {
onCommentUpdated(updatedComment);
setShowEditBox(false);
}}
onCancel={() => setShowEditBox(false)}
/>
const replyBox =
<CreateCommentBox
blogPostId={comment.blogPostId}
title="Write a reply"
defaultText={comment.parentCommentId ? `@${comment.author.username} ` : ""}
onCommentCreated={(newReply) => {
onReplyCreated(newReply);
setShowReplyBox(false);
}}
parentCommentId={comment.parentCommentId || comment._id}
onCancel={() => setShowReplyBox(false)}
showCancel
/>
const deleteConfirmationSection =
<div>
<p className="text-danger">Do you really want to delete this comment?</p>
<Button
variant="danger"
className="me-2"
onClick={deleteComment}
disabled={deleteInProgress}>
Delete
</Button>
<Button
variant="outline-danger"
onClick={() => setShowDeleteConfirmation(false)}>
Cancel
</Button>
</div>
return (
<div className="w-100">
<hr />
{showEditBox ? editCommentBox : commentLayout}
{showDeleteConfirmation && deleteConfirmationSection}
{showReplyBox && replyBox}
</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>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment