feedback-free.openapps.xyz

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.
Owned app · rev 4a4c54cd95c6
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 54b9c9d7fdc2b68029ce2e57
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.
← All files
src/Create.tsx
// The landing page's board maker: name it, and the board, its admin key and you as its owner are
// written in one batch. The browser keeps the key, so it can show "Your boards" and the admin link.
import { useState } from "react";
import { setMany, serverTimestamp } from "@bounded-sh/client";
import { me, explain } from "./lib/bounded";
import { rememberBoard } from "./lib/mine";
import { navigate } from "./lib/router";
import { DEFAULT_TAGS, LIMITS, randomId } from "../functions/lib/feedback";

export function CreateBoard() {
  const [name, setName] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const create = async (e: React.FormEvent) => {
    e.preventDefault();
    const n = name.trim();
    if (!n) { setError("Name your board, like \"Acme feedback\"."); return; }
    setBusy(true);
    setError(null);
    try {
      const { id: uid } = await me();
      const boardId = randomId(20);
      const key = randomId(24);
      const tags = DEFAULT_TAGS.map((t) => ({ id: randomId(6), name: t }));
      await setMany([
        { path: `boards/${boardId}`, document: { name: n, intro: "", tags: JSON.stringify(tags), createdBy: uid, createdAt: serverTimestamp() } },
        { path: `boards/${boardId}/joins/${uid}`, document: { at: serverTimestamp() } },
        { path: `boards/${boardId}/owners/${uid}`, document: { at: serverTimestamp() } },
        { path: `boardkeys/${boardId}`, document: { key } },
      ]);
      rememberBoard({ id: boardId, title: n, key });
      localStorage.setItem(`feedback:new:${boardId}`, "1");
      navigate(`/b/${boardId}`);
    } catch (err) {
      setError(explain(err));
      setBusy(false);
    }
  };

  return (
    <form className="card create" onSubmit={create}>
      <label className="field">
        <span>Name your board</span>
        <input className="input input-lg" value={name} maxLength={LIMITS.boardName} placeholder="Acme feedback" onChange={(e) => { setName(e.target.value); setError(null); }} />
      </label>
      {error && <p className="notice notice-error" role="alert">{error}</p>}
      <button className="btn btn-lg btn-block" type="submit" disabled={busy}>{busy ? "Creating..." : "Create a free board"}</button>
      <p className="hint">No sign-up. You get a link to share and a private admin link.</p>
    </form>
  );
}

Change history

Point store links to openapps.free
4a4c54cd95 · 18 hours ago
Release with preview
1d0728818d · 19 hours ago
Retry release
1abc843b3f · 19 hours ago
Bind app agent address in policy
00eb15f50d · 19 hours ago
Use OpenApps service URLs and add contribution strip
347a7b8ca0 · 19 hours ago
Bounded source sync
a0aaceae63 · 1 day ago
Bounded source sync
3125b8113f · 1 day ago
Bounded source sync
882a76ba95 · 1 day ago
Bounded source sync
852d9782ee · 1 day ago
Bounded source sync
a8ee705109 · 1 day ago