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:
co-authored by
Claude Sonnet 4.6
parent
d77eabc272
commit
7cc696e04a
@@ -605,39 +605,58 @@
|
|||||||
|
|
||||||
<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');
|
|
||||||
|
function atBottom() {
|
||||||
|
return container.scrollHeight - container.scrollTop - container.clientHeight < 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToBottom() {
|
||||||
|
container.scrollTop = container.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBadge(n) {
|
||||||
|
if (!badge || !countEl) return;
|
||||||
|
countEl.textContent = n;
|
||||||
|
badge.classList.remove('hidden');
|
||||||
|
badge.classList.add('flex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideBadge() {
|
||||||
|
if (!badge) return;
|
||||||
|
badge.classList.add('hidden');
|
||||||
|
badge.classList.remove('flex');
|
||||||
|
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;
|
if (!container) return;
|
||||||
|
|
||||||
let userScrolledUp = false;
|
// Scroll inicial
|
||||||
let newMsgCount = 0;
|
scrollToBottom();
|
||||||
let lastScrollHeight = container.scrollHeight;
|
|
||||||
|
|
||||||
function atBottom() {
|
|
||||||
return container.scrollHeight - container.scrollTop - container.clientHeight < 120;
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToBottom() {
|
|
||||||
container.scrollTop = container.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showBadge(n) {
|
|
||||||
if (!badge || !countEl) return;
|
|
||||||
countEl.textContent = n;
|
|
||||||
badge.classList.remove('hidden');
|
|
||||||
badge.classList.add('flex');
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideBadge() {
|
|
||||||
if (!badge) return;
|
|
||||||
badge.classList.add('hidden');
|
|
||||||
badge.classList.remove('flex');
|
|
||||||
newMsgCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
|||||||
Reference in New Issue
Block a user