import { useState } from "react"; import { Check, CheckCircle, Circle, MessageSquare } from "lucide-react"; import { useReviewCommentReplyMutation, useReviewCommentResolveMutation, useReviewCommentUnresolveMutation, } from "@/hooks/usePullRequestDetailQuery"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { IrRenderer } from "@/lib/ir/renderer"; import { extractIrNodes } from "@/lib/ir/parser"; import type { ReviewCommentThread } from "@/client/model"; function relativeTime(dateStr: string) { if (!dateStr) return ""; const diff = Date.now() - new Date(dateStr).getTime(); const mins = Math.floor(diff / 60000); if (mins < 1) return "just now"; if (mins < 60) return `${mins}m ago`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `${hrs}h ago`; const days = Math.floor(hrs / 24); if (days < 30) return `${days}d ago`; return new Date(dateStr).toLocaleDateString(); } interface InlineCommentThreadProps { thread: ReviewCommentThread; namespace: string; repo: string; prNumber: number; } export function InlineCommentThread({ thread, namespace, repo, prNumber, }: InlineCommentThreadProps) { const [showReplyForm, setShowReplyForm] = useState(false); const [replyBody, setReplyBody] = useState(""); const resolveMutation = useReviewCommentResolveMutation(); const unresolveMutation = useReviewCommentUnresolveMutation(); const replyMutation = useReviewCommentReplyMutation(); const root = thread.root; const replies = thread.replies || []; const isResolved = root.resolved; const handleResolve = () => { resolveMutation.mutate({ namespace, repo, prNumber, commentId: root.id }); }; const handleUnresolve = () => { unresolveMutation.mutate({ namespace, repo, prNumber, commentId: root.id }); }; const handleReply = () => { if (!replyBody.trim()) return; replyMutation.mutate( { namespace, repo, prNumber, commentId: root.id, request: { body: replyBody.trim() }, }, { onSuccess: () => { setReplyBody(""); setShowReplyForm(false); }, } ); }; return (
{/* Root comment */}
{(root.author_username || root.author)?.[0]?.toUpperCase() || "?"}
{/* Header */}
{root.author_username || root.author} {relativeTime(root.created_at)} {isResolved && ( Resolved )}
{/* Body */}
{/* Actions */}
{/* Replies */} {replies.length > 0 && (
{replies.map((reply) => (
{(reply.author_username || reply.author)?.[0]?.toUpperCase() || "?"}
{reply.author_username || reply.author} {relativeTime(reply.created_at)}
))}
)} {/* Reply form */} {showReplyForm && (