♿️(frontend) adjust sr announcements for idle disconnect timer
reduces screen reader noise while keeping key countdown cues
This commit is contained in:
@@ -12,6 +12,10 @@ and this project adheres to
|
|||||||
|
|
||||||
- 🔒️(frontend) fix an XSS vulnerability on the recording page #911
|
- 🔒️(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
|
## [1.4.0] - 2026-01-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -4,16 +4,20 @@ import { css } from '@/styled-system/css'
|
|||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { connectionObserverStore } from '@/stores/connectionObserver'
|
import { connectionObserverStore } from '@/stores/connectionObserver'
|
||||||
import { HStack } from '@/styled-system/jsx'
|
import { HStack } from '@/styled-system/jsx'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { navigateTo } from '@/navigation/navigateTo'
|
import { navigateTo } from '@/navigation/navigateTo'
|
||||||
import humanizeDuration from 'humanize-duration'
|
import humanizeDuration from 'humanize-duration'
|
||||||
import i18n from 'i18next'
|
import i18n from 'i18next'
|
||||||
|
|
||||||
const IDLE_DISCONNECT_TIMEOUT_MS = 120000 // 2 minutes
|
const IDLE_DISCONNECT_TIMEOUT_MS = 120000 // 2 minutes
|
||||||
|
const COUNTDOWN_ANNOUNCEMENT_SECONDS = [120, 90, 60, 30]
|
||||||
|
const FINAL_COUNTDOWN_SECONDS = 10
|
||||||
|
|
||||||
export const IsIdleDisconnectModal = () => {
|
export const IsIdleDisconnectModal = () => {
|
||||||
const connectionObserverSnap = useSnapshot(connectionObserverStore)
|
const connectionObserverSnap = useSnapshot(connectionObserverStore)
|
||||||
const [timeRemaining, setTimeRemaining] = useState(IDLE_DISCONNECT_TIMEOUT_MS)
|
const [timeRemaining, setTimeRemaining] = useState(IDLE_DISCONNECT_TIMEOUT_MS)
|
||||||
|
const [srMessage, setSrMessage] = useState('')
|
||||||
|
const lastAnnouncementRef = useRef<number | null>(null)
|
||||||
|
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'isIdleDisconnectModal' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'isIdleDisconnectModal' })
|
||||||
|
|
||||||
@@ -35,10 +39,43 @@ export const IsIdleDisconnectModal = () => {
|
|||||||
}
|
}
|
||||||
}, [connectionObserverSnap.isIdleDisconnectModalOpen])
|
}, [connectionObserverSnap.isIdleDisconnectModalOpen])
|
||||||
|
|
||||||
const minutes = Math.floor(timeRemaining / 1000 / 60)
|
useEffect(() => {
|
||||||
const seconds = (timeRemaining / 1000) % 60
|
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')}`
|
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 (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
isOpen={connectionObserverSnap.isIdleDisconnectModalOpen}
|
isOpen={connectionObserverSnap.isIdleDisconnectModalOpen}
|
||||||
@@ -65,9 +102,13 @@ export const IsIdleDisconnectModal = () => {
|
|||||||
color: 'blue.800',
|
color: 'blue.800',
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
})}
|
})}
|
||||||
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
{formattedTime}
|
{formattedTime}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="sr-only" aria-live="polite" aria-atomic="true">
|
||||||
|
{srMessage}
|
||||||
|
</div>
|
||||||
<H lvl={2} centered>
|
<H lvl={2} centered>
|
||||||
{t('title')}
|
{t('title')}
|
||||||
</H>
|
</H>
|
||||||
|
|||||||
@@ -156,7 +156,8 @@
|
|||||||
"body": "Du bist der einzige Teilnehmer. Dieses Gespräch endet in {{duration}}. Möchtest du das Gespräch fortsetzen?",
|
"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.",
|
"settings": "Um diese Nachricht nicht mehr zu sehen, gehe zu Einstellungen > Allgemein.",
|
||||||
"stayButton": "Gespräch fortsetzen",
|
"stayButton": "Gespräch fortsetzen",
|
||||||
"leaveButton": "Jetzt verlassen"
|
"leaveButton": "Jetzt verlassen",
|
||||||
|
"countdownAnnouncement": "Das Gespräch endet in {{duration}}."
|
||||||
},
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
"microphone": "Mikrofon",
|
"microphone": "Mikrofon",
|
||||||
|
|||||||
@@ -156,7 +156,8 @@
|
|||||||
"body": "You are the only participant. This call will end in {{duration}}. Would you like to continue the call?",
|
"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.",
|
"settings": "To stop seeing this message, go to Settings > General.",
|
||||||
"stayButton": "Continue the call",
|
"stayButton": "Continue the call",
|
||||||
"leaveButton": "Leave now"
|
"leaveButton": "Leave now",
|
||||||
|
"countdownAnnouncement": "Call ends in {{duration}}."
|
||||||
},
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
"microphone": "Microphone",
|
"microphone": "Microphone",
|
||||||
|
|||||||
@@ -156,7 +156,8 @@
|
|||||||
"body": "Vous êtes le seul participant. Cet appel va donc se terminer dans {{duration}}. Voulez-vous poursuivre l'appel ?",
|
"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.",
|
"settings": "Pour ne plus voir ce message, accèder à Paramètres > Général.",
|
||||||
"stayButton": "Poursuivre l'appel",
|
"stayButton": "Poursuivre l'appel",
|
||||||
"leaveButton": "Quitter maintenant"
|
"leaveButton": "Quitter maintenant",
|
||||||
|
"countdownAnnouncement": "L'appel se termine dans {{duration}}."
|
||||||
},
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
"microphone": "Microphone",
|
"microphone": "Microphone",
|
||||||
|
|||||||
@@ -156,7 +156,8 @@
|
|||||||
"body": "Je bent de enige deelnemer. Dit gesprek wordt over {{duration}} beëindigd. Wil je het gesprek voortzetten?",
|
"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.",
|
"settings": "Om dit bericht niet meer te zien, ga naar Instellingen > Algemeen.",
|
||||||
"stayButton": "Gesprek voortzetten",
|
"stayButton": "Gesprek voortzetten",
|
||||||
"leaveButton": "Nu verlaten"
|
"leaveButton": "Nu verlaten",
|
||||||
|
"countdownAnnouncement": "Het gesprek eindigt over {{duration}}."
|
||||||
},
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
"microphone": "Microfoon",
|
"microphone": "Microfoon",
|
||||||
|
|||||||
Reference in New Issue
Block a user