From e1aeec60530fb71e1fe61489792dcf77763d2e7c Mon Sep 17 00:00:00 2001 From: Cyril Date: Mon, 26 Jan 2026 11:00:28 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BF=EF=B8=8F(frontend)=20adjust=20sr=20an?= =?UTF-8?q?nouncements=20for=20idle=20disconnect=20timer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reduces screen reader noise while keeping key countdown cues --- CHANGELOG.md | 4 ++ .../components/IsIdleDisconnectModal.tsx | 47 +++++++++++++++++-- src/frontend/src/locales/de/rooms.json | 3 +- src/frontend/src/locales/en/rooms.json | 3 +- src/frontend/src/locales/fr/rooms.json | 3 +- src/frontend/src/locales/nl/rooms.json | 3 +- 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 017fdbf7..2ad5ac5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to - 🔒️(frontend) fix an XSS vulnerability on the recording page #911 +### Changed + +- ♿(frontend) adjust sr announcements for idle disconnect timer #908 + ## [1.4.0] - 2026-01-25 ### Added diff --git a/src/frontend/src/features/rooms/livekit/components/IsIdleDisconnectModal.tsx b/src/frontend/src/features/rooms/livekit/components/IsIdleDisconnectModal.tsx index d2eac89a..4133b86e 100644 --- a/src/frontend/src/features/rooms/livekit/components/IsIdleDisconnectModal.tsx +++ b/src/frontend/src/features/rooms/livekit/components/IsIdleDisconnectModal.tsx @@ -4,16 +4,20 @@ import { css } from '@/styled-system/css' import { useSnapshot } from 'valtio' import { connectionObserverStore } from '@/stores/connectionObserver' import { HStack } from '@/styled-system/jsx' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { navigateTo } from '@/navigation/navigateTo' import humanizeDuration from 'humanize-duration' import i18n from 'i18next' const IDLE_DISCONNECT_TIMEOUT_MS = 120000 // 2 minutes +const COUNTDOWN_ANNOUNCEMENT_SECONDS = [120, 90, 60, 30] +const FINAL_COUNTDOWN_SECONDS = 10 export const IsIdleDisconnectModal = () => { const connectionObserverSnap = useSnapshot(connectionObserverStore) const [timeRemaining, setTimeRemaining] = useState(IDLE_DISCONNECT_TIMEOUT_MS) + const [srMessage, setSrMessage] = useState('') + const lastAnnouncementRef = useRef(null) const { t } = useTranslation('rooms', { keyPrefix: 'isIdleDisconnectModal' }) @@ -35,10 +39,43 @@ export const IsIdleDisconnectModal = () => { } }, [connectionObserverSnap.isIdleDisconnectModalOpen]) - const minutes = Math.floor(timeRemaining / 1000 / 60) - const seconds = (timeRemaining / 1000) % 60 + useEffect(() => { + if (!connectionObserverSnap.isIdleDisconnectModalOpen) { + lastAnnouncementRef.current = null + setSrMessage('') + } + }, [connectionObserverSnap.isIdleDisconnectModalOpen]) + + const remainingSeconds = Math.ceil(timeRemaining / 1000) + const minutes = Math.floor(remainingSeconds / 60) + const seconds = remainingSeconds % 60 const formattedTime = `${minutes}:${seconds.toString().padStart(2, '0')}` + useEffect(() => { + if (!connectionObserverSnap.isIdleDisconnectModalOpen) return + + const shouldAnnounce = + COUNTDOWN_ANNOUNCEMENT_SECONDS.includes(remainingSeconds) || + remainingSeconds <= FINAL_COUNTDOWN_SECONDS + + if (shouldAnnounce && remainingSeconds !== lastAnnouncementRef.current) { + lastAnnouncementRef.current = remainingSeconds + const message = t('countdownAnnouncement', { + duration: humanizeDuration(remainingSeconds * 1000, { + language: i18n.language, + round: true, + }), + }) + setSrMessage(message) + + const timer = setTimeout(() => { + setSrMessage('') + }, 3000) + + return () => clearTimeout(timer) + } + }, [connectionObserverSnap.isIdleDisconnectModalOpen, remainingSeconds, t]) + return ( { color: 'blue.800', margin: 'auto', })} + aria-hidden="true" > {formattedTime} +
+ {srMessage} +
{t('title')} diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 1c685745..61522d74 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -156,7 +156,8 @@ "body": "Du bist der einzige Teilnehmer. Dieses Gespräch endet in {{duration}}. Möchtest du das Gespräch fortsetzen?", "settings": "Um diese Nachricht nicht mehr zu sehen, gehe zu Einstellungen > Allgemein.", "stayButton": "Gespräch fortsetzen", - "leaveButton": "Jetzt verlassen" + "leaveButton": "Jetzt verlassen", + "countdownAnnouncement": "Das Gespräch endet in {{duration}}." }, "controls": { "microphone": "Mikrofon", diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index 77d457ed..27cf2c85 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -156,7 +156,8 @@ "body": "You are the only participant. This call will end in {{duration}}. Would you like to continue the call?", "settings": "To stop seeing this message, go to Settings > General.", "stayButton": "Continue the call", - "leaveButton": "Leave now" + "leaveButton": "Leave now", + "countdownAnnouncement": "Call ends in {{duration}}." }, "controls": { "microphone": "Microphone", diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 2c6cca90..59acc640 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -156,7 +156,8 @@ "body": "Vous êtes le seul participant. Cet appel va donc se terminer dans {{duration}}. Voulez-vous poursuivre l'appel ?", "settings": "Pour ne plus voir ce message, accèder à Paramètres > Général.", "stayButton": "Poursuivre l'appel", - "leaveButton": "Quitter maintenant" + "leaveButton": "Quitter maintenant", + "countdownAnnouncement": "L'appel se termine dans {{duration}}." }, "controls": { "microphone": "Microphone", diff --git a/src/frontend/src/locales/nl/rooms.json b/src/frontend/src/locales/nl/rooms.json index 580f730d..7ae75cfb 100644 --- a/src/frontend/src/locales/nl/rooms.json +++ b/src/frontend/src/locales/nl/rooms.json @@ -156,7 +156,8 @@ "body": "Je bent de enige deelnemer. Dit gesprek wordt over {{duration}} beëindigd. Wil je het gesprek voortzetten?", "settings": "Om dit bericht niet meer te zien, ga naar Instellingen > Algemeen.", "stayButton": "Gesprek voortzetten", - "leaveButton": "Nu verlaten" + "leaveButton": "Nu verlaten", + "countdownAnnouncement": "Het gesprek eindigt over {{duration}}." }, "controls": { "microphone": "Microfoon",