chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { Badge } from '#/components/atoms/badge.jsx';
import { Link } from '#/components/atoms/link.jsx';
import { TextLink } from '#/components/molecules/text-link.jsx';
import { bytes, dateTime } from '#/lib/format.js';
import { parseAtUri } from '#/lib/record-link.js';
/**
* The records this file appears in, each opening in the records browser. A
* file no record points at is flagged instead: the link table is this server's
* bookkeeping, so it is phrased as what is known, not a promise.
*/
function UsedBy({ records, recordHref, onNavigate }) {
if (!records.length) {
return (
<div className="mt-2.5">
<Badge>Not used by any record</Badge>
<p className="mt-1.5 text-[12px] text-faint leading-relaxed">
No record points at this file. Unused files are cleaned up
automatically.
</p>
</div>
);
}
return (
<div className="mt-2.5">
<div className="text-[12px] text-faint">Used by</div>
<div className="mt-1 flex flex-col items-start gap-1">
{records.map((uri) => {
const target = parseAtUri(uri);
if (!target) return null;
return (
<Link
key={uri}
href={recordHref(target.collection, target.rkey)}
onNavigate={onNavigate}
className="break-all font-mono text-[12px] text-key underline-offset-2 hover:text-foreground hover:underline"
>
{target.collection}/{target.rkey}
</Link>
);
})}
</div>
</div>
);
}
/** One file's full details; the body of both the card and the tile dialog. */
export function FileDetails({ blob, blobBase, recordHref, onNavigate }) {
return (
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-0.5">
<span className="min-w-0 break-all font-medium text-[14px] text-foreground">
{blob.mimeType || 'File'}
</span>
<span className="text-[12.5px] text-muted-foreground">
{bytes(blob.size)}
</span>
{(blob.time || blob.createdAt) && (
<span
className="ml-auto text-[12px] text-faint"
title={
blob.timeFromRecord && blob.createdAt
? `Stored here ${dateTime(blob.createdAt)}`
: undefined
}
>
{blob.timeFromRecord
? dateTime(blob.time)
: `Stored ${dateTime(blob.time || blob.createdAt)}`}
</span>
)}
</div>
<div className="mt-0.5 break-all font-mono text-[11.5px] text-faint">
{blob.cid}
</div>
<UsedBy
records={blob.records}
recordHref={recordHref}
onNavigate={onNavigate}
/>
{blobBase && (
<TextLink
href={`${blobBase}&cid=${encodeURIComponent(blob.cid)}`}
external
className="mt-3 text-[12.5px]"
>
Download
</TextLink>
)}
</div>
);
}