119 lines · 4.6 KB · TypeScript
RawDownload
import { isAtomCommandInterrupted } from "@t3tools/client-runtime/state/runtime";import type { EnvironmentId, PullRequestRef } from "@t3tools/contracts";import { cloneElement, useState, type ComponentPropsWithoutRef, type ReactElement } from "react"; import { formatRelativeTimeLabel } from "~/timestampFormat";import { pullRequestEnvironment } from "~/state/pullRequests";import { useAtomQueryRunner } from "~/state/use-atom-query-runner";import { useEnvironmentQuery } from "~/state/query"; import { PreviewCard, PreviewCardPopup, PreviewCardTrigger } from "../ui/preview-card";import { PullRequestActorAvatar, resolvePullRequestState } from "./pullRequestPresentation"; interface PullRequestLinkPreviewTarget {  readonly environmentId: EnvironmentId;  readonly input: PullRequestRef;} type PullRequestLinkElement = ReactElement<ComponentPropsWithoutRef<"a">>; export function PullRequestLinkPreview({  link,  originalUrl,  target,  confirmBeforeOpen,  onOpenPullRequest,  onOpenFallback,}: {  link: PullRequestLinkElement;  originalUrl: string;  target: PullRequestLinkPreviewTarget;  confirmBeforeOpen: boolean;  onOpenPullRequest: (url: string) => boolean;  onOpenFallback: (url: string) => Promise<void>;}) {  const [open, setOpen] = useState(false);  const [resolvingClick, setResolvingClick] = useState(false);  const detailQuery = useEnvironmentQuery(    open      ? pullRequestEnvironment.detail({          environmentId: target.environmentId,          input: target.input,        })      : null,  );  const readDetail = useAtomQueryRunner(pullRequestEnvironment.detail, {    reportFailure: false,    reportDefect: false,  });   const trigger = confirmBeforeOpen    ? cloneElement(link, {        onClick: (event) => {          if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;          event.preventDefault();          event.stopPropagation();          if (resolvingClick) return;          setOpen(false);          setResolvingClick(true);          void readDetail({ environmentId: target.environmentId, input: target.input })            .then(async (result) => {              if (isAtomCommandInterrupted(result)) return;              if (result._tag === "Success" && onOpenPullRequest(result.value.url)) return;              await onOpenFallback(originalUrl);            })            .catch((error: unknown) => {              console.error("[pull-request-link-preview] failed to open link", error);            })            .finally(() => setResolvingClick(false));        },      })    : link;  const detail = detailQuery.data;  const state =    detail === null      ? null      : resolvePullRequestState({ state: detail.state, isDraft: detail.isDraft });  const authorLabel =    detail?.author === null      ? "ghost"      : detail?.author.name && detail.author.name !== detail.author.login        ? `${detail.author.name} (@${detail.author.login})`        : (detail?.author.login ?? null);   return (    <PreviewCard open={open} onOpenChange={setOpen}>      <PreviewCardTrigger render={trigger} delay={350} closeDelay={120} />      <PreviewCardPopup align="center" className="w-80 max-w-[calc(100vw-2rem)] p-3">        {detail === null ? (          <p className="text-xs leading-relaxed text-muted-foreground wrap-anywhere">            {detailQuery.isPending ? "Loading pull request details…" : originalUrl}          </p>        ) : (          <div className="min-w-0">            <div className="flex min-w-0 items-center gap-1.5 text-[11px] text-muted-foreground">              <span className="min-w-0 truncate">{detail.repository}</span>              <span className="shrink-0">#{detail.number}</span>              <span aria-hidden>·</span>              {state === null ? null : (                <span className="inline-flex shrink-0 items-center gap-1">                  <state.Icon aria-hidden className={`size-3 ${state.toneClassName}`} />                  {state.label}                </span>              )}            </div>            <p className="mt-1 text-sm font-medium leading-snug text-foreground text-pretty">              {detail.title}            </p>            <div className="mt-2 flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">              <PullRequestActorAvatar actor={detail.author} className="size-4" />              <span className="min-w-0 truncate">{authorLabel}</span>              <span aria-hidden>·</span>              <span className="shrink-0">opened {formatRelativeTimeLabel(detail.createdAt)}</span>            </div>          </div>        )}      </PreviewCardPopup>    </PreviewCard>  );}