From 42c5117bcdd3158d24cb275f4c5275619a31ed51 Mon Sep 17 00:00:00 2001 From: ayla Date: Fri, 3 Oct 2025 16:02:38 -0300 Subject: [PATCH] add handling of app links --- src/components/search.tsx | 28 +++++----- src/utils/app-urls.ts | 112 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 src/utils/app-urls.ts diff --git a/src/components/search.tsx b/src/components/search.tsx index 6254f60..5dcd98a 100644 --- a/src/components/search.tsx +++ b/src/components/search.tsx @@ -2,6 +2,7 @@ import { Client, CredentialManager } from "@atcute/client"; import { A, useLocation, useNavigate } from "@solidjs/router"; import { createResource, createSignal, For, onCleanup, onMount, Show } from "solid-js"; import { isTouchDevice } from "../layout"; +import { appHandleLink, appList } from "../utils/app-urls"; import { createDebouncedValue } from "../utils/hooks/debounced"; export const [showSearch, setShowSearch] = createSignal(false); @@ -68,22 +69,23 @@ const Search = () => { setShowSearch(false); if (input === "me" && localStorage.getItem("lastSignedIn") !== null) { navigate(`/at://${localStorage.getItem("lastSignedIn")}`); - } else if ( - !input.startsWith("https://bsky.app/") && - (input.startsWith("https://") || input.startsWith("http://")) - ) { - navigate(`/${input.replace("https://", "").replace("http://", "").replace("/", "")}`); } else if (search()?.length) { navigate(`/at://${search()![0].did}`); + } else if (input.startsWith("https://") || input.startsWith("http://")) { + const hostLength = input.indexOf("/", 8); + const host = input.slice(0, hostLength).replace("https://", "").replace("http://", ""); + + if (!(host in appList)) { + navigate(`/${input.replace("https://", "").replace("http://", "").replace("/", "")}`); + } else { + const app = appList[host as keyof typeof appList]; + const path = input.slice(hostLength + 1).split("/"); + + const uri = appHandleLink[app](path); + navigate(`/${uri}`); + } } else { - const uri = input - .replace("at://", "") - .replace("https://bsky.app/profile/", "") - .replace("/post/", "/app.bsky.feed.post/"); - const uriParts = uri.split("/"); - navigate( - `/at://${uriParts[0]}${uriParts.length > 1 ? `/${uriParts.slice(1).join("/")}` : ""}`, - ); + navigate(`/at://${input}`); } setShowSearch(false); }; diff --git a/src/utils/app-urls.ts b/src/utils/app-urls.ts new file mode 100644 index 0000000..989b68f --- /dev/null +++ b/src/utils/app-urls.ts @@ -0,0 +1,112 @@ +export type AppUrl = `${string}.${string}` | `localhost:${number}`; +type AppUrlObject = Record; +type AppHandleLinkObject = Record string>; + +export enum App { + Bluesky = 0, + Tangled = 1, + Whitewind = 2, + Frontpage = 3, + Pinksea = 4, + Linkat = 5, +} + +export const appList: AppUrlObject = { + "localhost:19006": App.Bluesky, + "blacksky.community": App.Bluesky, + "bsky.app": App.Bluesky, + "catsky.social": App.Bluesky, + "deer.aylac.top": App.Bluesky, + "deer-social-ayla.pages.dev": App.Bluesky, + "deer.social": App.Bluesky, + "main.bsky.dev": App.Bluesky, + "social.daniela.lol": App.Bluesky, + "tangled.org": App.Tangled, + "whtwnd.com": App.Whitewind, + "frontpage.fyi": App.Frontpage, + "pinksea.art": App.Pinksea, + "linkat.blue": App.Linkat, +}; + +export const appHandleLink: AppHandleLinkObject = { + [App.Bluesky]: (path) => { + const baseType = path[0]; + const user = path[1]; + + if (baseType === "profile") { + if (path[2]) { + const type = path[2]; + const rkey = path[3]; + + if (type === "post") { + return `at://${user}/app.bsky.feed.post/${rkey}`; + } else if (type === "list") { + return `at://${user}/app.bsky.graph.list/${rkey}`; + } else if (type === "feed") { + return `at://${user}/app.bsky.feed.generator/${rkey}`; + } else if (type === "follows") { + return `at://${user}/app.bsky.graph.follow/${rkey}`; + } + } else { + return `at://${user}`; + } + } else if (baseType === "starter-pack") { + return `at://${user}/app.bsky.graph.starterpack/${path[2]}`; + } + return `at://${user}`; + }, + [App.Tangled]: (path) => { + if (path[0] === "strings") { + return `at://${path[1]}/sh.tangled.string/${path[2]}`; + } + + let query: string | undefined; + if (path[path.length - 1].includes("?")) { + const split = path[path.length - 1].split("?"); + query = split[1]; + path[path.length - 1] = split[0]; + } + + const user = path[0].replace("@", ""); + + if (path.length === 1) { + if (query === "tab=repos") { + return `at://${user}/sh.tangled.repo`; + } else if (query === "tab=starred") { + return `at://${user}/sh.tangled.feed.star`; + } else if (query === "tab=strings") { + return `at://${user}/sh.tangled.string`; + } + } else if (path.length === 2) { + // no way to convert the repo name to an rkey afaik + // same reason why there's nothing related to issues in here + return `at://${user}/sh.tangled.repo`; + } + + return `at://${user}`; + }, + [App.Whitewind]: (path) => { + if (path.length === 2) { + return `at://${path[0]}/com.whtwnd.blog.entry/${path[1]}`; + } + + return `at://${path[0]}/com.whtwnd.blog.entry`; + }, + [App.Frontpage]: (path) => { + if (path.length === 3) { + return `at://${path[1]}/fyi.unravel.frontpage.post/${path[2]}`; + } else if (path.length === 5) { + return `at://${path[3]}/fyi.unravel.frontpage.comment/${path[4]}`; + } + + return `at://${path[0]}`; + }, + [App.Pinksea]: (path) => { + if (path.length === 2) { + return `at://${path[0]}/com.shinolabs.pinksea.oekaki/${path[1]}`; + } + + return `at://${path[0]}`; + }, + [App.Linkat]: (path) => `at://${path[0]}/blue.linkat.board/self`, +}; -- 2.43.0