import { Trans, useLingui } from "@lingui/react/macro"; import type { ScratchpadItem } from "@rakazo/contracts"; import { Button, Checkbox, Input } from "@rakazo/ui-web"; import { X } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { rpc } from "../lib/rpc"; export function ScratchpadSection({ botId }: { botId: string }) { const { t } = useLingui(); const [items, setItems] = useState([]); const [draft, setDraft] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const listGeneration = useRef(0); async function refresh() { const generation = ++listGeneration.current; const list = await rpc.scratchpad.list({ botId }); if (generation !== listGeneration.current) return; setItems(list); } useEffect(() => { const generation = ++listGeneration.current; void rpc.scratchpad .list({ botId }) .then((list) => { if (generation !== listGeneration.current) return; setItems(list); }) .catch(() => { if (generation !== listGeneration.current) return; setItems([]); }); return () => { listGeneration.current += 1; }; }, [botId]); async function addItem() { const title = draft.trim(); if (!title || busy) return; setBusy(true); setError(null); try { const created = await rpc.scratchpad.create({ botId, title }); setDraft(""); setItems((current) => [created, ...current.filter((item) => item.id !== created.id)]); try { await refresh(); } catch { setError(t`Saved, but list refresh failed`); } } catch { setError(t`Could not add`); } finally { setBusy(false); } } async function setStatus(item: ScratchpadItem, status: ScratchpadItem["status"]) { if (busy) return; setBusy(true); setError(null); try { const updated = await rpc.scratchpad.update({ itemId: item.id, status }); setItems((current) => { const next = current.map((entry) => (entry.id === updated.id ? updated : entry)); return status === "done" ? next.filter((entry) => entry.status !== "done") : next; }); try { await refresh(); } catch { setError(t`Saved, but list refresh failed`); } } catch { setError(t`Could not update`); } finally { setBusy(false); } } async function removeItem(item: ScratchpadItem) { if (busy) return; setBusy(true); setError(null); try { await rpc.scratchpad.remove({ itemId: item.id }); setItems((current) => current.filter((entry) => entry.id !== item.id)); try { await refresh(); } catch { setError(t`Removed, but list refresh failed`); } } catch { setError(t`Could not remove`); } finally { setBusy(false); } } return (
Open work
{items.length === 0 ? (
None yet
) : ( items.map((item) => (
void setStatus(item, checked ? "done" : "open")} className="mt-0.5" />
{item.title}
{item.notes ? (
{item.notes}
) : null}
{item.status} {item.status === "open" ? ( ) : item.status === "parked" ? ( ) : null}
)) )}
{ event.preventDefault(); void addItem(); }} > setDraft(event.target.value)} placeholder={t`Add item`} aria-label={t`New open-work item`} maxLength={200} className="min-w-0 flex-1" />
{error ?
{error}
: null}
); }