HomePage

tcmts.registry.pages.HomePage · #bb5lzqlp · term · runs on the server

is an async component

Home: what's happened lately, newest first: releases, reviews and advisories (and yanks), or one of them (show); for a visitor signed in, what people they follow did (following), or what's happened to libraries they use (mine: in releases they've announced, or in the codebase they gave). Beside it, what to show, and, signed in, the libraries they use.

import {
  CODEBASE_COOKIE, everyone, followsOf, libraries, librariesUsed, libraryHref, libraryKey, registryShare, releases,
  said, shownAttestations, shownRelease, visitor,
} from "#codebase/tcmts/registry";
import { AttestationItem, CodebaseForm, FeedItem, Legend, RegistryFrame } from "#codebase/tcmts/registry/pages";
import { cookie } from "#codebase/tcmts/server";
import { at, NavTabs } from "#codebase/tcmts/share";

export async function HomePage({ show }: { show: string }) {
  registryShare();
  const rels = (await releases.find({ state: "mirrored" })).map((r) => r.value);
  const said_ = await shownAttestations();
  const who = await everyone();
  const byNamespace = new Map(
    (await releases.find())
      .map((r) => r.value)
      .filter(shownRelease)
      .map((r) => [r.namespace, r]),
  );
  const me = await visitor();
  const codebase = cookie(CODEBASE_COOKIE) ?? "";
  const follows = me && show === "following" ? await followsOf(me.did) : undefined;
  const mine = me ? await librariesUsed(me.did, codebase || undefined) : undefined;
  const used = show === "mine" ? mine : undefined;
  const theirs = (did: string) => !follows || follows.has(did);
  const about = (namespace: string) => {
    const r = byNamespace.get(namespace);
    return !used || (r !== undefined && used.has(libraryKey(r)));
  };
  const mixed = show === "" || show === "following" || show === "mine";
  const items = (
    (show === "following" || show === "mine") && !me
      ? []
      : [
          ...(mixed || show === "releases"
            ? rels
                .filter((r) => theirs(r.did) && about(r.namespace))
                .map((r) => ({ at: r.createdAt, r }))
            : []),
          ...said_
            .filter((a) =>
              mixed
                ? true
                : show === "reviews"
                  ? a.kind === "review"
                  : show === "advisories"
                    ? a.kind === "advisory"
                    : false,
            )
            .filter((a) => theirs(a.did) && about(a.namespace))
            .map((a) => ({ at: a.createdAt, a })),
        ]
  ).sort((x, y) => Date.parse(y.at) - Date.parse(x.at));
  const count = (k: string) => said_.filter((a) => a.kind === k).length;
  const filter = (id: string, label: string, n?: number) => ({
    id: id || "all",
    label,
    href: at(id ? `/?show=${id}` : "/"),
    ...(n ? { count: n } : {}),
  });
  // (theirs, yanked ones too: it's who uses one that most needs to know)
  const usedLibs = mine
    ? (await libraries({ yanked: true })).filter((l) => mine.has(libraryKey(l)))
    : [];
  return (
    <RegistryFrame tab="activity" title="Activity">
      <div className="page cols-feed">
        <div className="feedfilters">
          <NavTabs
            label="What to show"
            vertical
            selected={show || "all"}
            tabs={[
              filter("", "Everything"),
              filter("releases", "Releases"),
              filter("reviews", "Reviews", count("review")),
              filter("advisories", "Advisories", count("advisory")),
            ]}
          />
          {me && (
            <>
              <hr />
              <NavTabs
                label="Yours"
                vertical
                selected={show}
                tabs={[filter("following", "People I follow"), filter("mine", "Libraries I use")]}
              />
              <p className="muted">
                <small>
                  "People I follow" uses your Bluesky follows; "Libraries I use", your releases and
                  the codebase you give.
                </small>
              </p>
            </>
          )}
        </div>
        <div>
          {show === "mine" && me && (
            <div className="box boxbody">
              <div className="muted">
                The libraries in releases you've announced{codebase ? ", and in your codebase" : ""}
                : {used?.size ?? 0}. Give the address of a codebase you serve (
                <code>tcm serve</code>) to add the libraries it holds.
              </div>
              <CodebaseForm url={codebase} />
            </div>
          )}
          {items.length ? (
            <div className="box feed">
              {items
                .slice(0, 100)
                .map((x) =>
                  "r" in x ? (
                    <FeedItem key={x.r.uri} r={x.r} who={who.get(x.r.did) ?? { did: x.r.did }} />
                  ) : (
                    <AttestationItem
                      key={x.a.uri}
                      a={x.a}
                      who={who.get(x.a.did) ?? { did: x.a.did }}
                      r={byNamespace.get(x.a.namespace)}
                      author={
                        who.get(byNamespace.get(x.a.namespace)?.did ?? "") ??
                        (x.a.author ? { did: x.a.author } : undefined)
                      }
                    />
                  ),
                )}
            </div>
          ) : (show === "following" || show === "mine") && !me ? (
            <div className="empty">
              Sign in to see what people you follow, and libraries you use, are up to.
            </div>
          ) : show === "following" ? (
            <div className="empty">Nobody you follow has announced or said anything here yet.</div>
          ) : show === "mine" ? (
            <div className="empty">Nothing has happened to the libraries you use yet.</div>
          ) : (
            <div className="empty">
              Nothing announced yet: <code>tcm release --announce</code> announces a release.
            </div>
          )}
        </div>
        <div className="side">
          {me && (
            <div className="sect">
              <h4>Libraries your codebase uses</h4>
              {usedLibs.map((l) => {
                const w = who.get(l.did) ?? { did: l.did };
                const advised = said_.some(
                  (a) =>
                    a.kind === "advisory" && l.versions.some((v) => v.namespace === a.namespace),
                );
                return (
                  <div className="row" key={libraryKey(l)}>
                    <a className="mono" href={libraryHref(w, l.package)}>
                      {said(w)}/{l.package} {l.latest.version}
                    </a>
                    {!l.versions.length ? (
                      <span className="right bad">yanked</span>
                    ) : (
                      advised && <span className="right bad">advisory</span>
                    )}
                  </div>
                );
              })}
              {!usedLibs.length && (
                <div className="muted">
                  None the registry knows yet: <a href={at("/?show=mine")}>give your codebase</a>.
                </div>
              )}
            </div>
          )}
          <div className="sect">
            <h4>Where code runs</h4>
            <Legend />
          </div>
        </div>
      </div>
    </RegistryFrame>
  );
}

uses

used by