diff --git a/packages/api-client/src/platform/xhr-upload-client.ts b/packages/api-client/src/platform/xhr-upload-client.ts index 60d4e6b8f6..1f0394d013 100644 --- a/packages/api-client/src/platform/xhr-upload-client.ts +++ b/packages/api-client/src/platform/xhr-upload-client.ts @@ -97,6 +97,7 @@ export abstract class XHRUploadClient extends AbstractModrinthClient { const xhr = new XMLHttpRequest() const metadata = context.metadata as UploadMetadata const fallbackTotal = this.getUploadPayloadSize(metadata) + const abortUpload = () => xhr.abort() xhr.upload.addEventListener('progress', (e) => { const total = e.lengthComputable ? e.total : fallbackTotal @@ -125,6 +126,12 @@ export abstract class XHRUploadClient extends AbstractModrinthClient { xhr.addEventListener('error', () => reject(new ModrinthApiError('Upload failed'))) xhr.addEventListener('abort', () => reject(new ModrinthApiError('Upload cancelled'))) + abortController.signal.addEventListener('abort', abortUpload, { once: true }) + + if (abortController.signal.aborted) { + reject(new ModrinthApiError('Upload cancelled')) + return + } // build URL with params (unlike $fetch, XHR doesn't handle params automatically) let url = context.url @@ -143,22 +150,21 @@ export abstract class XHRUploadClient extends AbstractModrinthClient { } // Send either FormData or file depending on what was provided - const data = 'formData' in metadata ? metadata.formData : metadata.file + const data = metadata.formData instanceof FormData ? metadata.formData : metadata.file xhr.send(data) - abortController.signal.addEventListener('abort', () => xhr.abort()) }) } private getUploadPayloadSize(metadata: UploadMetadata): number { - if ('file' in metadata) { - return metadata.file.size + if (metadata.formData instanceof FormData) { + let total = 0 + metadata.formData.forEach((value) => { + total += value instanceof Blob ? value.size : new Blob([value]).size + }) + return total } - let total = 0 - metadata.formData.forEach((value) => { - total += value instanceof Blob ? value.size : new Blob([value]).size - }) - return total + return metadata.file instanceof Blob ? metadata.file.size : 0 } protected createUploadError(xhr: XMLHttpRequest): ModrinthApiError { diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/content.vue b/packages/ui/src/layouts/wrapped/hosting/manage/content.vue index ad6a9fb289..679bc191fb 100644 --- a/packages/ui/src/layouts/wrapped/hosting/manage/content.vue +++ b/packages/ui/src/layouts/wrapped/hosting/manage/content.vue @@ -733,6 +733,9 @@ function handleUploadFiles() { uploadState.value.uploadedBytes = clampedUploadedBytes uploadState.value.completedFiles = completedFiles uploadState.value.currentFileName = files[currentFileIndex]?.name ?? null + if (clampedUploadedBytes >= totalBytes) { + cancelUpload.value = null + } } const handle = client.kyros.content_v1.uploadAddonFile(wid, files, { @@ -742,6 +745,7 @@ function handleUploadFiles() { try { await handle.promise + cancelUpload.value = null uploadState.value.uploadedBytes = totalBytes uploadState.value.completedFiles = files.length uploadState.value.currentFileName = files[files.length - 1]?.name ?? null