diff --git a/src/packages/core/temporary-file/index.ts b/src/packages/core/temporary-file/index.ts index 0c344d30b4..0a663e6dd2 100644 --- a/src/packages/core/temporary-file/index.ts +++ b/src/packages/core/temporary-file/index.ts @@ -1,3 +1,4 @@ export * from './temporary-file.repository.js'; export * from './components/temporary-file-badge.element.js'; export * from './temporary-file-manager.class.js'; +export * from './types.js'; diff --git a/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/packages/core/temporary-file/temporary-file-manager.class.ts index e89f8979aa..227749055d 100644 --- a/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/packages/core/temporary-file/temporary-file-manager.class.ts @@ -1,21 +1,13 @@ import { UmbTemporaryFileRepository } from './temporary-file.repository.js'; +import { + TemporaryFileStatus, + type UmbQueueHandlerCallback, + type UmbTemporaryFileModel, + type UmbUploadOptions, +} from './types.js'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -///export type TemporaryFileStatus = 'success' | 'waiting' | 'error'; - -export enum TemporaryFileStatus { - SUCCESS = 'success', - WAITING = 'waiting', - ERROR = 'error', -} - -export interface UmbTemporaryFileModel { - file: File; - temporaryUnique: string; - status?: TemporaryFileStatus; -} - export class UmbTemporaryFileManager< UploadableItem extends UmbTemporaryFileModel = UmbTemporaryFileModel, > extends UmbControllerBase { @@ -24,7 +16,7 @@ export class UmbTemporaryFileManager< readonly #queue = new UmbArrayState([], (item) => item.temporaryUnique); public readonly queue = this.#queue.asObservable(); - async uploadOne(uploadableItem: UploadableItem): Promise { + async uploadOne(uploadableItem: UploadableItem, options?: UmbUploadOptions): Promise { this.#queue.setValue([]); const item: UploadableItem = { @@ -33,15 +25,18 @@ export class UmbTemporaryFileManager< }; this.#queue.appendOne(item); - return (await this.#handleQueue())[0]; + return (await this.#handleQueue({ ...options, chunkSize: 1 }))[0]; } - async upload(queueItems: Array): Promise> { + async upload( + queueItems: Array, + options?: UmbUploadOptions, + ): Promise> { this.#queue.setValue([]); const items = queueItems.map((item): UploadableItem => ({ status: TemporaryFileStatus.WAITING, ...item })); this.#queue.append(items); - return this.#handleQueue(); + return this.#handleQueue({ ...options }); } removeOne(unique: string) { @@ -52,30 +47,44 @@ export class UmbTemporaryFileManager< this.#queue.remove(uniques); } - async #handleQueue() { + async #handleQueue(options?: UmbUploadOptions): Promise> { const filesCompleted: Array = []; const queue = this.#queue.getValue(); if (!queue.length) return filesCompleted; - for (const item of queue) { - if (!item.temporaryUnique) throw new Error(`Unique is missing for item ${item}`); + const handler: UmbQueueHandlerCallback = async (item) => { + const completedUpload = await this.#handleUpload(item); + filesCompleted.push(completedUpload); - const { error } = await this.#temporaryFileRepository.upload(item.temporaryUnique, item.file); - //await new Promise((resolve) => setTimeout(resolve, (Math.random() + 0.5) * 1000)); // simulate small delay so that the upload badge is properly shown + if (options?.callback) await options.callback(completedUpload); + }; - let status: TemporaryFileStatus; - if (error) { - status = TemporaryFileStatus.ERROR; - this.#queue.updateOne(item.temporaryUnique, { ...item, status }); - } else { - status = TemporaryFileStatus.SUCCESS; - this.#queue.updateOne(item.temporaryUnique, { ...item, status }); - } + const chunkSize = options?.chunkSize ?? 5; + const chunks = Math.ceil(queue.length / chunkSize); - filesCompleted.push({ ...item, status }); + for (let i = 0; i < chunks; i++) { + const chunk = queue.slice(i * chunkSize, i * chunkSize + chunkSize); + await Promise.all(chunk.map(handler)); } return filesCompleted; } + + async #handleUpload(item: UploadableItem) { + if (!item.temporaryUnique) throw new Error(`Unique is missing for item ${item}`); + + const { error } = await this.#temporaryFileRepository.upload(item.temporaryUnique, item.file); + + let status: TemporaryFileStatus; + if (error) { + status = TemporaryFileStatus.ERROR; + this.#queue.updateOne(item.temporaryUnique, { ...item, status }); + } else { + status = TemporaryFileStatus.SUCCESS; + this.#queue.updateOne(item.temporaryUnique, { ...item, status }); + } + + return { ...item, status }; + } } diff --git a/src/packages/core/temporary-file/types.ts b/src/packages/core/temporary-file/types.ts new file mode 100644 index 0000000000..2c28850f47 --- /dev/null +++ b/src/packages/core/temporary-file/types.ts @@ -0,0 +1,18 @@ +export enum TemporaryFileStatus { + SUCCESS = 'success', + WAITING = 'waiting', + ERROR = 'error', +} + +export interface UmbTemporaryFileModel { + file: File; + temporaryUnique: string; + status?: TemporaryFileStatus; +} + +export type UmbQueueHandlerCallback = (item: TItem) => Promise; + +export type UmbUploadOptions = { + chunkSize?: number; + callback?: UmbQueueHandlerCallback; +};