Allow stalled chat uploads to be cancelled

This commit is contained in:
pewdiepie-archdaemon
2026-06-29 02:06:39 +00:00
parent b419caf9f7
commit e2c8b8eb37
2 changed files with 56 additions and 1 deletions
+44 -1
View File
@@ -15,6 +15,9 @@ let uploaded = [];
let _lastUploadedMeta = [];
let API_BASE = '';
let _uploadSpinners = [];
let _uploadAbortCtrl = null;
let _uploading = false;
let _lastUploadCancelled = false;
const _previewUrls = new WeakMap();
const MAX_FILES = 10;
@@ -130,6 +133,7 @@ function _createChip(f, idx) {
* Remove a pending file by index
*/
export function removePending(idx) {
if (_uploading) cancelUpload();
_revokePreviewUrl(pendingFiles[idx]);
pendingFiles.splice(idx, 1);
renderAttachStrip();
@@ -140,6 +144,7 @@ export function removePending(idx) {
*/
export async function uploadPending() {
if (pendingFiles.length === 0) return [];
_lastUploadCancelled = false;
// The message bubble is shown immediately, but the upload can take a moment —
// dim the chips and overlay a whirlpool so it's clear the files are still
@@ -164,11 +169,19 @@ export async function uploadPending() {
const fd = new FormData();
pendingFiles.forEach(f => fd.append('files', f, f.name || 'paste.png'));
_uploadAbortCtrl = new AbortController();
_uploading = true;
const timeoutId = setTimeout(() => {
if (_uploadAbortCtrl && !_uploadAbortCtrl.signal.aborted) {
try { _uploadAbortCtrl.abort(); } catch (_) {}
}
}, 120000);
try {
const res = await fetch(`${API_BASE}/api/upload`, {
method: 'POST',
body: fd
body: fd,
signal: _uploadAbortCtrl.signal,
});
if (!res.ok) {
// Surface the failure instead of swallowing it. Previously a non-OK
@@ -193,7 +206,18 @@ export async function uploadPending() {
// returned shape as `ids` for backward-compatibility with existing call sites.
_lastUploadedMeta = uploaded;
return uploaded.map(x => x.id);
} catch (e) {
if (e && e.name === 'AbortError') {
_lastUploadCancelled = true;
_showToast('Upload cancelled');
return [];
}
_showToast('Upload failed: ' + (e?.message || 'network error'));
return [];
} finally {
clearTimeout(timeoutId);
_uploading = false;
_uploadAbortCtrl = null;
_uploadSpinners.forEach(sp => { try { sp.stop && sp.stop(); } catch (_) {} });
_uploadSpinners = [];
if (strip) strip.classList.remove('attach-uploading');
@@ -266,6 +290,7 @@ export function getPendingInfo() {
* Clear all pending files
*/
export function clearPending() {
if (_uploading) cancelUpload();
pendingFiles.forEach(_revokePreviewUrl);
pendingFiles = [];
renderAttachStrip();
@@ -276,6 +301,21 @@ export function getLastUploadedMeta() {
return _lastUploadedMeta;
}
export function isUploading() {
return _uploading;
}
export function wasLastUploadCancelled() {
return _lastUploadCancelled;
}
export function cancelUpload() {
_lastUploadCancelled = true;
if (_uploadAbortCtrl && !_uploadAbortCtrl.signal.aborted) {
try { _uploadAbortCtrl.abort(); } catch (_) {}
}
}
var escapeHtml = uiModule.esc;
const fileHandlerModule = {
@@ -290,6 +330,9 @@ const fileHandlerModule = {
getPendingRaw,
clearPending,
getLastUploadedMeta,
isUploading,
wasLastUploadCancelled,
cancelUpload,
};
export default fileHandlerModule;