Skip to content

Commit abf2528

Browse files
docs: add Soroban storage strategies guide (#2649)
* add storage strategies guide with animated diagrams * codex worked oncorrecting Soroban storage strategies guide * fix storage guide facts, diagram styling and animations * polish storage guide, add markdown-source plugin * fix ttl animation, clarify tier and cap wording * clarify strategies 7-8, fix swap-and-pop diagram * polish strategy titles, s9 wording and diagrams * rework strategy 10 and its diagram, note cap-85 * clarify decision path, cheatsheet, s11 diagram * add checkpoints strategy as s10, renumber s10-11 to s11-12 * make d1/d3/d7 animations finite, show allowance deadline checks The three looping diagrams auto-started on scroll and never stopped, breaking the component's plays-once contract (WCAG 2.2.2); capped at 2-3 iterations (~5s) since Replay covers re-watching. The Strategy 4 snippet claimed the deadline was checked but only stored it — now it shows the write-side validation and read-side expiration guard from the canonical token example. * drop the markdown-source dev middleware Serving raw markdown under docusaurus start meant re-deriving source paths from URLs, which missed README-backed section pages, /docs.md, and front-matter slugs, and required a fragile re-install of the eval source map middleware that silently failed on Docusaurus 3.10. Not worth the surface: the Open Markdown button works in production and PR previews, where docusaurus-markdown-source-plugin resolves sources from route metadata. * apply review wording and formatting feedback * correct snippet semantics and cost accounting * relabel d5 ttl diagram as not to scale * show bump-on-read via a balance getter also polish comment * show diagram final frames when javascript is off * cite oz smart account instead of passkey-kit
1 parent e8416b6 commit abf2528

6 files changed

Lines changed: 3213 additions & 0 deletions

File tree

docs/build/guides/storage/storage-strategies.mdx

Lines changed: 725 additions & 0 deletions
Large diffs are not rendered by default.

routes.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
/docs/build/guides/storage
112112
/docs/build/guides/storage/choosing-the-right-storage
113113
/docs/build/guides/storage/migrate-contract-storage
114+
/docs/build/guides/storage/storage-strategies
114115
/docs/build/guides/storage/use-instance
115116
/docs/build/guides/storage/use-persistent
116117
/docs/build/guides/storage/use-temporary
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React, { useEffect, useRef, useState } from "react";
2+
import "./diagrams.css";
3+
4+
interface AnimatedDiagramProps {
5+
id: string;
6+
caption?: string;
7+
children: React.ReactNode;
8+
}
9+
10+
// Card that plays its CSS/SVG animations once it scrolls into view, with a
11+
// replay button. Animations are gated behind the .play class (see diagrams.css).
12+
export default function AnimatedDiagram({
13+
id,
14+
caption,
15+
children,
16+
}: AnimatedDiagramProps) {
17+
const ref = useRef<HTMLDivElement>(null);
18+
// .play must live in React state: it gates animation-play-state in CSS, and
19+
// an imperatively added class would be dropped on the next re-render.
20+
const [playing, setPlaying] = useState(false);
21+
const [restored, setRestored] = useState({
22+
instance: false,
23+
persistent: false,
24+
});
25+
26+
useEffect(() => {
27+
const el = ref.current;
28+
if (!el) {
29+
return undefined;
30+
}
31+
const io = new IntersectionObserver(
32+
(entries) => {
33+
entries.forEach((entry) => {
34+
if (entry.isIntersecting) {
35+
setPlaying(true);
36+
io.unobserve(el);
37+
}
38+
});
39+
},
40+
{ threshold: 0.45 },
41+
);
42+
io.observe(el);
43+
return () => io.disconnect();
44+
}, []);
45+
46+
const replay = () => {
47+
const el = ref.current;
48+
if (!el) {
49+
return;
50+
}
51+
setRestored({ instance: false, persistent: false });
52+
setPlaying(true);
53+
// Toggling .play only pauses/resumes; to restart from frame 0 the
54+
// animations themselves must be torn down and recreated.
55+
const anims = el.querySelectorAll<HTMLElement | SVGElement>(".anim");
56+
anims.forEach((a) => {
57+
a.style.animation = "none";
58+
});
59+
void (el as HTMLElement).offsetWidth;
60+
anims.forEach((a) => {
61+
a.style.animation = "";
62+
});
63+
};
64+
65+
const className = [
66+
"ssd",
67+
playing ? "play" : "",
68+
restored.instance ? "restored-instance" : "",
69+
restored.persistent ? "restored-persistent" : "",
70+
]
71+
.filter(Boolean)
72+
.join(" ");
73+
74+
return (
75+
<div className={className} id={id} ref={ref}>
76+
<div className="diagram-controls">
77+
{id === "d0" ? (
78+
<>
79+
<button
80+
className="restore"
81+
type="button"
82+
aria-pressed={restored.instance}
83+
onClick={() =>
84+
setRestored((current) => ({ ...current, instance: true }))
85+
}
86+
>
87+
Restore instance
88+
</button>
89+
<button
90+
className="restore"
91+
type="button"
92+
aria-pressed={restored.persistent}
93+
onClick={() =>
94+
setRestored((current) => ({ ...current, persistent: true }))
95+
}
96+
>
97+
Restore persistent entry
98+
</button>
99+
</>
100+
) : null}
101+
<button className="replay" type="button" onClick={replay}>
102+
{id === "d0" ? "↻ Replay expiry" : "↻ Replay"}
103+
</button>
104+
</div>
105+
{children}
106+
{caption ? <p className="caption">{caption}</p> : null}
107+
</div>
108+
);
109+
}

0 commit comments

Comments
 (0)