feat: start call#7024
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Organization UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (2)
WalkthroughAdds room-based call start APIs and permission gating, updates UI to use them across headers, room actions, and room info; adds Item component, stories and tests; minor layout, i18n, and status indicator size tweaks. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant UI as UI Component
participant MediaSession as mediaSessionInstance
participant CallStore as Call Store
participant Navigation as Navigation
User->>UI: Tap call button
UI->>MediaSession: startCallByRoom(room)
MediaSession->>MediaSession: getUidDirectMessage(room)
MediaSession->>MediaSession: startCall(userId, 'user')
MediaSession->>CallStore: store call (role: caller)
CallStore->>Navigation: navigate to CallView (callUUID)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ 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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/views/RoomView/components/HeaderCallButton.tsx (1)
19-47:⚠️ Potential issue | 🟠 MajorThe call button should be conditionally rendered based on
callEnabledto match other components.The
callEnabledcheck fromuseVideoConfis still actively used inRoomInfoButtons.tsxandCallSection.tsxto respect server-side call configuration. Removing it fromHeaderCallButtoncreates inconsistent behavior—the button will now always render even if the server has disabled the call feature, while identical functionality in other components still respects this setting.Either restore the conditional check or update the other components to match this new pattern.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomView/components/HeaderCallButton.tsx` around lines 19 - 47, Restore conditional rendering using useVideoConf(rid) and the callEnabled flag so the header call button respects server-side settings: call useVideoConf to get { showInitCallActionSheet, callEnabled, disabledTooltip }, return the <HeaderButton.Item ... /> only when callEnabled is true, and wire its disabled prop to disabledTooltip || disabled and its onPress to showInitCallActionSheet (or preserve handlePress but only render it when callEnabled). Ensure you still reference mediaSessionInstance.startCallByRoom/room or showInitCallActionSheet consistently so behavior matches RoomInfoButtons.tsx and CallSection.tsx.
🧹 Nitpick comments (8)
app/views/NewMessageView/HeaderNewMessage.tsx (1)
101-101: Prefer passingonChangeTextdirectly.The inline lambda is redundant here and creates a new function every render.
♻️ Proposed simplification
- <SearchBox onChangeText={(text: string) => onChangeText(text)} testID='new-message-view-search' /> + <SearchBox onChangeText={onChangeText} testID='new-message-view-search' />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/NewMessageView/HeaderNewMessage.tsx` at line 101, The inline lambda passed to SearchBox is redundant and creates a new function each render; instead, pass the existing onChangeText handler directly to the SearchBox prop. Replace the JSX line that reads SearchBox onChangeText={(text: string) => onChangeText(text)} ... with a direct prop reference (onChangeText={onChangeText}) so the SearchBox uses the same function identity; update the component in HeaderNewMessage.tsx accordingly.app/views/NewMessageView/Item.tsx (1)
40-47: Type interface may not match actual usage:roomis required but tests passnull.The
IIteminterface declaresroom: TSubscriptionModelas required, but the test file (line 134) passesnull as any. Consider making the prop optional to reflect actual usage:🔧 Suggested type fix
interface IItem { - room: TSubscriptionModel; + room?: TSubscriptionModel | null; name: string; username: string; onPress(): void; testID: string; onLongPress?: () => void; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/NewMessageView/Item.tsx` around lines 40 - 47, The IItem interface declares room: TSubscriptionModel as required but tests pass null, so update the prop type on IItem (the room property in Item.tsx) to accept null/undefined (e.g., make it optional or union with null: room?: TSubscriptionModel | null) and then adjust any usages inside the Item component (e.g., guard accesses to room in render/handlers) to handle the null/undefined case to avoid runtime errors.app/lib/services/voip/MediaSessionInstance.ts (2)
118-121: Consider handling the case whenthis.instanceis null.The optional chaining on
this.instance?.startCallmeans calls will silently fail if the media session isn't initialized. This could lead to a confusing user experience where pressing the call button does nothing.🔧 Suggested improvement
public startCall = (userId: string, actor: CallActorType) => { console.log('[VoIP] Starting call:', userId); + if (!this.instance) { + console.error('[VoIP] Cannot start call: media session not initialized'); + return; + } - this.instance?.startCall(actor, userId); + this.instance.startCall(actor, userId); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/lib/services/voip/MediaSessionInstance.ts` around lines 118 - 121, The startCall method in MediaSessionInstance uses optional chaining on this.instance so a missing media session silently no-ops; update startCall to explicitly guard for this.instance (the underlying media session instance) and handle the null case: if this.instance exists call this.instance.startCall(actor, userId), otherwise log a clear warning/error (or return/throw a predictable error value) and/or emit a failure event so callers know the call didn’t start; adjust any callers to expect the new return/throw behavior if you choose that path.
111-116: Consider adding logging whenotherUserIdis null.When
getUidDirectMessagereturns null (e.g., for group chats or invalid rooms), the call silently fails without any feedback. Adding a log statement would help with debugging.🔧 Suggested improvement
public startCallByRoom = (room: TSubscriptionModel | ISubscription) => { const otherUserId = getUidDirectMessage(room); if (otherUserId) { this.startCall(otherUserId, 'user'); + } else { + console.log('[VoIP] Cannot start call: unable to determine other user ID'); } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/lib/services/voip/MediaSessionInstance.ts` around lines 111 - 116, startCallByRoom in MediaSessionInstance silently returns when getUidDirectMessage(room) is null; update startCallByRoom to log a warning including identifying info from the room (e.g., room.id, room._id, room.type or the room object) before returning so failures are discoverable — use the instance logger (this.logger.warn/error) if available, otherwise console.warn, but leave the call to this.startCall(otherUserId, 'user') unchanged when a UID is present.app/views/NewMessageView/Item.test.tsx (1)
22-46: Consider extractingcreateMockRoomto a shared test utility.This helper function is duplicated nearly identically in
Item.stories.tsx(lines 18-42). Extracting it to a shared test utility (e.g.,__mocks__/mockRoom.tsor a test helpers file) would reduce duplication and ensure consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/NewMessageView/Item.test.tsx` around lines 22 - 46, The createMockRoom helper in Item.test.tsx is duplicated in Item.stories.tsx; extract it to a shared test utility (e.g., __mocks__/mockRoom.ts or tests/helpers/mockRoom.ts) and replace local definitions with an import. Move the function named createMockRoom (keeping its signature and Partial<TSubscriptionModel> overrides) into the new module, export it, then update both Item.test.tsx and Item.stories.tsx to import { createMockRoom } from that shared file so both files consume the single source of truth.app/views/RoomView/components/HeaderCallButton.tsx (1)
8-18: Consider removing unusedridprop.The
ridprop is declared in the interface but is commented out in the destructuring (line 9) and not used anywhere in the active code. If it's truly unused, removing it would clean up the API.🧹 Suggested cleanup
export const HeaderCallButton = ({ - // rid, disabled, accessibilityLabel, room }: { - rid: string; disabled: boolean; accessibilityLabel: string; room?: TSubscriptionModel; }): React.ReactElement | null => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomView/components/HeaderCallButton.tsx` around lines 8 - 18, The HeaderCallButton component declares an unused prop rid (commented out in the destructuring) — remove rid from the component's props type and from the parameter list of HeaderCallButton, and delete any leftover commented reference; also search callers that pass rid and stop passing it (or remove it from their JSX) so the prop type and all usages remain consistent. Ensure the exported HeaderCallButton signature only includes disabled, accessibilityLabel, and room (and update any related TS types/interfaces accordingly).app/views/RoomActionsView/components/CallSection.tsx (2)
17-20: Redundant null check for required prop.The
roomparameter is required per the TypeScript signature, so theif (!room) return;guard is unnecessary.Suggested simplification
const handleVoiceCallPress = () => { - if (!room) return; mediaSessionInstance.startCallByRoom(room); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomActionsView/components/CallSection.tsx` around lines 17 - 20, The function handleVoiceCallPress contains an unnecessary null check for the required prop room; remove the guard and directly call mediaSessionInstance.startCallByRoom(room) so the function becomes just a single call using the guaranteed parameter (refer to handleVoiceCallPress, room, and mediaSessionInstance.startCallByRoom) and run type-check/lint to ensure no remaining warnings.
24-24: Unresolved TODO comments for permission checks.The component has two TODO comments indicating that permission validation is pending:
- Line 24: check if the user has the permission to call
- Lines 50-51: return null if the user has no permission to voice/video
Consider addressing these before merging or creating a tracking issue. Allowing users to initiate calls without permission checks could lead to unauthorized access.
Would you like me to open an issue to track the permission check implementation?
Also applies to: 50-51
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomActionsView/components/CallSection.tsx` at line 24, The CallSection component currently has TODOs for permission checks—implement a permission guard in the CallSection React component so it verifies the current user's call permissions before rendering or enabling voice/video call UI; locate the CallSection component and use the app's existing auth/permission utilities (e.g., currentUser, usePermissions, hasPermission or similar) to check both voice and video call rights and return null (or hide/disable call buttons) when the user lacks those permissions, ensuring the checks run before any call-start handlers (e.g., button onClick) are rendered or invoked.
🤖 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/useCallStore.ts`:
- Around line 176-180: The endCall implementation unconditionally calls
call.hangup(), which breaks signaling for incoming ringing calls; update the
endCall logic in useCallStore (referencing the endCall function and get()) to
check call.state === 'ringing' and call.call.reject() for that case, otherwise
call.call.hangup(); then proceed to call RNCallKeep.endCall(callUUID) if
callUUID exists and finally call get().reset() to clear state.
In `@app/views/NewMessageView/index.tsx`:
- Around line 93-97: The Item component is receiving mixed search results
(ISearch and TSubscriptionModel) but the room prop and onPress/goRoom assume a
TSubscriptionModel; ensure you only pass a room when the entry is actually a
subscription/room: detect the type of itemModel (e.g., via a type guard or
checking a distinguishing field) and change the props passed to Item so
room={itemModel} and onPress={() => goRoom(itemModel)} are only used for
TSubscriptionModel entries, otherwise pass room={undefined} and provide a safe
no-op or alternate handler for non-room ISearch items to avoid routing non-room
objects into the room-based call path.
In `@app/views/NewMessageView/Item.tsx`:
- Around line 74-76: The call button defined with BorderlessButton
(onPress={handleCallPress}, testID={`${testID}-call`} and containing CustomIcon
name={'phone'}) is missing an accessibilityLabel for screen readers; add an
accessibilityLabel prop (e.g., accessibilityLabel={`${testID}-call-button` or a
human-readable string like "Call user") to the BorderlessButton so that
assistive technologies can announce its purpose while keeping existing testID,
onPress handler (handleCallPress), and styles intact.
In `@app/views/RoomActionsView/components/CallSection.tsx`:
- Around line 34-46: The Video_call List.Item is rendering only when callEnabled
is false due to an inverted condition; update the conditional in the CallSection
component so the block that renders the List.Item (which uses List.Item,
showInitCallActionSheet, disabledTooltip/disabled) is shown when callEnabled is
true (replace the current negated check with a positive check for callEnabled)
so its behavior matches RoomInfoButtons.tsx.
---
Outside diff comments:
In `@app/views/RoomView/components/HeaderCallButton.tsx`:
- Around line 19-47: Restore conditional rendering using useVideoConf(rid) and
the callEnabled flag so the header call button respects server-side settings:
call useVideoConf to get { showInitCallActionSheet, callEnabled, disabledTooltip
}, return the <HeaderButton.Item ... /> only when callEnabled is true, and wire
its disabled prop to disabledTooltip || disabled and its onPress to
showInitCallActionSheet (or preserve handlePress but only render it when
callEnabled). Ensure you still reference
mediaSessionInstance.startCallByRoom/room or showInitCallActionSheet
consistently so behavior matches RoomInfoButtons.tsx and CallSection.tsx.
---
Nitpick comments:
In `@app/lib/services/voip/MediaSessionInstance.ts`:
- Around line 118-121: The startCall method in MediaSessionInstance uses
optional chaining on this.instance so a missing media session silently no-ops;
update startCall to explicitly guard for this.instance (the underlying media
session instance) and handle the null case: if this.instance exists call
this.instance.startCall(actor, userId), otherwise log a clear warning/error (or
return/throw a predictable error value) and/or emit a failure event so callers
know the call didn’t start; adjust any callers to expect the new return/throw
behavior if you choose that path.
- Around line 111-116: startCallByRoom in MediaSessionInstance silently returns
when getUidDirectMessage(room) is null; update startCallByRoom to log a warning
including identifying info from the room (e.g., room.id, room._id, room.type or
the room object) before returning so failures are discoverable — use the
instance logger (this.logger.warn/error) if available, otherwise console.warn,
but leave the call to this.startCall(otherUserId, 'user') unchanged when a UID
is present.
In `@app/views/NewMessageView/HeaderNewMessage.tsx`:
- Line 101: The inline lambda passed to SearchBox is redundant and creates a new
function each render; instead, pass the existing onChangeText handler directly
to the SearchBox prop. Replace the JSX line that reads SearchBox
onChangeText={(text: string) => onChangeText(text)} ... with a direct prop
reference (onChangeText={onChangeText}) so the SearchBox uses the same function
identity; update the component in HeaderNewMessage.tsx accordingly.
In `@app/views/NewMessageView/Item.test.tsx`:
- Around line 22-46: The createMockRoom helper in Item.test.tsx is duplicated in
Item.stories.tsx; extract it to a shared test utility (e.g.,
__mocks__/mockRoom.ts or tests/helpers/mockRoom.ts) and replace local
definitions with an import. Move the function named createMockRoom (keeping its
signature and Partial<TSubscriptionModel> overrides) into the new module, export
it, then update both Item.test.tsx and Item.stories.tsx to import {
createMockRoom } from that shared file so both files consume the single source
of truth.
In `@app/views/NewMessageView/Item.tsx`:
- Around line 40-47: The IItem interface declares room: TSubscriptionModel as
required but tests pass null, so update the prop type on IItem (the room
property in Item.tsx) to accept null/undefined (e.g., make it optional or union
with null: room?: TSubscriptionModel | null) and then adjust any usages inside
the Item component (e.g., guard accesses to room in render/handlers) to handle
the null/undefined case to avoid runtime errors.
In `@app/views/RoomActionsView/components/CallSection.tsx`:
- Around line 17-20: The function handleVoiceCallPress contains an unnecessary
null check for the required prop room; remove the guard and directly call
mediaSessionInstance.startCallByRoom(room) so the function becomes just a single
call using the guaranteed parameter (refer to handleVoiceCallPress, room, and
mediaSessionInstance.startCallByRoom) and run type-check/lint to ensure no
remaining warnings.
- Line 24: The CallSection component currently has TODOs for permission
checks—implement a permission guard in the CallSection React component so it
verifies the current user's call permissions before rendering or enabling
voice/video call UI; locate the CallSection component and use the app's existing
auth/permission utilities (e.g., currentUser, usePermissions, hasPermission or
similar) to check both voice and video call rights and return null (or
hide/disable call buttons) when the user lacks those permissions, ensuring the
checks run before any call-start handlers (e.g., button onClick) are rendered or
invoked.
In `@app/views/RoomView/components/HeaderCallButton.tsx`:
- Around line 8-18: The HeaderCallButton component declares an unused prop rid
(commented out in the destructuring) — remove rid from the component's props
type and from the parameter list of HeaderCallButton, and delete any leftover
commented reference; also search callers that pass rid and stop passing it (or
remove it from their JSX) so the prop type and all usages remain consistent.
Ensure the exported HeaderCallButton signature only includes disabled,
accessibilityLabel, and room (and update any related TS types/interfaces
accordingly).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snapis excluded by!**/*.snapapp/containers/SearchBox/__snapshots__/SearchBox.test.tsx.snapis excluded by!**/*.snapapp/views/NewMessageView/__snapshots__/Item.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (19)
app/containers/MediaCallHeader/components/Subtitle.tsxapp/containers/MediaCallHeader/components/Title.tsxapp/containers/SearchBox/index.tsxapp/i18n/locales/en.jsonapp/lib/services/voip/MediaSessionInstance.tsapp/lib/services/voip/MediaSessionStore.tsapp/lib/services/voip/useCallStore.tsapp/views/NewMessageView/ButtonCreate.tsxapp/views/NewMessageView/HeaderNewMessage.tsxapp/views/NewMessageView/Item.stories.tsxapp/views/NewMessageView/Item.test.tsxapp/views/NewMessageView/Item.tsxapp/views/NewMessageView/index.tsxapp/views/RoomActionsView/components/CallSection.tsxapp/views/RoomActionsView/index.tsxapp/views/RoomInfoView/components/RoomInfoButtons.tsxapp/views/RoomView/RightButtons.tsxapp/views/RoomView/components/HeaderCallButton.tsxapp/views/RoomsListView/hooks/useHeader.tsx
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (12)
app/views/NewMessageView/ButtonCreate.tsx (1)
app/lib/constants/colors.ts (1)
themes(304-304)
app/views/RoomView/components/HeaderCallButton.tsx (2)
app/definitions/ISubscription.ts (1)
TSubscriptionModel(133-136)app/lib/services/voip/MediaSessionInstance.ts (1)
mediaSessionInstance(185-185)
app/views/NewMessageView/HeaderNewMessage.tsx (3)
app/lib/constants/colors.ts (1)
themes(304-304)app/lib/services/restApi.ts (4)
createChannel(31-59)createTeam(174-203)createGroupChat(939-945)createDiscussion(128-151)app/ee/omnichannel/sagas/inquiry.js (1)
serverVersion(17-17)
app/lib/services/voip/MediaSessionStore.ts (1)
app/lib/services/voip/MediaCallLogger.ts (1)
MediaCallLogger(3-21)
app/views/RoomActionsView/components/CallSection.tsx (2)
app/definitions/ISubscription.ts (1)
TSubscriptionModel(133-136)app/lib/services/voip/MediaSessionInstance.ts (1)
mediaSessionInstance(185-185)
app/views/NewMessageView/Item.tsx (4)
app/definitions/ISubscription.ts (1)
TSubscriptionModel(133-136)app/theme.tsx (1)
useTheme(29-29)app/lib/services/voip/MediaSessionInstance.ts (1)
mediaSessionInstance(185-185)app/lib/constants/colors.ts (1)
colors(280-302)
app/lib/services/voip/MediaSessionInstance.ts (3)
app/lib/services/voip/useCallStore.ts (1)
useCallStore(58-199)app/definitions/ISubscription.ts (2)
TSubscriptionModel(133-136)ISubscription(40-126)app/lib/methods/helpers/helpers.ts (1)
getUidDirectMessage(17-41)
app/views/NewMessageView/Item.test.tsx (3)
app/definitions/ISubscription.ts (1)
TSubscriptionModel(133-136)app/actions/login.ts (1)
setUser(91-96).rnstorybook/generateSnapshots.tsx (1)
generateSnapshots(10-22)
app/views/NewMessageView/index.tsx (1)
app/lib/constants/colors.ts (1)
colors(280-302)
app/views/RoomActionsView/index.tsx (2)
app/views/RoomActionsView/components/CallSection.tsx (1)
CallSection(8-52)app/lib/encryption/utils.ts (1)
hasE2EEWarning(176-192)
app/views/NewMessageView/Item.stories.tsx (1)
app/definitions/ISubscription.ts (1)
TSubscriptionModel(133-136)
app/views/RoomInfoView/components/RoomInfoButtons.tsx (5)
app/containers/ActionSheet/Provider.tsx (2)
useActionSheet(44-44)TActionSheetOptionsItem(8-20)app/lib/hooks/useVideoConf/index.tsx (1)
useVideoConf(29-82)app/lib/services/voip/MediaSessionInstance.ts (1)
mediaSessionInstance(185-185)app/lib/methods/helpers/room.ts (1)
isBlocked(7-15)app/containers/MessageComposer/components/Buttons/BaseButton.tsx (1)
BaseButton(23-40)
🔇 Additional comments (20)
app/containers/SearchBox/index.tsx (1)
10-13: Layout update looks good.Directional margins plus explicit bottom-margin override are clear and safe for this component-level spacing tweak.
app/views/NewMessageView/HeaderNewMessage.tsx (1)
73-100: UI structure refactor is clean and behavior-preserving.The updated container/background and conditional button rendering remain consistent with existing permission/version gates, and the flattened layout improves readability.
app/views/RoomsListView/hooks/useHeader.tsx (1)
127-127: Icon swap is safe and consistent.Line 127 updates only the icon glyph; action wiring, accessibility text, and disabled behavior are preserved.
app/views/NewMessageView/ButtonCreate.tsx (1)
26-26: Theme background update looks good.Line 26 keeps the row styling aligned with theme tokens used elsewhere in this component.
app/containers/MediaCallHeader/components/Title.tsx (1)
35-35: Status size increase is a clean UI adjustment.Line 35 is a presentation-only change and keeps call header behavior intact.
app/i18n/locales/en.json (1)
117-117: Call-related localization updates are consistent.Line 117 and Line 979 align status/action wording for ongoing call flows.
Also applies to: 979-979
app/containers/MediaCallHeader/components/Subtitle.tsx (1)
27-29: Ringing-state handling is clearer now.Lines 27-29 correctly separate ringing from the generic connecting path, improving subtitle accuracy.
app/views/NewMessageView/index.tsx (1)
104-104: List background token change is good.Line 104 is a straightforward visual update and does not affect list behavior.
app/lib/services/voip/MediaSessionStore.ts (1)
63-64: No action required. Thefeatures: ['audio']option is supported and correctly implemented.The
@rocket.chat/media-signalingversion 0.1.1 explicitly definesfeatures: CallFeature[]as part ofMediaSignalingSessionConfig.CallFeatureis a literal type limited to"audio", makingfeatures: ['audio']the only valid value. This is not a silent no-op configuration—it's a documented, required part of the session initialization.app/views/NewMessageView/Item.test.tsx (2)
12-18: LGTM: Mock setup for MediaSessionInstanceThe mock correctly captures and tracks calls to
startCallByRoom, enabling verification of call button behavior.
48-157: LGTM: Test suite is comprehensive and well-structured.The tests cover:
- Basic rendering and display
- Press and long press callbacks
- Call button integration with MediaSessionInstance
- Edge case handling (null room)
- Accessibility labels
- Snapshot generation
app/views/NewMessageView/Item.stories.tsx (1)
1-97: LGTM: Storybook stories provide good coverage of component variants.The stories demonstrate the Item component in various configurations (default, long press, custom style, long name), which is useful for visual testing and documentation.
app/lib/services/voip/MediaSessionInstance.ts (1)
81-84: LGTM: Caller role handling correctly initializes call state and navigation.When the call role is 'caller', the code properly sets up the call in the store and navigates to the CallView with the generated UUID.
app/views/RoomInfoView/components/RoomInfoButtons.tsx (3)
111-188: LGTM: Data-driven button rendering with overflow handling.The refactored approach using
allItemsarray withButtonConfigtype is clean and maintainable. The overflow logic (showing "More" button when >4 items) is well-implemented.
104-109: TheVoice_calli18n key is properly defined. The key exists inapp/i18n/locales/en.jsonand translates to "Voice call". No issues found.
85-85: No action needed. The React Compiler is already enabled and properly configured withcompilationMode: 'annotation'inbabel.config.js, so the'use memo'directive on line 85 will function as intended and provide the expected optimization benefits.app/views/RoomView/components/HeaderCallButton.tsx (1)
35-38: LGTM: handlePress correctly guards against undefined room.The early return when
roomis undefined prevents callingstartCallByRoomwith invalid data.app/views/RoomView/RightButtons.tsx (1)
527-532: LGTM: Room prop correctly passed to HeaderCallButton.The change properly passes
this.roomto enable room-based call initiation. SinceHeaderCallButtonacceptsroomas optional, the undefined case (when subscription isn't found) is handled gracefully.app/views/NewMessageView/Item.tsx (1)
49-79: LGTM: Component implementation is clean and well-structured.The Item component properly integrates the Avatar, text display, and call button with appropriate theming and press handlers.
app/views/RoomActionsView/index.tsx (1)
1092-1092: LGTM: CallSection prop updated to use room object.The change correctly passes the
roomobject instead ofrid, aligning with the updatedCallSectionAPI that now uses room-based call initiation viamediaSessionInstance.startCallByRoom(room).
…call initiation and improve UI interactions
…eactNative into feat.start-call
There was a problem hiding this comment.
♻️ Duplicate comments (1)
app/views/NewMessageView/index.tsx (1)
92-99:⚠️ Potential issue | 🟠 MajorGuard call target derivation to user/DM entries only.
At Line 92,
userIdis derived fromitemSearch._idfor any search hit. Since search runs withfilterRooms: false, non-user results can flow here, and the call button can attempt a user call with a non-user id.Suggested fix
- const userId = itemSearch.search ? itemSearch._id : getUidDirectMessage(itemModel); + const isSearchEntry = Boolean(itemSearch.search); + // Use your existing discriminant for "direct/user" entries (example: t === 'd') + const isDirectTarget = isSearchEntry ? itemSearch.t === 'd' : itemModel.t === 'd'; + const userId = isDirectTarget ? (isSearchEntry ? itemSearch._id : getUidDirectMessage(itemModel)) : '';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/NewMessageView/index.tsx` around lines 92 - 99, The current derivation of userId uses itemSearch._id for any search hit which can be a non-user result; change the logic so userId is taken from itemSearch._id only when the search result is a user/DM entry (e.g., check a discriminant like itemSearch.type === 'user' or itemSearch.type === 'd' or ensure itemSearch._id exists AND itemSearch.isUser flag), otherwise fall back to getUidDirectMessage(itemModel) or null; update the Item props and any call-button rendering (the Item component usage and call-related logic) to only enable/call when the computed userId is valid/truthy.
🧹 Nitpick comments (2)
app/views/RoomActionsView/components/CallSection.tsx (2)
17-20: Redundant null check:roomis typed as non-optional.The guard
if (!room) return;is unreachable if the typeroom: TSubscriptionModelis accurate. If callers might passundefined, update the type toTSubscriptionModel | undefined; otherwise, remove the check.♻️ Proposed fix (if type is accurate)
const handleVoiceCallPress = () => { - if (!room) return; mediaSessionInstance.startCallByRoom(room); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomActionsView/components/CallSection.tsx` around lines 17 - 20, The redundant null check in handleVoiceCallPress should be removed if room is truly non-optional: delete the guard `if (!room) return;` and call `mediaSessionInstance.startCallByRoom(room);` directly; if callers may pass undefined, instead update the parameter/type where `room` is declared (type `TSubscriptionModel`) to `TSubscriptionModel | undefined` and keep the guard so `mediaSessionInstance.startCallByRoom` only receives a valid `room`.
50-51: Remove dead code or address the TODO.The commented-out
return null;and trailing TODO appear to be leftover scaffolding. If permission checks are planned, consider tracking them in a separate issue rather than leaving dead code.♻️ Proposed cleanup
</List.Section> ); - - // TODO: return null if the user has no permission to voice/video - // return null; }Do you want me to open an issue to track implementing the permission check for voice/video calls mentioned in the TODOs at lines 24 and 50?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomActionsView/components/CallSection.tsx` around lines 50 - 51, In CallSection.tsx remove the dead commented-out "return null;" and the TODO (or implement the intended permission check) to avoid leftover scaffolding: either delete the commented line and TODO inside the CallSection component, or replace them by an explicit permission guard that checks the room/user permissions (e.g., via an existing permission helper or prop like canUseVoiceVideo) and returns null when false; ensure you update the component signature or callers to accept a permission flag if you implement the guard and add a test or open an issue tracking the full permissions work if you opt to simply remove the TODO.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@app/views/NewMessageView/index.tsx`:
- Around line 92-99: The current derivation of userId uses itemSearch._id for
any search hit which can be a non-user result; change the logic so userId is
taken from itemSearch._id only when the search result is a user/DM entry (e.g.,
check a discriminant like itemSearch.type === 'user' or itemSearch.type === 'd'
or ensure itemSearch._id exists AND itemSearch.isUser flag), otherwise fall back
to getUidDirectMessage(itemModel) or null; update the Item props and any
call-button rendering (the Item component usage and call-related logic) to only
enable/call when the computed userId is valid/truthy.
---
Nitpick comments:
In `@app/views/RoomActionsView/components/CallSection.tsx`:
- Around line 17-20: The redundant null check in handleVoiceCallPress should be
removed if room is truly non-optional: delete the guard `if (!room) return;` and
call `mediaSessionInstance.startCallByRoom(room);` directly; if callers may pass
undefined, instead update the parameter/type where `room` is declared (type
`TSubscriptionModel`) to `TSubscriptionModel | undefined` and keep the guard so
`mediaSessionInstance.startCallByRoom` only receives a valid `room`.
- Around line 50-51: In CallSection.tsx remove the dead commented-out "return
null;" and the TODO (or implement the intended permission check) to avoid
leftover scaffolding: either delete the commented line and TODO inside the
CallSection component, or replace them by an explicit permission guard that
checks the room/user permissions (e.g., via an existing permission helper or
prop like canUseVoiceVideo) and returns null when false; ensure you update the
component signature or callers to accept a permission flag if you implement the
guard and add a test or open an issue tracking the full permissions work if you
opt to simply remove the TODO.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
app/views/NewMessageView/Item.tsxapp/views/NewMessageView/index.tsxapp/views/RoomActionsView/components/CallSection.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
🧠 Learnings (1)
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
Applied to files:
app/views/NewMessageView/Item.tsxapp/views/RoomActionsView/components/CallSection.tsx
🔇 Additional comments (4)
app/views/RoomActionsView/components/CallSection.tsx (3)
8-15: LGTM on the API change to acceptroomobject.The component now accepts the full
TSubscriptionModelinstead of justrid, which aligns with the refactoring to usemediaSessionInstance.startCallByRoom(room). Theroom.ridextraction foruseVideoConfis correct.
34-46: Correct conditional forVideo_callvisibility.The condition
{callEnabled ? (now correctly shows the video call option only when the feature is enabled, consistent withRoomInfoButtons.tsx.
25-27: TheList.Itemcomponent already handles i18n translation internally. ThetranslateTitleprop defaults totrue, which causes the component to automatically callI18n.t()on string titles. The titles'Voice_call'and'Video_call'in CallSection.tsx will be translated without requiring explicit wrapping.app/views/NewMessageView/Item.tsx (1)
74-80: Nice accessibility fix on the call button.
accessibilityLabelandaccessibilityRole='button'on the call action are good additions for screen-reader usability.
a51efe4 to
00f4b33
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/RoomInfoView/components/RoomInfoButtons.stories.tsx`:
- Line 4: The story imports RoomInfoVideoConfOverrideContext which is not
exported from RoomInfoButtons.tsx; fix by exporting the context from that module
(or defining and exporting it in the story) so the symbol is available — update
RoomInfoButtons.tsx to export the existing RoomInfoVideoConfOverrideContext (as
a named export alongside RoomInfoButtons) or add an explicit export declaration
for the context object used by RoomInfoButtons so the import in
RoomInfoButtons.stories.tsx (import { RoomInfoButtons,
RoomInfoVideoConfOverrideContext }) resolves.
- Line 7: The decorator is dispatching the async loader setEnterpriseModules
(which takes no payload) with an array; replace that with the payload-capable
action creator used for mocked store setup (instead of setEnterpriseModules,
import and dispatch the action creator that accepts modules, e.g., the
setEnterpriseModules payload action) and remove the incorrect payload passed to
the async loader; update all occurrences (including the other spots mentioned
around lines 56-57) to import and call the correct payload action creator and
keep clearEnterpriseModules usage as-is.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.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!**/*.snap
📒 Files selected for processing (12)
app/lib/hooks/useMediaCallPermission/index.tsapp/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.tsapp/lib/hooks/useMediaCallPermission/useMediaCallPermission.tsapp/views/NewMessageView/Item.stories.tsxapp/views/NewMessageView/Item.test.tsxapp/views/NewMessageView/Item.tsxapp/views/RoomActionsView/components/CallSection.tsxapp/views/RoomInfoView/components/RoomInfoABAC.stories.tsxapp/views/RoomInfoView/components/RoomInfoButtons.stories.tsxapp/views/RoomInfoView/components/RoomInfoButtons.test.tsxapp/views/RoomInfoView/components/RoomInfoButtons.tsxapp/views/RoomView/components/HeaderCallButton.tsx
✅ Files skipped from review due to trivial changes (1)
- app/lib/hooks/useMediaCallPermission/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- app/views/NewMessageView/Item.test.tsx
- app/views/RoomView/components/HeaderCallButton.tsx
- app/views/NewMessageView/Item.stories.tsx
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
Applied to files:
app/views/NewMessageView/Item.tsxapp/views/RoomActionsView/components/CallSection.tsx
🧬 Code graph analysis (5)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts (3)
app/lib/hooks/useAppSelector.ts (1)
useAppSelector(6-6)app/lib/methods/enterpriseModules.ts (1)
isVoipModuleAvailable(65-68)app/lib/hooks/usePermissions.ts (1)
usePermissions(52-59)
app/views/NewMessageView/Item.tsx (4)
app/theme.tsx (1)
useTheme(29-29)app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts (1)
useMediaCallPermission(4-12)app/lib/services/voip/MediaSessionInstance.ts (1)
mediaSessionInstance(185-185)app/lib/constants/colors.ts (1)
colors(280-302)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts (1)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts (1)
useMediaCallPermission(4-12)
app/views/RoomInfoView/components/RoomInfoButtons.test.tsx (3)
app/definitions/ISubscription.ts (1)
ISubscription(40-126)app/views/RoomInfoView/components/RoomInfoButtons.tsx (1)
RoomInfoButtons(72-191).rnstorybook/generateSnapshots.tsx (1)
generateSnapshots(10-22)
app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx (4)
app/definitions/ISubscription.ts (1)
ISubscription(40-126)app/lib/methods/getPermissions.ts (1)
setPermissions(76-85)app/actions/login.ts (1)
setUser(91-96)app/views/RoomInfoView/components/RoomInfoButtons.tsx (1)
RoomInfoButtons(72-191)
🔇 Additional comments (7)
app/views/RoomInfoView/components/RoomInfoABAC.stories.tsx (1)
10-10: Story title namespace update is clean.Keeps this story grouped under
RoomInfoViewwithout affecting runtime behavior.app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts (1)
4-12: Permission gating logic looks solid.The hook cleanly centralizes VOIP-module + permission checks and keeps call-site conditions simple.
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts (1)
21-65: Good behavior-matrix coverage for the new hook.These cases validate the key combinations and protect the permission gate from regressions.
app/views/NewMessageView/Item.tsx (1)
76-86: Voice call action integration looks good.Nice addition of permission-gated rendering plus explicit accessibility metadata on the call button.
app/views/RoomActionsView/components/CallSection.tsx (1)
24-57: Call action visibility logic is now correctly structured.Returning
nullonly when neither call path is available and independently rendering Voice/Video options is a good improvement.app/views/RoomInfoView/components/RoomInfoButtons.test.tsx (1)
116-170: Test coverage for room action buttons is strong.The suite exercises the new voice/video flows and visibility gating in meaningful scenarios.
app/views/RoomInfoView/components/RoomInfoButtons.tsx (1)
112-189: Data-driven button configuration is a solid refactor.This makes action visibility/behavior easier to reason about and keeps overflow handling centralized.
d5445fc to
df28ae6
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx (1)
48-54: Drop unusedshowActionSheetfrom story handlers.
RoomInfoButtonsdoes not accept this prop, so keeping it indefaultHandlersadds dead setup in stories.♻️ Proposed cleanup
const defaultHandlers = { handleCreateDirectMessage: () => {}, handleIgnoreUser: () => {}, handleBlockUser: () => {}, - handleReportUser: () => {}, - showActionSheet: () => {} + handleReportUser: () => {} };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx` around lines 48 - 54, The story's defaultHandlers includes an unused prop showActionSheet that RoomInfoButtons doesn't accept; remove showActionSheet from the defaultHandlers object in RoomInfoButtons.stories.tsx and any references to it in that file (leave handleCreateDirectMessage, handleIgnoreUser, handleBlockUser, handleReportUser intact), and update any related story args/type annotations if they referenced showActionSheet so the story setup contains only props actually accepted by the RoomInfoButtons 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/RoomInfoView/components/RoomInfoButtons.tsx`:
- Around line 98-102: Remove the debug console.log calls that expose room and
user identifiers: delete the logs referencing isIgnored, room, roomUserId, and
isBlocked in RoomInfoButtons (the block that currently does
console.log('isIgnored', isIgnored); console.log('room', room);
console.log('roomUserId', roomUserId); console.log('isBlocked', isBlocked);). If
you still need visibility for debugging, replace them with a single,
non-identifying log or use a debug-level logger that redacts sensitive fields
(log only boolean flags such as isIgnored or isBlocked without outputting the
entire room or user id).
---
Nitpick comments:
In `@app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx`:
- Around line 48-54: The story's defaultHandlers includes an unused prop
showActionSheet that RoomInfoButtons doesn't accept; remove showActionSheet from
the defaultHandlers object in RoomInfoButtons.stories.tsx and any references to
it in that file (leave handleCreateDirectMessage, handleIgnoreUser,
handleBlockUser, handleReportUser intact), and update any related story
args/type annotations if they referenced showActionSheet so the story setup
contains only props actually accepted by the RoomInfoButtons component.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.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!**/*.snap
📒 Files selected for processing (5)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.tsapp/views/NewMessageView/Item.stories.tsxapp/views/NewMessageView/Item.test.tsxapp/views/RoomInfoView/components/RoomInfoButtons.stories.tsxapp/views/RoomInfoView/components/RoomInfoButtons.tsx
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (3)
app/views/NewMessageView/Item.stories.tsx (2)
app/lib/methods/enterpriseModules.ts (1)
setEnterpriseModules(11-30)app/actions/login.ts (1)
setUser(91-96)
app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx (5)
app/definitions/ISubscription.ts (1)
ISubscription(40-126)app/lib/methods/enterpriseModules.ts (1)
setEnterpriseModules(11-30)app/lib/methods/getPermissions.ts (1)
setPermissions(76-85)app/actions/login.ts (1)
setUser(91-96)app/views/RoomInfoView/components/RoomInfoButtons.tsx (1)
RoomInfoButtons(72-195)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts (1)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts (1)
useMediaCallPermission(4-12)
🔇 Additional comments (3)
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts (1)
21-65: Strong permission-matrix coverage.The test cases exercise the expected combinations of VOIP module availability and permission flags, and the assertions make the hook contract explicit.
app/views/NewMessageView/Item.test.tsx (1)
87-137: Good behavioral coverage around call initiation and gating.The suite verifies both positive and negative call-button paths (permission denied and falsy
userId) in addition to interaction and snapshot coverage.app/views/NewMessageView/Item.stories.tsx (1)
20-72: Story coverage for permission states looks good.Using dedicated decorators for enabled/disabled voice-call conditions makes the UI states easy to validate in Storybook.
10e8fe0 to
41bfaf7
Compare
…/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
Issue(s)
https://rocketchat.atlassian.net/browse/VMUX-14
How to test or reproduce
Screenshots
Types of changes
Checklist
Further comments
Summary by CodeRabbit