A React component library for rendering common AT Protocol records for applications such as Bluesky and Leaflet.
1import { useEffect, useState } from "react"; 2import { usePdsEndpoint } from "./usePdsEndpoint"; 3import { createAtprotoClient } from "../utils/atproto-client"; 4 5/** 6 * Minimal profile fields returned by the Bluesky actor profile endpoint. 7 */ 8export interface BlueskyProfileData { 9 /** Actor DID. */ 10 did: string; 11 /** Actor handle. */ 12 handle: string; 13 /** Display name configured by the actor. */ 14 displayName?: string; 15 /** Profile description/bio. */ 16 description?: string; 17 /** Avatar blob (CID reference). */ 18 avatar?: string; 19 /** Banner image blob (CID reference). */ 20 banner?: string; 21 /** Creation timestamp for the profile. */ 22 createdAt?: string; 23} 24 25/** 26 * Fetches a Bluesky actor profile for a DID and exposes loading/error state. 27 * 28 * @param did - Actor DID whose profile should be retrieved. 29 * @returns {{ data: BlueskyProfileData | undefined; loading: boolean; error: Error | undefined }} Object exposing the profile payload, loading flag, and any error. 30 */ 31export function useBlueskyProfile(did: string | undefined) { 32 const { endpoint } = usePdsEndpoint(did); 33 const [data, setData] = useState<BlueskyProfileData | undefined>(); 34 const [loading, setLoading] = useState<boolean>(!!did); 35 const [error, setError] = useState<Error | undefined>(); 36 37 useEffect(() => { 38 let cancelled = false; 39 async function run() { 40 if (!did || !endpoint) return; 41 setLoading(true); 42 try { 43 const { rpc } = await createAtprotoClient({ 44 service: endpoint, 45 }); 46 const client = rpc as unknown as { 47 get: ( 48 nsid: string, 49 options: { params: { actor: string } }, 50 ) => Promise<{ ok: boolean; data: unknown }>; 51 }; 52 const res = await client.get("app.bsky.actor.getProfile", { 53 params: { actor: did }, 54 }); 55 if (!res.ok) throw new Error("Profile request failed"); 56 if (!cancelled) setData(res.data as BlueskyProfileData); 57 } catch (e) { 58 if (!cancelled) setError(e as Error); 59 } finally { 60 if (!cancelled) setLoading(false); 61 } 62 } 63 run(); 64 return () => { 65 cancelled = true; 66 }; 67 }, [did, endpoint]); 68 69 return { data, loading, error }; 70}