"use client"; import { useRouter } from "next/navigation"; import { useState, useTransition, type ReactNode } from "react"; import { Surface, Textarea } from "@unlocalhosted/metalui"; import { replyThreadAction } from "@/app/actions/pulls"; import { AgentAvatar } from "@/components/agent"; import { CheckIcon } from "@/components/icons"; import { Alert, Avatar, Button, Label } from "@/components/ui"; export type ThreadComment = { id: string; author: string; agent: string | null; when: string; body: ReactNode }; /** A review thread on a line of the changes: its comments, a reply box, and resolving it. */ export function ThreadView({ owner, name, number, rootId, resolved, outdated, comments, code, canReply, canResolve, }: { owner: string; name: string; number: number; rootId: string; resolved: boolean; outdated: boolean; comments: ThreadComment[]; /** The lines it's about, for threads shown away from the diff. */ code?: ReactNode; canReply: boolean; canResolve: boolean; }) { const router = useRouter(); const [open, setOpen] = useState(!resolved); const [replying, setReplying] = useState(false); const [body, setBody] = useState(""); const [error, setError] = useState(null); const [pending, startTransition] = useTransition(); const send = (input: { body: string; resolve?: boolean }) => startTransition(async () => { const result = await replyThreadAction(owner, name, number, rootId, input); if (result.error) return setError(result.error); setError(null); setBody(""); setReplying(false); if (input.resolve !== undefined) setOpen(!input.resolve); router.refresh(); }); return ( {open && (
{code}
    {comments.map((c) => (
  • {c.agent ? : }

    {c.agent ?? c.author} {c.agent && <> for {c.author}} · {c.when}

    {c.body}
  • ))}
{(canReply || canResolve) && (
{replying && (