import { Group, Text, Box, Badge } from "@mantine/core"; import React, { useEffect, useRef, useState } from "react"; import classes from "./comment.module.css"; import { useAtom, useAtomValue } from "jotai"; import { useTimeAgo } from "@/hooks/use-time-ago"; import CommentEditor from "@/features/comment/components/comment-editor"; import { pageEditorAtom } from "@/features/editor/atoms/editor-atoms"; import CommentActions from "@/features/comment/components/comment-actions"; import CommentMenu from "@/features/comment/components/comment-menu"; import { useHasFeature } from "@/ee/hooks/use-feature"; import { Feature } from "@/ee/features"; import ResolveComment from "@/ee/comment/components/resolve-comment"; import { useHover } from "@mantine/hooks"; import { useDeleteCommentMutation, useUpdateCommentMutation, } from "@/features/comment/queries/comment-query"; import { useResolveCommentMutation } from "@/ee/comment/queries/comment-query"; import { IComment } from "@/features/comment/types/comment.types"; import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; import { currentUserAtom } from "@/features/user/atoms/current-user-atom.ts"; import { useTranslation } from "react-i18next"; interface CommentListItemProps { comment: IComment; pageId: string; canComment: boolean; userSpaceRole?: string; } function CommentListItem({ comment, pageId, canComment, userSpaceRole, }: CommentListItemProps) { const { t } = useTranslation(); const { hovered, ref } = useHover(); const [isEditing, setIsEditing] = useState(false); const [isLoading, setIsLoading] = useState(false); const editor = useAtomValue(pageEditorAtom); const [content, setContent] = useState(comment.content); const editContentRef = useRef(null); const updateCommentMutation = useUpdateCommentMutation(); const deleteCommentMutation = useDeleteCommentMutation(comment.pageId); const resolveCommentMutation = useResolveCommentMutation(); const [currentUser] = useAtom(currentUserAtom); const canResolve = useHasFeature(Feature.COMMENT_RESOLUTION); const createdAtAgo = useTimeAgo(comment.createdAt); useEffect(() => { setContent(comment.content); }, [comment]); async function handleUpdateComment() { try { setIsLoading(true); const commentToUpdate = { commentId: comment.id, content: JSON.stringify(editContentRef.current ?? content), }; await updateCommentMutation.mutateAsync(commentToUpdate); if (editContentRef.current) { setContent(editContentRef.current); editContentRef.current = null; } setIsEditing(false); } catch (error) { console.error("Failed to update comment:", error); } finally { setIsLoading(false); } } async function handleDeleteComment() { try { await deleteCommentMutation.mutateAsync(comment.id); editor?.commands.unsetComment(comment.id); } catch (error) { console.error("Failed to delete comment:", error); } } async function handleResolveComment() { if (!canResolve) return; try { const isResolved = comment.resolvedAt != null; await resolveCommentMutation.mutateAsync({ commentId: comment.id, pageId: comment.pageId, resolved: !isResolved, }); if (editor) { editor.commands.setCommentResolved(comment.id, !isResolved); } } catch (error) { console.error("Failed to toggle resolved state:", error); } } function handleCommentClick(comment: IComment) { const el = document.querySelector( `.comment-mark[data-comment-id="${comment.id}"]`, ); if (el) { el.scrollIntoView({ behavior: "smooth", block: "center" }); el.classList.add("comment-highlight"); setTimeout(() => { el.classList.remove("comment-highlight"); }, 3000); } } function handleEditToggle() { setIsEditing(true); } function cancelEdit() { editContentRef.current = null; setIsEditing(false); } return (
{comment.creator.name}
{!comment.parentCommentId && canComment && canResolve && ( )} {(currentUser?.user?.id === comment.creatorId || userSpaceRole === 'admin') && ( )}
{createdAtAgo}
{!comment.parentCommentId && comment?.selection && ( handleCommentClick(comment)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); handleCommentClick(comment); } }} role="button" tabIndex={0} aria-label={t("Jump to comment selection")} > {comment?.selection} )} {!isEditing ? ( ) : ( <> { editContentRef.current = newContent; }} onSave={handleUpdateComment} autofocus={true} /> )}
); } export default CommentListItem;