diff --git a/apps/web/components/social/comments-section.tsx b/apps/web/components/social/comments-section.tsx index be0dc44..a6bb128 100644 --- a/apps/web/components/social/comments-section.tsx +++ b/apps/web/components/social/comments-section.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, useMemo } from "react"; import { MessageCircle, Reply, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { useTranslations } from "next-intl"; @@ -9,6 +9,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Separator } from "@/components/ui/separator"; import { CommentReactions } from "@/components/social/comment-reactions"; +import { cn } from "@/lib/utils"; type Comment = { id: string; @@ -21,6 +22,8 @@ type Comment = { userAvatarUrl: string | null; }; +const MAX_VISUAL_INDENT = 4; + function timeAgo(dateStr: string) { const diff = Date.now() - new Date(dateStr).getTime(); const mins = Math.floor(diff / 60000); @@ -85,13 +88,15 @@ function CommentForm({ function CommentItem({ comment, - replies, + childrenByParent, + depth, currentUserId, recipeId, onRefresh, }: { comment: Comment; - replies: Comment[]; + childrenByParent: Map; + depth: number; currentUserId?: string; recipeId: string; onRefresh: () => void; @@ -99,6 +104,8 @@ function CommentItem({ const [showReply, setShowReply] = useState(false); const isOwn = comment.userId === currentUserId; const t = useTranslations("social"); + const replies = childrenByParent.get(comment.id) ?? []; + const indented = depth > 0 && depth <= MAX_VISUAL_INDENT; async function deleteComment() { const res = await fetch(`/api/v1/comments/${comment.id}`, { method: "DELETE" }); @@ -107,9 +114,9 @@ function CommentItem({ } return ( -
+
- + {comment.userName.slice(0, 2).toUpperCase()} @@ -151,32 +158,17 @@ function CommentItem({
{replies.length > 0 && ( -
+
{replies.map((reply) => ( -
- - - {reply.userName.slice(0, 2).toUpperCase()} - -
-
- {reply.userName} - {timeAgo(reply.createdAt)} -
-

{reply.content}

- {reply.userId === currentUserId && ( - - )} -
-
+ ))}
)} @@ -203,8 +195,20 @@ export function CommentsSection({ useEffect(() => { void load(); }, [load]); - const topLevel = comments.filter((c) => !c.parentId); - const replies = (parentId: string) => comments.filter((c) => c.parentId === parentId); + const { topLevel, childrenByParent } = useMemo(() => { + const byParent = new Map(); + const top: Comment[] = []; + for (const c of comments) { + if (!c.parentId) { + top.push(c); + continue; + } + const siblings = byParent.get(c.parentId) ?? []; + siblings.push(c); + byParent.set(c.parentId, siblings); + } + return { topLevel: top, childrenByParent: byParent }; + }, [comments]); return (
@@ -229,7 +233,8 @@ export function CommentsSection({ {i > 0 && }