import type { PullRequestActor, PullRequestDetailView } from "@t3tools/contracts"; import { ChevronDownIcon, ExternalLinkIcon, FileCode2Icon, GitCommitHorizontalIcon, GitMergeIcon, GitPullRequestClosedIcon, GitPullRequestIcon, MessageSquareIcon, } from "lucide-react"; import { useState, type ReactNode } from "react"; 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 { buildPullRequestTimeline, groupPullRequestTimelineConversations, type PullRequestTimelineEvent, } from "./pullRequestDetail.logic"; import { PullRequestMarkdown } from "./PullRequestMarkdown"; import { PullRequestActorAvatar, PullRequestDiffStat, PullRequestMetaLine, } from "./pullRequestPresentation"; function TimelineBody({ body, markdown, cwd }: { body: string; markdown: boolean; cwd: string }) { return (
{markdown ? ( ) : (

{body}

)}
); } function ActorName({ actor }: { actor: PullRequestActor | null }) { return {actor?.login ?? "ghost"}; } function TimelineMarker({ children, className, }: { children: ReactNode; className?: string | undefined; }) { return ( {children} ); } function IconMarker({ icon, className }: { icon: ReactNode; className?: string | undefined }) { return ( {icon} ); } function ActorTimelineMarker({ actors, className, fallback, muted = false, }: { actors: ReadonlyArray; className?: string | undefined; fallback: ReactNode; muted?: boolean; }) { const actor = actors[0]; return actor === undefined ? ( ) : ( ); } function friendlyReviewState(value: string): string { const words = value.toLowerCase().replaceAll("_", " ").replaceAll("-", " "); return words.replace(/^\w/u, (letter) => letter.toUpperCase()); } function ReviewStateBadge({ state }: { state: string }) { return ( {friendlyReviewState(state)} ); } function OpenOnHostButton({ url, onOpen }: { url: string | null; onOpen: (url: string) => void }) { return url === null ? null : ( ); } function ConversationCard({ event, cwd, onOpen, }: { event: PullRequestTimelineEvent; cwd: string; onOpen: (url: string) => void; }) { return (
{event.title} {event.reviewState ? : null}
{formatRelativeTimeLabel(event.at)} {event.path ? ( {event.path} ) : null}
{event.body ? (
) : null}
); } function uniqueConversationActors(events: ReadonlyArray) { const actors = new Map(); for (const event of events) { const actor = event.actor; if (actor !== null && !actors.has(actor.login)) actors.set(actor.login, actor); } return [...actors.values()]; } function ConversationGroup({ events, cwd, onOpen, }: { events: ReadonlyArray; cwd: string; onOpen: (url: string) => void; }) { const [open, setOpen] = useState(false); const actors = uniqueConversationActors(events); const first = events[0]; if (first === undefined) return null; return (
} muted={!open} />
{events.length.toLocaleString()} {events.length === 1 ? "comment" : "comments"} {actors.length.toLocaleString()} {actors.length === 1 ? "author" : "authors"} ยท{" "} {formatRelativeTimeLabel(first.at)} {open ? (
{events.map((event) => ( ))}
) : null}
); } function CommitEvent({ event, onOpen, }: { event: PullRequestTimelineEvent; onOpen: (oid: string) => void; }) { return ( ); } function LifecycleEvent({ event }: { event: PullRequestTimelineEvent }) { const presentation = event.kind === "opened" ? { icon: , label: "Pull request opened", } : event.kind === "merged" ? { icon: , label: "Pull request merged", } : { icon: , label: "Pull request closed", }; return (
{event.actor ? : null} {presentation.label}
{formatRelativeTimeLabel(event.at)}
); } export function PullRequestTimelineTab({ detail, order, onOpenCommit, }: { detail: PullRequestDetailView; order: "newest" | "oldest"; onOpenCommit: (oid: string) => void; }) { const events = buildPullRequestTimeline(detail); const orderedEvents = order === "newest" ? events : events.toReversed(); const rows = groupPullRequestTimelineConversations(orderedEvents); const openOnHost = (url: string) => { void readLocalApi()?.shell.openExternal(url); }; return (
{rows.map((row) => { if (row.kind === "comments") { return ( ); } const event = row.event; if (event.kind === "commit") { return ; } return ; })}
{events.length === 0 ? (

No activity yet.

) : null}
); }