A React component library for rendering common AT Protocol records for applications such as Bluesky and Leaflet.
1import { useEffect, useMemo, useState } from "react"; 2import { useAtProto } from "../providers/AtProtoProvider"; 3 4/** 5 * Resolves a handle to its DID, or returns the DID immediately when provided. 6 * 7 * @param handleOrDid - Bluesky handle or DID string. 8 * @returns {{ did: string | undefined; error: Error | undefined; loading: boolean }} Object containing the resolved DID, error (if any), and loading state. 9 */ 10export function useDidResolution(handleOrDid: string | undefined) { 11 const { resolver, didCache } = useAtProto(); 12 const [did, setDid] = useState<string | undefined>(); 13 const [handle, setHandle] = useState<string | undefined>(); 14 const [error, setError] = useState<Error | undefined>(); 15 const [loading, setLoading] = useState(false); 16 17 const normalizedInput = useMemo(() => { 18 if (!handleOrDid) return undefined; 19 const trimmed = handleOrDid.trim(); 20 return trimmed || undefined; 21 }, [handleOrDid]); 22 23 useEffect(() => { 24 let cancelled = false; 25 const reset = () => { 26 setDid(undefined); 27 setHandle(undefined); 28 setError(undefined); 29 setLoading(false); 30 }; 31 if (!normalizedInput) { 32 reset(); 33 return () => { 34 cancelled = true; 35 }; 36 } 37 38 const isDid = normalizedInput.startsWith("did:"); 39 const normalizedHandle = !isDid 40 ? normalizedInput.toLowerCase() 41 : undefined; 42 const cached = isDid 43 ? didCache.getByDid(normalizedInput) 44 : didCache.getByHandle(normalizedHandle); 45 46 const initialDid = cached?.did ?? (isDid ? normalizedInput : undefined); 47 const initialHandle = 48 cached?.handle ?? (!isDid ? normalizedHandle : undefined); 49 50 setError(undefined); 51 setDid(initialDid); 52 setHandle(initialHandle); 53 54 const needsHandleResolution = !isDid && !cached?.did; 55 const needsDocResolution = 56 isDid && (!cached?.doc || cached.handle === undefined); 57 58 if (!needsHandleResolution && !needsDocResolution) { 59 setLoading(false); 60 return () => { 61 cancelled = true; 62 }; 63 } 64 65 setLoading(true); 66 67 (async () => { 68 try { 69 let snapshot = cached; 70 if (!isDid && normalizedHandle && needsHandleResolution) { 71 snapshot = await didCache.ensureHandle( 72 resolver, 73 normalizedHandle, 74 ); 75 } 76 77 if (isDid) { 78 snapshot = await didCache.ensureDidDoc( 79 resolver, 80 normalizedInput, 81 ); 82 } 83 84 if (!cancelled) { 85 const resolvedDid = 86 snapshot?.did ?? (isDid ? normalizedInput : undefined); 87 const resolvedHandle = 88 snapshot?.handle ?? 89 (!isDid ? normalizedHandle : undefined); 90 setDid(resolvedDid); 91 setHandle(resolvedHandle); 92 setError(undefined); 93 } 94 } catch (e) { 95 if (!cancelled) { 96 setError(e as Error); 97 } 98 } finally { 99 if (!cancelled) setLoading(false); 100 } 101 })(); 102 103 return () => { 104 cancelled = true; 105 }; 106 }, [normalizedInput, resolver, didCache]); 107 108 return { did, handle, error, loading }; 109}