an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
99
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 665413c98b9af5747e26885708f7eec017db6f4b 150 lines 3.7 kB view raw
1import { AtUri } from "@atproto/api"; 2import { useNavigate, type UseNavigateResult } from "@tanstack/react-router"; 3import { useState } from "react"; 4 5/** 6 * Basically the best equivalent to Search that i can do 7 */ 8export function Import() { 9 const [textInput, setTextInput] = useState<string | undefined>(); 10 const navigate = useNavigate(); 11 12 const handleEnter = () => { 13 if (!textInput) return; 14 handleImport({ 15 text: textInput, 16 navigate, 17 }); 18 }; 19 20 return ( 21 <div className="w-full relative"> 22 <IconMaterialSymbolsSearch className="w-5 h-5 absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 dark:text-gray-500" /> 23 24 <input 25 type="text" 26 placeholder="Import..." 27 value={textInput} 28 onChange={(e) => setTextInput(e.target.value)} 29 onKeyDown={(e) => { 30 if (e.key === "Enter") handleEnter(); 31 }} 32 className="w-full h-12 pl-12 pr-4 rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-400 dark:focus:ring-gray-500 box-border transition" 33 /> 34 </div> 35 ); 36} 37 38function handleImport({ 39 text, 40 navigate, 41}: { 42 text: string; 43 navigate: UseNavigateResult<string>; 44}) { 45 const trimmed = text.trim(); 46 // parse text 47 /** 48 * text might be 49 * 1. bsky dot app url (reddwarf link segments might be uri encoded,) 50 * 2. aturi 51 * 3. plain handle 52 * 4. plain did 53 */ 54 55 // 1. Check if it’s a URL 56 try { 57 const url = new URL(text); 58 const knownHosts = [ 59 "bsky.app", 60 "social.daniela.lol", 61 "deer.social", 62 "reddwarf.whey.party", 63 "reddwarf.app", 64 "main.bsky.dev", 65 "catsky.social", 66 "blacksky.community", 67 "red-dwarf-social-app.whey.party", 68 "zeppelin.social", 69 ]; 70 if (knownHosts.includes(url.hostname)) { 71 // parse path to get URI or handle 72 const path = decodeURIComponent(url.pathname.slice(1)); // remove leading / 73 console.log("BSky URL path:", path); 74 navigate({ 75 to: `/${path}`, 76 }); 77 return; 78 } 79 } catch { 80 // not a URL, continue 81 } 82 83 // 2. Check if text looks like an at-uri 84 try { 85 if (text.startsWith("at://")) { 86 console.log("AT URI detected:", text); 87 const aturi = new AtUri(text); 88 switch (aturi.collection) { 89 case "app.bsky.feed.post": { 90 navigate({ 91 to: "/profile/$did/post/$rkey", 92 params: { 93 did: aturi.host, 94 rkey: aturi.rkey, 95 }, 96 }); 97 return; 98 } 99 case "app.bsky.actor.profile": { 100 navigate({ 101 to: "/profile/$did", 102 params: { 103 did: aturi.host, 104 }, 105 }); 106 return; 107 } 108 // todo add more handlers as more routes are added. like feeds, lists, etc etc thanks! 109 default: { 110 // continue 111 } 112 } 113 } 114 } catch { 115 // continue 116 } 117 118 // 3. Plain handle (starts with @) 119 try { 120 if (text.startsWith("@")) { 121 const handle = text.slice(1); 122 console.log("Handle detected:", handle); 123 navigate({ to: "/profile/$did", params: { did: handle } }); 124 return; 125 } 126 } catch { 127 // continue 128 } 129 130 // 4. Plain DID (starts with did:) 131 try { 132 if (text.startsWith("did:")) { 133 console.log("did detected:", text); 134 navigate({ to: "/profile/$did", params: { did: text } }); 135 return; 136 } 137 } catch { 138 // continue 139 } 140 141 // if all else fails 142 143 // try { 144 // // probably a user? 145 // navigate({ to: "/profile/$did", params: { did: text } }); 146 // return; 147 // } catch { 148 // // continue 149 // } 150}