feat: smart scroll — keep position when reading, badge for new messages

- Add pollMensajes() so wire:poll no longer force-scrolls to bottom
- MutationObserver detects new DOM nodes: auto-scrolls only if user
  is already at the bottom (within 80px threshold)
- Floating '↓ N mensaje nuevo' badge appears when new bot messages
  arrive while user is scrolled up; tap badge to jump to bottom
- scroll-chat event (emitted on button clicks/sends) still forces
  scroll since the user just took an action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-07-14 13:36:31 +00:00
co-authored by Claude Sonnet 4.6
parent a5bac9cee7
commit 26953c9dd5
3 changed files with 132 additions and 24 deletions
+22 -1
View File
@@ -1225,7 +1225,7 @@ class PublicChat extends Component
$this->mensajes = ChatMessage::where('conversation_id', $this->convId)
->orderBy('id')
->get()
->map(fn($m) => [
->map(fn ($m) => [
'tipo' => $m->tipo,
'tipo_ui' => $m->tipo_ui ?? 'text',
'contenido' => $m->contenido,
@@ -1237,6 +1237,27 @@ class PublicChat extends Component
$this->dispatchBrowserEvent('scroll-chat');
}
// Llamado por wire:poll — igual que cargarMensajes pero sin forzar scroll
public function pollMensajes(): void
{
if (! $this->convId) {
return;
}
$this->mensajes = ChatMessage::where('conversation_id', $this->convId)
->orderBy('id')
->get()
->map(fn ($m) => [
'tipo' => $m->tipo,
'tipo_ui' => $m->tipo_ui ?? 'text',
'contenido' => $m->contenido,
'payload' => $m->payload ?? [],
'created_at' => $m->created_at->format('H:i'),
])
->toArray();
// No dispatches scroll-chat — el MutationObserver del JS maneja el badge
}
// ─────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────