A React component library for rendering common AT Protocol records for applications such as Bluesky and Leaflet.
1import React from 'react';
2import { useAtProtoRecord } from '../hooks/useAtProtoRecord';
3
4interface AtProtoRecordRenderProps<T> {
5 renderer?: React.ComponentType<{ record: T; loading: boolean; error?: Error }>;
6 fallback?: React.ReactNode;
7 loadingIndicator?: React.ReactNode;
8}
9
10type AtProtoRecordFetchProps<T> = AtProtoRecordRenderProps<T> & {
11 did: string;
12 collection: string;
13 rkey: string;
14 record?: undefined;
15};
16
17type AtProtoRecordProvidedRecordProps<T> = AtProtoRecordRenderProps<T> & {
18 record: T;
19 did?: string;
20 collection?: string;
21 rkey?: string;
22};
23
24export type AtProtoRecordProps<T = unknown> = AtProtoRecordFetchProps<T> | AtProtoRecordProvidedRecordProps<T>;
25
26export function AtProtoRecord<T = unknown>(props: AtProtoRecordProps<T>) {
27 const { renderer: Renderer, fallback = null, loadingIndicator = 'Loading…' } = props;
28 const hasProvidedRecord = 'record' in props;
29 const providedRecord = hasProvidedRecord ? props.record : undefined;
30
31 const { record: fetchedRecord, error, loading } = useAtProtoRecord<T>({
32 did: hasProvidedRecord ? undefined : props.did,
33 collection: hasProvidedRecord ? undefined : props.collection,
34 rkey: hasProvidedRecord ? undefined : props.rkey,
35 });
36
37 const record = providedRecord ?? fetchedRecord;
38 const isLoading = loading && !providedRecord;
39
40 if (error && !record) return <>{fallback}</>;
41 if (!record) return <>{isLoading ? loadingIndicator : fallback}</>;
42 if (Renderer) return <Renderer record={record} loading={isLoading} error={error} />;
43 return <pre style={{ fontSize: 12, padding: 8, background: '#f5f5f5', overflow: 'auto' }}>{JSON.stringify(record, null, 2)}</pre>;
44}