import * as Schema from "effect/Schema"; import { Linking } from "react-native"; const ExternalUrlTarget = Schema.Literals([ "file-preview", "markdown-link", "pull-request", "provider-auth", ]); export type ExternalUrlTarget = typeof ExternalUrlTarget.Type; export class ExternalUrlOpenError extends Schema.TaggedError()( "ExternalUrlOpenError", { target: ExternalUrlTarget, scheme: Schema.String, host: Schema.optional(Schema.String), cause: Schema.Defect(), }, ) { override get message(): string { return `Failed to open ${this.target} URL with the ${this.scheme} scheme.`; } } function externalUrlMetadata(url: string): { readonly scheme: string; readonly host?: string } { try { const parsed = new URL(url); return { scheme: parsed.protocol.replace(/:$/, "") || "unknown", host: parsed.hostname || undefined, }; } catch { return { scheme: /^([a-z][a-z\d+.-]*):/i.exec(url)?.[1]?.toLowerCase() ?? "unknown", }; } } export async function tryOpenExternalUrl(url: string, target: ExternalUrlTarget): Promise { try { await Linking.openURL(url); return true; } catch (cause) { const error = new ExternalUrlOpenError({ target, ...externalUrlMetadata(url), cause }); console.error(error.message, { _tag: error._tag, target: error.target, scheme: error.scheme, host: error.host, stack: error.stack, }); return false; } }