Let email sends continue after closing compose tab

This commit is contained in:
pewdiepie-archdaemon
2026-06-01 13:42:14 +09:00
parent a4349f4b29
commit 8df5ed2a96
+67 -18
View File
@@ -2776,6 +2776,7 @@ import * as Modals from './modalManager.js';
} }
async function _sendEmail() { async function _sendEmail() {
const sendDocId = activeDocId;
const to = document.getElementById('doc-email-to')?.value?.trim(); const to = document.getElementById('doc-email-to')?.value?.trim();
const cc = document.getElementById('doc-email-cc')?.value?.trim() || ''; const cc = document.getElementById('doc-email-cc')?.value?.trim() || '';
const bcc = document.getElementById('doc-email-bcc')?.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)); const _sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
let sendSpinner = null; let sendSpinner = null;
let origBtnHtml = ''; let origBtnHtml = '';
let detachedEmailDoc = null;
if (btn) { if (btn) {
btn.disabled = true; btn.disabled = true;
origBtnHtml = btn.innerHTML; origBtnHtml = btn.innerHTML;
@@ -2822,8 +2824,11 @@ import * as Modals from './modalManager.js';
onAction: () => { canceled = true; }, onAction: () => { canceled = true; },
}); });
} }
detachedEmailDoc = _detachActiveEmailForBackground(sendDocId);
await _sleep(1200); await _sleep(1200);
if (canceled) { if (canceled) {
_restoreDetachedEmailDoc(detachedEmailDoc);
detachedEmailDoc = null;
if (uiModule) uiModule.showToast('Send canceled'); if (uiModule) uiModule.showToast('Send canceled');
return; return;
} }
@@ -2839,6 +2844,8 @@ import * as Modals from './modalManager.js';
} }
await _sleep(2200); await _sleep(2200);
if (undone) { if (undone) {
_restoreDetachedEmailDoc(detachedEmailDoc);
detachedEmailDoc = null;
if (uiModule) uiModule.showToast('Send undone'); if (uiModule) uiModule.showToast('Send undone');
return; return;
} }
@@ -2858,15 +2865,6 @@ import * as Modals from './modalManager.js';
}); });
const data = await res.json(); const data = await res.json();
if (data.success) { 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) { if (uiModule) {
uiModule.showToast('Message sent', { uiModule.showToast('Message sent', {
duration: 7000, duration: 7000,
@@ -2908,22 +2906,31 @@ import * as Modals from './modalManager.js';
// Tell the inbox to refresh so the answered state shows // Tell the inbox to refresh so the answered state shows
window.dispatchEvent(new CustomEvent('email-answered', { detail: { uid: sourceUid } })); window.dispatchEvent(new CustomEvent('email-answered', { detail: { uid: sourceUid } }));
} }
// Delete the document after successful send // Delete the compose document after successful send. It was usually
if (activeDocId) { // already detached from the visible tabs so sending can finish in the
fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE' }).catch(() => {}); // background while the user continues in the next tab.
docs.delete(activeDocId); if (sendDocId) {
const remaining = Array.from(docs.keys()); fetch(`${API_BASE}/api/document/${sendDocId}`, { method: 'DELETE' }).catch(() => {});
if (remaining.length > 0) { const wasActiveSentDoc = activeDocId === sendDocId;
switchToDoc(remaining[0]); docs.delete(sendDocId);
if (wasActiveSentDoc) {
activeDocId = null;
const nextId = _visibleDocIdsForCurrentSession().find(id => docs.has(id));
if (nextId) switchToDoc(nextId);
else closePanel();
} else { } else {
closePanel(); renderTabs();
} }
renderTabs(); _syncDocIndicator();
} }
} else { } else {
_restoreDetachedEmailDoc(detachedEmailDoc);
detachedEmailDoc = null;
if (uiModule) uiModule.showError(data.error || 'Failed to send'); if (uiModule) uiModule.showError(data.error || 'Failed to send');
} }
} catch (e) { } catch (e) {
_restoreDetachedEmailDoc(detachedEmailDoc);
detachedEmailDoc = null;
if (uiModule) uiModule.showError(e?.message ? `Failed to send email: ${e.message}` : 'Failed to send email'); if (uiModule) uiModule.showError(e?.message ? `Failed to send email: ${e.message}` : 'Failed to send email');
} finally { } finally {
if (sendSpinner) sendSpinner.destroy(); if (sendSpinner) sendSpinner.destroy();
@@ -2986,6 +2993,48 @@ import * as Modals from './modalManager.js';
_closeWithoutDeleting(true); _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) { function _closeWithoutDeleting(deleteDoc = false) {
if (!activeDocId) return; if (!activeDocId) return;
if (deleteDoc) { if (deleteDoc) {