Wetier am Cahstity game gebasterln
This commit is contained in:
251
xxxthegame/src/main/resources/static/benachrichtigungen.html
Normal file
251
xxxthegame/src/main/resources/static/benachrichtigungen.html
Normal file
@@ -0,0 +1,251 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Benachrichtigungen – XXX The Game</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;
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.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;
|
||||
max-width: 680px;
|
||||
}
|
||||
</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/sidebar.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,'&').replace(/</g,'<').replace(/>/g,'>')
|
||||
.replace(/"/g,'"');
|
||||
}
|
||||
|
||||
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>
|
||||
Reference in New Issue
Block a user