/* ── Shared notification dropdown module ──────────────────────────── */ (function() { let _notifOpen = false; let _sse = null; const _ICONS = { achievement: { bg:'#FEF3C7', c:'#D97706', svg:'' }, assignment: { bg:'#DBEAFE', c:'#2563EB', svg:'' }, grade: { bg:'#DCFCE7', c:'#16A34A', svg:'' }, revision: { bg:'#FEE2E2', c:'#DC2626', svg:'' }, submission: { bg:'#E0F2FE', c:'#0284C7', svg:'' }, session: { bg:'rgba(155,93,229,.12)', c:'#9B5DE5', svg:'' }, join: { bg:'#F0FDF4', c:'#16A34A', svg:'' }, announcement: { bg:'#EDE9FE', c:'#7C3AED', svg:'' }, }; function _icon(type) { const t = _ICONS[type] || { bg:'rgba(155,93,229,.1)', c:'#9B5DE5', svg:'' }; return `
${t.svg}
`; } function renderNotifDrop(data) { const drop = document.getElementById('notif-drop'); const badge = document.getElementById('notif-badge'); if (!drop || !badge) return; if (data.unread > 0) { badge.textContent = data.unread > 9 ? '9+' : data.unread; badge.style.display = ''; } else badge.style.display = 'none'; drop.innerHTML = `
Уведомления ${data.unread > 0 ? `${data.unread}` : ''}
${data.unread > 0 ? `` : ''}
${data.notifications.length ? data.notifications.map(n => ` ${_icon(n.type)}
${LS.esc(n.message)}
${LS.fmtRelTime(n.created_at)}
`).join('') : '
Уведомлений нет
'}`; } async function load() { try { renderNotifDrop(await LS.getNotifications()); } catch {} } function toggle() { const drop = document.getElementById('notif-drop'); if (!drop) return; _notifOpen = !_notifOpen; if (_notifOpen) { const btn = document.getElementById('notif-btn'); if (btn) { const r = btn.getBoundingClientRect(); const vh = window.innerHeight; // Anchor bottom of dropdown to button bottom, but don't go above viewport const bottom = vh - r.bottom; drop.style.top = 'auto'; drop.style.bottom = Math.max(8, bottom) + 'px'; drop.style.left = (r.right + 8) + 'px'; } drop.style.display = 'block'; load(); } else { drop.style.display = 'none'; } } async function clickNotif(e, id, link) { e.preventDefault(); await LS.markNotifRead(id).catch(() => {}); await load(); if (link && link !== '#') window.location.href = link; } async function markAllRead() { await LS.markAllNotifsRead().catch(() => {}); await load(); } let _inited = false; function init() { if (_inited) return; _inited = true; // Note: button already has onclick="LS.notif.toggle()" in HTML // Close on outside click document.addEventListener('click', e => { const drop = document.getElementById('notif-drop'); if (!drop || !_notifOpen) return; if (!e.target.closest('#notif-drop') && !e.target.closest('#notif-btn')) { _notifOpen = false; drop.style.display = 'none'; } }); // SSE real-time _sse = LS.connectSSE(ev => { if (ev.type) load(); }); // Initial load load(); } // Expose as LS.notif window.LS = window.LS || {}; LS.notif = { init, toggle, load, click: clickNotif, markAllRead }; })();