From 4b9468702c31665cdac2aca11d6f5399feb95b30 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 24 Jan 2026 09:31:59 -0500 Subject: [PATCH] Update conversations.php --- conversations.php | 173 +++++++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 73 deletions(-) diff --git a/conversations.php b/conversations.php index 6f57b13..b8f3085 100644 --- a/conversations.php +++ b/conversations.php @@ -2143,6 +2143,8 @@ // no reemplazar ni limpiar la conversación actual; solo indicar que no hay más. if (!initial && Array.isArray(messages) && messages.length === 0) { try { if (topLoader && topLoader.parentNode) topLoader.remove(); } catch(e) {} + // Asegurar que no quedemos en estado de loading + this.loadingMessages = false; this.hasMoreMessages = false; // show hint and preserve current view this.showNoMoreMessagesHint(); @@ -2150,30 +2152,45 @@ return; } + // Si es carga inicial y el servidor devolvió vacío, pero ya teníamos mensajes, preservarlos. + if (initial && Array.isArray(messages) && messages.length === 0 && Array.isArray(this.conversations) && this.conversations.length > 0) { + try { if (topLoader && topLoader.parentNode) topLoader.remove(); } catch(e) {} + this.loadingMessages = false; + if (typeof showAlert === 'function') showAlert('No se pudieron recargar mensajes; manteniendo historial actual', 'warning'); + console.warn('Initial load returned empty but existing view contains messages - preserving current conversation'); + return; + } + if (initial) { + // Reemplazar sólo en carga inicial válida (puede estar vacía si no hay nada en DB) this.conversations = messages; } else { - // Evitar introducir duplicados que ya existen en la vista actual - try { - const existingKeys = new Set(this.conversations.map(m => this.messageKey(m))); - const beforeLen = messages.length; - messages = messages.filter(m => { - const k = this.messageKey(m); - const seen = existingKeys.has(k); - if (!seen) existingKeys.add(k); // mark as seen to avoid duplicates within the batch - else { - if (console && console.debug) console.debug('Skipping duplicate message from server (already in view)', m && (m.id || m.message_id), m && m.created_at); + // Prepend sólo si hay mensajes nuevos + if (Array.isArray(messages) && messages.length > 0) { + // Evitar introducir duplicados que ya existen en la vista actual + try { + const existingKeys = new Set((this.conversations || []).map(m => this.messageKey(m))); + const beforeLen = messages.length; + messages = messages.filter(m => { + const k = this.messageKey(m); + const seen = existingKeys.has(k); + if (!seen) existingKeys.add(k); // mark as seen to avoid duplicates within the batch + else { + if (console && console.debug) console.debug('Skipping duplicate message from server (already in view)', m && (m.id || m.message_id), m && m.created_at); + } + return !seen; + }); + const dropped = beforeLen - messages.length; + if (dropped > 0) { + if (console && console.info) console.info(`Omitted ${dropped} messages because they already exist in view`); } - return !seen; - }); - const dropped = beforeLen - messages.length; - if (dropped > 0) { - if (console && console.info) console.info(`Omitted ${dropped} messages because they already exist in view`); - } - } catch (e) { console.warn('duplicate filtering failed', e); } + } catch (e) { console.warn('duplicate filtering failed', e); } - // Prepend mensajes antiguos - this.conversations = messages.concat(this.conversations); + // Prepend mensajes antiguos + this.conversations = messages.concat(this.conversations || []); + } else { + // No hay mensajes nuevos para añadir; nada que hacer + } } // Actualizar paginación (earliest timestamp y id) @@ -2182,68 +2199,72 @@ // Improved dedupe: pick the most "readable" message when duplicates exist try { - const best = new Map(); // key => { index, score } - const toRemove = []; + if (!this.conversations || this.conversations.length === 0) { + // Nothing to dedupe + } else { + const best = new Map(); // key => { index, score } + const toRemove = []; - const scoreFor = (m) => { - let s = 0; - if (!m) return s; - // Prefer explicit media type messages - if (m.message_type && m.message_type !== 'text') s += 50; - // Prefer messages with thumbnails or local files - if (m.local_thumb || m.local_file || m.media_url_external || m.media_url) s += 30; - // Prefer human text content (avoid raw JSON blobs) - if (m.content) { - const c = String(m.content).trim(); - const looksJson = c.startsWith('{') && c.endsWith('}'); - if (looksJson) { - s -= 20; // penalize raw JSON-looking content - } else { - s += 10; - if (c.length < 200) s += 5; + const scoreFor = (m) => { + let s = 0; + if (!m) return s; + // Prefer explicit media type messages + if (m.message_type && m.message_type !== 'text') s += 50; + // Prefer messages with thumbnails or local files + if (m.local_thumb || m.local_file || m.media_url_external || m.media_url) s += 30; + // Prefer human text content (avoid raw JSON blobs) + if (m.content) { + const c = String(m.content).trim(); + const looksJson = c.startsWith('{') && c.endsWith('}'); + if (looksJson) { + s -= 20; // penalize raw JSON-looking content + } else { + s += 10; + if (c.length < 200) s += 5; + } } - } - if (m.message_id) s += 3; - if (m.reply_to_message_id) s += 2; - return s; - }; + if (m.message_id) s += 3; + if (m.reply_to_message_id) s += 2; + return s; + }; - for (let i = this.conversations.length - 1; i >= 0; i--) { - const m = this.conversations[i]; - if (!m) continue; - const mid = m.message_id || m.id || null; - let key = null; - if (mid) key = String(mid); - else { - const partMedia = m.media_url || m.media_url_external || m.local_file || m.local_thumb || m.content || ''; - const ts = m.created_at ? String(Math.floor(new Date(m.created_at).getTime() / 1000)) : ''; - key = `${m.direction||'?'}|${m.message_type||m.media_type||'text'}|${partMedia}|${ts}`; - } + for (let i = this.conversations.length - 1; i >= 0; i--) { + const m = this.conversations[i]; + if (!m) continue; + const mid = m.message_id || m.id || null; + let key = null; + if (mid) key = String(mid); + else { + const partMedia = m.media_url || m.media_url_external || m.local_file || m.local_thumb || m.content || ''; + const ts = m.created_at ? String(Math.floor(new Date(m.created_at).getTime() / 1000)) : ''; + key = `${m.direction||'?'}|${m.message_type||m.media_type||'text'}|${partMedia}|${ts}`; + } - const s = scoreFor(m); - if (!best.has(key)) { - best.set(key, { index: i, score: s }); - } else { - const prev = best.get(key); - if (s > prev.score) { - // keep current, remove previous - toRemove.push(prev.index); + const s = scoreFor(m); + if (!best.has(key)) { best.set(key, { index: i, score: s }); } else { - // remove current - toRemove.push(i); + const prev = best.get(key); + if (s > prev.score) { + // keep current, remove previous + toRemove.push(prev.index); + best.set(key, { index: i, score: s }); + } else { + // remove current + toRemove.push(i); + } } } - } - if (toRemove.length) { - // dedupe unique indices - const uniq = Array.from(new Set(toRemove)).sort((a,b) => a - b); - // remove from highest index down - for (let j = uniq.length - 1; j >= 0; j--) { - const idx = uniq[j]; - const removed = this.conversations.splice(idx, 1)[0]; - console.debug('Dedupe removed message at index', idx, 'removed=', removed && (removed.message_id || removed.id || removed.content || removed.media_url) ); + if (toRemove.length) { + // dedupe unique indices + const uniq = Array.from(new Set(toRemove)).sort((a,b) => a - b); + // remove from highest index down + for (let j = uniq.length - 1; j >= 0; j--) { + const idx = uniq[j]; + const removed = this.conversations.splice(idx, 1)[0]; + console.debug('Dedupe removed message at index', idx, 'removed=', removed && (removed.message_id || removed.id || removed.content || removed.media_url) ); + } } } } catch (e) { @@ -2263,7 +2284,13 @@ // Renderizar incrementalmente para evitar parpadeos y preservar reproducción de media // Si cargamos paginación (initial=false), pasar la cantidad de mensajes recién obtenidos para insertarlos al inicio const newlyFetched = (!initial && Array.isArray(messages)) ? messages.length : 0; - this.renderMessagesIncremental(initial, newlyFetched); + if (this.conversations && this.conversations.length > 0) { + this.renderMessagesIncremental(initial, newlyFetched); + } else if (initial) { + // Si es carga inicial y no tenemos mensajes, mostrar una vista vacía + const container = document.getElementById('chat-conversations'); + if (container) container.innerHTML = `

No hay mensajes

Envía un mensaje para iniciar la conversación.

`; + } // Replace media rendering uses window.renderMediaMessage inside element creation/update