A React component library for rendering common AT Protocol records for applications such as Bluesky and Leaflet.
1import React from "react";
2import { AtProtoRecord } from "../core/AtProtoRecord";
3import { TangledStringRenderer } from "../renderers/TangledStringRenderer";
4import type { TangledStringRecord } from "../renderers/TangledStringRenderer";
5
6/**
7 * Props for rendering Tangled String records.
8 */
9export interface TangledStringProps {
10 /** DID of the repository that stores the string record. */
11 did: string;
12 /** Record key within the `sh.tangled.string` collection. */
13 rkey: string;
14 /** Prefetched Tangled String record. When provided, skips fetching from the network. */
15 record?: TangledStringRecord;
16 /** Optional renderer override for custom presentation. */
17 renderer?: React.ComponentType<TangledStringRendererInjectedProps>;
18 /** Fallback node displayed before loading begins. */
19 fallback?: React.ReactNode;
20 /** Indicator node shown while data is loading. */
21 loadingIndicator?: React.ReactNode;
22 /** Preferred color scheme for theming. */
23 colorScheme?: "light" | "dark" | "system";
24}
25
26/**
27 * Values injected into custom Tangled String renderer implementations.
28 */
29export type TangledStringRendererInjectedProps = {
30 /** Loaded Tangled String record value. */
31 record: TangledStringRecord;
32 /** Indicates whether the record is currently loading. */
33 loading: boolean;
34 /** Fetch error, if any. */
35 error?: Error;
36 /** Preferred color scheme for downstream components. */
37 colorScheme?: "light" | "dark" | "system";
38 /** DID associated with the record. */
39 did: string;
40 /** Record key for the string. */
41 rkey: string;
42 /** Canonical external URL for linking to the string. */
43 canonicalUrl: string;
44};
45
46/** NSID for Tangled String records. */
47export const TANGLED_COLLECTION = "sh.tangled.string";
48
49/**
50 * Resolves a Tangled String record and renders it with optional overrides while computing a canonical link.
51 *
52 * @param did - DID whose Tangled String should be fetched.
53 * @param rkey - Record key within the Tangled String collection.
54 * @param renderer - Optional component override that will receive injected props.
55 * @param fallback - Node rendered before the first load begins.
56 * @param loadingIndicator - Node rendered while the Tangled String is loading.
57 * @param colorScheme - Preferred color scheme for theming the renderer.
58 * @returns A JSX subtree representing the Tangled String record with loading states handled.
59 */
60export const TangledString: React.FC<TangledStringProps> = React.memo(({
61 did,
62 rkey,
63 record,
64 renderer,
65 fallback,
66 loadingIndicator,
67 colorScheme,
68}) => {
69 const Comp: React.ComponentType<TangledStringRendererInjectedProps> =
70 renderer ?? ((props) => <TangledStringRenderer {...props} />);
71 const Wrapped: React.FC<{
72 record: TangledStringRecord;
73 loading: boolean;
74 error?: Error;
75 }> = (props) => (
76 <Comp
77 {...props}
78 colorScheme={colorScheme}
79 did={did}
80 rkey={rkey}
81 canonicalUrl={`https://tangled.org/strings/${did}/${encodeURIComponent(rkey)}`}
82 />
83 );
84
85 if (record !== undefined) {
86 return (
87 <AtProtoRecord<TangledStringRecord>
88 record={record}
89 renderer={Wrapped}
90 fallback={fallback}
91 loadingIndicator={loadingIndicator}
92 />
93 );
94 }
95
96 return (
97 <AtProtoRecord<TangledStringRecord>
98 did={did}
99 collection={TANGLED_COLLECTION}
100 rkey={rkey}
101 renderer={Wrapped}
102 fallback={fallback}
103 loadingIndicator={loadingIndicator}
104 />
105 );
106});
107
108export default TangledString;