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, appName, AppUrl } from "../utils/app-urls"; import { createDebouncedValue } from "../utils/hooks/debounced"; import { Modal } from "./modal"; export const [showSearch, setShowSearch] = createSignal(false); const SearchButton = () => { onMount(() => window.addEventListener("keydown", keyEvent)); onCleanup(() => window.removeEventListener("keydown", keyEvent)); const keyEvent = (ev: KeyboardEvent) => { if (document.querySelector("dialog")) return; if ((ev.ctrlKey || ev.metaKey) && ev.key == "k") { ev.preventDefault(); setShowSearch(!showSearch()); } else if (ev.key == "Escape") { ev.preventDefault(); setShowSearch(false); } }; return ( ); }; const Search = () => { const navigate = useNavigate(); let searchInput!: HTMLInputElement; const rpc = new Client({ handler: new CredentialManager({ service: "https://public.api.bsky.app" }), }); onMount(() => { if (useLocation().pathname !== "/") searchInput.focus(); }); const fetchTypeahead = async (input: string) => { if (!input.length) return []; const res = await rpc.get("app.bsky.actor.searchActorsTypeahead", { params: { q: input, limit: 5 }, }); if (res.ok) { return res.data.actors; } return []; }; const [input, setInput] = createSignal(); const [search] = createResource(createDebouncedValue(input, 250), fetchTypeahead); const processInput = (input: string) => { input = input.trim().replace(/^@/, ""); if (!input.length) return; setShowSearch(false); 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 AppUrl]; const path = input.slice(hostLength + 1).split("/"); const uri = appHandleLink[app](path); navigate(`/${uri}`); } } else { navigate(`/at://${input.replace("at://", "")}`); } }; return (
{ e.preventDefault(); processInput(searchInput.value); }} >
searchInput.focus()} > setInput(e.currentTarget.value)} />
); }; const ListUrlsTooltip = () => { const [openList, setOpenList] = createSignal(false); let urls: Record = {}; for (const [appUrl, appView] of Object.entries(appList)) { if (!urls[appView]) urls[appView] = [appUrl as AppUrl]; else urls[appView].push(appUrl as AppUrl); } return ( <> setOpenList(false)}>
Supported URLs
Links that will be parsed automatically, as long as all the data necessary is on the URL.
{([appView, name]) => { return (

{name}

{(url) => ( {url} )}
); }}
); }; export { Search, SearchButton };