ActButton
uses useState
A contribution's merge, close or reopen button: done as the viewer may, then the page shows what it came to, and a toast says so (or why not).
import { actOn, toasts } from "#codebase/tcmts/share"; import { useState } from "react"; import { Button } from "react-aria-components"; export function ActButton({ id, what, label, kind, }: { id: number; what: "merge" | "close" | "reopen"; label: string; kind?: string; }) { const [busy, setBusy] = useState(false); return ( <Button className={`btn${kind ? ` ${kind}` : ""}`} isPending={busy} onPress={async () => { setBusy(true); try { await actOn(id, what); toasts.add( { title: { merge: "Merged", close: "Closed", reopen: "Reopened" }[what], tone: "ok" }, { timeout: 4000 }, ); } catch (e) { toasts.add( { title: `Couldn't ${what} it`, description: (e as Error).message, tone: "bad" }, { timeout: 8000 }, ); } setBusy(false); }} > {busy ? `${label}…` : label} </Button> ); }