LibraryActions
uses useState
What to do with a library: Upgrade, which first says what upgrading would change (tcm lib upgrade --check), then
does it; Remove, asking first, with what uses it (which would then use code nothing names).
import { checkLib, removeLib, toldOf, upgradeLib } from "#codebase/tcmts/studio"; import { useState } from "react"; import { Button, Dialog, DialogTrigger, Heading, Popover } from "react-aria-components"; export function LibraryActions({ path, users }: { path: string; users: string[] }) { const [check, setCheck] = useState<{ text: string; ok: boolean } | undefined>(); const [busy, setBusy] = useState(false); return ( <span className="actions rowacts"> <DialogTrigger onOpenChange={(open) => { if (!open) return; setCheck(undefined); checkLib(path).then( (text) => setCheck({ text, ok: true }), (e: Error) => setCheck({ text: e.message, ok: false }), ); }} > <Button className="btn small">Upgrade</Button> <Popover className="menu-pop getit wide" placement="bottom end" offset={6}> <Dialog className="getit-body" aria-label={`Upgrade ${path}`}> {({ close }) => { const current = !!check && check.ok && /\balready\b/.test(check.text); return ( <> <Heading slot="title" className="getit-title"> Upgrade {path} </Heading> {!check ? ( <p className="muted">Asking what its newest release changes…</p> ) : ( <pre className={`code applog${check.ok ? "" : " bad"}`}>{check.text}</pre> )} <div className="actions"> <Button className="btn" onPress={close}> {current || (check && !check.ok) ? "Close" : "Cancel"} </Button> {check?.ok && !current && ( <Button className="btn primary" isPending={busy} onPress={async () => { setBusy(true); if ( await toldOf( upgradeLib(path), `Upgraded ${path}`, `Couldn't upgrade ${path}`, ) ) close(); setBusy(false); }} > Upgrade </Button> )} </div> </> ); }} </Dialog> </Popover> </DialogTrigger> <DialogTrigger> <Button className="btn small danger">Remove</Button> <Popover className="menu-pop getit" placement="bottom end" offset={6}> <Dialog className="getit-body" aria-label={`Remove ${path}`}> {({ close }) => ( <> <Heading slot="title" className="getit-title"> Remove {path}? </Heading> <p> {users.length ? `${users.length} definition${users.length === 1 ? " uses" : "s use"} it (${users.slice(0, 4).join(", ")}${users.length > 4 ? ", …" : ""}): ${users.length === 1 ? "it keeps" : "they keep"} working, by hash, using code nothing names then.` : "Nothing outside it uses it. Its names go, and its entry in the record beside it; history keeps it."} </p> <div className="actions"> <Button className="btn" onPress={close}> Keep it </Button> <Button className="btn danger" onPress={() => { close(); void toldOf(removeLib(path), `Removed ${path}`, `Couldn't remove ${path}`); }} > Remove </Button> </div> </> )} </Dialog> </Popover> </DialogTrigger> </span> ); }