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<{
6 record: T;
7 loading: boolean;
8 error?: Error;
9 }>;
10 fallback?: React.ReactNode;
11 loadingIndicator?: React.ReactNode;
12}
13
14type AtProtoRecordFetchProps<T> = AtProtoRecordRenderProps<T> & {
15 did: string;
16 collection: string;
17 rkey: string;
18 record?: undefined;
19};
20
21type AtProtoRecordProvidedRecordProps<T> = AtProtoRecordRenderProps<T> & {
22 record: T;
23 did?: string;
24 collection?: string;
25 rkey?: string;
26};
27
28export type AtProtoRecordProps<T = unknown> =
29 | AtProtoRecordFetchProps<T>
30 | AtProtoRecordProvidedRecordProps<T>;
31
32export function AtProtoRecord<T = unknown>(props: AtProtoRecordProps<T>) {
33 const {
34 renderer: Renderer,
35 fallback = null,
36 loadingIndicator = "Loading…",
37 } = props;
38 const hasProvidedRecord = "record" in props;
39 const providedRecord = hasProvidedRecord ? props.record : undefined;
40
41 const {
42 record: fetchedRecord,
43 error,
44 loading,
45 } = useAtProtoRecord<T>({
46 did: hasProvidedRecord ? undefined : props.did,
47 collection: hasProvidedRecord ? undefined : props.collection,
48 rkey: hasProvidedRecord ? undefined : props.rkey,
49 });
50
51 const record = providedRecord ?? fetchedRecord;
52 const isLoading = loading && !providedRecord;
53
54 if (error && !record) return <>{fallback}</>;
55 if (!record) return <>{isLoading ? loadingIndicator : fallback}</>;
56 if (Renderer)
57 return <Renderer record={record} loading={isLoading} error={error} />;
58 return (
59 <pre
60 style={{
61 fontSize: 12,
62 padding: 8,
63 background: "#f5f5f5",
64 overflow: "auto",
65 }}
66 >
67 {JSON.stringify(record, null, 2)}
68 </pre>
69 );
70}