StarButton

tcmts.registry.pages.StarButton · #ansvsfko · term · runs in the browser

uses useState

A library's stars, and the visitor's, as a toggle (React Aria's ToggleButton): starring writes a codes.tcm.star record to their own repository (one per library, across its versions), and taking it back deletes it; the registry checks it there at once. Signed out, or if it doesn't work, a toast says so.

import { STAR_COLLECTION, starKey, starRecord } from "#codebase/tcmts/records";
import { restar } from "#codebase/tcmts/registry";
import { registrySession } from "#codebase/tcmts/registry/session";
import { toasts } from "#codebase/tcmts/share";
import { useState } from "react";
import { ToggleButton } from "react-aria-components";

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 press = async () => {
    const s = await registrySession();
    if (!s)
      return void toasts.add(
        {
          title: "Sign in to star it",
          description: "A star is a record in your own repository: sign in with your handle first.",
        },
        { timeout: 6000 },
      );
    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) {
      toasts.add(
        {
          title: shown.starred ? "Couldn't take the star back" : "Couldn't star it",
          description: (e as Error).message,
          tone: "bad",
        },
        { timeout: 8000 },
      );
    } finally {
      setBusy(false);
    }
  };
  return (
    <span className="star">
      <ToggleButton
        className="btn small"
        isSelected={shown.starred}
        isDisabled={busy}
        onChange={() => void press()}
      >
        {shown.starred ? "★ Starred" : "☆ Star"}
        <span className="count">{shown.count}</span>
      </ToggleButton>
    </span>
  );
}

uses

used by