Skip to content

Commit 93a65ac

Browse files
authored
Merge pull request Expensify#85742 from Expensify/claude-timerCountdownAccessibility
Announce timer countdown for screen readers
2 parents 5f7e6bc + b9108e5 commit 93a65ac

11 files changed

Lines changed: 33 additions & 0 deletions

File tree

src/components/ValidateCodeCountdown/index.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {useEffect, useImperativeHandle, useRef, useState} from 'react';
22
import RenderHTML from '@components/RenderHTML';
3+
import useAccessibilityAnnouncement from '@hooks/useAccessibilityAnnouncement';
34
import useLocalize from '@hooks/useLocalize';
45
import CONST from '@src/CONST';
56
import type {ValidateCodeCountdownProps} from './types';
@@ -27,6 +28,18 @@ function ValidateCodeCountdown({onCountdownFinish, ref}: ValidateCodeCountdownPr
2728
};
2829
}, [onCountdownFinish, timeRemaining]);
2930

31+
// Announce countdown start/reset/expiration for screen readers.
32+
// We check timeRemaining === 1 (not 0) because the component unmounts immediately at 0s, so the expired announcement wouldn't be spoken.
33+
// We use timeRemaining % 10 === 1 to announce every 10 seconds (at 21s, 11s, 1s) to avoid overwhelming screen reader users.
34+
useAccessibilityAnnouncement(
35+
timeRemaining === 1 ? translate('validateCodeForm.timeExpiredAnnouncement') : translate('validateCodeForm.timeRemainingAnnouncement', {timeRemaining: timeRemaining - 1}),
36+
timeRemaining % 10 === 1,
37+
{
38+
shouldAnnounceOnNative: true,
39+
shouldAnnounceOnWeb: true,
40+
},
41+
);
42+
3043
return (
3144
<RenderHTML
3245
html={translate('validateCodeForm.requestNewCode', {

src/languages/de.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2683,6 +2683,8 @@ ${amount} für ${merchant} – ${date}`,
26832683
requiredWhen2FAEnabled: 'Erforderlich, wenn 2FA aktiviert ist',
26842684
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Fordere einen neuen Code an in <a>${timeRemaining}</a>`,
26852685
requestNewCodeAfterErrorOccurred: 'Neuen Code anfordern',
2686+
timeRemainingAnnouncement: ({timeRemaining}) => `Verbleibende Zeit: ${timeRemaining} ${timeRemaining === 1 ? 'Sekunde' : 'Sekunden'}`,
2687+
timeExpiredAnnouncement: 'Die Zeit ist abgelaufen',
26862688
error: {
26872689
pleaseFillMagicCode: 'Bitte gib deinen Magic Code ein',
26882690
incorrectMagicCode: 'Falscher oder ungültiger Magic-Code. Bitte versuche es erneut oder fordere einen neuen Code an.',

src/languages/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,6 +2731,8 @@ const translations = {
27312731
requiredWhen2FAEnabled: 'Required when 2FA is enabled',
27322732
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Request a new code in <a>${timeRemaining}</a>`,
27332733
requestNewCodeAfterErrorOccurred: 'Request a new code',
2734+
timeRemainingAnnouncement: ({timeRemaining}: {timeRemaining: number}) => `Time remaining: ${timeRemaining} ${timeRemaining === 1 ? 'second' : 'seconds'}`,
2735+
timeExpiredAnnouncement: 'The time has expired',
27342736
error: {
27352737
pleaseFillMagicCode: 'Please enter your magic code',
27362738
incorrectMagicCode: 'Incorrect or invalid magic code. Please try again or request a new code.',

src/languages/es.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,6 +2591,8 @@ ${amount} para ${merchant} - ${date}`,
25912591
requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado',
25922592
requestNewCode: ({timeRemaining}) => `Pedir un código nuevo en <a>${timeRemaining}</a>`,
25932593
requestNewCodeAfterErrorOccurred: 'Solicitar un nuevo código',
2594+
timeRemainingAnnouncement: ({timeRemaining}) => `Tiempo restante: ${timeRemaining} ${timeRemaining === 1 ? 'segundo' : 'segundos'}`,
2595+
timeExpiredAnnouncement: 'El tiempo ha expirado',
25942596
error: {
25952597
pleaseFillMagicCode: 'Por favor, introduce el código mágico.',
25962598
incorrectMagicCode: 'Código mágico incorrecto o no válido. Inténtalo de nuevo o solicita otro código.',

src/languages/fr.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,8 @@ ${amount} pour ${merchant} - ${date}`,
26902690
requiredWhen2FAEnabled: 'Obligatoire lorsque l’authentification à deux facteurs est activée',
26912691
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Demander un nouveau code dans <a>${timeRemaining}</a>`,
26922692
requestNewCodeAfterErrorOccurred: 'Demander un nouveau code',
2693+
timeRemainingAnnouncement: ({timeRemaining}) => `Temps restant : ${timeRemaining} ${timeRemaining === 1 ? 'seconde' : 'secondes'}`,
2694+
timeExpiredAnnouncement: 'Le temps est écoulé',
26932695
error: {
26942696
pleaseFillMagicCode: 'Veuillez saisir votre code magique',
26952697
incorrectMagicCode: 'Code magique incorrect ou non valide. Veuillez réessayer ou demander un nouveau code.',

src/languages/it.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2679,6 +2679,8 @@ ${amount} per ${merchant} - ${date}`,
26792679
requiredWhen2FAEnabled: 'Obbligatorio quando l’autenticazione a due fattori è abilitata',
26802680
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Richiedi un nuovo codice tra <a>${timeRemaining}</a>`,
26812681
requestNewCodeAfterErrorOccurred: 'Richiedi un nuovo codice',
2682+
timeRemainingAnnouncement: ({timeRemaining}) => `Tempo rimanente: ${timeRemaining} ${timeRemaining === 1 ? 'secondo' : 'secondi'}`,
2683+
timeExpiredAnnouncement: 'Il tempo è scaduto',
26822684
error: {
26832685
pleaseFillMagicCode: 'Inserisci il tuo codice magico',
26842686
incorrectMagicCode: 'Codice magico errato o non valido. Riprova o richiedi un nuovo codice.',

src/languages/ja.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,6 +2652,8 @@ ${date} の ${merchant} への ${amount}`,
26522652
requiredWhen2FAEnabled: '2要素認証が有効な場合は必須',
26532653
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `<a>${timeRemaining}</a> 後に新しいコードをリクエストする`,
26542654
requestNewCodeAfterErrorOccurred: '新しいコードをリクエスト',
2655+
timeRemainingAnnouncement: ({timeRemaining}) => `残り時間: ${timeRemaining}秒`,
2656+
timeExpiredAnnouncement: '時間切れです',
26552657
error: {
26562658
pleaseFillMagicCode: 'マジックコードを入力してください',
26572659
incorrectMagicCode: '魔法コードが間違っているか無効です。もう一度お試しいただくか、新しいコードをリクエストしてください。',

src/languages/nl.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,8 @@ ${amount} voor ${merchant} - ${date}`,
26772677
requiredWhen2FAEnabled: 'Vereist wanneer 2FA is ingeschakeld',
26782678
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Vraag een nieuwe code aan over <a>${timeRemaining}</a>`,
26792679
requestNewCodeAfterErrorOccurred: 'Een nieuwe code aanvragen',
2680+
timeRemainingAnnouncement: ({timeRemaining}) => `Resterende tijd: ${timeRemaining} ${timeRemaining === 1 ? 'seconde' : 'seconden'}`,
2681+
timeExpiredAnnouncement: 'De tijd is verstreken',
26802682
error: {
26812683
pleaseFillMagicCode: 'Voer je magische code in',
26822684
incorrectMagicCode: 'Onjuiste of ongeldige magische code. Probeer het opnieuw of vraag een nieuwe code aan.',

src/languages/pl.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2671,6 +2671,8 @@ ${amount} dla ${merchant} - ${date}`,
26712671
requiredWhen2FAEnabled: 'Wymagane, gdy włączone jest 2FA',
26722672
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Poproś o nowy kod za <a>${timeRemaining}</a>`,
26732673
requestNewCodeAfterErrorOccurred: 'Poproś o nowy kod',
2674+
timeRemainingAnnouncement: ({timeRemaining}) => `Pozostały czas: ${timeRemaining} ${timeRemaining === 1 ? 'sekunda' : 'sekund'}`,
2675+
timeExpiredAnnouncement: 'Czas minął',
26742676
error: {
26752677
pleaseFillMagicCode: 'Wprowadź swój magiczny kod',
26762678
incorrectMagicCode: 'Nieprawidłowy lub niepoprawny kod magiczny. Spróbuj ponownie lub poproś o nowy kod.',

src/languages/pt-BR.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,6 +2670,8 @@ ${amount} para ${merchant} - ${date}`,
26702670
requiredWhen2FAEnabled: 'Obrigatório quando a 2FA estiver ativada',
26712671
requestNewCode: ({timeRemaining}: {timeRemaining: string}) => `Solicite um novo código em <a>${timeRemaining}</a>`,
26722672
requestNewCodeAfterErrorOccurred: 'Solicitar um novo código',
2673+
timeRemainingAnnouncement: ({timeRemaining}) => `Tempo restante: ${timeRemaining} ${timeRemaining === 1 ? 'segundo' : 'segundos'}`,
2674+
timeExpiredAnnouncement: 'O tempo expirou',
26732675
error: {
26742676
pleaseFillMagicCode: 'Insira seu código mágico',
26752677
incorrectMagicCode: 'Código mágico incorreto ou inválido. Tente novamente ou solicite um novo código.',

0 commit comments

Comments
 (0)