68 lines · 2.7 KB · TypeScript
RawDownload
import type { ScopedThreadRef } from "@t3tools/contracts";import {  isAtomCommandInterrupted,  squashAtomCommandFailure,} from "@t3tools/client-runtime/state/runtime";import { useCallback } from "react"; import { recordVisitForThread } from "~/browserHistoryStore";import { readLocalApi } from "~/localApi";import { previewEnvironment } from "~/state/preview";import { useAtomCommand } from "~/state/use-atom-command"; import {  canOpenLinksInApp,  resolveBrowserLinkTargetPreference,  resolveLinkTarget,} from "./browserLinkTarget";import { BrowserSettingsReadError, openUrlInPreview } from "./openFileInPreview"; const NO_MODIFIER = { metaKey: false, ctrlKey: false } as const; /** * Opens a URL where the "Open links in" setting says, for buttons that sit * beside a thread but are not markdown anchors: CI check details, a pull * request that has no project to open in the panel. Without a thread there is * nowhere to put an in-app tab, so the link goes to the system browser. * * An in-app open that fails falls back to the system browser rather than * dropping the click: the user asked for the link, and the setting only says * where it should go first. Failed settings reads reject without opening a * browser. The promise also rejects if the system-browser fallback fails. */export function useOpenLink(threadRef: ScopedThreadRef | null | undefined): (  url: string,  options?: {    readonly event?: { readonly metaKey: boolean; readonly ctrlKey: boolean };    /** Thread to open beside when it is not the hook's own, e.g. a sidebar row's. */    readonly threadRef?: ScopedThreadRef | undefined;  },) => Promise<void> {  const openPreview = useAtomCommand(previewEnvironment.open, { reportFailure: false });  return useCallback(    async (url, options = {}) => {      const targetThreadRef = options.threadRef ?? threadRef;      const target = resolveLinkTarget({        url,        event: options.event ?? NO_MODIFIER,        preference: await resolveBrowserLinkTargetPreference(),        canOpenInApp: canOpenLinksInApp(Boolean(targetThreadRef)),      });      if (target === "app" && targetThreadRef) {        const result = await openUrlInPreview({ threadRef: targetThreadRef, url, openPreview });        if (isAtomCommandInterrupted(result)) return;        if (result._tag === "Success") {          recordVisitForThread(targetThreadRef, url);          return;        }        const failure = squashAtomCommandFailure(result);        if (failure instanceof BrowserSettingsReadError) throw failure;        console.error(result.cause);      }      const api = readLocalApi();      if (!api) throw new Error("Link opening is unavailable.");      await api.shell.openExternal(url);    },    [openPreview, threadRef],  );}