mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-14 12:48:03 +00:00
Allow stalled chat uploads to be cancelled
This commit is contained in:
@@ -292,6 +292,9 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
|
|||||||
|
|
||||||
// If currently streaming, stop it
|
// If currently streaming, stop it
|
||||||
if (isStreaming) {
|
if (isStreaming) {
|
||||||
|
if (fileHandlerModule.isUploading && fileHandlerModule.isUploading()) {
|
||||||
|
fileHandlerModule.cancelUpload && fileHandlerModule.cancelUpload();
|
||||||
|
}
|
||||||
// Cancel server-side research if in progress
|
// Cancel server-side research if in progress
|
||||||
const _cancelSid = sessionModule.getCurrentSessionId();
|
const _cancelSid = sessionModule.getCurrentSessionId();
|
||||||
if (_cancelSid && _researchingStreamIds.has(_cancelSid)) {
|
if (_cancelSid && _researchingStreamIds.has(_cancelSid)) {
|
||||||
@@ -687,6 +690,15 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
|
|||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error('upload failed', e);
|
console.error('upload failed', e);
|
||||||
}
|
}
|
||||||
|
if (_pendingAttachInfo && !ids.length && !(_pendingRegenAttachments && _pendingRegenAttachments.length)) {
|
||||||
|
if (_userMsgEl && _userMsgEl.parentNode) _userMsgEl.remove();
|
||||||
|
if (fileHandlerModule.wasLastUploadCancelled && !fileHandlerModule.wasLastUploadCancelled()) {
|
||||||
|
uiModule.showError && uiModule.showError('Upload failed. Attachment kept so you can retry.');
|
||||||
|
}
|
||||||
|
updateSubmitButton('idle', submitBtn);
|
||||||
|
_releaseSendFlag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Carry over the original message's file-ids on a regenerate so the new
|
// Carry over the original message's file-ids on a regenerate so the new
|
||||||
// send still references the same photos / docs (and picks up the user's
|
// send still references the same photos / docs (and picks up the user's
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ let uploaded = [];
|
|||||||
let _lastUploadedMeta = [];
|
let _lastUploadedMeta = [];
|
||||||
let API_BASE = '';
|
let API_BASE = '';
|
||||||
let _uploadSpinners = [];
|
let _uploadSpinners = [];
|
||||||
|
let _uploadAbortCtrl = null;
|
||||||
|
let _uploading = false;
|
||||||
|
let _lastUploadCancelled = false;
|
||||||
const _previewUrls = new WeakMap();
|
const _previewUrls = new WeakMap();
|
||||||
|
|
||||||
const MAX_FILES = 10;
|
const MAX_FILES = 10;
|
||||||
@@ -130,6 +133,7 @@ function _createChip(f, idx) {
|
|||||||
* Remove a pending file by index
|
* Remove a pending file by index
|
||||||
*/
|
*/
|
||||||
export function removePending(idx) {
|
export function removePending(idx) {
|
||||||
|
if (_uploading) cancelUpload();
|
||||||
_revokePreviewUrl(pendingFiles[idx]);
|
_revokePreviewUrl(pendingFiles[idx]);
|
||||||
pendingFiles.splice(idx, 1);
|
pendingFiles.splice(idx, 1);
|
||||||
renderAttachStrip();
|
renderAttachStrip();
|
||||||
@@ -140,6 +144,7 @@ export function removePending(idx) {
|
|||||||
*/
|
*/
|
||||||
export async function uploadPending() {
|
export async function uploadPending() {
|
||||||
if (pendingFiles.length === 0) return [];
|
if (pendingFiles.length === 0) return [];
|
||||||
|
_lastUploadCancelled = false;
|
||||||
|
|
||||||
// The message bubble is shown immediately, but the upload can take a moment —
|
// 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
|
// 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();
|
const fd = new FormData();
|
||||||
pendingFiles.forEach(f => fd.append('files', f, f.name || 'paste.png'));
|
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 {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/api/upload`, {
|
const res = await fetch(`${API_BASE}/api/upload`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: fd
|
body: fd,
|
||||||
|
signal: _uploadAbortCtrl.signal,
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
// Surface the failure instead of swallowing it. Previously a non-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.
|
// returned shape as `ids` for backward-compatibility with existing call sites.
|
||||||
_lastUploadedMeta = uploaded;
|
_lastUploadedMeta = uploaded;
|
||||||
return uploaded.map(x => x.id);
|
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 {
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
_uploading = false;
|
||||||
|
_uploadAbortCtrl = null;
|
||||||
_uploadSpinners.forEach(sp => { try { sp.stop && sp.stop(); } catch (_) {} });
|
_uploadSpinners.forEach(sp => { try { sp.stop && sp.stop(); } catch (_) {} });
|
||||||
_uploadSpinners = [];
|
_uploadSpinners = [];
|
||||||
if (strip) strip.classList.remove('attach-uploading');
|
if (strip) strip.classList.remove('attach-uploading');
|
||||||
@@ -266,6 +290,7 @@ export function getPendingInfo() {
|
|||||||
* Clear all pending files
|
* Clear all pending files
|
||||||
*/
|
*/
|
||||||
export function clearPending() {
|
export function clearPending() {
|
||||||
|
if (_uploading) cancelUpload();
|
||||||
pendingFiles.forEach(_revokePreviewUrl);
|
pendingFiles.forEach(_revokePreviewUrl);
|
||||||
pendingFiles = [];
|
pendingFiles = [];
|
||||||
renderAttachStrip();
|
renderAttachStrip();
|
||||||
@@ -276,6 +301,21 @@ export function getLastUploadedMeta() {
|
|||||||
return _lastUploadedMeta;
|
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;
|
var escapeHtml = uiModule.esc;
|
||||||
|
|
||||||
const fileHandlerModule = {
|
const fileHandlerModule = {
|
||||||
@@ -290,6 +330,9 @@ const fileHandlerModule = {
|
|||||||
getPendingRaw,
|
getPendingRaw,
|
||||||
clearPending,
|
clearPending,
|
||||||
getLastUploadedMeta,
|
getLastUploadedMeta,
|
||||||
|
isUploading,
|
||||||
|
wasLastUploadCancelled,
|
||||||
|
cancelUpload,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fileHandlerModule;
|
export default fileHandlerModule;
|
||||||
|
|||||||
Reference in New Issue
Block a user