(function () { // Badge + SSE service (kein Sidebar-Rendering mehr) // ── Badge-Zähler ── function setBadge(ids, count, topbarType) { ids.forEach(id => { if (!id) return; const el = document.getElementById(id); if (!el) return; el.textContent = count; el.style.display = count > 0 ? '' : 'none'; }); if (topbarType && window.__topbarSetBadge) window.__topbarSetBadge(topbarType, count); } // ── Ton abspielen ── let userHasInteracted = false; document.addEventListener('click', () => { userHasInteracted = true; }, { passive: true }); document.addEventListener('keydown', () => { userHasInteracted = true; }, { passive: true }); document.addEventListener('touchstart', () => { userHasInteracted = true; }, { passive: true }); function playSound(src) { if (!userHasInteracted) return; try { const audio = new Audio(src); audio.volume = 0.6; audio.play().catch(() => {}); } catch(e) {} } // ── Initiale Badge-Counts laden ── fetch('/social/friends/pending/count') .then(r => r.ok ? r.json() : 0) .then(n => setBadge(['socialFriendsBadge'], n, null)) .catch(() => {}); fetch('/social/messages/unread/count') .then(r => r.ok ? r.json() : 0) .then(n => setBadge(['socialMsgBadge'], n, 'msg')) .catch(() => {}); fetch('/notifications/unread/count') .then(r => r.ok ? r.json() : 0) .then(n => setBadge(['socialNotifBadge'], n, 'notif')) .catch(() => {}); Promise.all([ fetch('/gruppen/requests/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/gruppen/reports/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0) ]).then(([joins, reports]) => setBadge(['socialGruppenBadge'], joins + reports, null)) .catch(() => {}); Promise.all([ fetch('/keyholder/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/lockee/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/bdsm/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/vanilla/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0) ]).then(([kh, lockee, bdsm, vanilla]) => setBadge(['socialInvBadge'], kh + lockee + bdsm + vanilla, 'inv') ).catch(() => {}); // ── SSE: Echtzeit-Push vom Server ── function connectSse() { const es = new EventSource('/events/stream'); es.addEventListener('DM', e => { try { const data = JSON.parse(e.data); setBadge(['socialMsgBadge'], data.unreadCount || 0, 'msg'); if (window.location.pathname !== '/community/nachrichten.html') { playSound('/audio/message.mp3'); } if (typeof window.__sseOnDm === 'function') window.__sseOnDm(data); } catch(ex) {} }); es.addEventListener('NOTIFICATION', e => { try { const data = JSON.parse(e.data); setBadge(['socialNotifBadge'], data.unreadCount || 0, 'notif'); if (window.location.pathname !== '/community/benachrichtigungen.html') { playSound('/audio/notification.mp3'); } if (typeof window.__sseOnNotification === 'function') window.__sseOnNotification(data); } catch(ex) {} }); es.addEventListener('INVITATION', () => { try { if (typeof window.__topbarReloadInvBadge === 'function') window.__topbarReloadInvBadge(); } catch(ex) {} }); es.addEventListener('MATCH', e => { try { const data = JSON.parse(e.data); if (typeof window._onSseMatch === 'function') window._onSseMatch(data); } catch(ex) {} }); es.onerror = () => { es.close(); // Vor dem Reconnect prüfen ob noch eingeloggt (verhindert Endlos-Schleife bei abgelaufener Session) setTimeout(() => { fetch('/login/me', { method: 'GET' }) .then(r => { if (r.ok) connectSse(); }) .catch(() => {}); }, 5000); }; } // SSE nur starten wenn authentifiziert – verhindert Fehler-Spam bei nicht eingeloggten Seiten fetch('/login/me', { method: 'GET' }) .then(r => { if (r.ok) connectSse(); }) .catch(() => {}); })();