- GET /api/files/{id}/preview: an allow-list on the stored extension (downloads/preview.py) picks
image / PDF / text; served inline under `CSP: sandbox; frame-ancestors 'self'`, the type from the
table; HTML and SVG arrive as plain text; a text is its first 1 MiB (X-Preview-Truncated), sent
as UTF-8 whatever it was written in (BOM, UTF-8, else cp1250); a NUL means binary: 415
- the list says which preview each file gets (asset.preview); FE/BE contract on kinds and header
- the viewer (shared dialog shell): ← / → through the list's such files in its order, Save, open in
a tab, the file's menu; one stage height for every kind; a no-preview card offers the file
- the name, the card's picture and the menu's Preview open it; an image up to 8 MB is its own
thumbnail
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import type { DownloadJob } from "./api";
|
|
|
|
// Which row actions apply to a completed library / shared item — gated on the stored media_kind and
|
|
// job_kind. A generic uploaded file (media_kind='file') opens in the Files viewer instead of a player,
|
|
// and has no trim and no details edit. Re-download needs a re-fetchable source, which an edit clip and an upload
|
|
// both lack (the backend 409s both). A single source for the gate the Library + Shared rows render,
|
|
// so it's unit-testable instead of buried in JSX conditionals — and the two tabs can't drift apart.
|
|
|
|
const isMediaKind = (job: DownloadJob): boolean => job.asset?.media_kind !== "file";
|
|
const hasDuration = (job: DownloadJob): boolean => (job.asset?.duration_s ?? 0) > 0;
|
|
|
|
export interface LibraryRowActions {
|
|
play: boolean;
|
|
/** Opens in the Files viewer: a file that is not media (its preview, or the "no preview" card). */
|
|
view: boolean;
|
|
edit: boolean;
|
|
editDetails: boolean;
|
|
redownload: boolean;
|
|
}
|
|
|
|
export function libraryRowActions(job: DownloadJob): LibraryRowActions {
|
|
const media = isMediaKind(job);
|
|
return {
|
|
play: media,
|
|
view: !media && job.can_download,
|
|
edit: media && job.can_download && hasDuration(job),
|
|
editDetails: media,
|
|
redownload: !job.job_kind || job.job_kind === "download",
|
|
};
|
|
}
|
|
|
|
export interface SharedRowActions {
|
|
play: boolean;
|
|
view: boolean;
|
|
save: boolean;
|
|
edit: boolean;
|
|
}
|
|
|
|
export function sharedRowActions(job: DownloadJob): SharedRowActions {
|
|
const media = isMediaKind(job);
|
|
// The owner's `can_download` gates save/play/edit on a shared item (it reflects their link ACL).
|
|
return {
|
|
play: media && job.can_download,
|
|
view: !media && job.can_download,
|
|
save: job.can_download,
|
|
edit: media && job.can_download && hasDuration(job),
|
|
};
|
|
}
|