diff --git a/static/js/emailInbox.js b/static/js/emailInbox.js index 1d038af..762fb44 100644 --- a/static/js/emailInbox.js +++ b/static/js/emailInbox.js @@ -639,7 +639,8 @@ function _createEmailItem(em) { } async function _openEmail(em, itemEl, preloadedData = null, mode = 'reply') { - const wantsAiReply = mode === 'ai-reply'; + const aiReplyMode = mode === 'ai-reply-fast' ? 'fast' : (mode === 'ai-reply-full' ? 'full' : ''); + const wantsAiReply = mode === 'ai-reply' || !!aiReplyMode; let aiSuggestedBody = null; if (wantsAiReply) { // Fall through to reply-all (not plain reply) so the generated AI @@ -696,7 +697,7 @@ async function _openEmail(em, itemEl, preloadedData = null, mode = 'reply') { message_id: data.message_id || '', uid: String(em.uid || ''), folder: _currentFolder, - fast: _shouldUseFastAiReply(data), + fast: aiReplyMode ? aiReplyMode === 'fast' : _shouldUseFastAiReply(data), }), }); const result = await res.json(); diff --git a/static/js/emailLibrary.js b/static/js/emailLibrary.js index 7880848..8817554 100644 --- a/static/js/emailLibrary.js +++ b/static/js/emailLibrary.js @@ -115,6 +115,24 @@ function _syncReminderClearButton() { document.getElementById('email-reminders-clear-btn')?.classList.toggle('hidden', state._libFilter !== 'reminders'); } +function _renderAccountsLoading() { + const strip = document.getElementById('email-lib-accounts'); + if (!strip) return; + strip.style.display = 'flex'; + strip.innerHTML = ''; + try { + const wp = spinnerModule.createWhirlpool(14); + wp.element.classList.add('email-accounts-loading-whirlpool'); + const label = document.createElement('span'); + label.className = 'email-accounts-loading-label'; + label.textContent = 'Accounts'; + strip.appendChild(wp.element); + strip.appendChild(label); + } catch (_) { + strip.textContent = 'Accounts...'; + } +} + function _syncEmailReminderBellVisibility(enabled) { const btn = document.getElementById('email-reminder-btn'); const wrap = document.querySelector('#email-lib-modal .email-search-wrap'); @@ -437,7 +455,7 @@ function _resetEmailListForFreshLoad() { state._libTotal = 0; _libLoadSeq += 1; const grid = document.getElementById('email-lib-grid'); - if (grid) grid.innerHTML = ''; + if (grid) _renderEmailLoading(grid); const stats = document.getElementById('email-lib-stats'); if (stats) stats.textContent = 'Loading...'; } @@ -1063,6 +1081,7 @@ export function openEmailLibrary(opts = {}) { }; document.addEventListener('keydown', state._libEscHandler, true); + _renderAccountsLoading(); _loadAccounts(); _loadFolders(); _loadEmailReminderBellVisibility(); @@ -1296,9 +1315,7 @@ async function _doSearch() { } const grid = document.getElementById('email-lib-grid'); if (!grid) return; - grid.innerHTML = ''; - const sp = spinnerModule.createWhirlpool(28); - grid.appendChild(sp.element); + const sp = _renderEmailLoading(grid); try { const res = await fetch(`${API_BASE}/api/email/search?folder=${encodeURIComponent(state._libFolder)}${_acct()}&q=${encodeURIComponent(q)}&limit=100`); @@ -1317,6 +1334,24 @@ async function _doSearch() { } } +function _renderEmailLoading(grid) { + if (!grid) return null; + grid.innerHTML = ''; + const wrap = document.createElement('div'); + wrap.className = 'email-loading email-loading-with-label'; + let sp = null; + try { + sp = spinnerModule.createWhirlpool(28); + wrap.appendChild(sp.element); + } catch (_) {} + const label = document.createElement('div'); + label.className = 'email-loading-label'; + label.textContent = 'Loading emails'; + wrap.appendChild(label); + grid.appendChild(wrap); + return sp; +} + // Refreshes the small accent-pill in the modal title with the unread count // for the current folder. When the inbox is currently filtered to unread, the // pill flips to show the total-emails count + "all" label, because clicking @@ -1401,9 +1436,7 @@ async function _loadEmails({ force = false, useCache = true } = {}) { const stats = document.getElementById('email-lib-stats'); if (stats) stats.textContent = `${state._libTotal} emails`; } else { - grid.innerHTML = ''; - sp = spinnerModule.createWhirlpool(28); - grid.appendChild(sp.element); + sp = _renderEmailLoading(grid); } try { @@ -2015,8 +2048,8 @@ async function _toggleCardPreview(card, em) {
- - + +
@@ -2067,28 +2100,7 @@ async function _toggleCardPreview(card, em) { _snapEmailModalToLeftSidebar(ev.currentTarget.closest('.modal')); if (state._onEmailClick) await state._onEmailClick({ email: em, emailData: data, mode: 'reply-all' }); }); - reader.querySelector('[data-act="ai-reply"]')?.addEventListener('click', async (ev) => { - ev.stopPropagation(); - _snapEmailModalToLeftSidebar(ev.currentTarget.closest('.modal')); - const btn = ev.currentTarget; - btn.disabled = true; - const orig = btn.innerHTML; - // Use the app-wide whirlpool spinner for consistency. - let _wp = null; - try { - _wp = spinnerModule.createWhirlpool(14); - _wp.element.style.cssText = 'width:14px;height:14px;display:inline-block;vertical-align:middle;position:relative;top:-2px;'; - btn.innerHTML = ''; - btn.appendChild(_wp.element); - } catch (_) {} - try { - if (state._onEmailClick) await state._onEmailClick({ email: em, emailData: data, mode: 'ai-reply' }); - } finally { - try { _wp && _wp.stop(); } catch (_) {} - btn.disabled = false; - btn.innerHTML = orig; - } - }); + reader.querySelector('[data-act="ai-reply"]')?.addEventListener('click', (ev) => _handleAiReplyButton(ev, em, data)); reader.querySelector('[data-act="forward"]')?.addEventListener('click', async (ev) => { ev.stopPropagation(); if (state._onEmailClick) await state._onEmailClick({ email: em, emailData: data, mode: 'forward' }); @@ -3730,8 +3742,8 @@ async function _openEmailAsTab(em, folder) {
- - + +