StarButton
uses useState
A library's stars, and the visitor's: starring writes a codes.tcm.star record to their own repository (one per
library, across its versions), and taking it back deletes it; the hub checks it there at once.
import { restar } from "#codebase/tcmts/hub"; import { hubSession } from "#codebase/tcmts/hub/session"; import { STAR_COLLECTION, starKey, starRecord } from "#codebase/tcmts/records"; import { useState } from "react"; export function StarButton({ author, pkg, count, starred, }: { author: string; pkg: string; count: number; starred: boolean; }) { const [shown, setShown] = useState({ count, starred }); const [busy, setBusy] = useState(false); const [error, setError] = useState<string | undefined>(); const press = async () => { setError(undefined); const s = await hubSession(); if (!s) return setError("Sign in to star it."); setBusy(true); try { const rkey = starKey(author, pkg); const res = await s.fetch( `/xrpc/com.atproto.repo.${shown.starred ? "deleteRecord" : "putRecord"}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ repo: s.did, collection: STAR_COLLECTION, rkey, ...(shown.starred ? {} : { record: starRecord(author, pkg), }), }), }, ); if (!res.ok) throw new Error(`your server said ${res.status}`); setShown(await restar(s.did, author, pkg)); } catch (e) { setError(`That didn't work: ${(e as Error).message}`); } finally { setBusy(false); } }; return ( <span className="star"> <button type="button" aria-pressed={shown.starred} disabled={busy} onClick={() => void press()} > {shown.starred ? "★ Starred" : "☆ Star"} </button>{" "} <span className="muted">{shown.count}</span> {error && ( <span className="error" role="alert"> {" "} {error} </span> )} </span> ); }
uses
- tcmts.hub.restar
- tcmts.hub.session.hubSession
- tcmts.records.STAR_COLLECTION
- tcmts.records.starKey
- tcmts.records.starRecord