Keep email composer open during fast edits

This commit is contained in:
pewdiepie-archdaemon 2026-06-30 11:54:34 +00:00
parent 4f387e089a
commit baecea2681
3 changed files with 39 additions and 34 deletions

View file

@ -219,8 +219,8 @@
}, { once: true }); }, { once: true });
})(); })();
</script> </script>
<link rel="stylesheet" href="/static/style.css?v=20260630emailtype"> <link rel="stylesheet" href="/static/style.css?v=20260630emailrace">
<link rel="modulepreload" href="/static/app.js?v=20260630emailtype"> <link rel="modulepreload" href="/static/app.js?v=20260630emailrace">
<link rel="modulepreload" href="/static/js/chat.js"> <link rel="modulepreload" href="/static/js/chat.js">
<link rel="modulepreload" href="/static/js/ui.js"> <link rel="modulepreload" href="/static/js/ui.js">
<link rel="modulepreload" href="/static/js/sessions.js"> <link rel="modulepreload" href="/static/js/sessions.js">
@ -2512,7 +2512,7 @@
<script type="module" src="/static/js/settings.js"></script> <script type="module" src="/static/js/settings.js"></script>
<script type="module" src="/static/js/admin.js"></script> <script type="module" src="/static/js/admin.js"></script>
<script type="module" src="/static/js/assistant.js"></script> <script type="module" src="/static/js/assistant.js"></script>
<script type="module" src="/static/app.js?v=20260630emailtype"></script> <!-- app.js must be LAST --> <script type="module" src="/static/app.js?v=20260630emailrace"></script> <!-- app.js must be LAST -->
<script type="module" src="/static/js/init.js"></script> <script type="module" src="/static/js/init.js"></script>
<script type="module" src="/static/js/a11y.js"></script> <script type="module" src="/static/js/a11y.js"></script>
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script> <script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script>

View file

