import type { LocalApi } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { type MouseEvent, useCallback } from "react"; import { stackedThreadToast, toastManager } from "../components/ui/toast"; import { readLocalApi } from "../localApi"; export class PullRequestLinkOpenError extends Schema.TaggedErrorClass()( "PullRequestLinkOpenError", { targetOrigin: Schema.NullOr(Schema.String), cause: Schema.Defect(), }, ) { static fromCause(targetUrl: string, cause: unknown): PullRequestLinkOpenError { let targetOrigin: string | null = null; try { targetOrigin = new URL(targetUrl).origin; } catch { // Keep malformed URLs out of diagnostics while preserving the open failure below. } return new PullRequestLinkOpenError({ targetOrigin, cause }); } override get message(): string { return this.targetOrigin === null ? "Unable to open pull request link." : `Unable to open pull request link at ${this.targetOrigin}.`; } } export async function openPullRequestLink( shell: Pick, targetUrl: string, ): Promise { try { await shell.openExternal(targetUrl); } catch (cause) { throw PullRequestLinkOpenError.fromCause(targetUrl, cause); } } /** * Returns a click handler that opens a pull request URL in the system browser. * * Stops event propagation/default so activating the link does not also trigger * an enclosing row or trigger (e.g. opening the branch dropdown), and surfaces a * toast when the local API is unavailable or the open fails. */ export function useOpenPrLink() { return useCallback((event: MouseEvent, prUrl: string) => { event.preventDefault(); event.stopPropagation(); const api = readLocalApi(); if (!api) { toastManager.add({ type: "error", title: "Link opening is unavailable.", }); return; } void openPullRequestLink(api.shell, prUrl).catch((error) => { console.error(error); toastManager.add( stackedThreadToast({ type: "error", title: "Unable to open pull request link", description: error instanceof Error ? error.message : "An error occurred.", }), ); }); }, []); }