~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
97 lines · 3.2 KB Raw History
import { useQuery } from '@tanstack/react-query';
import { PackageIcon } from 'lucide-react';
import { EmptyState } from '#/components/molecules/empty-state.jsx';
import { ListBox } from '#/components/molecules/list-box.jsx';
import { ListSkeleton } from '#/components/molecules/list-skeleton.jsx';
import { PackageCard } from '#/components/molecules/package-card.jsx';
import { PageBar } from '#/components/molecules/page-bar.jsx';
import { PageNotice } from '#/components/molecules/page-notice.jsx';
import { Screen } from '#/components/molecules/screen.jsx';
import { TabBar } from '#/components/molecules/tab-bar.jsx';
import { repoRecord } from '#/lib/git.js';
import { loadPackages } from '#/lib/packages.js';

/** What each tab holds, and what an empty one says. */
const TABS = [
  { key: '', label: 'All', noun: 'package', plural: 'packages' },
  { key: 'npm', label: 'npm', noun: 'npm package', plural: 'npm packages' },
  {
    key: 'oci',
    label: 'images',
    noun: 'container image',
    plural: 'container images',
  },
];

/**
 * Everything this repository publishes to the account's own registries, with
 * the command that installs each one. The account holds the registries, so a
 * reader installs from the same host that serves this page.
 *
 * A repository can name many packages, so the page filters by registry. The
 * registries there are (npm and oci) stay fixed tabs rather than being read
 * from the data, so a tab is a stable address and the "nothing here" line
 * tells the two registries apart.
 */
export function PackagesPage({ repo, registry, space }) {
  const record = repoRecord(repo, space);
  const { data } = useQuery({
    queryKey: ['packages', repo],
    queryFn: () => loadPackages(repo),
  });

  if (!record) {
    return <PageNotice>No such repository.</PageNotice>;
  }

  const active = TABS.find((entry) => entry.key === registry) ?? TABS[0];
  const at = `/${encodeURIComponent(repo)}/packages`;
  const of = (entry) =>
    (data ?? []).filter(
      (pkg) => entry.key === '' || pkg.registry === entry.key,
    );
  const shown = data ? of(active) : [];

  return (
    <Screen
      bar={
        <PageBar tabs>
          <TabBar
            className="border-b-0"
            active={active.key}
            tabs={TABS.map((entry) => ({
              key: entry.key,
              label: entry.label,
              href: entry.key === '' ? at : `${at}/${entry.key}`,
              count: data ? of(entry).length : undefined,
            }))}
          />
        </PageBar>
      }
    >
      <ListBox>
        {!data ? (
          <ListSkeleton rows={3} />
        ) : data.length === 0 ? (
          <EmptyState
            icon={<PackageIcon />}
            title="No packages"
            description="This repository names no published package."
          />
        ) : shown.length === 0 ? (
          <EmptyState
            icon={<PackageIcon />}
            title={`No ${active.plural}`}
            description={`This repository names no ${active.noun}.`}
          />
        ) : (
          <ul>
            {shown.map((pkg) => (
              <PackageCard key={`${pkg.registry}:${pkg.name}`} pkg={pkg} />
            ))}
          </ul>
        )}
      </ListBox>
    </Screen>
  );
}