fix(turnero): corregir anuncios de sonido en display_global

- Bug 1: announcedCalls usaba el ID del turno como clave, silenciando todas
  las re-llamadas. Ahora usa key=codigo|destino|llamado_at, igual que
  lastSeenKeys, para que cada re-llamada anuncie correctamente.
- Bug 2: anunciarTurno no verificaba sonidoActivo antes de llamar a
  speechSynthesis.speak(); agregado guard al inicio de la función.
- Bug 3: Chrome congela speechSynthesis tras ~15 min sin interacción;
  agregado keepalive de 14s con pause()+resume() para mantener el motor activo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-04 12:09:51 -05:00
co-authored by Claude Sonnet 4.6
parent 5af985581c
commit 4f25cd6b59
+28 -24
View File
@@ -511,25 +511,31 @@ function getVozES() {
}
if ('speechSynthesis' in window) {
window.speechSynthesis.onvoiceschanged = () => { _vozES = null; getVozES(); };
// Keepalive: Chrome pausa speechSynthesis tras ~15 min sin interacción del usuario
setInterval(() => {
if (!window.speechSynthesis.speaking) {
window.speechSynthesis.pause();
window.speechSynthesis.resume();
}
}, 14000);
}
function anunciarTurno(codigo, destino, paciente) {
playBeep();
if ('speechSynthesis' in window) {
setTimeout(() => {
window.speechSynthesis.cancel();
const letras = codigo.split('').join(' ');
let texto = `Turno ${letras}`;
if (paciente) texto += `, ${paciente}`;
texto += `, pase a ${destino}`;
const utt = new SpeechSynthesisUtterance(texto);
utt.lang = 'es-CO';
const voz = getVozES();
if (voz) utt.voice = voz;
utt.rate = 0.85; utt.pitch = 1.05; utt.volume = 1;
window.speechSynthesis.speak(utt);
}, 800);
}
if (!sonidoActivo || !('speechSynthesis' in window)) return;
setTimeout(() => {
window.speechSynthesis.cancel();
const letras = codigo.split('').join(' ');
let texto = `Turno ${letras}`;
if (paciente) texto += `, ${paciente}`;
texto += `, pase a ${destino}`;
const utt = new SpeechSynthesisUtterance(texto);
utt.lang = 'es-CO';
const voz = getVozES();
if (voz) utt.voice = voz;
utt.rate = 0.85; utt.pitch = 1.05; utt.volume = 1;
window.speechSynthesis.speak(utt);
}, 800);
}
/* ── Fullscreen ── */
@@ -591,7 +597,7 @@ function showAnnouncement({ codigo, destino, paciente, color }) {
/* ── State ── */
let lastSeenKeys = new Set();
let announcedCalls = new Set(); // dedup por id+llamado_at, evita anunciar mismo turno desde varios escritorios
let announcedCalls = new Set(); // dedup por codigo+destino+llamado_at; usar key (no id) permite re-llamadas
let llamados = [];
let _firstLoad = true;
@@ -615,14 +621,13 @@ function renderSnapshot(snap) {
const currentKeys = new Set();
let anuncioNuevo = false;
allTurnos.forEach(t => {
const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || '');
const callKey = String(t.id || t.codigo);
const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || '');
currentKeys.add(key);
if (!lastSeenKeys.has(key)) {
const c = t.prioridad_color || '<?= $_labColor ?>';
const nomVoz = t.paciente_id ? t.paciente_nombre : null;
if (!announcedCalls.has(callKey)) {
announcedCalls.add(callKey);
if (!announcedCalls.has(key)) {
announcedCalls.add(key);
if (!_firstLoad) {
queueAnnouncement({ _key: key, codigo: t.codigo, destino: t.destino, paciente: nomVoz, color: c });
}
@@ -641,10 +646,9 @@ function renderSnapshot(snap) {
if (sorted.length > 0) {
const t = sorted[0];
const c = t.prioridad_color || '<?= $_labColor ?>';
const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || '');
const callKey = String(t.id || t.codigo);
const nomVoz = t.paciente_id ? t.paciente_nombre : null;
announcedCalls.add(callKey);
const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || '');
const nomVoz = t.paciente_id ? t.paciente_nombre : null;
announcedCalls.add(key);
queueAnnouncement({ _key: key, codigo: t.codigo, destino: t.destino, paciente: nomVoz, color: c });
}
}