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({ service: endpoint });
44 const client = rpc as unknown as {
45 get: (nsid: string, options: { params: { actor: string } }) => Promise<{ ok: boolean; data: unknown }>;
46 };
47 const res = await client.get('app.bsky.actor.getProfile', { params: { actor: did } });
48 if (!res.ok) throw new Error('Profile request failed');
49 if (!cancelled) setData(res.data as BlueskyProfileData);
50 } catch (e) {
51 if (!cancelled) setError(e as Error);
52 } finally {
53 if (!cancelled) setLoading(false);
54 }
55 }
56 run();
57 return () => { cancelled = true; };
58 }, [did, endpoint]);
59
60 return { data, loading, error };
61}