Fix: alle funktionalen Inline-Event-Handler → addEventListener/Delegation (von CSP-Härtung 65cfa25 app-weit blockiert)

Chat (senden/öffnen/löschen/Foto), Tagebuch-Buch, KI-Berichte, Wiki-Moderation,
Events-Detail, Walks-Lightbox, Routen-Foto, Navigations-CTAs (data-page),
Presse-Copy + Züchter-Landing (externes JS). 35x UI.modal.close → data-modal-close,
28x totes event.stopPropagation entfernt. Verbleibend: kosmetische onerror/Hover. SW v1164
This commit is contained in:
rene 2026-06-04 13:59:27 +02:00
parent 152fde716c
commit 2ddd8ac350
34 changed files with 228 additions and 173 deletions

View file

@ -18,6 +18,23 @@ window.Page_chat = (() => {
_container = container;
_myId = appState?.user?.id || null;
// Delegierter Click-Handler — Inline-onclick wird von der CSP blockiert.
if (!_container._chatClickBound) {
_container.addEventListener('click', e => {
const t = e.target.closest('[data-chat-action]');
if (!t) return;
switch (t.dataset.chatAction) {
case 'open': _openThread(parseInt(t.dataset.chatId, 10)); break;
case 'list': _showList(); break;
case 'photo': document.getElementById('chat-photo-input')?.click(); break;
case 'send': _send(); break;
case 'delete': _deleteMsg(parseInt(t.dataset.chatId, 10)); break;
case 'img': window.open(t.dataset.chatUrl, '_blank'); break;
}
});
_container._chatClickBound = true;
}
// Heartbeat: alle 30s online-Status senden
API.chat.heartbeat().catch(() => {});
_heartbeatTimer = setInterval(() => {
@ -132,7 +149,7 @@ window.Page_chat = (() => {
? `<span class="online-dot" title="Online"></span>`
: '';
return `
<div class="chat-conv-item" onclick="Page_chat._openThread(${c.id})">
<div class="chat-conv-item" data-chat-action="open" data-chat-id="${c.id}">
<div style="position:relative;flex-shrink:0">
<div class="chat-conv-avatar">${initials}</div>
${onlineDot ? `<span class="online-dot chat-avatar-dot"></span>` : ''}
@ -166,14 +183,14 @@ window.Page_chat = (() => {
// Aktive Markierung in der Liste
document.querySelectorAll('.chat-conv-item').forEach(el =>
el.classList.toggle('active', el.getAttribute('onclick')?.includes(String(convId)))
el.classList.toggle('active', el.dataset.chatId === String(convId))
);
const threadHTML = `
<div class="chat-thread" id="chat-thread">
<div class="chat-thread-header">
${_isDesktop() ? '' : `
<button class="btn btn-ghost btn-sm" onclick="Page_chat._showList()" style="padding:var(--space-1)">
<button class="btn btn-ghost btn-sm" data-chat-action="list" style="padding:var(--space-1)">
<svg class="ph-icon"><use href="/icons/phosphor.svg#arrow-left"></use></svg>
</button>`}
<div style="position:relative;flex-shrink:0">
@ -188,14 +205,13 @@ window.Page_chat = (() => {
</div>
</div>
<div class="chat-input-bar">
<input type="file" id="chat-photo-input" accept="image/*" class="hidden"
onchange="Page_chat._onPhotoSelected(this)">
<button class="chat-photo-btn" onclick="document.getElementById('chat-photo-input').click()" title="Foto senden">
<input type="file" id="chat-photo-input" accept="image/*" class="hidden">
<button class="chat-photo-btn" data-chat-action="photo" title="Foto senden">
<svg class="ph-icon"><use href="/icons/phosphor.svg#camera"></use></svg>
</button>
<textarea id="chat-input" class="chat-input" rows="1"
placeholder="Nachricht…" maxlength="2000"></textarea>
<button class="chat-send-btn" id="chat-send-btn" onclick="Page_chat._send()">
<button class="chat-send-btn" id="chat-send-btn" data-chat-action="send">
<svg class="ph-icon"><use href="/icons/phosphor.svg#paper-plane-tilt"></use></svg>
</button>
</div>
@ -219,9 +235,11 @@ window.Page_chat = (() => {
input.addEventListener('keydown', e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
Page_chat._send();
_send();
}
});
document.getElementById('chat-photo-input')
?.addEventListener('change', e => _onPhotoSelected(e.target));
await _loadMessages(true);
await API.chat.markRead(_convId).catch(() => {});
@ -313,7 +331,7 @@ window.Page_chat = (() => {
const timeStr = _fmtTime(m.created_at);
const deleteBtn = isMine && !m.is_deleted
? `<button class="btn btn-ghost" style="padding:2px;opacity:0.4;font-size:var(--text-xs)"
onclick="Page_chat._deleteMsg(${m.id})" title="Löschen">
data-chat-action="delete" data-chat-id="${m.id}" title="Löschen">
<svg class="ph-icon" style="width:12px;height:12px"><use href="/icons/phosphor.svg#trash"></use></svg>
</button>`
: '';
@ -328,7 +346,7 @@ window.Page_chat = (() => {
// Medieninhalt
let bubbleContent = '';
if (m.media_url) {
bubbleContent += `<img src="${UI.escape(m.media_url)}" class="chat-bubble-img" alt="Foto" onclick="window.open('${UI.escape(m.media_url)}','_blank')">`;
bubbleContent += `<img src="${UI.escape(m.media_url)}" class="chat-bubble-img" alt="Foto" data-chat-action="img" data-chat-url="${UI.escape(m.media_url)}">`;
}
if (m.text) {
bubbleContent += (m.media_url ? `<div style="margin-top:var(--space-1)">` : '') +