+1
-1
package.json
+1
-1
package.json
+23
-160
src/App.tsx
+23
-160
src/App.tsx
···
6
useRef,
7
} from "react";
8
import { AtProtoProvider } from "../lib/providers/AtProtoProvider";
9
-
import { AtProtoRecord } from "../lib/core/AtProtoRecord";
10
import { TangledString } from "../lib/components/TangledString";
11
import { LeafletDocument } from "../lib/components/LeafletDocument";
12
import { BlueskyProfile } from "../lib/components/BlueskyProfile";
···
37
);
38
}`;
39
40
-
const customComponentSnippet = `import { useLatestRecord, useColorScheme, AtProtoRecord } from 'atproto-ui';
41
import type { FeedPostRecord } from 'atproto-ui';
42
43
-
const LatestPostSummary: React.FC<{ did: string }> = ({ did }) => {
44
-
const scheme = useColorScheme('system');
45
-
const { rkey, loading, error } = useLatestRecord<FeedPostRecord>(did, 'app.bsky.feed.post');
46
47
if (loading) return <span>Loading…</span>;
48
-
if (error || !rkey) return <span>No post yet.</span>;
49
50
-
return (
51
-
<AtProtoRecord<FeedPostRecord>
52
-
did={did}
53
-
collection="app.bsky.feed.post"
54
-
rkey={rkey}
55
-
renderer={({ record }) => (
56
-
<article data-color-scheme={scheme}>
57
-
<strong>{record?.text ?? 'Empty post'}</strong>
58
-
</article>
59
-
)}
60
-
/>
61
-
);
62
};`;
63
64
const codeBlockBase: React.CSSProperties = {
···
219
const basicCodeRef = useRef<HTMLElement | null>(null);
220
const customCodeRef = useRef<HTMLElement | null>(null);
221
222
-
// Latest Bluesky post
223
const {
224
rkey: latestPostRkey,
225
loading: loadingLatestPost,
226
empty: noPosts,
227
error: latestPostError,
228
-
} = useLatestRecord<unknown>(did, BLUESKY_POST_COLLECTION);
229
230
const quoteSampleDid = "did:plc:ttdrpj45ibqunmfhdsb4zdwq";
231
const quoteSampleRkey = "3m2prlq6xxc2v";
···
323
<div style={columnStackStyle}>
324
<section style={panelStyle}>
325
<h3 style={sectionHeaderStyle}>
326
-
Latest Bluesky Post
327
</h3>
328
{loadingLatestPost && (
329
<div style={loadingBox}>
330
Loading latest post…
···
345
No posts found.
346
</div>
347
)}
348
-
{!loadingLatestPost && latestPostRkey && (
349
<BlueskyPost
350
did={did}
351
rkey={latestPostRkey}
352
colorScheme={colorSchemePreference}
353
/>
354
)}
···
392
</>
393
)}
394
<section style={{ ...panelStyle, marginTop: 32 }}>
395
-
<h3 style={sectionHeaderStyle}>Build your own component</h3>
396
<p style={{ color: mutedTextColor, margin: "4px 0 8px" }}>
397
Wrap your app with the provider once and drop the ready-made
398
components wherever you need them.
···
407
</code>
408
</pre>
409
<p style={{ color: mutedTextColor, margin: "16px 0 8px" }}>
410
-
Need to make your own component? Compose your own renderer
411
-
with the hooks and utilities that ship with the library.
412
</p>
413
<pre style={codeBlockStyle}>
414
<code
···
416
className="language-tsx"
417
style={codeTextStyle}
418
>
419
-
{customComponentSnippet}
420
</code>
421
</pre>
422
-
{did && (
423
-
<div
424
-
style={{
425
-
marginTop: 16,
426
-
display: "flex",
427
-
flexDirection: "column",
428
-
gap: 12,
429
-
}}
430
-
>
431
-
<p style={{ color: mutedTextColor, margin: 0 }}>
432
-
Live example with your handle:
433
-
</p>
434
-
<LatestPostSummary
435
-
did={did}
436
-
handle={showHandle}
437
-
colorScheme={colorSchemePreference}
438
-
/>
439
-
</div>
440
-
)}
441
</section>
442
</div>
443
);
444
};
445
446
-
const LatestPostSummary: React.FC<{
447
-
did: string;
448
-
handle?: string;
449
-
colorScheme: ColorSchemePreference;
450
-
}> = ({ did, colorScheme }) => {
451
-
const { record, rkey, loading, error } = useLatestRecord<FeedPostRecord>(
452
-
did,
453
-
BLUESKY_POST_COLLECTION,
454
-
);
455
-
const scheme = useColorScheme(colorScheme);
456
-
const palette =
457
-
scheme === "dark"
458
-
? latestSummaryPalette.dark
459
-
: latestSummaryPalette.light;
460
461
-
if (loading) return <div style={palette.muted}>Loading summary…</div>;
462
-
if (error)
463
-
return <div style={palette.error}>Failed to load the latest post.</div>;
464
-
if (!rkey) return <div style={palette.muted}>No posts published yet.</div>;
465
-
466
-
const atProtoProps = record
467
-
? { record }
468
-
: { did, collection: "app.bsky.feed.post", rkey };
469
-
470
-
return (
471
-
<AtProtoRecord<FeedPostRecord>
472
-
{...atProtoProps}
473
-
renderer={({ record: resolvedRecord }) => (
474
-
<article data-color-scheme={scheme}>
475
-
<strong>{resolvedRecord?.text ?? "Empty post"}</strong>
476
-
</article>
477
-
)}
478
-
/>
479
-
);
480
-
};
481
482
const sectionHeaderStyle: React.CSSProperties = {
483
margin: "4px 0",
···
486
const loadingBox: React.CSSProperties = { padding: 8 };
487
const errorBox: React.CSSProperties = { padding: 8, color: "crimson" };
488
const infoBox: React.CSSProperties = { padding: 8, color: "#555" };
489
-
490
-
const latestSummaryPalette = {
491
-
light: {
492
-
card: {
493
-
border: "1px solid #e2e8f0",
494
-
background: "#ffffff",
495
-
borderRadius: 12,
496
-
padding: 12,
497
-
display: "flex",
498
-
flexDirection: "column",
499
-
gap: 8,
500
-
} satisfies React.CSSProperties,
501
-
header: {
502
-
display: "flex",
503
-
alignItems: "baseline",
504
-
justifyContent: "space-between",
505
-
gap: 12,
506
-
color: "#0f172a",
507
-
} satisfies React.CSSProperties,
508
-
time: {
509
-
fontSize: 12,
510
-
color: "#64748b",
511
-
} satisfies React.CSSProperties,
512
-
text: {
513
-
margin: 0,
514
-
color: "#1f2937",
515
-
whiteSpace: "pre-wrap",
516
-
} satisfies React.CSSProperties,
517
-
link: {
518
-
color: "#2563eb",
519
-
fontWeight: 600,
520
-
fontSize: 12,
521
-
textDecoration: "none",
522
-
} satisfies React.CSSProperties,
523
-
muted: {
524
-
color: "#64748b",
525
-
} satisfies React.CSSProperties,
526
-
error: {
527
-
color: "crimson",
528
-
} satisfies React.CSSProperties,
529
-
},
530
-
dark: {
531
-
card: {
532
-
border: "1px solid #1e293b",
533
-
background: "#0f172a",
534
-
borderRadius: 12,
535
-
padding: 12,
536
-
display: "flex",
537
-
flexDirection: "column",
538
-
gap: 8,
539
-
} satisfies React.CSSProperties,
540
-
header: {
541
-
display: "flex",
542
-
alignItems: "baseline",
543
-
justifyContent: "space-between",
544
-
gap: 12,
545
-
color: "#e2e8f0",
546
-
} satisfies React.CSSProperties,
547
-
time: {
548
-
fontSize: 12,
549
-
color: "#cbd5f5",
550
-
} satisfies React.CSSProperties,
551
-
text: {
552
-
margin: 0,
553
-
color: "#e2e8f0",
554
-
whiteSpace: "pre-wrap",
555
-
} satisfies React.CSSProperties,
556
-
link: {
557
-
color: "#38bdf8",
558
-
fontWeight: 600,
559
-
fontSize: 12,
560
-
textDecoration: "none",
561
-
} satisfies React.CSSProperties,
562
-
muted: {
563
-
color: "#94a3b8",
564
-
} satisfies React.CSSProperties,
565
-
error: {
566
-
color: "#f472b6",
567
-
} satisfies React.CSSProperties,
568
-
},
569
-
} as const;
570
571
export const App: React.FC = () => {
572
return (
···
6
useRef,
7
} from "react";
8
import { AtProtoProvider } from "../lib/providers/AtProtoProvider";
9
+
10
import { TangledString } from "../lib/components/TangledString";
11
import { LeafletDocument } from "../lib/components/LeafletDocument";
12
import { BlueskyProfile } from "../lib/components/BlueskyProfile";
···
37
);
38
}`;
39
40
+
const prefetchedDataSnippet = `import { BlueskyPost, useLatestRecord } from 'atproto-ui';
41
import type { FeedPostRecord } from 'atproto-ui';
42
43
+
const LatestPostWithPrefetch: React.FC<{ did: string }> = ({ did }) => {
44
+
// Fetch once with the hook
45
+
const { record, rkey, loading } = useLatestRecord<FeedPostRecord>(
46
+
did,
47
+
'app.bsky.feed.post'
48
+
);
49
50
if (loading) return <span>Loading…</span>;
51
+
if (!record || !rkey) return <span>No posts yet.</span>;
52
53
+
// Pass prefetched record—BlueskyPost won't re-fetch it
54
+
return <BlueskyPost did={did} rkey={rkey} record={record} />;
55
};`;
56
57
const codeBlockBase: React.CSSProperties = {
···
212
const basicCodeRef = useRef<HTMLElement | null>(null);
213
const customCodeRef = useRef<HTMLElement | null>(null);
214
215
+
// Latest Bluesky post - fetch with record for prefetch demo
216
const {
217
+
record: latestPostRecord,
218
rkey: latestPostRkey,
219
loading: loadingLatestPost,
220
empty: noPosts,
221
error: latestPostError,
222
+
} = useLatestRecord<FeedPostRecord>(did, BLUESKY_POST_COLLECTION);
223
224
const quoteSampleDid = "did:plc:ttdrpj45ibqunmfhdsb4zdwq";
225
const quoteSampleRkey = "3m2prlq6xxc2v";
···
317
<div style={columnStackStyle}>
318
<section style={panelStyle}>
319
<h3 style={sectionHeaderStyle}>
320
+
Latest Post (Prefetched Data)
321
</h3>
322
+
<p style={{ fontSize: 12, color: mutedTextColor, margin: "0 0 8px" }}>
323
+
Using <code style={{ background: scheme === "dark" ? "#1e293b" : "#e2e8f0", padding: "2px 4px", borderRadius: 3 }}>useLatestRecord</code> to fetch once, then passing <code style={{ background: scheme === "dark" ? "#1e293b" : "#e2e8f0", padding: "2px 4px", borderRadius: 3 }}>record</code> prop—no re-fetch!
324
+
</p>
325
{loadingLatestPost && (
326
<div style={loadingBox}>
327
Loading latest post…
···
342
No posts found.
343
</div>
344
)}
345
+
{!loadingLatestPost && latestPostRkey && latestPostRecord && (
346
<BlueskyPost
347
did={did}
348
rkey={latestPostRkey}
349
+
record={latestPostRecord}
350
colorScheme={colorSchemePreference}
351
/>
352
)}
···
390
</>
391
)}
392
<section style={{ ...panelStyle, marginTop: 32 }}>
393
+
<h3 style={sectionHeaderStyle}>Code Examples</h3>
394
<p style={{ color: mutedTextColor, margin: "4px 0 8px" }}>
395
Wrap your app with the provider once and drop the ready-made
396
components wherever you need them.
···
405
</code>
406
</pre>
407
<p style={{ color: mutedTextColor, margin: "16px 0 8px" }}>
408
+
Pass prefetched data to components to skip API calls—perfect for SSR or caching.
409
</p>
410
<pre style={codeBlockStyle}>
411
<code
···
413
className="language-tsx"
414
style={codeTextStyle}
415
>
416
+
{prefetchedDataSnippet}
417
</code>
418
</pre>
419
</section>
420
</div>
421
);
422
};
423
424
425
426
const sectionHeaderStyle: React.CSSProperties = {
427
margin: "4px 0",
···
430
const loadingBox: React.CSSProperties = { padding: 8 };
431
const errorBox: React.CSSProperties = { padding: 8, color: "crimson" };
432
const infoBox: React.CSSProperties = { padding: 8, color: "#555" };
433
434
export const App: React.FC = () => {
435
return (