fix: scroll chat usa Livewire.hook oficial + debounce para Android

MutationObserver disparaba scroll repetido durante render de Livewire
bloqueando el touch scroll en Android. Ahora usa Livewire.hook('message.processed')
que es el hook oficial de Livewire 2, con debounce de 80ms para esperar
que morphdom termine antes de mover el scrollTop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-07-19 22:12:51 +00:00
co-authored by Claude Sonnet 4.6
parent d77eabc272
commit 7cc696e04a
@@ -605,16 +605,10 @@
<script> <script>
(function () { (function () {
function initChatScroll() { var container, badge, badgeBtn, countEl;
const container = document.getElementById('pub-messages'); var userScrolledUp = false;
const badge = document.getElementById('new-msg-badge'); var newMsgCount = 0;
const badgeBtn = document.getElementById('new-msg-badge-btn'); var debounceTimer = null;
const countEl = document.getElementById('new-msg-count');
if (!container) return;
let userScrolledUp = false;
let newMsgCount = 0;
let lastScrollHeight = container.scrollHeight;
function atBottom() { function atBottom() {
return container.scrollHeight - container.scrollTop - container.clientHeight < 120; return container.scrollHeight - container.scrollTop - container.clientHeight < 120;
@@ -638,6 +632,31 @@
newMsgCount = 0; newMsgCount = 0;
} }
function onUpdate() {
if (!userScrolledUp) {
scrollToBottom();
} else {
newMsgCount++;
showBadge(newMsgCount);
}
}
function debouncedUpdate() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(onUpdate, 80);
}
document.addEventListener('livewire:load', function () {
container = document.getElementById('pub-messages');
badge = document.getElementById('new-msg-badge');
badgeBtn = document.getElementById('new-msg-badge-btn');
countEl = document.getElementById('new-msg-count');
if (!container) return;
// Scroll inicial
scrollToBottom();
// Click en badge baja al fondo
if (badgeBtn) { if (badgeBtn) {
badgeBtn.addEventListener('click', function () { badgeBtn.addEventListener('click', function () {
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
@@ -645,6 +664,7 @@
}); });
} }
// Detectar scroll manual del usuario
container.addEventListener('scroll', function () { container.addEventListener('scroll', function () {
if (atBottom()) { if (atBottom()) {
userScrolledUp = false; userScrolledUp = false;
@@ -654,32 +674,10 @@
} }
}, { passive: true }); }, { passive: true });
// Scroll inicial al fondo // Hook oficial Livewire 2: se ejecuta después de cada update del componente
scrollToBottom(); Livewire.hook('message.processed', function (message, component) {
debouncedUpdate();
// MutationObserver: detecta cuando Livewire agrega/modifica nodos en el contenedor });
const observer = new MutationObserver(function () {
const newHeight = container.scrollHeight;
if (newHeight === lastScrollHeight) return;
lastScrollHeight = newHeight;
if (!userScrolledUp) {
scrollToBottom();
} else {
newMsgCount++;
showBadge(newMsgCount);
}
}); });
observer.observe(container, { childList: true, subtree: true });
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initChatScroll);
} else {
initChatScroll();
}
document.addEventListener('livewire:load', initChatScroll);
})(); })();
</script> </script>