atmosphere explorer

reactive plcDirectory setting

handle.invalid 4698a710 e13f3356

verified
+51 -33
+8 -3
src/auth/oauth-config.ts
··· 1 - import { configureOAuth, defaultIdentityResolver } from "@atcute/oauth-browser-client"; 1 + import { LocalActorResolver } from "@atcute/identity-resolver"; 2 + import { configureOAuth } from "@atcute/oauth-browser-client"; 2 3 import { didDocumentResolver, handleResolver } from "../utils/api"; 3 4 5 + const reactiveDidDocumentResolver = { 6 + resolve: async (did: string) => didDocumentResolver().resolve(did as any), 7 + }; 8 + 4 9 configureOAuth({ 5 10 metadata: { 6 11 client_id: import.meta.env.VITE_OAUTH_CLIENT_ID, 7 12 redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URL, 8 13 }, 9 - identityResolver: defaultIdentityResolver({ 14 + identityResolver: new LocalActorResolver({ 10 15 handleResolver: handleResolver, 11 - didDocumentResolver: didDocumentResolver, 16 + didDocumentResolver: reactiveDidDocumentResolver, 12 17 }), 13 18 });
+3 -4
src/layout.tsx
··· 12 12 import { Search, SearchButton, showSearch } from "./components/search.jsx"; 13 13 import { themeEvent } from "./components/theme.jsx"; 14 14 import { resolveHandle } from "./utils/api.js"; 15 + import { plcDirectory } from "./views/settings.jsx"; 15 16 16 17 export const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 1; 17 18 ··· 187 188 </Show> 188 189 </div> 189 190 <NotificationContainer /> 190 - <Show 191 - when={localStorage.plcDirectory && localStorage.plcDirectory !== "https://plc.directory"} 192 - > 191 + <Show when={plcDirectory() !== "https://plc.directory"}> 193 192 <div class="dark:bg-dark-500 fixed right-0 bottom-0 left-0 z-10 flex items-center justify-center bg-neutral-100 px-3 py-1 text-xs"> 194 193 <span> 195 - PLC directory: <span class="font-medium">{localStorage.plcDirectory}</span> 194 + PLC directory: <span class="font-medium">{plcDirectory()}</span> 196 195 </span> 197 196 </div> 198 197 </Show>
+21 -13
src/utils/api.ts
··· 16 16 import { DohJsonLexiconAuthorityResolver, LexiconSchemaResolver } from "@atcute/lexicon-resolver"; 17 17 import { Did, Handle } from "@atcute/lexicons"; 18 18 import { AtprotoDid, isHandle, Nsid } from "@atcute/lexicons/syntax"; 19 + import { createMemo } from "solid-js"; 19 20 import { createStore } from "solid-js/store"; 20 21 import { setPDS } from "../components/navbar"; 22 + import { plcDirectory } from "../views/settings"; 21 23 22 - export const didDocumentResolver = new CompositeDidDocumentResolver({ 23 - methods: { 24 - plc: new PlcDidDocumentResolver({ 25 - apiUrl: localStorage.getItem("plcDirectory") ?? "https://plc.directory", 24 + export const didDocumentResolver = createMemo( 25 + () => 26 + new CompositeDidDocumentResolver({ 27 + methods: { 28 + plc: new PlcDidDocumentResolver({ 29 + apiUrl: plcDirectory(), 30 + }), 31 + web: new AtprotoWebDidDocumentResolver(), 32 + }, 26 33 }), 27 - web: new AtprotoWebDidDocumentResolver(), 28 - }, 29 - }); 34 + ); 30 35 31 36 export const handleResolver = new CompositeHandleResolver({ 32 37 strategy: "dns-first", ··· 40 45 dohUrl: "https://dns.google/resolve?", 41 46 }); 42 47 43 - const schemaResolver = new LexiconSchemaResolver({ 44 - didDocumentResolver: didDocumentResolver, 45 - }); 48 + const schemaResolver = createMemo( 49 + () => 50 + new LexiconSchemaResolver({ 51 + didDocumentResolver: didDocumentResolver(), 52 + }), 53 + ); 46 54 47 55 const didPDSCache: Record<string, string> = {}; 48 56 const [labelerCache, setLabelerCache] = createStore<Record<string, string>>({}); ··· 54 62 throw new Error("Not a valid DID identifier"); 55 63 } 56 64 57 - const doc = await didDocumentResolver.resolve(did); 65 + const doc = await didDocumentResolver().resolve(did); 58 66 didDocCache[did] = doc; 59 67 60 68 const pds = getPdsEndpoint(doc); ··· 83 91 if (!isAtprotoDid(did)) { 84 92 throw new Error("Not a valid DID identifier"); 85 93 } 86 - return await didDocumentResolver.resolve(did); 94 + return await didDocumentResolver().resolve(did); 87 95 }; 88 96 89 97 const validateHandle = async (handle: Handle, did: Did) => { ··· 145 153 }; 146 154 147 155 const resolveLexiconSchema = async (authority: AtprotoDid, nsid: Nsid) => { 148 - return await schemaResolver.resolve(authority, nsid); 156 + return await schemaResolver().resolve(authority, nsid); 149 157 }; 150 158 151 159 interface LinkData {
+2 -3
src/views/logs.tsx
··· 8 8 import { createEffect, createResource, createSignal, For, Show } from "solid-js"; 9 9 import { localDateFromTimestamp } from "../utils/date.js"; 10 10 import { createOperationHistory, DiffEntry, groupBy } from "../utils/plc-logs.js"; 11 + import { plcDirectory } from "./settings.jsx"; 11 12 12 13 type PlcEvent = "handle" | "rotation_key" | "service" | "verification_method"; 13 14 ··· 23 24 !activePlcEvent() || diffs.some((d) => d.type.startsWith(activePlcEvent()!)); 24 25 25 26 const fetchPlcLogs = async () => { 26 - const res = await fetch( 27 - `${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`, 28 - ); 27 + const res = await fetch(`${plcDirectory()}/${props.did}/log/audit`); 29 28 const json = await res.json(); 30 29 const logs = defs.indexedEntryLog.parse(json); 31 30 setRawLogs(logs);
+1 -1
src/views/record.tsx
··· 57 57 const schemaPromise = (async () => { 58 58 let didDocPromise = documentCache.get(authority); 59 59 if (!didDocPromise) { 60 - didDocPromise = didDocumentResolver.resolve(authority); 60 + didDocPromise = didDocumentResolver().resolve(authority); 61 61 documentCache.set(authority, didDocPromise); 62 62 } 63 63
+4 -5
src/views/repo.tsx
··· 42 42 import { detectDidKeyType, detectKeyType } from "../utils/key.js"; 43 43 import { BlobView } from "./blob.jsx"; 44 44 import { PlcLogView } from "./logs.jsx"; 45 + import { plcDirectory } from "./settings.jsx"; 45 46 46 47 export const RepoView = () => { 47 48 const params = useParams(); ··· 101 102 }; 102 103 103 104 const getRotationKeys = async () => { 104 - const res = await fetch( 105 - `${localStorage.plcDirectory ?? "https://plc.directory"}/${did}/log/last`, 106 - ); 105 + const res = await fetch(`${plcDirectory()}/${did}/log/last`); 107 106 const json = await res.json(); 108 107 setRotationKeys(json.rotationKeys ?? []); 109 108 }; ··· 364 363 <NavMenu 365 364 href={ 366 365 did.startsWith("did:plc") ? 367 - `${localStorage.plcDirectory ?? "https://plc.directory"}/${did}` 366 + `${plcDirectory()}/${did}` 368 367 : `https://${did.split("did:web:")[1]}/.well-known/did.json` 369 368 } 370 369 newTab ··· 373 372 /> 374 373 <Show when={did.startsWith("did:plc")}> 375 374 <NavMenu 376 - href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${did}/log/audit`} 375 + href={`${plcDirectory()}/${did}/log/audit`} 377 376 newTab 378 377 label="Audit log" 379 378 icon="lucide--external-link"
+12 -4
src/views/settings.tsx
··· 4 4 import { ThemeSelection } from "../components/theme.jsx"; 5 5 6 6 export const [hideMedia, setHideMedia] = createSignal(localStorage.hideMedia === "true"); 7 + export const [plcDirectory, setPlcDirectory] = createSignal( 8 + localStorage.plcDirectory || "https://plc.directory", 9 + ); 7 10 8 11 const Settings = () => { 9 12 return ( ··· 17 20 </label> 18 21 <TextInput 19 22 id="plcDirectory" 20 - value={localStorage.plcDirectory || "https://plc.directory"} 23 + value={plcDirectory()} 21 24 onInput={(e) => { 22 - e.currentTarget.value.length ? 23 - (localStorage.plcDirectory = e.currentTarget.value) 24 - : localStorage.removeItem("plcDirectory"); 25 + const value = e.currentTarget.value; 26 + if (value.length) { 27 + localStorage.plcDirectory = value; 28 + setPlcDirectory(value); 29 + } else { 30 + localStorage.removeItem("plcDirectory"); 31 + setPlcDirectory("https://plc.directory"); 32 + } 25 33 }} 26 34 /> 27 35 </div>