Skip to content
Source
Sign inSign up

attachmentUploadState.ts

51 lines1.7 KB

1import type { EnvironmentId } from "@t3tools/contracts";
2​
3export type ReadyAttachmentUpload = {
4 readonly status: "ready";
5 readonly environmentId: EnvironmentId;
6 readonly attachmentId: string;
7};
8​
9export type AttachmentUploadState =
10 | {
11 readonly status: "uploading";
12 readonly environmentId: EnvironmentId;
13 readonly progress: number;
14 readonly previous?: ReadyAttachmentUpload;
15 }
16 | ReadyAttachmentUpload
17 | {
18 readonly status: "failed";
19 readonly environmentId: EnvironmentId;
20 readonly reason: string;
21 /** Retry/release deletes this id; never use a persisted draft attachment id. */
22 readonly attachmentId?: string;
23 readonly previous?: ReadyAttachmentUpload;
24 };
25​
26export function attachmentUploadBlockReason(input: {
27 readonly imageIds: ReadonlyArray<string>;
28 readonly uploadsByImageId: Readonly<Record<string, AttachmentUploadState>>;
29 readonly environmentId: EnvironmentId;
30}): string | null {
31 let pending = 0;
32​
33 for (const imageId of input.imageIds) {
34 const upload = input.uploadsByImageId[imageId];
35 // Send retries failed uploads, so they must not disable it.
36 if (upload?.status === "failed" && upload.environmentId === input.environmentId) continue;
37 if (upload?.status !== "ready" || upload.environmentId !== input.environmentId) {
38 pending += 1;
39 }
40 }
41​
42 if (pending > 0) {
43 return pending === 1 ? "Attachment still uploading" : "Attachments still uploading";
44 }
45 return null;
47​
48export function formatAttachmentUploadProgress(progress: number): string {
49 const bounded = Math.max(0, Math.min(1, Number.isFinite(progress) ? progress : 0));
50 return `${Math.floor(bounded * 100)}%`;