/** * ReplyThread - Displays a threaded tree of replies. * Reconstructs tree from flat API response, assigns depth-first post numbers. * Post numbers start at 2 (post #1 is the topic itself). * Uses viewport-aware indent steps for responsive nesting. * @see specs/prd-web.md Section 4 (Topic Components) */ 'use client' import { useMemo } from 'react' import type { Reply } from '@/lib/api/types' import { cn } from '@/lib/utils' import { buildReplyTree, flattenReplyTree } from '@/lib/build-reply-tree' import { useThreadIndent } from '@/hooks/use-thread-indent' import { ThreadHoverProvider } from '@/context/thread-hover-context' import { ReplyBranch } from './reply-branch' interface ReplyThreadProps { replies: Reply[] topicUri: string onReply?: (target: { uri: string; cid: string; authorHandle: string; snippet: string }) => void onDeleteReply?: () => void currentUserDid?: string className?: string } export function ReplyThread({ replies, topicUri, onReply, onDeleteReply, currentUserDid, className, }: ReplyThreadProps) { const replyCount = replies.length const heading = replyCount === 0 ? 'Replies' : replyCount === 1 ? '1 Reply' : `${replyCount} Replies` const { indentStep, showChevron } = useThreadIndent() const { tree, postNumberMap, allReplies } = useMemo(() => { const builtTree = buildReplyTree(replies, topicUri) const flat = flattenReplyTree(builtTree) const map = new Map() flat.forEach((reply, index) => { map.set(reply.uri, index + 2) }) const replyMap = new Map(replies.map((r) => [r.uri, r])) return { tree: builtTree, postNumberMap: map, allReplies: replyMap } }, [replies, topicUri]) return (

{heading}

{replyCount === 0 ? (

No replies yet. Be the first to respond!

) : ( )}
) }