import { page } from "$app/state"; import { avatarUrl } from "$lib/avatar"; export function resolveOgreUrl(baseUrl?: string): string { if (baseUrl) return baseUrl; try { return page.data.publicConfig?.ogreUrl ?? ""; } catch { return ""; } } export function resolveAppUrl(appUrl?: string): string { if (appUrl) return appUrl.replace(/\/+$/, ""); try { return page.url.origin; } catch { return "https://tangled.org"; } } const ogAvatarUrl = (did: string | undefined, appUrl?: string): string | undefined => did ? `${resolveAppUrl(appUrl)}${avatarUrl(did, 256, "jpeg")}` : undefined; export interface OgRepoPayload { type: "repository"; repoName: string; ownerHandle: string; stars?: number; pulls?: number; issues?: number; createdAt?: string; avatarUrl?: string; languages?: { color: string; percentage: number }[]; } export interface OgIssuePayload { type: "issue"; repoName: string; ownerHandle: string; authorHandle: string; title: string; issueNumber: number | string; status: "open" | "closed"; avatarUrl?: string; authorAvatarUrl?: string; commentCount?: number; createdAt: string; } export interface OgPullPayload { type: "pullRequest"; repoName: string; ownerHandle: string; authorHandle: string; title: string; pullRequestNumber: number | string; status: "open" | "closed" | "merged"; avatarUrl?: string; authorAvatarUrl?: string; commentCount?: number; createdAt: string; } export type OgPayload = OgRepoPayload | OgIssuePayload | OgPullPayload; export function encodeOgPayload(payload: OgPayload): string { const entries = Object.entries(payload).filter( ([_, v]) => v != null && v !== "" && !(Array.isArray(v) && v.length === 0) ); const json = JSON.stringify(Object.fromEntries(entries)); if (typeof Buffer !== "undefined") { return Buffer.from(json, "utf-8").toString("base64url"); } const bytes = new TextEncoder().encode(json); let bin = ""; for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]); return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } export function ogCardUrl(payload: OgPayload, baseUrl?: string): string { return `${resolveOgreUrl(baseUrl)}/card?payload=${encodeOgPayload(payload)}`; } export function repoCard( repo: { name: string; ownerHandle: string; ownerDid?: string; createdAt?: string }, counts?: { stars?: number; pulls?: number; issues?: number } | null, baseUrl?: string, languages?: { color: string; percentage: number }[] | null, appUrl?: string ): string { return ogCardUrl( { type: "repository", repoName: repo.name, ownerHandle: repo.ownerHandle, stars: counts?.stars, pulls: counts?.pulls, issues: counts?.issues, createdAt: repo.createdAt, avatarUrl: ogAvatarUrl(repo.ownerDid, appUrl), languages: languages ?? undefined }, baseUrl ); } export function issueCard( repo: { name: string; ownerHandle: string; ownerDid?: string }, issue: { title: string; issueNumber?: number | string; rkey?: string; state?: string; createdAt: string; author?: { handle?: string; avatar?: string; did?: string }; authorHandle?: string; authorDid?: string; commentCount?: number; }, baseUrl?: string, appUrl?: string ): string { const authorHandle = issue.author?.handle || issue.authorHandle || ""; const authorDid = issue.author?.did || issue.authorDid; return ogCardUrl( { type: "issue", repoName: repo.name, ownerHandle: repo.ownerHandle, authorHandle, title: issue.title || "Issue", issueNumber: issue.issueNumber ?? issue.rkey ?? "", status: issue.state === "closed" ? "closed" : "open", createdAt: issue.createdAt, avatarUrl: ogAvatarUrl(repo.ownerDid, appUrl), authorAvatarUrl: issue.author?.avatar || ogAvatarUrl(authorDid, appUrl), commentCount: issue.commentCount }, baseUrl ); } export function pullCard( repo: { name: string; ownerHandle: string; ownerDid?: string }, pull: { title?: string; rkey: string; state?: "open" | "closed" | "merged"; createdAt: string; author?: { handle?: string; did?: string }; }, commentCount?: number, baseUrl?: string, appUrl?: string ): string { return ogCardUrl( { type: "pullRequest", repoName: repo.name, ownerHandle: repo.ownerHandle, authorHandle: pull.author?.handle || "", title: pull.title || "Pull Request", pullRequestNumber: pull.rkey, status: pull.state ?? "open", createdAt: pull.createdAt, avatarUrl: ogAvatarUrl(repo.ownerDid, appUrl), authorAvatarUrl: ogAvatarUrl(pull.author?.did, appUrl), commentCount }, baseUrl ); }