diff --git a/apps/web/features/issues/components/comment-card.tsx b/apps/web/features/issues/components/comment-card.tsx index b2b9727b..537b469a 100644 --- a/apps/web/features/issues/components/comment-card.tsx +++ b/apps/web/features/issues/components/comment-card.tsx @@ -193,6 +193,33 @@ function CommentCard({ }: CommentCardProps) { const { getActorName } = useActorName(); const [open, setOpen] = useState(true); + const [editing, setEditing] = useState(false); + const [editContent, setEditContent] = useState(""); + + const isOwn = entry.actor_type === "member" && entry.actor_id === currentUserId; + const isTemp = entry.id.startsWith("temp-"); + + const startEdit = () => { + setEditContent(entry.content ?? ""); + setEditing(true); + }; + + const cancelEdit = () => { + setEditing(false); + setEditContent(""); + }; + + const saveEdit = async () => { + const trimmed = editContent.trim(); + if (!trimmed) return; + try { + await onEdit(entry.id, trimmed); + setEditing(false); + setEditContent(""); + } catch { + toast.error("Failed to update comment"); + } + }; // Collect all nested replies recursively into a flat list const allNestedReplies: TimelineEntry[] = []; @@ -207,23 +234,36 @@ function CommentCard({ const replyCount = allNestedReplies.length; const contentPreview = (entry.content ?? "").replace(/\n/g, " ").slice(0, 80); + const reactions = entry.reactions ?? []; return ( - + - {/* Collapsed header — always visible */} -
- - - - + {/* Header — always visible, acts as toggle */} +
+
+ + + + + {getActorName(entry.actor_type, entry.actor_id)} - - {timeAgo(entry.created_at)} - + + + {timeAgo(entry.created_at)} + + } + /> + + {new Date(entry.created_at).toLocaleString()} + + + {!open && contentPreview && ( - + {contentPreview}{(entry.content ?? "").length > 80 ? "..." : ""} )} @@ -232,19 +272,81 @@ function CommentCard({ {replyCount} {replyCount === 1 ? "reply" : "replies"} )} - + + {open && !isTemp && ( + + + + + } + /> + + { + navigator.clipboard.writeText(entry.content ?? ""); + toast.success("Copied"); + }}> + + Copy + + {isOwn && ( + <> + + + + Edit + + + onDelete(entry.id)} variant="destructive"> + + Delete + + + )} + + + )} +
- {/* Expanded content */} + {/* Collapsible body */} -
- + {/* Parent comment body */} +
+ {editing ? ( +
{ e.preventDefault(); saveEdit(); }} + className="pl-10" + > + setEditContent(e.target.value)} + aria-label="Edit comment" + className="w-full text-sm bg-transparent border-b border-border outline-none py-1" + onKeyDown={(e) => { if (e.key === "Escape") cancelEdit(); }} + /> +
+ + +
+
+ ) : ( + <> +
+ {entry.content ?? ""} +
+ {!isTemp && ( + onToggleReaction(entry.id, emoji)} + className="mt-1.5 pl-10" + /> + )} + + )}
{/* Replies */}