krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.5.0: HutchSafariExtension/Resources/background.js · raw

  1const supportedHosts = new Set([
  2  "git.sr.ht",
  3  "hg.sr.ht",
  4  "todo.sr.ht",
  5  "builds.sr.ht",
  6  "lists.sr.ht",
  7  "meta.sr.ht",
  8  "sr.ht",
  9]);
 10
 11const browserAPI = globalThis.browser ?? globalThis.chrome;
 12
 13function isOwnerRootPath(path) {
 14  const components = path.split("/").filter(Boolean);
 15  return components.length === 1 && components[0].startsWith("~");
 16}
 17
 18function normalizedPath(pathname) {
 19  return pathname.split("/").filter(Boolean).join("/");
 20}
 21
 22function deepLinkPath(hostname, path) {
 23  const components = path.split("/").filter(Boolean);
 24  if (
 25    hostname === "sr.ht" &&
 26    components.length === 2 &&
 27    components[0] === "projects" &&
 28    components[1].startsWith("~")
 29  ) {
 30    return components[1];
 31  }
 32
 33  return path;
 34}
 35
 36function deepLinkService(hostname, path) {
 37  if (isOwnerRootPath(path)) {
 38    return "lookup";
 39  }
 40
 41  switch (hostname) {
 42    case "git.sr.ht":
 43      return "git";
 44    case "hg.sr.ht":
 45      return "hg";
 46    case "todo.sr.ht":
 47      return "todo";
 48    case "builds.sr.ht":
 49      return "builds";
 50    case "lists.sr.ht":
 51      return "lists";
 52    default:
 53      return "lookup";
 54  }
 55}
 56
 57function hutchDeepLinkFor(rawURL) {
 58  let url;
 59  try {
 60    url = new URL(rawURL);
 61  } catch {
 62    return null;
 63  }
 64
 65  if (url.protocol !== "https:" || !supportedHosts.has(url.hostname)) {
 66    return null;
 67  }
 68
 69  const path = deepLinkPath(url.hostname, normalizedPath(url.pathname));
 70  const service = deepLinkService(url.hostname, path);
 71  return `hutch://${service}${path ? `/${path}` : ""}${url.search}${url.hash}`;
 72}
 73
 74function showUnsupportedMessage(tabId) {
 75  if (!tabId) {
 76    return;
 77  }
 78
 79  const message = "Open in Hutch supports git, hg, todo, builds, lists, meta, and sr.ht pages.";
 80
 81  if (browserAPI?.scripting?.executeScript) {
 82    browserAPI.scripting.executeScript({
 83      target: { tabId },
 84      func: (text) => {
 85        window.alert(text);
 86      },
 87      args: [message],
 88    });
 89    return;
 90  }
 91
 92  browserAPI?.tabs?.sendMessage?.(tabId, { type: "hutchUnsupportedPage", message });
 93}
 94
 95function openURL(tabId, url) {
 96  if (browserAPI?.tabs?.update && tabId) {
 97    browserAPI.tabs.update(tabId, { url });
 98    return;
 99  }
100
101  if (browserAPI?.tabs?.create) {
102    browserAPI.tabs.create({ url });
103  }
104}
105
106browserAPI.action.onClicked.addListener((tab) => {
107  const hutchURL = tab?.url ? hutchDeepLinkFor(tab.url) : null;
108  if (!hutchURL) {
109    showUnsupportedMessage(tab?.id);
110    return;
111  }
112
113  console.debug("[Hutch] Opening deep link", { sourceURL: tab.url, hutchURL });
114  openURL(tab.id, hutchURL);
115});