feat(widget): mostrar que el asistente está escribiendo
Entre que el visitante manda y el agente contesta pasan varios segundos —más si tiene que buscar en la base de conocimiento— y el widget se quedaba mudo. Sin señal de vida, la reacción normal es pensar que se colgó y volver a mandar. Ahora aparecen los tres puntos que cualquiera reconoce de WhatsApp, y el campo se bloquea mientras responde: mandar tres mensajes seguidos desordena el hilo y multiplica el consumo sin que nadie lo pida. La limpieza va en un then final y no en el de éxito: si quedara ahí, un error de red dejaría los puntitos animándose para siempre y el campo bloqueado, que es peor que no haber puesto nada. Probado con la respuesta normal y con un fetch que falla. El indicador se inserta siempre al final, así que un mensaje que llegue mientras tanto no lo deja en el medio del hilo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
44686d409a
commit
9dba93e7f4
@@ -139,6 +139,16 @@ const umindWidgetJS = `(function () {
|
||||
'.umind-msg-user{color:#fff;margin-left:auto;border-bottom-right-radius:4px;}',
|
||||
'.umind-msg-bot a{color:var(--umind-color);text-decoration:underline;word-break:break-all;}',
|
||||
'.umind-msg-user a{color:#fff;text-decoration:underline;word-break:break-all;}',
|
||||
// "Escribiendo": los tres puntos que cualquiera reconoce de WhatsApp. Sin
|
||||
// esto, entre que la persona manda y el agente contesta —varios segundos
|
||||
// si tiene que buscar en el conocimiento— el widget se queda mudo y parece
|
||||
// colgado.
|
||||
'.umind-escribiendo{display:flex;gap:4px;align-items:center;width:fit-content;margin-bottom:10px;padding:11px 13px;background:#fff;border:1px solid #ececec;border-radius:13px;border-bottom-left-radius:4px;}',
|
||||
'.umind-escribiendo i{width:6px;height:6px;border-radius:50%;background:#b6b6b6;display:block;animation:umind-punto 1.3s infinite;}',
|
||||
'.umind-escribiendo i:nth-child(2){animation-delay:.18s;}',
|
||||
'.umind-escribiendo i:nth-child(3){animation-delay:.36s;}',
|
||||
'@keyframes umind-punto{0%,60%,100%{opacity:.3;transform:translateY(0);}30%{opacity:1;transform:translateY(-3px);}}',
|
||||
'@media (prefers-reduced-motion:reduce){.umind-escribiendo i{animation:none;opacity:.6;}}',
|
||||
'.umind-inputbar{display:flex;align-items:center;border-top:1px solid #eee;padding:8px;gap:6px;background:#fff;flex-shrink:0;}',
|
||||
'.umind-inputbar input{flex:1;border:1px solid #e5e5e5;border-radius:20px;padding:9px 14px;font-size:13px;outline:none;}',
|
||||
'.umind-inputbar button{border:none;color:#fff;width:34px;height:34px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;}',
|
||||
@@ -225,13 +235,38 @@ const umindWidgetJS = `(function () {
|
||||
}
|
||||
}
|
||||
|
||||
// El indicador es un elemento suelto que se agrega y se saca: así no hay que
|
||||
// llevar estado ni limpiar nada si la petición falla a mitad de camino.
|
||||
var escribiendoEl = null;
|
||||
|
||||
function mostrarEscribiendo() {
|
||||
if (escribiendoEl) return;
|
||||
escribiendoEl = document.createElement('div');
|
||||
escribiendoEl.className = 'umind-escribiendo';
|
||||
escribiendoEl.setAttribute('aria-label', 'El asistente está escribiendo');
|
||||
for (var i = 0; i < 3; i++) escribiendoEl.appendChild(document.createElement('i'));
|
||||
msgsEl.appendChild(escribiendoEl);
|
||||
msgsEl.scrollTop = msgsEl.scrollHeight;
|
||||
}
|
||||
|
||||
function ocultarEscribiendo() {
|
||||
if (!escribiendoEl) return;
|
||||
if (escribiendoEl.parentNode) escribiendoEl.parentNode.removeChild(escribiendoEl);
|
||||
escribiendoEl = null;
|
||||
}
|
||||
|
||||
function agregarMensaje(texto, quien) {
|
||||
var burbuja = document.createElement('div');
|
||||
pintarConEnlaces(burbuja, texto == null ? '' : texto);
|
||||
var esUser = quien === 'user';
|
||||
burbuja.className = 'umind-msg ' + (esUser ? 'umind-msg-user' : 'umind-msg-bot');
|
||||
if (esUser) burbuja.style.background = color;
|
||||
msgsEl.appendChild(burbuja);
|
||||
// Antes del indicador, para que los puntitos queden siempre al final.
|
||||
if (escribiendoEl && escribiendoEl.parentNode === msgsEl) {
|
||||
msgsEl.insertBefore(burbuja, escribiendoEl);
|
||||
} else {
|
||||
msgsEl.appendChild(burbuja);
|
||||
}
|
||||
msgsEl.scrollTop = msgsEl.scrollHeight;
|
||||
}
|
||||
|
||||
@@ -274,6 +309,13 @@ const umindWidgetJS = `(function () {
|
||||
if (!texto) return;
|
||||
inputEl.value = '';
|
||||
agregarMensaje(texto, 'user');
|
||||
mostrarEscribiendo();
|
||||
// Se bloquea el envío mientras responde: mandar tres mensajes seguidos
|
||||
// desordena el hilo y multiplica el consumo sin que nadie lo pida.
|
||||
inputEl.disabled = true;
|
||||
sendEl.disabled = true;
|
||||
sendEl.style.opacity = '.5';
|
||||
|
||||
fetch(apiBase + '/mensaje', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -290,6 +332,16 @@ const umindWidgetJS = `(function () {
|
||||
? 'El asistente no está disponible en este momento.'
|
||||
: 'Error de conexión, intenta de nuevo.',
|
||||
'bot');
|
||||
})
|
||||
.then(function () {
|
||||
// Pase lo que pase: si esto quedara solo en el .then de éxito, un
|
||||
// error dejaría los puntitos animándose para siempre y el campo de
|
||||
// texto bloqueado.
|
||||
ocultarEscribiendo();
|
||||
inputEl.disabled = false;
|
||||
sendEl.disabled = false;
|
||||
sendEl.style.opacity = '';
|
||||
inputEl.focus();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user