Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c65e3b8
docs: voip accessibility design spec and a11y conventions
diegolmello Apr 6, 2026
2c88a4e
docs: voip accessibility implementation plan
diegolmello Apr 6, 2026
0d20125
docs: update i18n task to use translation agent
diegolmello Apr 6, 2026
dce5b24
feat(a11y): add useIsScreenReaderEnabled hook
diegolmello Apr 6, 2026
22e6f14
feat(a11y): add Toggle_call_controls i18n key to all locales
diegolmello Apr 6, 2026
112591b
feat(a11y): hide call buttons from screen reader when controls are no…
diegolmello Apr 6, 2026
7722f5c
feat(a11y): add accessibility label and role to DialpadButton
diegolmello Apr 6, 2026
1d51a0c
feat(a11y): add accessibility label and role to PeerItem
diegolmello Apr 6, 2026
d2f7a0b
feat(a11y): keep call controls visible when screen reader is active
diegolmello Apr 6, 2026
ac681a4
feat(a11y): disable tap-to-hide controls when screen reader is active
diegolmello Apr 6, 2026
3922ca6
feat(a11y): landscape layout for CallView
diegolmello Apr 6, 2026
472ea40
docs: update accessibility spec, plan, and conventions
diegolmello Apr 6, 2026
9f5e774
chore: format code and fix lint issues
diegolmello Apr 6, 2026
4b0a087
chore: format code and fix lint issues
diegolmello Apr 6, 2026
e7bcfee
ignore worktrees
diegolmello Apr 6, 2026
7822932
test(voip): fix failing unit tests for CallView and CallButtons
diegolmello Apr 7, 2026
cc7f97b
Fix briding header
diegolmello Apr 7, 2026
93722b0
fix(a11y): remove debug style and fix screen reader hook race condition
diegolmello Apr 8, 2026
adfe401
Fix conflicts
diegolmello Apr 9, 2026
19ab42a
action: organized translations
diegolmello Apr 9, 2026
ea09f69
Merge branch 'feat.voip-lib-new' into feat.voip-a11y
diegolmello Apr 9, 2026
be1ffd0
fix(voip): address PR review comments for a11y
diegolmello Apr 9, 2026
8cd5b3a
chore: remove superpowers docs from PR
diegolmello Apr 9, 2026
a37efcc
Fix lint
diegolmello Apr 9, 2026
4067d15
fix: podfile (#7122)
OtavioStasiak Apr 10, 2026
37f86e5
Merge branch 'feat.voip-lib-new' into feat.voip-a11y
diegolmello Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ e2e/e2e_account.ts
skills-lock.json
CLAUDE.local.md
AGENTS.md
.superset/
.superset/
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Rocket.Chat React Native mobile client. Single-package React Native app (not a m
- React 19.1, React Native 0.81, Expo 54
- TypeScript with strict mode, baseUrl set to `app/` (imports resolve from there)
- Node: engines `>=18`, volta pins 24.13.1
- Read UBIQUITOUS_LANGUAGE.md

## Commands

Expand Down
2 changes: 2 additions & 0 deletions app/containers/NewMediaCall/PeerItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const PeerItem = ({ item, onSelectOption }: { item: TPeerItem; onSelectOp
{ backgroundColor: pressed && isIOS ? colors.surfaceSelected : colors.surfaceLight }
]}
onPress={() => onSelectOption(item)}
accessibilityLabel={item.label}
accessibilityRole='button'
testID={`new-media-call-option-${item.value}`}
android_ripple={{ color: colors.surfaceSelected }}>
<PeerItemInner item={item} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
User
</Text>
<View
accessibilityLabel="Alice Johnson"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down Expand Up @@ -215,6 +217,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
SIP
</Text>
<View
accessibilityLabel="+55 11 99999-9999"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down Expand Up @@ -347,6 +351,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
Long label
</Text>
<View
accessibilityLabel="Long display name to validate text truncation behavior for PeerItem in narrow widths"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
style={null}
>
<View
accessibilityLabel="+55 11 98888-7777"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down Expand Up @@ -214,6 +216,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
style={null}
>
<View
accessibilityLabel="Alice Johnson"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down Expand Up @@ -417,6 +421,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
style={null}
>
<View
accessibilityLabel="Long display name to validate text truncation behavior for PeerItem in narrow widths"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down Expand Up @@ -620,6 +626,8 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
style={null}
>
<View
accessibilityLabel="Bob Smith"
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@
"Threads_unread": "سلاسل المحادثات، {{unread}} غير مقروءة",
"Timezone": "المنطقة الزمنية",
"To_download": "للتحميل",
"Toggle_call_controls": "تبديل عناصر التحكم في المكالمة",
"Topic": "عنوان",
"topic": "عنوان",
"Translate": "ترجمة",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/bn-IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@
"Threads_unread": "থ্রেড, {{unread}} অপঠিত",
"Timezone": "সময় অঞ্চল",
"To_download": "ডাউনলোড করতে",
"Toggle_call_controls": "কল নিয়ন্ত্রণ টগল করুন",
"Token_expired": "আপনার সেশনের মেয়াদ শেষ হয়েছে। দয়া করে আবার লগ ইন করুন।",
"Topic": "বিষয়",
"topic": "বিষয়",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,7 @@
"Threads_unread": "Vlákna, {{unread}} nepřečtené",
"Timezone": "Časové pásmo",
"To_download": "Stáhnout",
"Toggle_call_controls": "Přepnout ovládání volání",
"Token_expired": "Vaše relace vypršela. Přihlaste se prosím znovu.",
"Topic": "Téma",
"topic": "téma",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@
"Threads_unread": "Threads, {{unread}} ungelesen",
"Timezone": "Zeitzone",
"To_download": "Herunterladen",
"Toggle_call_controls": "Anrufsteuerung umschalten",
"Token_expired": "Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.",
"Topic": "Thema",
"topic": "Thema",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,7 @@
"Timezone": "Timezone",
"To_continue_using_RocketChat": "To continue using the mobile app, you need to change your password.",
"To_download": "To download",
"Toggle_call_controls": "Toggle call controls",
"Token_expired": "Your session has expired. Please log in again.",
"Topic": "Topic",
"topic": "topic",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@
"Threads_unread": "Hilos, {{unread}} sin leer",
"Timezone": "Zona horaria",
"To_download": "Descargar",
"Toggle_call_controls": "Alternar controles de llamada",
"Topic": "Asunto",
"topic": "asunto",
"Translate": "Traducir",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@
"Threads_unread": "Ketjut, {{unread}} lukematonta",
"Timezone": "Aikavyöhyke",
"To_download": "Ladataaksesi",
"Toggle_call_controls": "Vaihda puhelun hallintoja",
Comment thread
diegolmello marked this conversation as resolved.
"Token_expired": "Istuntosi on vanhentunut. Kirjaudu uudelleen.",
"Topic": "Aihe",
"topic": "aihe",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@
"Threads_unread": "Discussions, {{unread}} non lues",
"Timezone": "Fuseau horaire",
"To_download": "Télécharger",
"Toggle_call_controls": "Basculer les contrôles d'appel",
"Token_expired": "Votre session a expiré. Veuillez vous reconnecter.",
"Topic": "Sujet",
"topic": "sujet",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/hi-IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@
"Threads_unread": "थ्रेड्स, {{unread}} अपठित",
"Timezone": "समय क्षेत्र",
"To_download": "डाउनलोड करने के लिए",
"Toggle_call_controls": "कॉल नियंत्रण टॉगल करें",
"Token_expired": "आपका सत्र समाप्त हो गया है। कृपया पुनः लॉग इन करें।",
"Topic": "विषय",
"topic": "विषय",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,7 @@
"Threads_unread": "Fonalak, {{unread}} olvasatlan",
"Timezone": "Időzóna",
"To_download": "Letöltéshez",
"Toggle_call_controls": "Hívásváltási vezérlők",
Comment thread
diegolmello marked this conversation as resolved.
"Token_expired": "Az Ön munkamenete lejárt. Kérjük, jelentkezzen be újra.",
"Topic": "Téma",
"topic": "téma",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@
"Threads_unread": "Discussioni, {{unread}} non lette",
"Timezone": "Fuso orario",
"To_download": "Scaricare",
"Toggle_call_controls": "Attiva/disattiva i controlli delle chiamate",
"Topic": "Argomento",
"topic": "argomento",
"Translate": "Traduci",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@
"Threads_unread": "スレッド、{{unread}} 未読",
"Timezone": "タイムゾーン",
"To_download": "ダウンロードする",
"Toggle_call_controls": "通話コントロールを切り替える",
"Topic": "トピック",
"topic": "トピック",
"Translate": "翻訳",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@
"Threads_unread": "Threads, {{unread}} ongelezen",
"Timezone": "Tijdzone",
"To_download": "Downloaden",
"Toggle_call_controls": "Gesprekbesturingselementen in-/uitschakelen",
"Token_expired": "Uw sessie is verlopen. Gelieve opnieuw in te loggen.",
"Topic": "Onderwerp",
"topic": "onderwerp",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/nn.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@
"This_room_is_read_only": "Dette rommet er kun skrivebeskyttet",
"Threads": "Tråder",
"Timezone": "Tidssone",
"Toggle_call_controls": "Slå samtalekontrollar på/av",
"Topic": "Emne",
"totp-invalid": "Kode eller passord er ugyldig",
"Translate": "Oversett",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/no.json
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@
"Threads": "Tråder",
"Timezone": "Tidssone",
"To_continue_using_RocketChat": "For å fortsette å bruke mobilappen må du endre passordet ditt.",
"Toggle_call_controls": "Slå samtalekontroller av/på",
"Token_expired": "Økten din er utløpt. Vennligst logg inn på nytt.",
"Topic": "Emne",
"topic": "emne",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,7 @@
"Timezone": "Fuso horário",
"To_continue_using_RocketChat": "Para continuar usando o aplicativo móvel, você precisa alterar sua senha.",
"To_download": "Para baixar",
"Toggle_call_controls": "Alternar controles de chamada",
"Token_expired": "Sua sessão expirou. Por favor entre novamente.",
"Topic": "Tópico",
"topic": "tópico",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/pt-PT.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@
"Threads_unread": "Threads, {{unread}} por ler",
"Timezone": "Fuso Horário",
"To_download": "Para descarregar",
"Toggle_call_controls": "Alternar controles de chamada",
Comment thread
diegolmello marked this conversation as resolved.
"Topic": "Tópico",
"topic": "tópico",
"Travel_and_places": "Viagens e locais",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@
"Threads_unread": "Темы, {{unread}} непрочитанных",
"Timezone": "Часовой пояс",
"To_download": "Скачать",
"Toggle_call_controls": "Переключить элементы управления вызовом",
"Token_expired": "Срок действия вашей сессии истек. Пожалуйста войдите снова.",
"Topic": "Тема",
"topic": "тема",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/sl-SI.json
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@
"Threads_unread": "Nit, {{unread}} neprebranih",
"Timezone": "Časovni pas",
"To_download": "Prenesi",
"Toggle_call_controls": "Preklopi upravljalce klicanja",
"Token_expired": "Vaša seja je potekla. Prosimo, prijavite se še enkrat.",
"Topic": "Tema",
"topic": "tema",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@
"Threads_unread": "Trådar, {{unread}} olästa",
"Timezone": "Tidszon",
"To_download": "Ladda ner",
"Toggle_call_controls": "Slå samtalskontroller på/av",
"Token_expired": "Din session har upphört. Logga in igen.",
"Topic": "Ämne",
"topic": "ämne",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/ta-IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@
"Threads_unread": "தோர்களில், {{unread}} படிக்காதவை",
"Timezone": "நேரமான முனை",
"To_download": "பதிவிறக்குவதற்கு",
"Toggle_call_controls": "அழைப்பு கட்டுப்பாடுகளை மாற்றவும்",
"Token_expired": "உங்கள் அமர்விதிகள் காலாவதியாகின்றது. தயவுசெய்து மீண்டும் உள்நுழைக.",
"Topic": "தலைப்பு",
"topic": "தலைப்பு",
Expand Down
3 changes: 0 additions & 3 deletions app/i18n/locales/te-IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,6 @@
"Permalink_copied_to_clipboard": "పర్మాలింక్ కోపీ అయ్యింది!",
"Person_or_channel": "వ్యక్తి లేదా చానల్",
"Phone": "ఫోన్",
"Phone_state_permission_message": "మీరు ఇప్పటికే ఫోన్ లేదా VoIP కాల్‌లో ఉన్నారో లేదో Rocket.Chat గుర్తించడానికి ఇది సహాయపడుతుంది, తద్వారా ఇన్‌కమింగ్ కాల్‌లు సరిగ్గా నిర్వహించబడతాయి.",
"Phone_state_permission_title": "ఫోన్ స్థితి యాక్సెస్‌ను అనుమతించండి",
"Pin": "పిన్",
"Pinned": "పిన్ చేసినది",
"Pinned_a_message": "ఒక సందేశాన్ని పించింది:",
Expand Down Expand Up @@ -879,7 +877,6 @@
"video-conf-provider-not-configured-header": "కాన్ఫరెన్స్ కాల్ అనేకంగా లేదు",
"View_Original": "అసలు చూడండి",
"View_Thread": "థ్రెడ్‌ను వీక్షించండి",
"VoIP_Call_Issue": "కాల్‌లో సమస్య ఉంది, తర్వాత మళ్లీ ప్రయత్నించండి.",
"Wait_activation_warning": "మీరు లాగిన్ చేయడానికి మొదటి స్థాయింలో మీ ఖాతాను ఒక అడ్మినిస్ట్రేటర్ మానవారం ప్రత్యామ్నాయం చేయాలి.",
"Waiting_for_answer": "జవాబు కోసం ఎదురు ఉంది",
"Waiting_for_network": "నెట్వర్క్ కోసం వేచి ఉండి...",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@
"Threads_unread": "Konular, {{unread}} okunmamış",
"Timezone": "Saat dilimi",
"To_download": "Indirmek için",
"Toggle_call_controls": "Arama denetimlerini aç/kapat",
"Topic": "Konu",
"topic": "konu",
"Translate": "Çevir",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@
"Threads_unread": "主题,{{unread}} 未读",
"Timezone": "时区",
"To_download": "下载",
"Toggle_call_controls": "切换通话控制",
"Topic": "主题",
"topic": "主题",
"Translate": "翻译",
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@
"Threads_unread": "討論串,{{unread}} 未讀訊息",
"Timezone": "時區",
"To_download": "下載",
"Toggle_call_controls": "切換通話控制",
"Topic": "主題",
"topic": "主題",
"Translate": "翻譯",
Expand Down
53 changes: 53 additions & 0 deletions app/lib/hooks/useIsScreenReaderEnabled.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { renderHook, act } from '@testing-library/react-native';
import { AccessibilityInfo } from 'react-native';

import { useIsScreenReaderEnabled } from './useIsScreenReaderEnabled';

describe('useIsScreenReaderEnabled', () => {
beforeEach(() => {
jest.spyOn(AccessibilityInfo, 'isScreenReaderEnabled').mockResolvedValue(false);
jest.spyOn(AccessibilityInfo, 'addEventListener').mockReturnValue({ remove: jest.fn() } as any);
});

afterEach(() => {
jest.restoreAllMocks();
});

it('returns false initially', () => {
const { result } = renderHook(() => useIsScreenReaderEnabled());
expect(result.current).toBe(false);
});

it('returns true after isScreenReaderEnabled resolves true', async () => {
jest.spyOn(AccessibilityInfo, 'isScreenReaderEnabled').mockResolvedValue(true);
const { result } = renderHook(() => useIsScreenReaderEnabled());
await act(async () => {});
expect(result.current).toBe(true);
});

it('updates when screenReaderChanged event fires', () => {
let capturedListener: (enabled: boolean) => void = () => {};
jest.spyOn(AccessibilityInfo, 'addEventListener').mockImplementation((_event, cb) => {
capturedListener = cb as unknown as (enabled: boolean) => void;
return { remove: jest.fn() } as any;
});

const { result } = renderHook(() => useIsScreenReaderEnabled());

act(() => {
capturedListener(true);
});

expect(result.current).toBe(true);
});

it('removes the event listener on unmount', () => {
const removeMock = jest.fn();
jest.spyOn(AccessibilityInfo, 'addEventListener').mockReturnValue({ remove: removeMock } as any);

const { unmount } = renderHook(() => useIsScreenReaderEnabled());
unmount();

expect(removeMock).toHaveBeenCalled();
});
});
26 changes: 26 additions & 0 deletions app/lib/hooks/useIsScreenReaderEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useState } from 'react';
import { AccessibilityInfo } from 'react-native';

export const useIsScreenReaderEnabled = (): boolean => {
const [enabled, setEnabled] = useState(false);

useEffect(() => {
let ignore = false;
AccessibilityInfo.isScreenReaderEnabled().then(result => {
if (!ignore) {
setEnabled(result);
}
});
const subscription = AccessibilityInfo.addEventListener('screenReaderChanged', result => {
if (!ignore) {
setEnabled(result);
}
});
return () => {
ignore = true;
subscription.remove();
};
}, []);

return enabled;
};
7 changes: 6 additions & 1 deletion app/lib/services/voip/useCallStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import InCallManager from 'react-native-incall-manager';

import Navigation from '../../navigation/appNavigation';
import { hideActionSheetRef } from '../../../containers/ActionSheet';
import { useIsScreenReaderEnabled } from '../../hooks/useIsScreenReaderEnabled';

const STALE_NATIVE_MS = 15_000;

Expand Down Expand Up @@ -307,4 +308,8 @@ export const useCallState = () => {

export const useCallContact = () => useCallStore(state => state.contact);
export const useDialpadValue = () => useCallStore(state => state.dialpadValue);
export const useControlsVisible = () => useCallStore(state => state.controlsVisible);
export const useControlsVisible = () => {
const controlsVisible = useCallStore(state => state.controlsVisible);
const isScreenReaderEnabled = useIsScreenReaderEnabled();
return controlsVisible || isScreenReaderEnabled;
};
Loading
Loading