feat(voip): tablet and landscape layout#7110
Conversation
Fixes transitive import crash in tests that import useCallStore (useCallStore → ActionSheet → TrueSheet native module).
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds responsive tablet ("wide") layout support for CallView, a mock call dev utility and navigation route, refactors Dialpad and CallButtons for layout-aware rendering, updates styles and Storybook stories, and removes a Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Mock as MockCall
participant Store as CallStore
participant Nav as Navigation
participant View as CallView
participant Buttons as CallButtons
Dev->>Mock: call launchMockCallView(overrides)
Mock->>Store: seedMockCall(overrides) — set call, flags, start time
Mock->>Nav: Navigation.navigate("CallView")
Nav->>View: mount CallView
View->>Store: read call & state
View->>View: useCallLayoutMode() -> layoutMode
View->>Buttons: render CallButtons(layoutMode)
Buttons->>Buttons: render single row (wide) or two rows (narrow)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/views/CallView/components/CallButtons.tsx (1)
13-15: Reduce reverse dependency by movingLayoutModeto a shared local type module.
CallButtonsimporting a type from its parent screen (..) couples component and screen layers. Atypes.tsinapp/views/CallView/(orcomponents/) would keep boundaries cleaner.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/CallView/components/CallButtons.tsx` around lines 13 - 15, Move the LayoutMode type out of the parent screen and into a shared local types module to avoid the reverse dependency: create a types.ts (or index types file) in app/views/CallView/ (or components/) exporting LayoutMode, update the CallButtons component to import LayoutMode from that new module (the component is CallButtons), and update the original parent screen and any other consumers to import LayoutMode from the new shared types file instead of '..' so the screen no longer depends on the child component.app/views/CallView/components/Dialpad/Dialpad.test.tsx (1)
13-16: MockuseWindowDimensionsfrom the publicreact-nativemodule instead of the internal path.React Native 0.79 deprecated deep imports into
Libraries/...paths. The stable public API surface is the rootreact-nativemodule. Change the mock from'react-native/Libraries/Utilities/useWindowDimensions'to'react-native'to ensure compatibility across React Native version upgrades.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/CallView/components/Dialpad/Dialpad.test.tsx` around lines 13 - 16, The test currently mocks the internal path 'react-native/Libraries/Utilities/useWindowDimensions'; change the jest.mock call to mock the public 'react-native' module instead and provide a useWindowDimensions export that returns { width: mockWindowWidth, height: mockWindowHeight, scale: 1, fontScale: 1 }; update the jest.mock invocation that references useWindowDimensions, mockWindowWidth and mockWindowHeight so the mock is attached to the root 'react-native' export (optionally preserving other exports via jest.requireActual if needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/lib/services/voip/mockCall.ts`:
- Around line 20-25: The default mock contact currently sets sipExtension:
'2244' which, without an explicit roomId, disables the Message action; update
the mock defaults so Message is enabled by either clearing sipExtension (set to
'') or adding a default roomId (e.g., 'mock-room') in the DEFAULT_CONTACT object
and mirror the same change in the other default mock state block (the
DEFAULT_STATE / similar defaults around lines 65-76) so the dev helper UI
includes the Message path by default.
---
Nitpick comments:
In `@app/views/CallView/components/CallButtons.tsx`:
- Around line 13-15: Move the LayoutMode type out of the parent screen and into
a shared local types module to avoid the reverse dependency: create a types.ts
(or index types file) in app/views/CallView/ (or components/) exporting
LayoutMode, update the CallButtons component to import LayoutMode from that new
module (the component is CallButtons), and update the original parent screen and
any other consumers to import LayoutMode from the new shared types file instead
of '..' so the screen no longer depends on the child component.
In `@app/views/CallView/components/Dialpad/Dialpad.test.tsx`:
- Around line 13-16: The test currently mocks the internal path
'react-native/Libraries/Utilities/useWindowDimensions'; change the jest.mock
call to mock the public 'react-native' module instead and provide a
useWindowDimensions export that returns { width: mockWindowWidth, height:
mockWindowHeight, scale: 1, fontScale: 1 }; update the jest.mock invocation that
references useWindowDimensions, mockWindowWidth and mockWindowHeight so the mock
is attached to the root 'react-native' export (optionally preserving other
exports via jest.requireActual if needed).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 408c51d1-5456-466b-805e-80a261a3f610
⛔ Files ignored due to path filters (14)
app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snapis excluded by!**/*.snapapp/containers/NewMediaCall/__snapshots__/FilterHeader.test.tsx.snapis excluded by!**/*.snapapp/containers/NewMediaCall/__snapshots__/PeerItem.test.tsx.snapis excluded by!**/*.snapapp/containers/NewMediaCall/__snapshots__/PeerList.test.tsx.snapis excluded by!**/*.snapapp/containers/NewMediaCall/__snapshots__/SelectedPeer.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/__snapshots__/index.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/components/Dialpad/__snapshots__/Dialpad.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/components/__snapshots__/CallerInfo.test.tsx.snapis excluded by!**/*.snapapp/views/DiscussionsView/__snapshots__/Item.test.tsx.snapis excluded by!**/*.snapapp/views/NewMessageView/__snapshots__/Item.test.tsx.snapis excluded by!**/*.snapapp/views/RoomInfoView/components/__snapshots__/RoomInfoButtons.test.tsx.snapis excluded by!**/*.snapapp/views/RoomView/LoadMore/__snapshots__/LoadMore.test.tsx.snapis excluded by!**/*.snapapp/views/ThreadMessagesView/__snapshots__/Item.test.tsx.snapis excluded by!**/*.snapyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (17)
app/containers/NewMediaCall/FilterHeader.tsxapp/lib/services/voip/mockCall.tsapp/sagas/deepLinking.jsapp/stacks/MasterDetailStack/index.tsxapp/stacks/MasterDetailStack/types.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/CallButtons.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/Dialpad/styles.tsapp/views/CallView/index.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/RoomsListView/components/ListHeader.tsxindex.jsjest.config.jsjest.setup.js
💤 Files with no reviewable changes (1)
- app/containers/NewMediaCall/FilterHeader.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line
Files:
index.jsapp/sagas/deepLinking.jsjest.config.jsapp/views/RoomsListView/components/ListHeader.tsxapp/stacks/MasterDetailStack/index.tsxapp/views/CallView/components/Dialpad/styles.tsjest.setup.jsapp/stacks/MasterDetailStack/types.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsxapp/lib/services/voip/mockCall.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use ESLint with
@rocket.chat/eslint-configbase configuration including React, React Native, TypeScript, and Jest plugins
Files:
index.jsapp/sagas/deepLinking.jsjest.config.jsapp/views/RoomsListView/components/ListHeader.tsxapp/stacks/MasterDetailStack/index.tsxapp/views/CallView/components/Dialpad/styles.tsjest.setup.jsapp/stacks/MasterDetailStack/types.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsxapp/lib/services/voip/mockCall.ts
index.js
📄 CodeRabbit inference engine (CLAUDE.md)
Register app entry point in index.js with conditional Storybook loading
Files:
index.js
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions
Files:
index.jsapp/sagas/deepLinking.jsjest.config.jsapp/views/RoomsListView/components/ListHeader.tsxapp/stacks/MasterDetailStack/index.tsxapp/views/CallView/components/Dialpad/styles.tsjest.setup.jsapp/stacks/MasterDetailStack/types.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsxapp/lib/services/voip/mockCall.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution
**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers
Files:
app/views/RoomsListView/components/ListHeader.tsxapp/stacks/MasterDetailStack/index.tsxapp/views/CallView/components/Dialpad/styles.tsapp/stacks/MasterDetailStack/types.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsxapp/lib/services/voip/mockCall.ts
app/views/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Place screen components in app/views/ directory
Files:
app/views/RoomsListView/components/ListHeader.tsxapp/views/CallView/components/Dialpad/styles.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsx
app/stacks/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use React Navigation 7 for navigation with stacks for InsideStack (authenticated), OutsideStack (login/register), MasterDetailStack (tablets), and ShareExtensionStack
Files:
app/stacks/MasterDetailStack/index.tsxapp/stacks/MasterDetailStack/types.ts
app/lib/services/voip/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Files:
app/lib/services/voip/mockCall.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/hooks/useResponsiveLayout/**/*.{ts,tsx} : Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Applied to files:
index.jsapp/sagas/deepLinking.jsapp/views/RoomsListView/components/ListHeader.tsxapp/stacks/MasterDetailStack/index.tsxapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsxapp/lib/services/voip/mockCall.ts
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use ESLint with rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins
Applied to files:
jest.config.js
📚 Learning: 2026-03-30T15:49:30.957Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: app/containers/RoomItem/Actions.tsx:12-12
Timestamp: 2026-03-30T15:49:30.957Z
Learning: In RocketChat/Rocket.Chat.ReactNative, `react-native-worklets` version 0.6.1 does NOT export a built-in Jest mock (e.g., no `react-native-worklets/lib/module/mock`). The correct Jest mock approach for this version is to add a manual mock in `jest.setup.js`: `jest.mock('react-native-worklets', () => ({ scheduleOnRN: jest.fn((fn, ...args) => fn(...args)) }))`.
Applied to files:
jest.config.jsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.test.tsxapp/lib/services/voip/mockCall.ts
📚 Learning: 2026-04-07T17:49:17.518Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.518Z
Learning: Applies to **/*.{js,jsx,ts,tsx,json} : Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line
Applied to files:
jest.config.js
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/index.tsx : Configure Redux provider, theme, navigation, and notifications in app/index.tsx
Applied to files:
app/views/RoomsListView/components/ListHeader.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/theme.tsx : Configure theming context in app/theme.tsx
Applied to files:
app/views/RoomsListView/components/ListHeader.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/stacks/**/*.{ts,tsx} : Use React Navigation 7 for navigation with stacks for InsideStack (authenticated), OutsideStack (login/register), MasterDetailStack (tablets), and ShareExtensionStack
Applied to files:
app/stacks/MasterDetailStack/index.tsxapp/stacks/MasterDetailStack/types.ts
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/views/**/*.{ts,tsx} : Place screen components in app/views/ directory
Applied to files:
app/stacks/MasterDetailStack/index.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/hooks/useResponsiveLayout/**/*.{ts,tsx} : Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook
Applied to files:
app/views/CallView/components/Dialpad/styles.tsapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/index.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/CallButtons.tsx
📚 Learning: 2026-03-30T15:49:26.708Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: app/containers/RoomItem/Actions.tsx:12-12
Timestamp: 2026-03-30T15:49:26.708Z
Learning: In Rocket.Chat.ReactNative, do not rely on `react-native-worklets` v0.6.1 exporting a built-in Jest mock (e.g., `react-native-worklets/lib/module/mock` does not exist for this version). Instead, add the Jest manual mock in your repo’s `jest.setup.js`/`jest.setup.ts`, mocking `react-native-worklets` to provide `scheduleOnRN: jest.fn((fn, ...args) => fn(...args))`. This ensures Jest can import the module and that `scheduleOnRN` executes the passed function during tests.
Applied to files:
jest.setup.js
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Support React 19, React Native 0.79, and Expo 53
Applied to files:
jest.setup.js
📚 Learning: 2026-03-17T19:15:30.463Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6970
File: .maestro/tests/room/share-message.yaml:77-79
Timestamp: 2026-03-17T19:15:30.463Z
Learning: In `.maestro/tests/room/share-message.yaml` (Rocket.Chat React Native), the `tapOn: point: 5%,10%` step is intentional: it taps the empty area above the bottom sheet and keyboard to dismiss both simultaneously. Using `action-sheet-handle` instead would only close the sheet but not the keyboard. This pattern is acceptable when both need to be dismissed together in a single step.
Applied to files:
app/views/CallView/components/CallButtons.test.tsx
📚 Learning: 2026-03-10T15:21:45.098Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7046
File: app/containers/InAppNotification/NotifierComponent.stories.tsx:46-75
Timestamp: 2026-03-10T15:21:45.098Z
Learning: In `app/containers/InAppNotification/NotifierComponent.tsx` (React Native, Rocket.Chat), `NotifierComponent` is exported as a Redux-connected component via `connect(mapStateToProps)`. The `isMasterDetail` prop is automatically injected from `state.app.isMasterDetail` and does not need to be passed explicitly at call sites or in Storybook stories that use the default (connected) export.
Applied to files:
app/views/CallView/index.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to index.js : Register app entry point in index.js with conditional Storybook loading
Applied to files:
app/views/CallView/index.test.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP
Applied to files:
app/lib/services/voip/mockCall.ts
🔇 Additional comments (18)
index.js (1)
50-50: Good stylistic cleanup; no behavior change.This update matches the repo’s Prettier rule (
arrow parens avoid) and keeps error handling intact.app/sagas/deepLinking.js (1)
116-121: Looks good — stylistic cleanup only.This change keeps behavior intact and improves consistency with single-arg arrow callback style.
app/views/CallView/components/Dialpad/styles.ts (1)
44-56: LGTM!The landscape styles are well-structured, using
flexDirection: 'row'for side-by-side layout and equalflex: 1distribution between input and grid sections. The implementation follows the existing style patterns in this file.app/views/CallView/components/Dialpad/Dialpad.tsx (2)
37-47: LGTM!Good extraction of
DialpadGridinto a reusable component, following the single responsibility principle. UsingrowIndexas key is acceptable here sinceDIALPAD_KEYSis a static constant that never reorders.
49-75: LGTM!The landscape detection logic using
useWindowDimensionsis clean and the conditional rendering properly handles both orientations. The side-by-side layout with separate input and grid sections provides good tablet UX.app/stacks/MasterDetailStack/index.tsx (2)
57-57: LGTM!Import is correctly placed with the InsideStackNavigator view imports.
238-238: LGTM!The
CallViewscreen registration follows the same pattern asJitsiMeetView— a full-screen call interface withheaderShown: false. This correctly integrates into theInsideStackfor authenticated users on tablets.app/stacks/MasterDetailStack/types.ts (1)
225-225: LGTM!The
CallView: undefinedtype is appropriate since the view retrieves call state from the Zustand store rather than navigation params. Based on learnings, VoIP uses Zustand stores instead of Redux, which aligns with this parameterless route design.jest.config.js (1)
3-5: LGTM!Adding
@lodev09/react-native-true-sheetto thetransformIgnorePatternsallowlist is necessary to ensure Jest can process the package before the mock injest.setup.jstakes effect.app/views/RoomsListView/components/ListHeader.tsx (1)
43-54: LGTM!The
__DEV__guard ensures this mock call button only appears in development builds. This provides a convenient way to test the CallView without requiring a real SIP session. The implementation follows the existingList.Itempattern used elsewhere in this component.jest.setup.js (1)
208-213: LGTM!The
TrueSheetmock follows the established pattern used for similar native components like@discord/bottom-sheet(mapping toView). This allows tests to render without requiring the native implementation.app/views/CallView/index.test.tsx (2)
12-16: LGTM!The mutable
mockWindowWidthvariable provides a clean way to test responsive behavior by allowing each test to set the desired screen width before rendering.
403-425: LGTM!Good coverage for the responsive layout feature. The tests clearly verify the expected behavior:
- Narrow layout (350px): buttons split across two rows
- Wide layout (800px): all buttons in a single row
The assertions use appropriate
getByTestIdfor expected elements andqueryByTestIdwithtoBeNull()for elements that should not exist.app/views/CallView/index.tsx (1)
18-20: Responsive layout mode wiring looks good.
layoutModeis derived clearly and passed down cleanly toCallButtons, which keeps the screen logic straightforward.Also applies to: 28-28
app/views/CallView/components/Dialpad/Dialpad.test.tsx (1)
133-156: Landscape/portrait coverage is a good addition.These assertions validate both branches explicitly and make the responsive Dialpad behavior safer to refactor.
app/views/CallView/components/CallButtons.test.tsx (1)
14-18: Test updates are aligned with the newCallButtonsAPI.Good adjustment to pass
layoutModeconsistently and keep behavior assertions intact.Also applies to: 48-96
app/views/CallView/styles.ts (1)
56-70: Button container/row style refactor matches the new layout behavior.The updated spacing and row alignment are coherent with the responsive button composition.
app/views/CallView/components/CallButtons.tsx (1)
126-148: Wide vs narrow rendering structure is clean and easy to verify.The branch layout is straightforward, and row-level
testIDs make this easy to test and maintain.
Slice 1 of PROGRESS-layout-review.md. Co-Authored-By: Diego Mello <diegolmello@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Slice 6 of PROGRESS-layout-review.md. Snapshots updated to reflect the removal of redundant alignContent: 'flex-start' from callerInfoContainer style and the trimmed TabletCallView comment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Slice 4 of PROGRESS-layout-review.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Slice 4 refactor inferred the buttons array as `Array<{ icon: string; ... }>`,
which broke `CustomIcon`'s `TIconsName` constraint and tripped TS1355 on the
`(cond ? 'active' : 'default') as const` pattern. Annotate the array with an
explicit ICallButtonConfig interface so icons stay typed and variants are a
plain string union.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
app/views/CallView/components/CallActionButton.stories.tsx (1)
42-50: Avoid double wrapper nesting in storiesThe decorator wraps with
<Wrapper>, but each story already wraps with<Wrapper>. This adds redundant padding/alignment and can skew visual baselines/snapshots.Proposed fix
decorators: [ (Story: React.ComponentType) => ( <ResponsiveLayoutContext.Provider value={responsiveLayoutProviderLargeFontValue(1)}> - <Wrapper> - <Story /> - </Wrapper> + <Story /> </ResponsiveLayoutContext.Provider> ) ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/CallView/components/CallActionButton.stories.tsx` around lines 42 - 50, The decorator currently double-wraps stories by nesting <Story /> inside <Wrapper> within the decorators array; remove the redundant <Wrapper> from the decorator so it only provides the ResponsiveLayoutContext.Provider and renders <Story /> directly, leaving per-story <Wrapper> usage intact—update the decorators definition that references ResponsiveLayoutContext.Provider, Wrapper, and Story accordingly.app/views/CallView/CallView.stories.tsx (1)
121-162: Drive the tablet stories through the real responsive path.These variants bypass
CallViewand only forceCallButtonswide while the shared decorator still provides a narrow width. That works today, but the stories will drift as soon as any otherCallViewchild starts readinguseResponsiveLayout. Rendering<CallView />under a wide responsive provider would keep the stories and snapshots aligned with production behavior.Based on learnings, "Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/CallView/CallView.stories.tsx` around lines 121 - 162, The Tablet stories are bypassing CallView by composing CallerInfo and CallButtons directly; change TabletCallView to render the real CallView wrapped in the app's wide responsive provider (so CallView and its children read useResponsiveLayout as in production) instead of returning <CallerInfo /> and <CallButtons />; remove the manual composition in TabletCallView and ensure the wide/layoutMode='wide' is provided via the responsive provider or by making useResponsiveLayout return 'wide' for these stories so all TabletConnectedCall, TabletConnectingCall, TabletMutedCall, TabletOnHoldCall, TabletMutedAndOnHold and TabletSpeakerOn continue to return <TabletCallView /> but now exercise the real CallView component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/views/CallView/components/CallActionButton.stories.tsx`:
- Around line 27-35: The mocked responsive layout helper
responsiveLayoutProviderLargeFontValue must mirror useResponsiveLayout's logic:
compute fontScaleLimited by clamping the incoming fontScale to the same min/max
constants used in useResponsiveLayout (use the same clamp logic or import the
constants), derive isLargeFontScale using the same LARGE_FONT_THRESHOLD (i.e.,
compare against the limited value as useResponsiveLayout does), and use
fontScaleLimited (not the raw fontScale) when computing rowHeight and
rowHeightCondensed; update the function to follow those same symbols
(fontScaleLimited, isLargeFontScale, BASE_ROW_HEIGHT, BASE_ROW_HEIGHT_CONDENSED
or the shared constants) so stories match production behavior.
In `@app/views/CallView/components/Dialpad/Dialpad.stories.tsx`:
- Around line 20-30: forceLayoutMode() is ineffective in Storybook because it
only alters a Jest mock; replace it by wrapping each story render with a
deterministic responsive context provider. Create a small LayoutWrapper
component that renders ResponsiveLayoutContext.Provider with value
responsiveValue(width) (use the widths you want for Default and
TabletLandscape), then wrap the story components (or add a story-level
decorator) with LayoutWrapper instead of calling forceLayoutMode; update any
references to forceLayoutMode in this file so stories use LayoutWrapper to
control layout deterministically.
In `@app/views/CallView/index.test.tsx`:
- Around line 12-16: The tests mock react-native's useWindowDimensions with a
static object (mockWindowWidth = 350) which causes
ResponsiveLayoutContext/useCallLayoutMode to always resolve to 'narrow',
breaking assertions for wider layouts; fix by either (A) mocking
useResponsiveLayout (the hook used by ResponsiveLayoutContext) to return an
object that reads the mutable mockWindowWidth so changing mockWindowWidth
affects layout, or (B) stop mocking the context and wrap the test Wrapper with
ResponsiveLayoutProvider so it consumes the mocked useWindowDimensions, or (C)
directly mock useCallLayoutMode to return 'narrow' or 'wide' based on
mockWindowWidth; update tests around Wrapper, useResponsiveLayout,
ResponsiveLayoutProvider, or useCallLayoutMode accordingly so width=800 yields
the expected single-row layout.
---
Nitpick comments:
In `@app/views/CallView/CallView.stories.tsx`:
- Around line 121-162: The Tablet stories are bypassing CallView by composing
CallerInfo and CallButtons directly; change TabletCallView to render the real
CallView wrapped in the app's wide responsive provider (so CallView and its
children read useResponsiveLayout as in production) instead of returning
<CallerInfo /> and <CallButtons />; remove the manual composition in
TabletCallView and ensure the wide/layoutMode='wide' is provided via the
responsive provider or by making useResponsiveLayout return 'wide' for these
stories so all TabletConnectedCall, TabletConnectingCall, TabletMutedCall,
TabletOnHoldCall, TabletMutedAndOnHold and TabletSpeakerOn continue to return
<TabletCallView /> but now exercise the real CallView component.
In `@app/views/CallView/components/CallActionButton.stories.tsx`:
- Around line 42-50: The decorator currently double-wraps stories by nesting
<Story /> inside <Wrapper> within the decorators array; remove the redundant
<Wrapper> from the decorator so it only provides the
ResponsiveLayoutContext.Provider and renders <Story /> directly, leaving
per-story <Wrapper> usage intact—update the decorators definition that
references ResponsiveLayoutContext.Provider, Wrapper, and Story accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 63770d96-dd8f-4436-85f4-38af714eb285
⛔ Files ignored due to path filters (6)
app/containers/NewMediaCall/__snapshots__/FilterHeader.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/__snapshots__/index.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/components/Dialpad/__snapshots__/Dialpad.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/components/__snapshots__/CallActionButton.test.tsx.snapis excluded by!**/*.snapapp/views/CallView/components/__snapshots__/CallerInfo.test.tsx.snapis excluded by!**/*.snapyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (16)
.gitignore.rnstorybook/storybook.requires.tsapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/components/CallButtons.tsxapp/views/CallView/components/CallerInfo.stories.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/Dialpad.test.tsxapp/views/CallView/components/Dialpad/Dialpad.tsxapp/views/CallView/components/Dialpad/useDialpadState.tsapp/views/CallView/index.test.tsxapp/views/CallView/index.tsxapp/views/CallView/styles.tsapp/views/CallView/types.tsapp/views/CallView/useCallLayoutMode.ts
✅ Files skipped from review due to trivial changes (4)
- app/views/CallView/types.ts
- .gitignore
- app/views/CallView/components/CallerInfo.stories.tsx
- .rnstorybook/storybook.requires.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- app/views/CallView/styles.ts
- app/views/CallView/components/Dialpad/Dialpad.test.tsx
- app/views/CallView/components/CallButtons.tsx
- app/views/CallView/components/Dialpad/Dialpad.tsx
- app/views/CallView/index.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line
Files:
app/views/CallView/useCallLayoutMode.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/useDialpadState.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use ESLint with
@rocket.chat/eslint-configbase configuration including React, React Native, TypeScript, and Jest plugins
Files:
app/views/CallView/useCallLayoutMode.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/useDialpadState.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution
**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers
Files:
app/views/CallView/useCallLayoutMode.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/useDialpadState.ts
app/views/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Place screen components in app/views/ directory
Files:
app/views/CallView/useCallLayoutMode.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/useDialpadState.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions
Files:
app/views/CallView/useCallLayoutMode.tsapp/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/useDialpadState.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/hooks/useResponsiveLayout/**/*.{ts,tsx} : Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/hooks/useResponsiveLayout/**/*.{ts,tsx} : Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook
Applied to files:
app/views/CallView/useCallLayoutMode.tsapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsx
📚 Learning: 2026-03-30T15:49:30.957Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: app/containers/RoomItem/Actions.tsx:12-12
Timestamp: 2026-03-30T15:49:30.957Z
Learning: In RocketChat/Rocket.Chat.ReactNative, `react-native-worklets` version 0.6.1 does NOT export a built-in Jest mock (e.g., no `react-native-worklets/lib/module/mock`). The correct Jest mock approach for this version is to add a manual mock in `jest.setup.js`: `jest.mock('react-native-worklets', () => ({ scheduleOnRN: jest.fn((fn, ...args) => fn(...args)) }))`.
Applied to files:
app/views/CallView/components/CallButtons.test.tsxapp/views/CallView/index.test.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Applied to files:
app/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/index.test.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsxapp/views/CallView/components/Dialpad/useDialpadState.ts
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/index.tsx : Configure Redux provider, theme, navigation, and notifications in app/index.tsx
Applied to files:
app/views/CallView/components/CallButtons.test.tsxapp/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/index.test.tsx
📚 Learning: 2026-03-17T19:15:30.463Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6970
File: .maestro/tests/room/share-message.yaml:77-79
Timestamp: 2026-03-17T19:15:30.463Z
Learning: In `.maestro/tests/room/share-message.yaml` (Rocket.Chat React Native), the `tapOn: point: 5%,10%` step is intentional: it taps the empty area above the bottom sheet and keyboard to dismiss both simultaneously. Using `action-sheet-handle` instead would only close the sheet but not the keyboard. This pattern is acceptable when both need to be dismissed together in a single step.
Applied to files:
app/views/CallView/components/CallButtons.test.tsx
📚 Learning: 2026-03-10T15:21:45.098Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7046
File: app/containers/InAppNotification/NotifierComponent.stories.tsx:46-75
Timestamp: 2026-03-10T15:21:45.098Z
Learning: In `app/containers/InAppNotification/NotifierComponent.tsx` (React Native, Rocket.Chat), `NotifierComponent` is exported as a Redux-connected component via `connect(mapStateToProps)`. The `isMasterDetail` prop is automatically injected from `state.app.isMasterDetail` and does not need to be passed explicitly at call sites or in Storybook stories that use the default (connected) export.
Applied to files:
app/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to app/AppContainer.tsx : Implement root navigation container logic in app/AppContainer.tsx to switch between authentication states
Applied to files:
app/views/CallView/CallView.stories.tsx
📚 Learning: 2026-03-15T13:55:42.038Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6911
File: app/containers/markdown/Markdown.stories.tsx:104-104
Timestamp: 2026-03-15T13:55:42.038Z
Learning: In Rocket.Chat React Native, the markdown parser requires a space between the underscore wrapping italic text and a mention sigil (_ mention _ instead of _mention_). Ensure stories and tests that include italic-wrapped mentions follow this form to guarantee proper parsing. Specifically, for files like app/containers/markdown/Markdown.stories.tsx, and any test/content strings that exercise italic-mentions, use the pattern _ mention _ (with spaces) to prevent the mention from being treated as plain text. Validate any test strings or story content accordingly.
Applied to files:
app/views/CallView/CallView.stories.tsxapp/views/CallView/components/CallActionButton.stories.tsxapp/views/CallView/components/Dialpad/Dialpad.stories.tsx
📚 Learning: 2026-04-07T17:49:17.519Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.519Z
Learning: Applies to index.js : Register app entry point in index.js with conditional Storybook loading
Applied to files:
app/views/CallView/index.test.tsx
🔇 Additional comments (4)
app/views/CallView/components/CallActionButton.stories.tsx (1)
94-105: Nice addition for tablet/wide visual coverageThis story cleanly captures the single-row tablet action layout and keeps stable
testIDs for inspection.app/views/CallView/useCallLayoutMode.ts (1)
5-7: Nice extraction of the layout breakpoint.Centralizing the
wide/narrowswitch here keeps the CallView pieces on one responsive rule.Based on learnings, "Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook."
app/views/CallView/components/Dialpad/useDialpadState.ts (1)
4-12: Clean view-state wrapper.Bundling theme colors and the dialpad value behind one hook keeps
Dialpadfocused on rendering.app/views/CallView/components/CallButtons.test.tsx (1)
102-179: Good use ofwithin()for row membership.These assertions pin the actual wide/narrow grouping instead of only checking that the buttons exist somewhere.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLAUDE.md`:
- Line 69: Update the platform name in the documentation string
`.github/PULL_REQUEST_TEMPLATE.md` so it reads "GitHub" with a capital H; locate
the occurrence of "Follow `.github/PULL_REQUEST_TEMPLATE.md` when opening PRs."
in CLAUDE.md (the exact quoted text) and change the platform name to "GitHub" to
match official capitalization in contributor-facing docs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2cd9fcab-4656-4e98-8ad2-5029c720d4f3
📒 Files selected for processing (2)
.gitignoreCLAUDE.md
✅ Files skipped from review due to trivial changes (1)
- .gitignore
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/hooks/useResponsiveLayout/**/*.{ts,tsx} : Use responsive layout with master-detail on tablets and single stack on phones via useResponsiveLayout hook
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Use Yarn as the package manager (npm will not work)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Node >= 18 required (CONTRIBUTING specifies 22.14.0)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Run `yarn lint` which executes both `eslint .` and `tsc` type-checking; both must pass
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Run Jest tests with `TZ=UTC` environment variable
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Use Redux for primary state management with reducers in `app/reducers/`, sagas in `app/sagas/`, and actions in `app/actions/`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Use Zustand for newer state management slices alongside Redux
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Use WatermelonDB (nozbe/watermelondb) for local database of messages/rooms with code in `app/lib/database/`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Use react-navigation v7 (native-stack + drawer) for navigation with stacks in `app/stacks/`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Tablet layouts should gate on `MIN_WIDTH_MASTER_DETAIL_LAYOUT` and `isTablet` for master-detail layout
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Do not bypass pre-commit hooks with `--no-verify`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Follow `.github/PULL_REQUEST_TEMPLATE.md` when opening PRs
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T14:35:41.091Z
Learning: Normalize translations by running `yarn organize-translations` in `app/i18n/`
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use ESLint with rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins
Applied to files:
CLAUDE.md
🪛 LanguageTool
CLAUDE.md
[uncategorized] ~69-~69: The official name of this software platform is spelled with a capital “H”.
Context: ...not bypass with --no-verify. - Follow .github/PULL_REQUEST_TEMPLATE.md when opening ...
(GITHUB)
- Add roomId?: string | null to MockCallOverrides so callers can override the seeded roomId. - Change DEFAULT_CONTACT.sipExtension from '2244' to '' so the Message-button predicate (Boolean(sipExtension) || roomId == null) evaluates to false by default. - Seed roomId as overrides.roomId ?? 'mock-room-id' in seedMockCall() so CallView's Message action button is enabled out of the box. Verification: CallButtons.tsx:50 messageDisabled = Boolean(contact.sipExtension) || roomId == null With sipExtension='' and roomId='mock-room-id', both operands are false → button enabled.
Replace the forceLayoutMode() hack (which patched the jest mock at render time and was a no-op in Storybook UI) with a LayoutWrapper component that supplies a fixed width via ResponsiveLayoutContext.Provider: width=350 → narrow (< MIN_WIDTH_MASTER_DETAIL_LAYOUT=700) width=800 → wide (≥ MIN_WIDTH_MASTER_DETAIL_LAYOUT=700) In Storybook UI the real useCallLayoutMode hook reads from this context, making the Default/TabletLandscape stories deterministic regardless of host device width. In Jest, useCallLayoutMode is already mocked at module level in Dialpad.test.tsx so the context provider is inert there. Removed: import of callLayoutModule and forceLayoutMode helper.
Previous mock replaced the ResponsiveLayoutContext export, but useResponsiveLayout's closure captured the original context, so components never saw the mocked values (fontScale ended up undefined, producing NaN font sizes in snapshots). Mock useResponsiveLayout directly and drive it from useWindowDimensions so that flipping mockWindowWidth between 350 and 800 actually toggles layoutMode. Regenerate snapshots with the now-correct font metrics.
Mirror useResponsiveLayout's FONT_SCALE_LIMIT clamp so stories render with the same layout math as production for non-default font scales.
Drop useDialpadState wrapper and inline useTheme + useDialpadValue directly in Dialpad. Move useCallLayoutMode out of CallView/index and into CallButtons so resize re-renders only the buttons subtree instead of the entire CallView.
Add layout-land/activity_incoming_call.xml so Android auto-selects a two-column layout when the device is in landscape. Header stays centered at the top (32dp padding), caller info fills the left half, and decline/ accept buttons sit side-by-side on the right half — matching the iOS landscape design shipped in #7110. Zero changes to IncomingCallActivity.kt, AndroidManifest.xml, or the existing portrait layout. All 12 view IDs required by the Activity are preserved with the same widget types.
…/Decline (#7215) * merge feat.voip-lib * feat(voip): enhance call handling with UUID mapping and event listeners * Base call UI * feat(voip): integrate Zustand for call state management and enhance CallView UI * feat(voip): add simulateCall function for mock call handling in UI development * refactor(CallView): update button handlers and improve UI responsiveness * Add pause-shape-unfilled icon * Base CallHeader * toggleFocus * collapse buttons * Header components * Hide header when no call * Timer * Add use memo * Add voice call item on sidebar * cleanup * Temp use @rocket.chat/media-signaling from .tgz * cleanup * Check module and permissions to enable voip * Refactor stop method to use optional chaining for media signal listeners * voip push first test * Add VoIP call handling with pending call management - Implemented VoIP push notification handling in index.js, including storing call info for later processing. - Added CallKeep event handlers for answering and ending calls from a cold start. - Introduced a new CallIdUUID module to convert call IDs to deterministic UUIDs for compatibility with CallKit. - Created a pending call store to manage incoming calls when the app is not fully initialized. - Updated deep linking actions to include VoIP call handling. - Enhanced MediaSessionInstance to process pending calls and manage call states effectively. * Remove pending store and create getInitialEvents on app/index * Attempt to make iOS calls work from cold state * lint and format * Patch callkeep ios * Temp send iOS voip push token on gcm * Temp fix require cycle * chore: format code and fix lint issues [skip ci] * CallIDUUID module on android and voip push * Add setCallUUID on useCallStore to persist calls accepted on native Android * remove callkeep from notification * Android Incoming Call UI POC * Refactor VoIP handling: Migrate VoIP-related classes to a new package structure, removing deprecated modules and consolidating functionality. Update imports in MainApplication and NotificationIntentHandler to reflect changes. This cleanup enhances code organization and prepares for future VoIP feature enhancements. * Remove VoipForegroundService * cleanup and use caller instead of callerName * Cleanup and make iOS build again * Refactor VoIP handling: Remove unused event emissions for call answered and declined, switch from SharedPreferences to in-memory storage for pending VoIP call data, and update method signatures for better clarity. This cleanup enhances performance and prepares for future VoIP feature improvements. * Refactor VoIP handling: Introduce a new VoipPayload class to encapsulate call data, streamline notification processing, and enhance method signatures across the VoIP module. This update improves code clarity and prepares for future feature enhancements. * Migrate react-native-voip-push-notifications to VoipModule * Refactor VoIP module: Update package structure by moving VoipTurboPackage to the main package and removing the obsolete NativeVoipSpec class. Adjust imports in MainApplication and VoipModule to reflect these changes, enhancing code organization and maintainability. * Unify emitters * Move CallKeep listeners from MediaSessionInstance to getInitialEvents * Clear callkeep on endcall * Unify getInitialEvents logic * getInitialEvents -> MediaCallEvents * chore: format code and fix lint issues [skip ci] * feat(Android): Add full screen incoming call (#6977) * feat: Update call UI (#6990) * feat: Handle audio routing, e.g., Bluetooth headset vs. internal speaker switching (#6992) * fix: empty space when not on call (#6993) * feat: Dialpad (#7000) * action: organized translations * feat: start call (#7024) * chore: format code and fix lint issues * feat: Pre flight (#7038) * action: organized translations * feat: Receive voip push notifications from backend (#7045) * feat: Refactor media session handling and improve disconnect logic (#7065) * feat: Control incoming call from native (#7066) * feat: Voice message blocks (#7057) * feat: native accept success event (#7068) * feat(voip): call waiting, busy detection, and videoconf blocking (#7077) * action: organized translations * feat(voip): tap-to-hide call controls with animations (#7078) * feat(voip): navigate to call DM from message button and header (#7082) * feat(voip): tablet and landscape layout (#7110) * chore: develop into feat.voip-lib-new (RN 81 + Expo 54 + reanimated 4 + true-sheet + iOS 26) (#7114) * chore: format code and fix lint issues * feat(voip): android landscape layout for IncomingCallActivity (#7116) * Update agents files * feat(voip): Support a11y (#7106) * Fix content cutting on iOS on some edge cases * pods * Ignore .worktrees on jest * chore: Merge develop into feat.voip-lib-new (#7129) * fix(voip): show CallKit UI when call is active in background (#7128) * chore: Update media-signaling to 0.2.0 (#7153) * feat(voip): migrate iOS accept/reject from DDP to REST (#7124) * Fix icons * feat(voip): migrate Android accept/reject from DDP to REST (#7127) * test(voip): integration tests for CallView pipeline (#7161) * feat(voip): display video conf provider as subtitle (#7160) * fix(voip): CallView button grid and correct landscape/dialpad layouts (#7164) * fix(voip): prevent stale MMKV cache on Android first-install accept MMKVKeyManager.initialize ran in MainApplication.onCreate before the JS engine started and opened the default MMKV file via the Tencent 1.2 JAR when it was still empty. Tencent caches instances per-ID in a singleton registry, so that empty-state view was held for the rest of the process. JS later wrote credentials through react-native-mmkv (MMKV Core 2.0), which has its own separate registry. When a VoIP push arrived, Ejson.getMMKV() got the cached empty Tencent instance and reported "No userId found in MMKV for server". Closing and reopening the app cleared the cache, which is why only the very first call after install failed. Drop the open/verify block — the encryption key is already cached from SecureKeystore, so no MMKV handle is needed here. The first Tencent instance is now created inside Ejson.getMMKV() after JS has written, so it scans the file fresh. * fix(voip): prevent duplicate ringtone on Android incoming call (#7158) * fix(voip): set explicit snaps for NewMediaCall bottom sheet (#7165) * Update app/lib/services/voip/MediaSessionStore.ts Co-authored-by: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> * fix: make startVoipFork reactive to permissions-changed (#7151) * fix(android): remove MediaProjectionService from merged manifest (#7190) * fix(voip): Phone account creation (#7170) * feat: add Enable Mobile Ringing toggle in user preferences (#7155) * fix(voip): ship blockers for PushKit, licensing, outbound calls, push tokens (#7167) * fix(android): Play Store mic discoverability, safer FCM logs, avatar auth via headers (#7171) * fix(ios): serialize VoipService bridge statics (#7169) * fix(voip): Android DDP thread safety and VoipPayload bundle parity (#7168) * chore(voip): dead-code and hygiene sweep (#7174) * refactor(voip): decouple navigateToCallRoom from Redux and backfill REST/connect tests (#7176) * test(voip): tighten ringing endCall assertion and add VideoConf VoIP-lock saga coverage (#7177) * fix(ios): harden VoIP DDP WebSocket client on receive failures and TLS (#7173) * refactor(voip): MediaCallEvents Redux adapters and resetVoipState (#7178) * refactor(voip): decouple peer autocomplete from Redux; simplify NewMediaCall (#7175) * fix(ios): add NS_SWIFT_NAME to Challenge.runChallenge for Swift 6.2 compatibility Swift 6.2 (Xcode 26.x / macos-26 runner) auto-renames the Objective-C method runChallenge:didReceiveChallenge:completionHandler: to run(_:didReceive:completionHandler:) when imported into Swift. Add NS_SWIFT_NAME to explicitly pin the Swift import name, preventing the compiler from applying its heuristics. This keeps the existing Swift call site in DDPClient.swift working without changes. * fix(ios): cancel old URLSession/webSocketTask before reconnecting in DDPClient.connect (#7197) * fix(ios): add NSLock to nativeAcceptHandledCallIds and 10s REST timeout to handleNativeAccept (#7198) * feat(android): create VoipCallService with FOREGROUND_SERVICE_MICROPHONE (#7199) * fix(android): start VoipCallService on accept, stop on hangup/timeout, install end-call listener (#7200) * fix(voip): enable DM nav for users with SIP extension (#7203) * fix(android): handle null VoiceConnection in answerIncomingCall, notify JS (#7201) * fix(voip): resolve closure capture ordering in handleNativeAccept (#7209) * fix(android): integrate VoIP modules with SSL-pinned OkHttpClient (#7208) * fix(push): gate id and voipToken behind server version checks, fix VideoConf caller extra (#7210) * fix(voip): remove sensitive data from production logs (#7207) * fix(android): remove isRunning guard + add double-tap guard on Accept/Decline - VoipCallService: remove if (!isRunning) guard, call startForeground unconditionally (idempotent on Android, fixes Android 14+ foreground service requirement) - IncomingCallActivity: add AtomicBoolean guard on handleAccept/handleDecline to prevent double-tap from triggering multiple service starts --------- Co-authored-by: diegolmello <diegolmello@users.noreply.github.com> Co-authored-by: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com>
Proposed changes
Adds a wide ("landscape/tablet") layout mode to
CallViewso the in-call UI adapts beyond the original portrait-phone design:useCallLayoutModehook centralizes layout decisions (portraitvswide) based on orientation and tablet master-detail state, withLayoutModeextracted toCallView/types.ts.CallButtonswas refactored to a config-array driven component, tightening types and making it trivial to reflow rows between layouts.Dialpadportrait and landscape variants were collapsed into a single component that positions the keypad side-by-side in wide mode.CallViewis now registered in the tablet master-detail stack and shown fromRoomsListView's list header entry.CallView.stories.tsx,Dialpad.stories.tsx,CallButtons.test.tsx,index.test.tsx); tests switched towithin()for row-membership assertions.TrueSheetmock added tojest.setup.js; deprecatedbottomSheetprop removed from tests.Issue(s)
https://rocketchat.atlassian.net/browse/VMUX-22
How to test or reproduce
yarn && yarn pod-installsimulateCall) a VoIP call and openCallView.CallButtonsreflow into the wide row config.CallViewfrom the rooms list header keeps master-detail intact.yarn testandyarn lintboth pass.Screenshots
Simulator.Screen.Recording.-.iPad.Pro.13-inch.M4.-.2026-04-07.at.15.26.49.mov
Simulator.Screen.Recording.-.iPad.Pro.13-inch.M4.-.2026-04-07.at.15.26.42.mov
Simulator.Screen.Recording.-.iPhone.16e.-.2026-04-07.at.15.26.31.mov
Simulator.Screen.Recording.-.iPhone.16e.-.2026-04-07.at.15.26.19.mov
Types of changes
Checklist
Further comments
This PR stacks on the base VoIP work (
feat.voip-lib-newline) and should merge after those land. The layout mode is intentionally a 2-value enum (portrait|wide) rather than raw breakpoints so thehook stays the single source of truth — if we need more variants later, extend
LayoutModeinCallView/types.ts.