diff --git a/static/js/document.js b/static/js/document.js index 9c0acea..cc5af1f 100644 --- a/static/js/document.js +++ b/static/js/document.js @@ -2776,6 +2776,7 @@ import * as Modals from './modalManager.js'; } async function _sendEmail() { + const sendDocId = activeDocId; const to = document.getElementById('doc-email-to')?.value?.trim(); const cc = document.getElementById('doc-email-cc')?.value?.trim() || ''; const bcc = document.getElementById('doc-email-bcc')?.value?.trim() || ''; @@ -2804,6 +2805,7 @@ import * as Modals from './modalManager.js'; const _sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); let sendSpinner = null; let origBtnHtml = ''; + let detachedEmailDoc = null; if (btn) { btn.disabled = true; origBtnHtml = btn.innerHTML; @@ -2822,8 +2824,11 @@ import * as Modals from './modalManager.js'; onAction: () => { canceled = true; }, }); } + detachedEmailDoc = _detachActiveEmailForBackground(sendDocId); await _sleep(1200); if (canceled) { + _restoreDetachedEmailDoc(detachedEmailDoc); + detachedEmailDoc = null; if (uiModule) uiModule.showToast('Send canceled'); return; } @@ -2839,6 +2844,8 @@ import * as Modals from './modalManager.js'; } await _sleep(2200); if (undone) { + _restoreDetachedEmailDoc(detachedEmailDoc); + detachedEmailDoc = null; if (uiModule) uiModule.showToast('Send undone'); return; } @@ -2858,15 +2865,6 @@ import * as Modals from './modalManager.js'; }); const data = await res.json(); if (data.success) { - // Satisfying send effect: fly the email doc upward with fade - const docPane = document.getElementById('doc-pane') || document.querySelector('.doc-pane'); - const emailHeader = document.getElementById('doc-email-header'); - const editorWrap = document.getElementById('doc-editor-wrap'); - const target = emailHeader?.parentElement || docPane; - if (target) { - target.classList.add('email-send-fx'); - setTimeout(() => target.classList.remove('email-send-fx'), 700); - } if (uiModule) { uiModule.showToast('Message sent', { duration: 7000, @@ -2908,22 +2906,31 @@ import * as Modals from './modalManager.js'; // Tell the inbox to refresh so the answered state shows window.dispatchEvent(new CustomEvent('email-answered', { detail: { uid: sourceUid } })); } - // Delete the document after successful send - if (activeDocId) { - fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE' }).catch(() => {}); - docs.delete(activeDocId); - const remaining = Array.from(docs.keys()); - if (remaining.length > 0) { - switchToDoc(remaining[0]); + // Delete the compose document after successful send. It was usually + // already detached from the visible tabs so sending can finish in the + // background while the user continues in the next tab. + if (sendDocId) { + fetch(`${API_BASE}/api/document/${sendDocId}`, { method: 'DELETE' }).catch(() => {}); + const wasActiveSentDoc = activeDocId === sendDocId; + docs.delete(sendDocId); + if (wasActiveSentDoc) { + activeDocId = null; + const nextId = _visibleDocIdsForCurrentSession().find(id => docs.has(id)); + if (nextId) switchToDoc(nextId); + else closePanel(); } else { - closePanel(); + renderTabs(); } - renderTabs(); + _syncDocIndicator(); } } else { + _restoreDetachedEmailDoc(detachedEmailDoc); + detachedEmailDoc = null; if (uiModule) uiModule.showError(data.error || 'Failed to send'); } } catch (e) { + _restoreDetachedEmailDoc(detachedEmailDoc); + detachedEmailDoc = null; if (uiModule) uiModule.showError(e?.message ? `Failed to send email: ${e.message}` : 'Failed to send email'); } finally { if (sendSpinner) sendSpinner.destroy(); @@ -2986,6 +2993,48 @@ import * as Modals from './modalManager.js'; _closeWithoutDeleting(true); } + function _visibleDocIdsForCurrentSession() { + const curSession = sessionModule?.getCurrentSessionId() || ''; + const ids = []; + for (const [id, doc] of docs) { + if (doc.sessionId && curSession && doc.sessionId !== curSession) continue; + ids.push(id); + } + return ids; + } + + function _detachActiveEmailForBackground(docId) { + if (!docId || !docs.has(docId)) return null; + saveCurrentToMap(); + const doc = docs.get(docId); + const snapshot = { id: docId, doc: { ...doc } }; + saveDocument({ silent: true }).catch(() => {}); + + const visibleBefore = _visibleDocIdsForCurrentSession(); + const idx = visibleBefore.indexOf(docId); + docs.delete(docId); + if (activeDocId === docId) activeDocId = null; + + const remaining = visibleBefore.filter(id => id !== docId && docs.has(id)); + const nextId = remaining[idx] || remaining[idx - 1] || remaining[0] || null; + if (nextId) { + switchToDoc(nextId); + } else { + closePanel(); + } + renderTabs(); + _syncDocIndicator(); + return snapshot; + } + + function _restoreDetachedEmailDoc(snapshot) { + if (!snapshot || !snapshot.id || !snapshot.doc) return; + if (!docs.has(snapshot.id)) docs.set(snapshot.id, snapshot.doc); + _ensureDocPaneMounted(); + switchToDoc(snapshot.id); + _syncDocIndicator(); + } + function _closeWithoutDeleting(deleteDoc = false) { if (!activeDocId) return; if (deleteDoc) {