'use strict'; // ---- Notifications: a bell with the count of things you have not seen yet (chat, reminders, announcements); tapping it opens the list. let inboxN = 0; const inboxBell = () => ``; function inboxWire() { const b = document.getElementById('inboxbtn'); if (b) b.onclick = inboxOpen; inboxPaint(); inboxCount(); if (typeof pushResync === 'function') pushResync(); } function inboxPaint() { const n = document.getElementById('inboxn'); if (n) { n.hidden = !inboxN; n.textContent = inboxN > 99 ? '99+' : inboxN; } } /** What a read or cleared notification stands for, on this device: its banner on the lock screen goes, and an announcement stops showing on Home. (The server marks the chats and mail read.) */ async function inboxSettled(items, all) { for (const i of items) if (i.ref && i.ref.announce) { try { localStorage.setItem('ch_ann_gone:' + (typeof ME !== 'undefined' && ME ? ME.id : ''), i.ref.announce); } catch {} const a = document.getElementById('annc'); if (a) a.innerHTML = ''; } try { const reg = await navigator.serviceWorker.ready, tags = items.map(i => i.tag).filter(Boolean); for (const n of await reg.getNotifications()) if (all || tags.includes(n.tag)) n.close(); } catch {} } async function inboxCount() { try { const r = await jfetch('/api/inbox'); inboxN = r.unread; inboxPaint(); } catch {} } setInterval(() => { if (!document.hidden && typeof ME !== 'undefined' && ME && document.getElementById('inboxbtn')) inboxCount(); }, 45000); const inboxAgo = t => { const m = Math.round((Date.now() - t) / 6e4); return m < 1 ? 'now' : m < 60 ? m + ' min ago' : m < 1440 ? Math.round(m / 60) + ' h ago' : new Date(t).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); }; const INBOX_ICON = { chat: ['chat', '#2aa6a0'], remind: ['bell', '#e0742b'], announce: ['bell', '#5b8def'] }; async function inboxOpen() { const el = openSheet('Notifications', '', '
Loading…
Nothing yet. Messages, reminders and announcements will show up here.
'; l.querySelectorAll('a.row').forEach(a => a.addEventListener('click', () => { const id = a.dataset.id; inboxSettled(items.filter(i => i.id === id)); jfetch('/api/inbox/read', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) }).then(r => { inboxN = r.unread; inboxPaint(); }).catch(() => {}); closeSheet(); })); }; try { const r = await jfetch('/api/inbox'); items = r.items; inboxN = r.unread; inboxPaint(); } catch (e) { toast(e.message); } draw(); const post = (p, b) => jfetch('/api/inbox/' + p, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(b || {}) }); el.querySelector('#inboxall').onclick = async () => { try { inboxSettled(items.filter(i => !i.read), true); const r = await post('read', { all: true }); items.forEach(i => i.read = true); inboxN = r.unread; inboxPaint(); draw(); } catch (e) { toast(e.message); } }; el.querySelector('#inboxclr').onclick = async () => { try { inboxSettled(items.filter(i => !i.read), true); await post('clear'); items = []; inboxN = 0; inboxPaint(); draw(); } catch (e) { toast(e.message); } }; }