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
+12 -8
View File
@@ -511,11 +511,18 @@ function getVozES() {
} }
if ('speechSynthesis' in window) { if ('speechSynthesis' in window) {
window.speechSynthesis.onvoiceschanged = () => { _vozES = null; getVozES(); }; 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) { function anunciarTurno(codigo, destino, paciente) {
playBeep(); playBeep();
if ('speechSynthesis' in window) { if (!sonidoActivo || !('speechSynthesis' in window)) return;
setTimeout(() => { setTimeout(() => {
window.speechSynthesis.cancel(); window.speechSynthesis.cancel();
const letras = codigo.split('').join(' '); const letras = codigo.split('').join(' ');
@@ -529,7 +536,6 @@ function anunciarTurno(codigo, destino, paciente) {
utt.rate = 0.85; utt.pitch = 1.05; utt.volume = 1; utt.rate = 0.85; utt.pitch = 1.05; utt.volume = 1;
window.speechSynthesis.speak(utt); window.speechSynthesis.speak(utt);
}, 800); }, 800);
}
} }
/* ── Fullscreen ── */ /* ── Fullscreen ── */
@@ -591,7 +597,7 @@ function showAnnouncement({ codigo, destino, paciente, color }) {
/* ── State ── */ /* ── State ── */
let lastSeenKeys = new Set(); 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 llamados = [];
let _firstLoad = true; let _firstLoad = true;
@@ -616,13 +622,12 @@ function renderSnapshot(snap) {
let anuncioNuevo = false; let anuncioNuevo = false;
allTurnos.forEach(t => { allTurnos.forEach(t => {
const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || ''); const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || '');
const callKey = String(t.id || t.codigo);
currentKeys.add(key); currentKeys.add(key);
if (!lastSeenKeys.has(key)) { if (!lastSeenKeys.has(key)) {
const c = t.prioridad_color || '<?= $_labColor ?>'; const c = t.prioridad_color || '<?= $_labColor ?>';
const nomVoz = t.paciente_id ? t.paciente_nombre : null; const nomVoz = t.paciente_id ? t.paciente_nombre : null;
if (!announcedCalls.has(callKey)) { if (!announcedCalls.has(key)) {
announcedCalls.add(callKey); announcedCalls.add(key);
if (!_firstLoad) { if (!_firstLoad) {
queueAnnouncement({ _key: key, codigo: t.codigo, destino: t.destino, paciente: nomVoz, color: c }); queueAnnouncement({ _key: key, codigo: t.codigo, destino: t.destino, paciente: nomVoz, color: c });
} }
@@ -642,9 +647,8 @@ function renderSnapshot(snap) {
const t = sorted[0]; const t = sorted[0];
const c = t.prioridad_color || '<?= $_labColor ?>'; const c = t.prioridad_color || '<?= $_labColor ?>';
const key = t.codigo + '|' + t.destino + '|' + (t.llamado_at || ''); 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; const nomVoz = t.paciente_id ? t.paciente_nombre : null;
announcedCalls.add(callKey); announcedCalls.add(key);
queueAnnouncement({ _key: key, codigo: t.codigo, destino: t.destino, paciente: nomVoz, color: c }); queueAnnouncement({ _key: key, codigo: t.codigo, destino: t.destino, paciente: nomVoz, color: c });
} }
} }