Files
xxx-sphere-web/bin/main/static/community/benachrichtigungen.html
Mario 2b0ce62d33
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Menp überarbeitet
2026-04-08 16:52:43 +02:00

253 lines
8.6 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/img/icon.png" type="image/png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Benachrichtigungen xXx Sphere</title>
<link rel="stylesheet" href="/css/variables.css">
<link rel="stylesheet" href="/css/style.css">
<style>
.notif-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
}
.notif-item {
background: var(--color-card);
border: 1px solid var(--color-secondary);
border-radius: 10px;
padding: 0.75rem 1rem;
display: flex;
gap: 0.75rem;
align-items: flex-start;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
text-decoration: none;
color: inherit;
}
.notif-item:hover {
border-color: var(--color-primary);
background: rgba(255,255,255,0.03);
}
.notif-item.unread {
border-left: 3px solid var(--color-primary);
background: rgba(var(--color-primary-rgb, 200,0,0), 0.05);
}
.notif-item.unread:hover {
background: rgba(var(--color-primary-rgb, 200,0,0), 0.09);
}
.notif-avatar {
width: 42px;
height: 42px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
background: var(--color-secondary);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
overflow: hidden;
}
.notif-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.notif-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-primary);
flex-shrink: 0;
margin-top: 0.45rem;
}
.notif-dot.read { background: transparent; }
.notif-body { flex: 1; min-width: 0; }
.notif-text {
font-size: 0.9rem;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
.notif-time {
font-size: 0.75rem;
color: var(--color-muted);
margin-top: 0.2rem;
}
.notif-arrow {
font-size: 0.75rem;
color: var(--color-muted);
flex-shrink: 0;
align-self: center;
}
.notif-empty {
color: var(--color-muted);
font-size: 0.9rem;
margin-top: 0.5rem;
}
.notif-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.25rem;
}
</style>
</head>
<body class="app">
<div class="main">
<div class="content">
<div class="notif-header">
<h1 style="margin:0;">🔔 Benachrichtigungen</h1>
<button id="markAllBtn" onclick="markAllRead()"
style="background:none;border:1px solid var(--color-secondary);color:var(--color-muted);
padding:0.35rem 0.85rem;border-radius:7px;cursor:pointer;font-size:0.82rem;width:auto;display:none;">
Alle als gelesen markieren
</button>
</div>
<div class="notif-list" id="notifList">
<p class="notif-empty" id="notifEmpty" style="display:none;">Keine Benachrichtigungen vorhanden.</p>
</div>
</div>
</div>
<script src="/js/icons.js"></script>
<script src="/js/nav.js"></script>
<script src="/js/social-sidebar.js"></script>
<script>
function fmtRelTime(isoStr) {
const diff = Date.now() - new Date(isoStr).getTime();
const min = Math.floor(diff / 60000);
const h = Math.floor(min / 60);
const d = Math.floor(h / 24);
if (d > 0) return `vor ${d} Tag${d > 1 ? 'en' : ''}`;
if (h > 0) return `vor ${h} Std.`;
if (min > 0) return `vor ${min} Min.`;
return 'gerade eben';
}
function esc(s) {
return String(s)
.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/"/g,'&quot;');
}
async function handleClick(id, targetUrl) {
// Als gelesen markieren
await fetch(`/notifications/${id}/read`, { method: 'POST' }).catch(() => {});
// Dot des angeklickten Items auf "gelesen" setzen
const dot = document.querySelector(`[data-notif-id="${id}"] .notif-dot`);
if (dot) dot.classList.add('read');
const item = document.querySelector(`[data-notif-id="${id}"]`);
if (item) item.classList.remove('unread');
// Badge aktualisieren
const remaining = document.querySelectorAll('.notif-item.unread').length;
['socialNotifBadge','socialMobileNotifBadge'].forEach(bid => {
const el = document.getElementById(bid);
if (!el) return;
el.textContent = remaining;
el.style.display = remaining > 0 ? '' : 'none';
});
if (remaining === 0) document.getElementById('markAllBtn').style.display = 'none';
// Navigieren wenn Zielseite vorhanden
if (targetUrl) window.location.href = targetUrl;
}
async function markAllRead() {
await fetch('/notifications/read-all', { method: 'POST' }).catch(() => {});
document.querySelectorAll('.notif-item.unread').forEach(el => {
el.classList.remove('unread');
const dot = el.querySelector('.notif-dot');
if (dot) dot.classList.add('read');
});
document.getElementById('markAllBtn').style.display = 'none';
['socialNotifBadge','socialMobileNotifBadge'].forEach(id => {
const el = document.getElementById(id);
if (el) { el.textContent = '0'; el.style.display = 'none'; }
});
}
async function loadNotifications() {
const list = document.getElementById('notifList');
const empty = document.getElementById('notifEmpty');
const btn = document.getElementById('markAllBtn');
// Vorhandene Einträge (außer Empty-Hint) entfernen
list.querySelectorAll('.notif-item').forEach(el => el.remove());
try {
const res = await fetch('/notifications');
if (!res.ok) return;
const items = await res.json();
if (items.length === 0) {
empty.style.display = '';
btn.style.display = 'none';
return;
}
empty.style.display = 'none';
const hasUnread = items.some(n => !n.read);
btn.style.display = hasUnread ? '' : 'none';
items.forEach(n => {
const div = document.createElement('div');
div.className = 'notif-item' + (n.read ? '' : ' unread');
div.dataset.notifId = n.id;
div.setAttribute('role', 'button');
div.setAttribute('tabindex', '0');
div.onclick = () => handleClick(n.id, n.targetUrl);
div.onkeydown = e => { if (e.key === 'Enter') handleClick(n.id, n.targetUrl); };
const arrow = n.targetUrl
? `<span class="notif-arrow"></span>`
: '';
const avatarInner = n.senderAvatar
? `<img src="data:image/png;base64,${n.senderAvatar}" alt="${esc(n.senderName || '')}">`
: `<span>🔔</span>`;
div.innerHTML = `
<div class="notif-avatar">${avatarInner}</div>
<div class="notif-dot${n.read ? ' read' : ''}"></div>
<div class="notif-body">
<div class="notif-text">${esc(n.text)}</div>
<div class="notif-time">${fmtRelTime(n.sentAt)}</div>
</div>
${arrow}`;
list.appendChild(div);
});
} catch(e) {
console.error(e);
}
}
// SSE: Neue Benachrichtigung → sofort neu laden
window.__sseOnNotification = () => loadNotifications();
loadNotifications();
</script>
</body>
</html>