Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 788ba0fa01083b300ad50913
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/BoardPage.tsx
// Opens a board on this device: joins it (the board id is enough to view while the view link is on),
// presents the edit key from the link, and from an admin link becomes an owner. Then loads the editor.
import { lazy, Suspense, useCallback, useEffect, useState } from "react";
import { get, set, setMany, serverTimestamp } from "@bounded-sh/client";
import { me, explain } from "./lib/bounded";
import { navigate } from "./lib/router";
import { boardOf, editPath, rememberBoard } from "./lib/mine";
import type { BoardDoc } from "./Editor";
const Editor = lazy(() => import("./Editor"));
const hashParam = (name: string) => new URLSearchParams(location.hash.slice(1)).get(name);
interface Ready { uid: string; owner: boolean; editKey: string | null; board: BoardDoc }
function Loading() {
return <div className="board-loading"><span className="spinner" /> Opening the board...</div>;
}
function Unavailable({ title, text }: { title: string; text: string }) {
return (
<div className="wrap page-pad narrow">
<h1 className="h1">{title}</h1>
<p className="lede" style={{ marginTop: 14 }}>{text}</p>
<a className="btn" style={{ marginTop: 24 }} href="/" onClick={(e) => { e.preventDefault(); navigate("/"); }}>Start a new board</a>
</div>
);
}
export function BoardPage({ boardId, admin }: { boardId: string; admin: boolean }) {
const [state, setState] = useState<"opening" | "ready" | "missing" | "badkey" | "gone" | "error">("opening");
const [ready, setReady] = useState<Ready | null>(null);
const [error, setError] = useState("");
useEffect(() => {
let cancelled = false;
const done = (s: typeof state) => { if (!cancelled) setState(s); };
(async () => {
try {
const uid = await me();
const linkKey = admin ? null : hashParam("edit");
const adminKey = admin ? hashParam("k") : null;
let editKey = linkKey || boardOf(boardId)?.edit || null;
const joinPath = `boards/${boardId}/joins/${uid}`;
const join = (await get(joinPath)) as { key?: string | null } | null;
if (!join) {
try {
await set(joinPath, { at: serverTimestamp(), ...(editKey ? { key: editKey } : {}) });
} catch {
return done("missing");
}
} else if (editKey && join.key !== editKey) {
await set(joinPath, { key: editKey });
}
let owner = Boolean(await get(`boards/${boardId}/owners/${uid}`));
if (adminKey) {
if (!owner) {
try {
await setMany([
{ path: `boardclaims/${boardId}/c/${uid}`, document: { key: adminKey } },
{ path: `boards/${boardId}/owners/${uid}`, document: { at: serverTimestamp() } },
]);
owner = true;
} catch {
return done("badkey");
}
rememberBoard({ id: boardId, admin: adminKey });
} else if (!boardOf(boardId)?.admin) {
// Already an owner, so the key was not checked: keep a key this browser already has over one from a link.
rememberBoard({ id: boardId, admin: adminKey });
}
}
const board = (await get(`boards/${boardId}`)) as BoardDoc | null;
if (!board) return done("missing");
if (owner) {
// Owners can always edit; keep the current edit link at hand for sharing.
editKey = ((await get(`editkeys/${boardId}`)) as { key?: string } | null)?.key ?? editKey;
if (editKey) await set(joinPath, { key: editKey });
// The admin key never stays in the address bar; this browser keeps it in "Your boards".
if (admin && editKey) history.replaceState(null, "", editPath(boardId, editKey));
}
rememberBoard({ id: boardId, title: board.title, ...(linkKey || owner ? { edit: editKey ?? undefined } : {}) });
if (!cancelled) {
setReady({ uid, owner, editKey, board });
setState("ready");
}
} catch (err) {
if (!cancelled) { setError(explain(err)); setState("error"); }
}
})();
return () => { cancelled = true; };
}, [boardId, admin]);
const onGone = useCallback(() => setState("gone"), []);
if (state === "opening") return <Loading />;
if (state === "missing") return <Unavailable title="This board is not available." text="It may have been deleted, or its owner turned its links off. Check the link you were sent: board links are long and every character counts." />;
if (state === "badkey") return <Unavailable title="That admin link is not right." text="The key at the end of the link does not match this board. Copy the whole admin link again." />;
if (state === "gone") return <Unavailable title="This board is no longer available." text="Its owner deleted it or turned its links off while you had it open." />;
if (state === "error" || !ready) return <div className="wrap page-pad narrow"><p className="notice notice-error">{error || "Something went wrong. Reload to try again."}</p></div>;
return (
<Suspense fallback={<Loading />}>
<Editor boardId={boardId} uid={ready.uid} owner={ready.owner} editKey={ready.editKey} initial={ready.board} onGone={onGone} />
</Suspense>
);
}
Change history
Point OpenApps links to openapps.free
Bind AGENT_ADDRESS in policy constants
Use functions.openapps.xyz alias, add OpenApp contribution strip
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync