import type { EnvironmentId, PullRequestDetailView, PullRequestRef } from "@t3tools/contracts"; import { ArrowDownUpIcon, ChevronRightIcon, HammerIcon, MessageSquareIcon, SendIcon, UsersIcon, } from "lucide-react"; import { useState, type ReactNode } from "react"; import { useAtomCommand } from "~/state/use-atom-command"; import { pullRequestEnvironment } from "~/state/pullRequests"; import { cn } from "~/lib/utils"; import { readLocalApi } from "~/localApi"; import { formatRelativeTimeLabel } from "~/timestampFormat"; import { Button } from "../ui/button"; import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible"; import { Textarea } from "../ui/textarea"; import { toastManager } from "../ui/toast"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; import { PullRequestActorLabel, PullRequestCheckStatusIcon, PullRequestMetaLine, pullRequestCheckStatusLabel, } from "./pullRequestPresentation"; import { PullRequestReviewerPicker } from "./PullRequestReviewerPicker"; import { PullRequestActivityUnavailableState } from "./PullRequestActivityUnavailableState"; import { orderPullRequestComments, pullRequestFindingKey, type PullRequestFinding, } from "./pullRequestDetail.logic"; import { PullRequestMarkdown } from "./PullRequestMarkdown"; import { PullRequestConversationGhost } from "./PullRequestGhosts"; function MetaRow({ icon, label, children, }: { icon: ReactNode; label: string; children: ReactNode; }) { return (
{icon} {label} {children}
); } function Section({ title, count, defaultOpen = true, actions, children, }: { title: string; count?: number; defaultOpen?: boolean; /** Controls riding on the heading row itself. A sibling of the trigger, not a child of it — a button cannot hold a button — and only while open, since they act on what is shown. */ actions?: ReactNode; children: ReactNode; }) { const [open, setOpen] = useState(defaultOpen); return (
{/* Title first, chevron riding to its right, count last: the row reads as a heading with an affordance rather than a tree node. */} {title} {count === undefined ? null : ( {count} )} {open ? actions : null}
{children}
); } function CommentComposer({ environmentId, detail, onCommented, }: { environmentId: EnvironmentId; detail: PullRequestDetailView; onCommented: () => void; }) { const [body, setBody] = useState(""); const [posting, setPosting] = useState(false); const postComment = useAtomCommand(pullRequestEnvironment.comment, { reportFailure: false }); const submit = async () => { const trimmed = body.trim(); if (trimmed.length === 0 || posting) return; setPosting(true); const result = await postComment({ environmentId, input: { projectId: detail.projectId, repository: detail.repository, number: detail.number, body: trimmed, }, }); setPosting(false); if (result._tag === "Failure") { toastManager.add({ type: "error", title: "Could not post the comment" }); return; } setBody(""); onCommented(); }; return (