chore(voip): consolidate PR #6918 review-cleanup#7295
Conversation
Replaces 12 console.error/warn sites in useCallStore, MediaSessionInstance, and playCallEndedSound with the project log helper so VoIP failures reach Bugsnag during rollout. Tests extended to mock log and assert Error-typed calls on each path.
Removes the FCM/APNs token argument from the success-path console.log in push.ts. Tokens are device PII and surface in screenshots and paste-bins; the keep-tokenless success message preserves the diagnostic without leaking.
Removes two // TESTING: leftovers in MediaSessionInstance.ts and the empty
.on('stateChange', ...) registration that had no effect. Annotates
resetMediaCallEventsStateForTesting as @internal so callers know it is a
test seam.
Reverts the unintended commenting-out from PR #6918 so dispatched actions log in dev builds again, matching develop.
Removes the leftover Cursor IDE submodule pointer that has no .gitmodules entry and breaks fresh clones. .cursor/ is already in .gitignore.
Reverts the unintended removal of height: '100%' from the shared fullContainer style (which was collapsing VideoConf, RoomView, and NewMessageView sheets to content height on Android) and introduces a new hugContent flag plumbed through Provider/ActionSheet/ BottomSheetContent. Only useNewMediaCall opts in (with isAndroid), so its Android sheet keeps hugging content while every other consumer returns to full-screen behavior. iOS unchanged.
…ing.* Moves 8 hardcoded English literals in VoipNotification and VoipCallService to a new strings_voip.xml so translators can supply locale-specific values. Reuses existing incoming_call_accept and incoming_call_reject for action labels. Call-from text uses %1\$s for translator-friendly reordering. English values only; values-<locale>/ work is a follow-up.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent 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)
🧰 Additional context used📓 Path-based instructions (4)**/*.{js,ts,jsx,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
app/containers/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-04-30T17:07:51.020ZApplied to files:
🔇 Additional comments (2)
WalkthroughRemoves a DDP queued-method infrastructure, centralizes Android device-id access, internationalizes VoIP notification strings, replaces console.* calls with a shared log helper across VoIP codepaths, adds an ActionSheet ChangesVoIP Refactor (Android native + RN logic + tests)
ActionSheet Layout Control for Media Calls
Miscellaneous
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsx (1)
275-298:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid
jest.resetModules()in this hook test.Resetting the module registry here and then
require-ing the hook can load a second React instance, which is a common cause of invalid hook call failures in Jest/RN tests. Use an isolated import block instead.Suggested safer pattern
- it('should pass hugContent: true to the action sheet when isAndroid is true', () => { - jest.resetModules(); - jest.doMock('../../methods/helpers/deviceInfo', () => ({ - isAndroid: true - })); - // Must load hook after doMock so `hugContent: isAndroid` uses Android. - // eslint-disable-next-line `@typescript-eslint/no-var-requires` - const { useNewMediaCall: useNewMediaCallAndroid } = require('./useNewMediaCall'); - - mockUseSubscription.mockReturnValue(undefined); - mockUseMediaCallPermission.mockReturnValue(false); - - const { result } = renderHook(() => useNewMediaCallAndroid('room-id')); - - act(() => { - result.current.openNewMediaCall(); - }); - - expect(mockShowActionSheetRef).toHaveBeenCalledTimes(1); - const [actionSheetArgs] = mockShowActionSheetRef.mock.calls[0]; - expect(React.isValidElement(actionSheetArgs.children)).toBe(true); - expect(actionSheetArgs.hugContent).toBe(true); - }); + it('should pass hugContent: true to the action sheet when isAndroid is true', () => { + jest.isolateModules(() => { + jest.doMock('../../methods/helpers/deviceInfo', () => ({ + isAndroid: true + })); + const { useNewMediaCall: useNewMediaCallAndroid } = require('./useNewMediaCall'); + + mockUseSubscription.mockReturnValue(undefined); + mockUseMediaCallPermission.mockReturnValue(false); + + const { result } = renderHook(() => useNewMediaCallAndroid('room-id')); + + act(() => { + result.current.openNewMediaCall(); + }); + }); + + expect(mockShowActionSheetRef).toHaveBeenCalledTimes(1); + const [actionSheetArgs] = mockShowActionSheetRef.mock.calls[0]; + expect(React.isValidElement(actionSheetArgs.children)).toBe(true); + expect(actionSheetArgs.hugContent).toBe(true); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsx` around lines 275 - 298, The test currently calls jest.resetModules() and then require('./useNewMediaCall'), which can load a second React instance; replace that pattern with jest.isolateModules to avoid touching the global module registry. Remove jest.resetModules(), move the jest.doMock('../../methods/helpers/deviceInfo', () => ({ isAndroid: true })) and the require('./useNewMediaCall') call into a jest.isolateModules(() => { ... }) block (i.e. call jest.isolateModules(() => { jest.doMock(...); const { useNewMediaCall: useNewMediaCallAndroid } = require('./useNewMediaCall'); /* renderHook and interact here or assign to an outer variable before assertions */ });) and keep mockUseSubscription.mockReturnValue and mockUseMediaCallPermission.mockReturnValue set as before so the test asserts mockShowActionSheetRef and actionSheetArgs.hugContent without creating multiple React instances.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@app/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsx`:
- Around line 275-298: The test currently calls jest.resetModules() and then
require('./useNewMediaCall'), which can load a second React instance; replace
that pattern with jest.isolateModules to avoid touching the global module
registry. Remove jest.resetModules(), move the
jest.doMock('../../methods/helpers/deviceInfo', () => ({ isAndroid: true })) and
the require('./useNewMediaCall') call into a jest.isolateModules(() => { ... })
block (i.e. call jest.isolateModules(() => { jest.doMock(...); const {
useNewMediaCall: useNewMediaCallAndroid } = require('./useNewMediaCall'); /*
renderHook and interact here or assign to an outer variable before assertions */
});) and keep mockUseSubscription.mockReturnValue and
mockUseMediaCallPermission.mockReturnValue set as before so the test asserts
mockShowActionSheetRef and actionSheetArgs.hugContent without creating multiple
React instances.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b0f44ba2-98b9-4755-9447-a03cd2443f8f
📒 Files selected for processing (21)
.cursor/skills/agent-skillsandroid/app/src/main/java/chat/rocket/reactnative/voip/DDPClient.ktandroid/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.ktandroid/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.ktandroid/app/src/main/res/values/strings_voip.xmlapp/containers/ActionSheet/ActionSheet.tsxapp/containers/ActionSheet/BottomSheetContent.tsxapp/containers/ActionSheet/Provider.tsxapp/containers/ActionSheet/styles.tsapp/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsxapp/lib/hooks/useNewMediaCall/useNewMediaCall.tsxapp/lib/notifications/push.tsapp/lib/services/voip/MediaCallEvents.tsapp/lib/services/voip/MediaSessionInstance.test.tsapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/playCallEndedSound.test.tsapp/lib/services/voip/playCallEndedSound.tsapp/lib/services/voip/useCallStore.ios.test.tsapp/lib/services/voip/useCallStore.test.tsapp/lib/services/voip/useCallStore.tsapp/lib/store/index.ts
💤 Files with no reviewable changes (2)
- .cursor/skills/agent-skills
- android/app/src/main/java/chat/rocket/reactnative/voip/DDPClient.kt
📜 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). (1)
- GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{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/lib/services/voip/useCallStore.ios.test.tsapp/lib/store/index.tsapp/containers/ActionSheet/Provider.tsxapp/containers/ActionSheet/BottomSheetContent.tsxapp/lib/services/voip/MediaCallEvents.tsapp/lib/services/voip/playCallEndedSound.test.tsapp/containers/ActionSheet/ActionSheet.tsxapp/lib/services/voip/playCallEndedSound.tsapp/lib/services/voip/useCallStore.test.tsapp/lib/notifications/push.tsapp/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsxapp/lib/hooks/useNewMediaCall/useNewMediaCall.tsxapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/MediaSessionInstance.test.tsapp/lib/services/voip/useCallStore.tsapp/containers/ActionSheet/styles.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{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
**/*.{ts,tsx}: Use TypeScript with strict mode enabled and baseUrl set to app/ for module imports
Support iOS 13.4+ and Android 6.0+ as minimum target platforms
Files:
app/lib/services/voip/useCallStore.ios.test.tsapp/lib/store/index.tsapp/containers/ActionSheet/Provider.tsxapp/containers/ActionSheet/BottomSheetContent.tsxapp/lib/services/voip/MediaCallEvents.tsapp/lib/services/voip/playCallEndedSound.test.tsapp/containers/ActionSheet/ActionSheet.tsxapp/lib/services/voip/playCallEndedSound.tsapp/lib/services/voip/useCallStore.test.tsapp/lib/notifications/push.tsapp/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsxapp/lib/hooks/useNewMediaCall/useNewMediaCall.tsxapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/MediaSessionInstance.test.tsapp/lib/services/voip/useCallStore.tsapp/containers/ActionSheet/styles.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use tabs for indentation with single quotes, 130 character line width, no trailing commas, and avoid arrow function parentheses when possible
Use ESLint with@rocket.chat/eslint-configbase including React, React Native, TypeScript, and Jest plugins
Files:
app/lib/services/voip/useCallStore.ios.test.tsapp/lib/store/index.tsapp/containers/ActionSheet/Provider.tsxapp/containers/ActionSheet/BottomSheetContent.tsxapp/lib/services/voip/MediaCallEvents.tsapp/lib/services/voip/playCallEndedSound.test.tsapp/containers/ActionSheet/ActionSheet.tsxapp/lib/services/voip/playCallEndedSound.tsapp/lib/services/voip/useCallStore.test.tsapp/lib/notifications/push.tsapp/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsxapp/lib/hooks/useNewMediaCall/useNewMediaCall.tsxapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/MediaSessionInstance.test.tsapp/lib/services/voip/useCallStore.tsapp/containers/ActionSheet/styles.ts
app/lib/services/voip/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Implement VoIP features in app/lib/services/voip/ directory using Zustand stores for WebRTC peer-to-peer audio calls with native CallKit (iOS) and Telecom (Android) integration
Files:
app/lib/services/voip/useCallStore.ios.test.tsapp/lib/services/voip/MediaCallEvents.tsapp/lib/services/voip/playCallEndedSound.test.tsapp/lib/services/voip/playCallEndedSound.tsapp/lib/services/voip/useCallStore.test.tsapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/MediaSessionInstance.test.tsapp/lib/services/voip/useCallStore.ts
app/lib/store/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Configure Redux middleware (saga, app state, internet state) in app/lib/store/ directory
Files:
app/lib/store/index.ts
app/containers/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Create reusable UI components in app/containers/ directory
Files:
app/containers/ActionSheet/Provider.tsxapp/containers/ActionSheet/BottomSheetContent.tsxapp/containers/ActionSheet/ActionSheet.tsxapp/containers/ActionSheet/styles.ts
🧠 Learnings (1)
📚 Learning: 2026-04-30T17:07:51.020Z
Learnt from: diegolmello
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7274
File: app/lib/services/voip/MediaCallEvents.ts:0-0
Timestamp: 2026-04-30T17:07:51.020Z
Learning: In this Rocket.Chat React Native codebase, the ESLint rule `no-void: error` is enforced. When you see a promise returned from an async call that is not awaited (a “floating promise”), do not silence it with the `void somePromise()` pattern. Instead, handle the promise explicitly by attaching `.catch(...)` (or otherwise awaiting/handling the error) so unhandled-rejection risks are addressed in a way that satisfies the existing ESLint configuration.
Applied to files:
app/lib/services/voip/useCallStore.ios.test.tsapp/lib/store/index.tsapp/containers/ActionSheet/Provider.tsxapp/containers/ActionSheet/BottomSheetContent.tsxapp/lib/services/voip/MediaCallEvents.tsapp/lib/services/voip/playCallEndedSound.test.tsapp/containers/ActionSheet/ActionSheet.tsxapp/lib/services/voip/playCallEndedSound.tsapp/lib/services/voip/useCallStore.test.tsapp/lib/notifications/push.tsapp/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsxapp/lib/hooks/useNewMediaCall/useNewMediaCall.tsxapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/MediaSessionInstance.test.tsapp/lib/services/voip/useCallStore.tsapp/containers/ActionSheet/styles.ts
🔇 Additional comments (20)
app/lib/services/voip/MediaCallEvents.ts (1)
56-56: Good internal-doc clarification for test-only export.Line 56 makes the intent explicit and avoids ambiguity around why this function remains exported.
app/lib/notifications/push.ts (1)
188-188: Good change: success logging no longer leaks token data.This keeps registration observability while removing sensitive token exposure from logs.
app/lib/store/index.ts (1)
8-23: Dev logger wiring looks correct.The logger is only added in
__DEV__, so production behavior stays unchanged while development gets the expected middleware visibility.app/containers/ActionSheet/styles.ts (1)
69-73: Restoring the full-container height looks correct.
height: '100%'is a sensible addition here and keeps the existing full-container behavior intact for callers that still opt in.app/lib/hooks/useNewMediaCall/useNewMediaCall.tsx (1)
29-32: The newhugContentopt-in is wired through cleanly.Passing
hugContent: isAndroidhere keeps the VoIP action sheet behavior platform-specific without affecting other ActionSheet callers.app/containers/ActionSheet/ActionSheet.tsx (1)
125-133: Prop forwarding forhugContentlooks right.This keeps the ActionSheet/BottomSheetContent contract aligned with the new option and preserves existing behavior for callers that do not set it.
app/containers/ActionSheet/Provider.tsx (1)
22-39: The option type update is consistent with the new ActionSheet flow.Adding
hugContent?: booleanhere is the right place for the new prop and keeps the public options shape aligned with the downstream components.app/lib/hooks/useNewMediaCall/useNewMediaCall.test.tsx (2)
84-90: The helper now checks the right prop.Switching the expectation from
fullContainertohugContentkeeps the test aligned with the updated ActionSheet contract.
300-314: The iOS assertion matches the new default behavior.Checking
hugContent: falsehere is the right expectation for the non-Android path.app/containers/ActionSheet/BottomSheetContent.tsx (1)
14-24: The newhugContentgating is consistent with the intended layout split.This keeps the existing full-container path for callers that still opt in, while letting Android consumers opt out cleanly when they want the sheet to hug its content.
Also applies to: 26-45, 81-88
app/lib/services/voip/useCallStore.ts (1)
7-7: Looks good: the shared logger is wired through the async and sync error paths.This keeps the existing behavior intact while routing the failures through the project logging helper.
Also applies to: 171-181, 262-270, 323-333
app/lib/services/voip/useCallStore.ios.test.ts (1)
8-12: Good coverage for the iOS no-op path and logger mock.The test setup stays aligned with the production import change and continues to verify that audio route sync is not invoked on iOS.
Also applies to: 90-107
app/lib/services/voip/MediaSessionInstance.ts (1)
31-31: Good: failure paths now report through the shared logger without changing session flow.This keeps the recovery behavior intact while making the VoIP errors observable in the project logging pipeline.
Also applies to: 57-59, 74-76, 129-131, 153-177, 186-190
app/lib/services/voip/MediaSessionInstance.test.ts (1)
12-16: Nice: the added assertions cover the new logging and rollback behavior.This gives solid coverage for the translated failure paths and the optimistic roomId cleanup on rejected start attempts.
Also applies to: 892-907, 911-994
app/lib/services/voip/useCallStore.test.ts (1)
9-13: Solid test coverage for the new logger-based error handling.The additions cover the changed VoIP failure paths without widening the behavioral surface area of the store.
Also applies to: 390-399, 500-543
app/lib/services/voip/playCallEndedSound.test.ts (1)
3-7: Good: the load failure path is now asserted against the shared logger.That keeps the test aligned with the production swap from console logging to the app logger.
Also applies to: 37-37, 154-161
app/lib/services/voip/playCallEndedSound.ts (1)
3-3: Looks good: playback failures are still contained, and the lock is released.The change only swaps the reporting path to the shared logger, which keeps the playback flow intact.
Also applies to: 60-64
android/app/src/main/res/values/strings_voip.xml (1)
1-9: Nice cleanup.The VoIP notification copy is centralized here cleanly, and the
%1$splaceholder is the right way to inject the caller name.android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt (1)
86-88: Good consolidation here.Reusing
deviceId(context)across the REST and DDP reconciliation paths keeps thecontractIdbehavior consistent, and switching the notification text/actions to string resources is a solid cleanup.Also applies to: 174-177, 321-324, 336-339, 507-510, 560-563, 701-701, 941-942, 950-951
android/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.kt (1)
6-6: Looks good.The service notification copy is now resource-backed without changing the foreground-service behavior.
Also applies to: 133-134, 152-152
… path Tracks PR #7295 — `console.warn('[VoIP] Call not found after accept:', id)` became `log(new Error('[VoIP] Call not found after accept: <id>'))`. In tests log() falls through to `console.error(Error)`, so the allowlist entry moves from the warn list to the error list and the assertion targets consoleErrorSpy.
|
iOS Build Available Rocket.Chat Experimental 4.72.0.108749 |
|
Android Build Available Rocket.Chat Experimental 4.72.0.108748 Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNRPIc430HePGGaAjFFicZ9eypREC_E6JSPROQcfJbWFrAwV5qNawYQNMpV8t_S9AGfWJe5JldSXVOEBkGjZ |
Proposed changes
Consolidates the review-comment cleanup for PR #6918 into a single mergeable PR. Eight scoped fixes, each landed individually on this branch and reviewed in isolation, addressing three problem classes:
Observability (rollout-blocking)
console.error/console.warnsites inuseCallStore,MediaSessionInstance, andplayCallEndedSoundthrough the projectlog()helper so failures reach Bugsnag during rollout.push.tsregistration success log line (PII surfaces in screenshots and paste-bins).Native cleanup
queueMethodCall,flushQueuedMethodCalls, etc.) fromDDPClient.ktand the now-emptyflushPendingQueuedSignalsIfNeededcallsite inVoipNotification.kt. Verified zero producers ever called the queue.deviceId(context)helper that collapses fourSettings.Secure.getString(... ANDROID_ID)duplications.VoipNotification.ktandVoipCallService.ktto a newstrings_voip.xml(channel descriptions, incoming-call title/text, ongoing-call title/text). Reuse existingincoming_call_accept/incoming_call_rejectfor action labels. Parameterize Call-from text with%1\$sfor translator-friendly reordering. English values only;values-<locale>/work is a follow-up.Regression repair
redux-loggermiddleware in the__DEV__enhancer chain ofapp/lib/store/index.ts(was unintentionally commented out).height: '100%'on the sharedfullContainerActionSheet style (its removal was collapsing VideoConf, RoomView, and NewMessageView sheets to content height on Android). Introduce a newhugContentopt-in plumbed throughProvider/ActionSheet/BottomSheetContent. OnlyuseNewMediaCallopts in (hugContent: isAndroid), so its Android sheet keeps hugging content while every other consumer returns to full-screen behavior. iOS unchanged across all consumers..cursor/skills/agent-skillsgitlink (no.gitmodulesentry, breaks fresh clones)..cursor/was already in.gitignore.Hygiene
// TESTING:labels and an empty no-op.on('stateChange', ...)listener inMediaSessionInstance.ts.resetMediaCallEventsStateForTestingas@internal.Issue(s)
Cleanup for #6918.
How to test or reproduce
Incoming call,Call from <caller>,Decline,Accept).VoIP Call,Call in progress).3fefee400is fixed).TZ=UTC yarn testshould pass for the modified files. Test additions cover the newlog()mocks and the newhugContentforwarding assertion.Screenshots
n/a — no visual additions.
Types of changes
Checklist
Further comments
git log --first-parentfor a per-concern walk.getDisplayMediatypo inMediaSessionStore.ts, theRNCallKeep.setupmove toMainApplication, polish refactors ('use memo',useCaller,useAppSelector, MediaCallHeader selector tightening, CallView hook extraction, NewMessageView/Item subcomponent split), and@VisibleForTestingannotation pass onDDPClient.ktare intentionally deferred.values-<locale>/directories for the new VoIP strings are a separate translation pass.Summary by CodeRabbit
New Features
Bug Fixes
Chores