feat: chat público con sensación de WhatsApp/Telegram real
Bugs corregidos:
- La imagen del comprobante nunca se veía: se guardaba en el disco 'public'
y se servía como asset('storage/...'), pero este proyecto no tiene el
symlink public/storage. Ahora usa store('photos') + asset($path), la
convención del resto del código (config/filesystems.php links).
- Dos wire:poll en el mismo nodo: Livewire v2 solo respeta el primero, así
que autoReintentarPago nunca se ejecutaba. Ahora va en su propio elemento
y solo se renderiza mientras hay un pago pendiente.
- Sin wire:key en los mensajes, morphdom recreaba toda la lista cada 3s:
parpadeo, scroll saltando y estado Alpine perdido (el "¡Copiado!" de las
credenciales desaparecía solo).
- pollMensajes cargaba la conversación entera cada 3s. Ahora consulta solo
MAX(id) y recarga únicamente si llegó algo nuevo; el historial se limita
a los 60 mensajes más recientes.
- El upload no se validaba: se añade regla image/mimes/max 8MB.
- Se elimina $procesandoImagen, propiedad muerta que viajaba en cada payload.
Interfaz:
- Envío optimista: la burbuja del usuario aparece al instante, sin esperar
el round-trip, con reloj de "pendiente" y checks al confirmarse.
- Indicador "escribiendo…" con puntos animados mientras el bot responde.
- Visor de imagen a pantalla completa al tocar el comprobante.
- Textarea que crece con el texto (Shift+Enter para salto de línea),
con wire:ignore para que el poll no le reinicie la altura al escribir.
- Colas de burbuja, fondo con textura y animación de entrada, esta última
activada solo tras pintar el historial para no animarlo entero al abrir.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
d88f67ab5b
commit
f4f689891c
@@ -87,6 +87,63 @@
|
||||
.safe-bottom { padding-bottom: env(safe-area-inset-bottom, 0px); }
|
||||
.safe-left { padding-left: env(safe-area-inset-left, 0px); }
|
||||
.safe-right { padding-right: env(safe-area-inset-right, 0px); }
|
||||
|
||||
/* ── Fondo con textura tipo WhatsApp ─────────────────────────── */
|
||||
.chat-bg {
|
||||
background-color: #0b141a;
|
||||
background-image:
|
||||
radial-gradient(circle at 25% 25%, rgba(255,255,255,.022) 1.2px, transparent 1.4px),
|
||||
radial-gradient(circle at 75% 75%, rgba(255,255,255,.018) 1.2px, transparent 1.4px);
|
||||
background-size: 34px 34px, 46px 46px;
|
||||
}
|
||||
|
||||
/* ── Colas de burbuja ────────────────────────────────────────── */
|
||||
.bubble-in, .bubble-out { position: relative; }
|
||||
.bubble-in::before, .bubble-out::before {
|
||||
content: ''; position: absolute; top: 0; width: 9px; height: 13px;
|
||||
}
|
||||
.bubble-in::before {
|
||||
left: -8px;
|
||||
background: #202c33;
|
||||
clip-path: polygon(100% 0, 100% 100%, 0 0);
|
||||
}
|
||||
.bubble-out::before {
|
||||
right: -8px;
|
||||
background: #005c4b;
|
||||
clip-path: polygon(0 0, 100% 0, 0 100%);
|
||||
}
|
||||
|
||||
/* ── Entrada de mensajes ─────────────────────────────────────── */
|
||||
@keyframes msgIn {
|
||||
from { opacity: 0; transform: translateY(8px) scale(.98); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
/* Solo se anima una vez pintado el historial, para que al abrir el chat
|
||||
los mensajes viejos no entren todos a la vez. La bandera vive en <body>
|
||||
(fuera del DOM de Livewire) para que morphdom no la revierta. */
|
||||
body.chat-listo .msg-in { animation: msgIn .18s ease-out; }
|
||||
|
||||
/* ── Puntos de "escribiendo…" ────────────────────────────────── */
|
||||
.typing-dot {
|
||||
width: 7px; height: 7px; border-radius: 9999px;
|
||||
background: #8696a0; display: inline-block;
|
||||
animation: typingBounce 1.3s infinite ease-in-out;
|
||||
}
|
||||
.typing-dot:nth-child(2) { animation-delay: .18s; }
|
||||
.typing-dot:nth-child(3) { animation-delay: .36s; }
|
||||
@keyframes typingBounce {
|
||||
0%, 60%, 100% { transform: translateY(0); opacity: .45; }
|
||||
30% { transform: translateY(-5px); opacity: 1; }
|
||||
}
|
||||
|
||||
/* Imagen que no cargó: no dejar un hueco enorme */
|
||||
.img-rota { min-height: 0 !important; opacity: .35; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.msg-in, .typing-dot { animation: none; }
|
||||
}
|
||||
|
||||
[x-cloak] { display: none !important; }
|
||||
</style>
|
||||
@livewireStyles
|
||||
</head>
|
||||
@@ -165,6 +222,11 @@
|
||||
}
|
||||
|
||||
scrollBottom();
|
||||
|
||||
/* Historial ya pintado: a partir de aquí sí animamos los mensajes nuevos */
|
||||
requestAnimationFrame(function () {
|
||||
requestAnimationFrame(function () { document.body.classList.add('chat-listo'); });
|
||||
});
|
||||
}
|
||||
|
||||
/* Bajar al fondo cuando el componente emite scroll-chat */
|
||||
|
||||
Reference in New Issue
Block a user