From 1091be531a2112a606aa7dd4702b8775df27850b Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Fri, 23 Jan 2026 23:36:20 -0500 Subject: [PATCH] Update conversations.php --- conversations.php | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/conversations.php b/conversations.php index 066e32a..7b0f4a4 100644 --- a/conversations.php +++ b/conversations.php @@ -729,6 +729,8 @@ this.currentConversationId = null; this.currentUserId = null; this.conversations = []; + // Track shown notifications to avoid duplicates from polling + this._shownNotifications = new Set(); // Message pagination / loading state this.messageLimit = 50; this.loadingMessages = false; @@ -787,6 +789,15 @@ document.body.appendChild(container); } + // Determine an id to dedupe notifications (prefer server id) + const nid = notification && notification.id ? String(notification.id) : ('msg:' + String((notification && notification.message) || '').slice(0,200)); + if (this._shownNotifications.has(nid)) { + // already shown + console.debug('Notification already shown, skipping', nid); + return; + } + this._shownNotifications.add(nid); + const toast = document.createElement('div'); // compact by default toast.className = 'notification-toast small'; @@ -808,11 +819,18 @@ const actions = document.createElement('div'); actions.className = 'nt-actions'; + const removeToast = () => { + if (toast.parentNode) toast.remove(); + if (this._shownNotifications.has(nid)) this._shownNotifications.delete(nid); + }; + const openBtn = document.createElement('button'); openBtn.className = notification.cool ? 'btn btn-sm btn-light' : 'btn btn-sm btn-primary'; openBtn.textContent = 'Abrir'; openBtn.onclick = async () => { - await fetch('api/mark_notification_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({id: notification.id})}); + try { + await fetch('api/mark_notification_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({id: notification.id})}); + } catch(e) { console.warn('mark_notification_read failed', e); } let data = {}; try { data = notification.data ? JSON.parse(notification.data) : {}; } catch(e) {} const userId = data.user_id || notification.user_id; @@ -824,7 +842,7 @@ this.openConversation(userId, 'Usuario', ''); } } - toast.remove(); + removeToast(); }; const dismiss = document.createElement('button'); @@ -832,8 +850,10 @@ dismiss.textContent = '×'; dismiss.title = 'Descartar'; dismiss.onclick = async () => { - await fetch('api/mark_notification_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({id: notification.id})}); - toast.remove(); + try { + await fetch('api/mark_notification_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({id: notification.id})}); + } catch(e) { console.warn('mark_notification_read failed', e); } + removeToast(); }; actions.appendChild(openBtn); @@ -842,12 +862,12 @@ container.appendChild(toast); - // Auto remove (shorter for compact notifications) + // Auto remove (shorter for compact notifications) with minimum enforced const _minToastDuration = 3000; let timeout = notification.duration ? Number(notification.duration) : (notification.cool ? 15000 : 8000); if (!isFinite(timeout) || timeout <= 0) timeout = (notification.cool ? 15000 : 8000); timeout = Math.max(timeout, _minToastDuration); - setTimeout(() => { if (toast.parentNode) toast.remove(); }, timeout); + setTimeout(removeToast, timeout); } setupEventListeners() {