Mail: a button per account with its own unread count (kept fresh), instead of a dropdown
build-container / build (push) Successful in 14m15s
build-container / build (push) Successful in 14m15s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7198efaccd
commit
e984f11a94
3 files changed
+22
-8
No files matched your search
+10
-7
@@ -38,7 +38,7 @@ const mailHasRemote = html => /<img[^>]+src=["']?https?:|url\(\s*["']?https?:/i.
|
||||
// ---- unread badge and Home card
|
||||
async function mailPoll() {
|
||||
if (!(allowed('mail') && cfg.mail)) return;
|
||||
try { const s = await jfetch('/api/mail/status'); mail.st = s; mail.accts = s.accounts || mail.accts; const n = s.connected && s.unread ? s.unread : 0; if (n !== mailUnread) { mailUnread = n; renderNav(); } } catch {}
|
||||
try { const s = await jfetch('/api/mail/status'); mail.st = s; mail.accts = s.accounts || mail.accts; const n = s.connected && s.unread ? s.unread : 0; if (n !== mailUnread) { mailUnread = n; renderNav(); } if (location.hash.startsWith('#/mail') && typeof mailDrawChips === 'function') mailDrawChips(); } catch {}
|
||||
}
|
||||
async function renderMailCard(el) {
|
||||
if (!el) return;
|
||||
@@ -180,12 +180,16 @@ async function viewMail(args = []) {
|
||||
jfetch('/api/mail/folders?acct=' + encodeURIComponent(acct)).then(f => { if (mail.acct !== acct) return; mail.folders = f; mcPut('f:' + acct, { folders: f }); mailRefreshFolders(); }).catch(() => {});
|
||||
mailPoll().then(() => { if (mail.st && location.hash.startsWith('#/mail')) mailRefreshFolders(); });
|
||||
}
|
||||
/** One button per account with its own unread count, so you can see which one has new mail without switching. */
|
||||
const mailChips = () => `<div class="maccts" role="tablist" aria-label="Accounts">${mail.accts.map(a => `<button type="button" role="tab" data-a="${esc(a.id)}" aria-selected="${a.id === mail.acct}" class="${a.id === mail.acct ? 'on' : ''}${a.unread ? ' new' : ''}"><span class="mal">${esc(a.name || a.address)}</span><small>${esc(a.name ? a.address : '')}</small>${a.unread ? `<i class="nbadge" style="position:static">${a.unread > 99 ? '99+' : a.unread}</i>` : ''}</button>`).join('')}</div>`;
|
||||
function mailBindChips() { document.querySelectorAll('.maccts button').forEach(b => b.onclick = () => { if (b.dataset.a === mail.acct) return; mail.q = ''; location.hash = '#/mail/f/' + encodeURIComponent(b.dataset.a + '~inbox'); }); }
|
||||
function mailDrawChips() { if (mail.accts.length < 2) return; document.querySelectorAll('.maccts').forEach(el => el.outerHTML = mailChips()); mailBindChips(); }
|
||||
/** Redraw the folder pickers (names and unread counts) without touching the list or the open message. */
|
||||
function mailRefreshFolders() {
|
||||
mailDrawChips();
|
||||
const fl = mail.folders; if (!fl.length) return;
|
||||
const f = $('#mfsel'); if (f) f.innerHTML = fl.map(x => `<option value="${esc(x.id)}" ${x.id === mail.folder ? 'selected' : ''}>${esc(x.name)}${x.unread ? ' (' + x.unread + ')' : ''}</option>`).join('');
|
||||
const side = document.querySelector('.mfold'); if (side) { const sel = side.querySelector('select'); side.innerHTML = (sel ? sel.outerHTML : '') + fl.map(x => `<a href="#/mail/f/${encodeURIComponent(x.id)}" class="${x.id === mail.folder ? 'on' : ''}"><span class="grow">${esc(x.name)}</span>${x.unread ? `<i class="nbadge" style="position:static">${x.unread}</i>` : ''}</a>`).join(''); const s2 = side.querySelector('select'); if (s2) s2.onchange = e => { mail.q = ''; location.hash = '#/mail/f/' + encodeURIComponent(e.target.value + '~inbox'); }; }
|
||||
const acc = $('#macct'); if (acc && mail.accts.length) acc.innerHTML = mail.accts.map(a => `<option value="${esc(a.id)}" ${a.id === mail.acct ? 'selected' : ''}>${esc(a.address)}${a.unread ? ' (' + a.unread + ')' : ''}</option>`).join('');
|
||||
const side = document.querySelector('.mfold'); if (side) { const sel = side.querySelector('select'); side.innerHTML = (mail.accts.length > 1 ? mailChips() : '') + fl.map(x => `<a href="#/mail/f/${encodeURIComponent(x.id)}" class="${x.id === mail.folder ? 'on' : ''}"><span class="grow">${esc(x.name)}</span>${x.unread ? `<i class="nbadge" style="position:static">${x.unread}</i>` : ''}</a>`).join(''); mailBindChips(); }
|
||||
}
|
||||
|
||||
function mailConnectScreen(st) {
|
||||
@@ -204,9 +208,9 @@ function mailConnectScreen(st) {
|
||||
function mailShell(mode) {
|
||||
const fl = mail.folders.length ? mail.folders : [{ id: 'inbox', name: 'Inbox', unread: mailUnread }];
|
||||
$('#view').innerHTML = `<div class="mailapp" data-mode="${mode}">
|
||||
<aside class="mfold" aria-label="Folders">${mail.accts.length > 1 ? `<select id="macct2" aria-label="Account" style="margin-bottom:8px">${mail.accts.map(a => `<option value="${esc(a.id)}" ${a.id === mail.acct ? 'selected' : ''}>${esc(a.address)}</option>`).join('')}</select>` : ''}${fl.map(f => `<a href="#/mail/f/${encodeURIComponent(f.id)}" class="${f.id === mail.folder ? 'on' : ''}"><span class="grow">${esc(f.name)}</span>${f.unread ? `<i class="nbadge" style="position:static">${f.unread}</i>` : ''}</a>`).join('')}</aside>
|
||||
<aside class="mfold" aria-label="Folders">${mail.accts.length > 1 ? mailChips() : ''}${fl.map(f => `<a href="#/mail/f/${encodeURIComponent(f.id)}" class="${f.id === mail.folder ? 'on' : ''}"><span class="grow">${esc(f.name)}</span>${f.unread ? `<i class="nbadge" style="position:static">${f.unread}</i>` : ''}</a>`).join('')}</aside>
|
||||
<section class="mlist" aria-label="Messages">
|
||||
${mail.accts.length > 1 ? `<div class="mtool" style="margin-bottom:4px"><select id="macct" aria-label="Account" style="max-width:100%;flex:1">${mail.accts.map(a => `<option value="${esc(a.id)}" ${a.id === mail.acct ? 'selected' : ''}>${esc(a.address)}${a.unread ? ' (' + a.unread + ')' : ''}</option>`).join('')}</select></div>` : ''}
|
||||
${mail.accts.length > 1 ? mailChips() : ''}
|
||||
<div class="mtool"><select id="mfsel" aria-label="Folder">${fl.map(f => `<option value="${esc(f.id)}" ${f.id === mail.folder ? 'selected' : ''}>${esc(f.name)}${f.unread ? ' (' + f.unread + ')' : ''}</option>`).join('')}</select>
|
||||
<input type="search" id="mq" placeholder="Search mail" value="${esc(mail.q)}" autocomplete="off" aria-label="Search mail">
|
||||
<button class="ibtn" id="mrf" aria-label="Refresh">${icon('refresh')}</button><button class="btn" id="mnew">${icon('edit')}New</button></div>
|
||||
@@ -214,8 +218,7 @@ function mailShell(mode) {
|
||||
</section>
|
||||
<section class="mread" id="mread" aria-label="Message"></section></div>`;
|
||||
$('#mfsel').onchange = e => { mail.q = ''; location.hash = '#/mail/f/' + encodeURIComponent(e.target.value); };
|
||||
if ($('#macct2')) $('#macct2').onchange = e => { mail.q = ''; location.hash = '#/mail/f/' + encodeURIComponent(e.target.value + '~inbox'); };
|
||||
if ($('#macct')) $('#macct').onchange = e => { mail.q = ''; location.hash = '#/mail/f/' + encodeURIComponent(e.target.value + '~inbox'); };
|
||||
mailBindChips();
|
||||
$('#mnew').onclick = () => mailCompose();
|
||||
$('#mrf').onclick = () => { mailLoadList(true); jfetch('/api/mail/folders?acct=' + encodeURIComponent(mail.acct)).then(f => { mail.folders = f; mailPoll(); }).catch(() => {}); };
|
||||
let t; $('#mq').oninput = e => { clearTimeout(t); t = setTimeout(() => { mail.q = e.target.value.trim(); mailLoadList(true); }, 400); };
|
||||
|
||||
Reference in new issue
Block a user