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 () => { cancelled = true; }; 34 } 35 36 const isDid = normalizedInput.startsWith('did:'); 37 const normalizedHandle = !isDid ? normalizedInput.toLowerCase() : undefined; 38 const cached = isDid 39 ? didCache.getByDid(normalizedInput) 40 : didCache.getByHandle(normalizedHandle); 41 42 const initialDid = cached?.did ?? (isDid ? normalizedInput : undefined); 43 const initialHandle = cached?.handle ?? (!isDid ? normalizedHandle : undefined); 44 45 setError(undefined); 46 setDid(initialDid); 47 setHandle(initialHandle); 48 49 const needsHandleResolution = !isDid && !cached?.did; 50 const needsDocResolution = isDid && (!cached?.doc || cached.handle === undefined); 51 52 if (!needsHandleResolution && !needsDocResolution) { 53 setLoading(false); 54 return () => { cancelled = true; }; 55 } 56 57 setLoading(true); 58 59 (async () => { 60 try { 61 let snapshot = cached; 62 if (!isDid && normalizedHandle && needsHandleResolution) { 63 snapshot = await didCache.ensureHandle(resolver, normalizedHandle); 64 } 65 66 if (isDid) { 67 snapshot = await didCache.ensureDidDoc(resolver, normalizedInput); 68 } 69 70 if (!cancelled) { 71 const resolvedDid = snapshot?.did ?? (isDid ? normalizedInput : undefined); 72 const resolvedHandle = snapshot?.handle ?? (!isDid ? normalizedHandle : undefined); 73 setDid(resolvedDid); 74 setHandle(resolvedHandle); 75 setError(undefined); 76 } 77 } catch (e) { 78 if (!cancelled) { 79 setError(e as Error); 80 } 81 } finally { 82 if (!cancelled) setLoading(false); 83 } 84 })(); 85 86 return () => { cancelled = true; }; 87 }, [normalizedInput, resolver, didCache]); 88 89 return { did, handle, error, loading }; 90}