@ -2668,6 +2668,15 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
summary.title = summary.textContent; summary.title = summary.textContent;
} }
function _setEmailHeaderInputValue(id, value, { preserveFocused = true, preserveNonEmpty = false } = {}) {
const el = document.getElementById(id);
if (!el) return;
const next = value || '';
if (preserveFocused && document.activeElement === el) return;
if (preserveNonEmpty && !next && el.value) return;
if (el.value !== next) el.value = next;
}
function _setEmailHeaderCollapsed(collapsed, { manual = true } = {}) { function _setEmailHeaderCollapsed(collapsed, { manual = true } = {}) {
const header = document.getElementById('doc-email-header'); const header = document.getElementById('doc-email-header');
const btn = document.getElementById('doc-email-collapse-btn'); const btn = document.getElementById('doc-email-collapse-btn');
@ -2730,13 +2739,10 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
document.getElementById('doc-editor-highlight')?.classList.add('email-mode'); document.getElementById('doc-editor-highlight')?.classList.add('email-mode');
let fields = _parseEmailHeader(doc.content || ''); let fields = _parseEmailHeader(doc.content || '');
fields = _emailFieldsWithLocalDraft(fields); fields = _emailFieldsWithLocalDraft(fields);
const toInput = document.getElementById('doc-email-to');
const subjectInput = document.getElementById('doc-email-subject'); const subjectInput = document.getElementById('doc-email-subject');
const inReplyTo = document.getElementById('doc-email-in-reply-to');
const refs = document.getElementById('doc-email-references');
const textarea = document.getElementById('doc-editor-textarea'); const textarea = document.getElementById('doc-editor-textarea');
if (toInput) toInput.value = fields.to; _setEmailHeaderInputValue('doc-email-to', fields.to, { preserveNonEmpty: true });
if (subjectInput) subjectInput.value = fields.subject; _setEmailHeaderInputValue('doc-email-subject', fields.subject, { preserveNonEmpty: true });
_setEmailHeaderCollapsed(!!(doc && doc._emailHeaderCollapsed), { manual: false }); _setEmailHeaderCollapsed(!!(doc && doc._emailHeaderCollapsed), { manual: false });
if (subjectInput && !subjectInput._emailTabBodyBound) { if (subjectInput && !subjectInput._emailTabBodyBound) {
subjectInput._emailTabBodyBound = true; subjectInput._emailTabBodyBound = true;
@ -2747,12 +2753,10 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
} }
}); });
} }
if (inReplyTo) inReplyTo.value = fields.inReplyTo; _setEmailHeaderInputValue('doc-email-in-reply-to', fields.inReplyTo, { preserveNonEmpty: true });
if (refs) refs.value = fields.references; _setEmailHeaderInputValue('doc-email-references', fields.references, { preserveNonEmpty: true });
const sourceUid = document.getElementById('doc-email-source-uid'); _setEmailHeaderInputValue('doc-email-source-uid', fields.sourceUid || '', { preserveNonEmpty: true });
const sourceFolder = document.getElementById('doc-email-source-folder'); _setEmailHeaderInputValue('doc-email-source-folder', fields.sourceFolder || '', { preserveNonEmpty: true });
if (sourceUid) sourceUid.value = fields.sourceUid || '';
if (sourceFolder) sourceFolder.value = fields.sourceFolder || '';
// Show/hide unread button only if we have a source UID (came from inbox) // Show/hide unread button only if we have a source UID (came from inbox)
const unreadBtn = document.getElementById('doc-email-unread-btn'); const unreadBtn = document.getElementById('doc-email-unread-btn');
if (unreadBtn) unreadBtn.style.display = fields.sourceUid ? '' : 'none'; if (unreadBtn) unreadBtn.style.display = fields.sourceUid ? '' : 'none';
@ -2875,11 +2879,14 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
const ccRow = document.getElementById('doc-email-cc-row'); const ccRow = document.getElementById('doc-email-cc-row');
const bccRow = document.getElementById('doc-email-bcc-row'); const bccRow = document.getElementById('doc-email-bcc-row');
const ccToggle = document.getElementById('doc-email-show-cc'); const ccToggle = document.getElementById('doc-email-show-cc');
const ccInput = document.getElementById('doc-email-cc'); _setEmailHeaderInputValue('doc-email-cc', fields.cc || '', { preserveNonEmpty: true });
const bccInput = document.getElementById('doc-email-bcc'); _setEmailHeaderInputValue('doc-email-bcc', fields.bcc || '', { preserveNonEmpty: true });
if (ccInput) ccInput.value = fields.cc || ''; const hasCcBcc = !!(
if (bccInput) bccInput.value = fields.bcc || ''; fields.cc ||
const hasCcBcc = !!(fields.cc || fields.bcc); fields.bcc ||
document.getElementById('doc-email-cc')?.value ||
document.getElementById('doc-email-bcc')?.value
);
if (ccRow) ccRow.style.display = hasCcBcc ? '' : 'none'; if (ccRow) ccRow.style.display = hasCcBcc ? '' : 'none';
if (bccRow) bccRow.style.display = hasCcBcc ? '' : 'none'; if (bccRow) bccRow.style.display = hasCcBcc ? '' : 'none';
if (ccToggle) ccToggle.style.display = hasCcBcc ? 'none' : ''; if (ccToggle) ccToggle.style.display = hasCcBcc ? 'none' : '';
@ -2898,19 +2905,14 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
return; return;
} }
const setValue = (id, value) => { _setEmailHeaderInputValue('doc-email-to', fields.to, { preserveNonEmpty: true });
const el = document.getElementById(id); _setEmailHeaderInputValue('doc-email-subject', fields.subject, { preserveNonEmpty: true });
const next = value || ''; _setEmailHeaderInputValue('doc-email-in-reply-to', fields.inReplyTo, { preserveNonEmpty: true });
if (el && el.value !== next) el.value = next; _setEmailHeaderInputValue('doc-email-references', fields.references, { preserveNonEmpty: true });
}; _setEmailHeaderInputValue('doc-email-source-uid', fields.sourceUid || '', { preserveNonEmpty: true });
setValue('doc-email-to', fields.to); _setEmailHeaderInputValue('doc-email-source-folder', fields.sourceFolder || '', { preserveNonEmpty: true });
setValue('doc-email-subject', fields.subject); _setEmailHeaderInputValue('doc-email-cc', fields.cc || '', { preserveNonEmpty: true });
setValue('doc-email-in-reply-to', fields.inReplyTo); _setEmailHeaderInputValue('doc-email-bcc', fields.bcc || '', { preserveNonEmpty: true });
setValue('doc-email-references', fields.references);
setValue('doc-email-source-uid', fields.sourceUid || '');
setValue('doc-email-source-folder', fields.sourceFolder || '');
setValue('doc-email-cc', fields.cc || '');
setValue('doc-email-bcc', fields.bcc || '');
const unreadBtn = document.getElementById('doc-email-unread-btn'); const unreadBtn = document.getElementById('doc-email-unread-btn');
if (unreadBtn) unreadBtn.style.display = fields.sourceUid ? '' : 'none'; if (unreadBtn) unreadBtn.style.display = fields.sourceUid ? '' : 'none';
@ -4029,7 +4031,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
const prevId = activeDocId; const prevId = activeDocId;
if (prevId && prevId !== docId && docs.has(prevId)) { if (prevId && prevId !== docId && docs.has(prevId)) {
const prev = docs.get(prevId); const prev = docs.get(prevId);
if (!(prev.content || '').trim() && !(prev.title || '').trim()) { if (prev.language !== 'email' && !(prev.content || '').trim() && !(prev.title || '').trim()) {
fetch(`${API_BASE}/api/document/${prevId}`, { method: 'DELETE' }).catch(() => {}); fetch(`${API_BASE}/api/document/${prevId}`, { method: 'DELETE' }).catch(() => {});
docs.delete(prevId); docs.delete(prevId);
_syncDocIndicator(); _syncDocIndicator();
@ -8828,6 +8830,9 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
}), }),
}); });
if (res.status === 404) { if (res.status === 404) {
if (silent && localDoc?.language === 'email') {
return;
}
// Streaming/empty email drafts can leave a local tab pointing at a temp // Streaming/empty email drafts can leave a local tab pointing at a temp
// or already-deleted document. Do not keep surfacing autosave errors for // or already-deleted document. Do not keep surfacing autosave errors for
// a document the backend no longer knows about. // a document the backend no longer knows about.

View file

@ -7,7 +7,7 @@
// - Other static assets (images/fonts/libs): cache-first with bg refresh. // - Other static assets (images/fonts/libs): cache-first with bg refresh.
// - API / non-GET: never cached. // - API / non-GET: never cached.
// Bump CACHE_NAME whenever the precache list or SW logic changes. // Bump CACHE_NAME whenever the precache list or SW logic changes.
const CACHE_NAME = 'odysseus-v332'; const CACHE_NAME = 'odysseus-v333';
// Core shell precached on install so repeat opens are instant without any // Core shell precached on install so repeat opens are instant without any
// network wait. Keep this list in sync with the <script type="module"> tags // network wait. Keep this list in sync with the <script type="module"> tags