163 lines · 5.8 KB · TypeScript
RawDownload
import type {  EnvironmentId,  PullRequestCheck,  PullRequestChecksState,  PullRequestRef,  ScopedThreadRef,} from "@t3tools/contracts"; import { useOpenLink } from "~/browser/useOpenLink";import { cn } from "~/lib/utils";import { pullRequestEnvironment } from "~/state/pullRequests";import { useEnvironmentQuery } from "~/state/query"; import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover";import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";import { toastManager } from "../ui/toast";import {  PullRequestCheckStatusIcon,  pullRequestCheckStatusLabel,  pullRequestChecksStatePresentation,  summarizePullRequestChecks,} from "./pullRequestPresentation"; /** * The checks behind the rollup, for a row that only carries the rollup. Mounted by the popup, so * the read starts when somebody opens it rather than once per row of a listing — the detail read * is a request per pull request, and a page of them at rest would be a hundred. */function LazyChecksBody({  environmentId,  reference,  threadRef,}: {  environmentId: EnvironmentId;  reference: PullRequestRef;  threadRef: ScopedThreadRef | null;}) {  const detailQuery = useEnvironmentQuery(    pullRequestEnvironment.detail({ environmentId, input: reference }),  );  if (detailQuery.error !== null) {    return <p className="text-muted-foreground text-xs">{detailQuery.error}</p>;  }  if (detailQuery.data === null) {    return (      <p className="text-muted-foreground text-xs">        {detailQuery.isPending ? "Loading checks…" : "No checks reported"}      </p>    );  }  return <ChecksBody checks={detailQuery.data.checks} threadRef={threadRef} />;} function ChecksBody({  checks,  threadRef,}: {  checks: ReadonlyArray<PullRequestCheck>;  threadRef: ScopedThreadRef | null;}) {  const openLink = useOpenLink(threadRef);  if (checks.length === 0) {    return <p className="text-muted-foreground text-xs">No checks reported</p>;  }  return (    <ul className="flex flex-col gap-1">      {/* Keyed by position as well as by name: the host is the one that decides how many runs          share a name, and a repeated key is a rendering fault rather than a wrong list. */}      {checks.map((check, index) => (        <li key={`${index}:${check.name}`} className="flex items-center gap-2 text-xs">          <PullRequestCheckStatusIcon status={check.status} />          <Tooltip>            <TooltipTrigger              render={<span className="min-w-0 flex-1 truncate">{check.name}</span>}            />            <TooltipPopup side="top">{check.description ?? check.name}</TooltipPopup>          </Tooltip>          <span className="shrink-0 text-muted-foreground">            {pullRequestCheckStatusLabel(check)}          </span>          {check.url === null ? null : (            <button              type="button"              className="shrink-0 text-primary hover:underline"              onClick={() => {                if (!check.url) return;                void openLink(check.url).catch((error: unknown) => {                  console.error(error);                  toastManager.add({ type: "error", title: "Unable to open check details" });                });              }}            >              Details            </button>          )}        </li>      ))}    </ul>  );} /** * The checks indicator and what it opens, in both places a change request is shown: a listing * row, which knows only the rollup, and the detail header, which is already holding every check. * * `checks` decides between the two. Given them, nothing is read; without them, the popup reads * the detail itself, which is why the row must also say which environment it came from. */export function PullRequestChecksPopover({  checksState,  checks,  environmentId,  reference,  threadRef = null,  className,}: {  checksState: PullRequestChecksState;  /** The checks already in hand, for the detail header. Absent on a listing row. */  checks?: ReadonlyArray<PullRequestCheck>;  environmentId?: EnvironmentId;  reference?: PullRequestRef;  /** Thread the popover sits beside; a listing row has none. */  threadRef?: ScopedThreadRef | null;  className?: string;}) {  const presentation = pullRequestChecksStatePresentation(checksState);  // Counts beat the rollup's own wording where they are known, the way GitHub's own header reads.  const summary = checks === undefined ? null : summarizePullRequestChecks(checks);  return (    <Popover>      {/* A listing row is itself a button, so the trigger renders as a span: a nested button is          not valid inside one. The click is stopped here so opening the checks does not also          select the row it sits on. */}      <PopoverTrigger        nativeButton={false}        render={          <span            role="button"            tabIndex={0}            aria-label={`Checks: ${presentation.label}`}            className={cn("inline-flex shrink-0 cursor-pointer items-center", className)}          />        }        onClick={(event) => event.stopPropagation()}      >        <presentation.Icon aria-hidden className={cn("size-3.5", presentation.toneClassName)} />      </PopoverTrigger>      <PopoverPopup align="start" className="w-80 max-w-full" side="bottom">        <p className="mb-2 font-medium text-sm">{presentation.label}</p>        {summary === null ? null : <p className="mb-2 text-muted-foreground text-xs">{summary}</p>}        {checks !== undefined ? (          <ChecksBody checks={checks} threadRef={threadRef} />        ) : environmentId !== undefined && reference !== undefined ? (          <LazyChecksBody            environmentId={environmentId}            reference={reference}            threadRef={threadRef}          />        ) : null}      </PopoverPopup>    </Popover>  );}