chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { useEffect, useState, useSyncExternalStore } from 'react';
import { bytes } from '#/lib/format.js';
import { onRepoProgress, readSource, repoProgress } from '#/lib/git.js';
/**
* How long a read has to run before it is worth telling anyone about. A
* repository already open answers in a few milliseconds, and a bar that
* appears and leaves inside one frame reads as a flicker rather than as
* progress.
*/
const QUIET_MS = 400;
/**
* What a repository being opened looks like while it opens.
*
* Read in this page, an indexed repository opens off its small index blobs
* and one without indexes downloads its whole bundle chain. Either way the
* record says how large the download is before the first byte arrives, so
* the wait can be an honest measure rather than a spinner. Read on the
* server, nothing is downloaded here, so the wait has no measure and says
* only that the server is at work.
*
* Nothing renders until the read has run past QUIET_MS, so the common case,
* an indexed repository or one already in memory, shows no wait at all.
* Whatever appears after that fades in, so a read that finishes just over
* the line does not snap on and straight off again.
*/
export function ReadingRepo({ size }) {
// The third argument is the snapshot for a render with no client behind
// it. The store is a plain module value, so it reads the same either way.
const progress = useSyncExternalStore(
onRepoProgress,
repoProgress,
repoProgress,
);
const source = useSyncExternalStore(onRepoProgress, readSource, readSource);
const [slow, setSlow] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setSlow(true), QUIET_MS);
return () => clearTimeout(timer);
}, []);
if (!slow) return null;
if (source === 'server' && progress.total === 0) {
return (
<div className="fade-in rounded-2xl border border-border bg-card px-5 py-6">
<div className="text-[13px] text-muted-foreground">
Reading the repository
</div>
<div className="mt-3 h-1.5 overflow-hidden rounded-full bg-muted">
<div className="h-full w-1/3 animate-pulse rounded-full bg-key" />
</div>
<p className="mt-3 text-[12.5px] text-faint">
The server reads it from its own storage. The answer is cached from
here on.
</p>
</div>
);
}
const total = progress.total || size || 0;
const done = total > 0 ? Math.min(1, progress.loaded / total) : 0;
return (
<div className="fade-in rounded-2xl border border-border bg-card px-5 py-6">
<div className="flex items-baseline justify-between gap-3 text-[13px]">
<span className="text-muted-foreground">Reading the repository</span>
<span className="font-mono text-[12.5px] text-faint tabular-nums">
{total > 0 ? `${bytes(progress.loaded)} of ${bytes(total)}` : ''}
</span>
</div>
<div className="mt-3 h-1.5 overflow-hidden rounded-full bg-muted">
<div
className="h-full rounded-full bg-key transition-[width] duration-200"
style={{ width: `${Math.round(done * 100)}%` }}
/>
</div>
<p className="mt-3 text-[12.5px] text-faint">
The repository is read straight from its bundles in blob storage. It is
cached from here on.
</p>
</div>
);
}