Skip to content

feat: start call#7024

Merged
diegolmello merged 9 commits into
feat.voip-lib-newfrom
feat.start-call
Mar 3, 2026
Merged

feat: start call#7024
diegolmello merged 9 commits into
feat.voip-lib-newfrom
feat.start-call

Conversation

@diegolmello

@diegolmello diegolmello commented Mar 2, 2026

Copy link
Copy Markdown
Member

Proposed changes

Issue(s)

https://rocketchat.atlassian.net/browse/VMUX-14

How to test or reproduce

Screenshots

Screenshot_1772548833 Screenshot_1772548830 Screenshot_1772548810

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

Summary by CodeRabbit

  • New Features
    • One-tap voice/video call actions added across rooms, headers, room info, room actions, and new-message lists; calls can be started from room and list items and are gated by media-call permission.
  • Style
    • Adjusted header/layout spacing, background tokens, status indicator size, search input margins, and item background; "Calling" label changed to "Calling...".
  • Bug Fixes
    • Improved call status text handling (distinct "Calling" vs "Connecting").
  • Documentation / Tests
    • Added Storybook stories and unit tests for new call UI and media-call permission hook.

@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10e8fe0 and 41bfaf7.

⛔ Files ignored due to path filters (3)
  • app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snap is excluded by !**/*.snap
  • app/views/NewMessageView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
  • app/views/RoomInfoView/components/__snapshots__/RoomInfoButtons.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • app/views/NewMessageView/Item.stories.tsx
  • app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx

Walkthrough

Adds 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

Cohort / File(s) Summary
VoIP core
app/lib/services/voip/MediaSessionInstance.ts, app/lib/services/voip/MediaSessionStore.ts, app/lib/services/voip/useCallStore.ts
Added startCallByRoom and startCall on MediaSessionInstance; MediaSignalingSession configured with features: ['audio']; endCall simplified to always call hangup.
Media call header
app/containers/MediaCallHeader/components/Subtitle.tsx, app/containers/MediaCallHeader/components/Title.tsx
Subtitle: explicit ringing → "Calling" and non-active → "Connecting"; Title: Status size increased from 12 → 16.
Permissions hook
app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts, app/lib/hooks/useMediaCallPermission/index.ts, app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts
New useMediaCallPermission hook, barrel export, and unit tests to determine voice-call permissions via enterprise modules and permission flags.
NewMessage item, stories & tests
app/views/NewMessageView/Item.tsx, app/views/NewMessageView/Item.test.tsx, app/views/NewMessageView/Item.stories.tsx
New Item component rendering avatar/name and a call button wired to mediaSessionInstance.startCall; comprehensive tests and Storybook stories added.
NewMessage integration & layout
app/views/NewMessageView/index.tsx, app/views/NewMessageView/HeaderNewMessage.tsx, app/views/NewMessageView/ButtonCreate.tsx
Replaced UserItem with local Item; compute/pass userId via getUidDirectMessage; adjusted layout and background tokens; List.Item backgroundColor set.
Room actions & header call buttons
app/views/RoomActionsView/components/CallSection.tsx, app/views/RoomActionsView/index.tsx, app/views/RoomView/components/HeaderCallButton.tsx, app/views/RoomView/RightButtons.tsx
CallSection and HeaderCallButton now accept/pass room: TSubscriptionModel; voice/video actions use mediaSessionInstance.startCallByRoom; rendering guarded by useMediaCallPermission.
Room info actions & overflow
app/views/RoomInfoView/components/RoomInfoButtons.tsx, app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx, app/views/RoomInfoView/components/RoomInfoButtons.test.tsx
Refactored to a data-driven action list with overflow ActionSheet; consolidated action rendering; added voice/video call handling via startCallByRoom; stories and tests added.
Search, header icon & i18n
app/containers/SearchBox/index.tsx, app/views/RoomsListView/hooks/useHeader.tsx, app/i18n/locales/en.json
Adjusted SearchBox margins; changed header icon createadd; updated translation CallingCalling... and added Video_call.
Misc & exports
app/lib/hooks/useMediaCallPermission/index.ts, story title tweaks, small UI layout changes
Added barrel re-export and minor Storybook title/visual tweaks and small UI layout adjustments.

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)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • feat: Update call UI #6990 — Modifies MediaCallHeader components (Subtitle/Title); closely related to status/label and sizing changes.

Suggested reviewers

  • OtavioStasiak
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Most changes directly support call initiation, but some changes appear tangential: translation updates ('Calling...' and 'Video_call'), styling adjustments, story file additions, and icon changes ('create' to 'add') are not core to call-starting functionality. Clarify whether translation updates, styling changes, icon changes, and new Storybook stories are necessary for the call-starting feature or if they should be split into separate PRs.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: start call' clearly and concisely describes the main objective of the PR - adding the ability to initiate calls from various UI locations.
Linked Issues check ✅ Passed The PR implements call initiation functionality across multiple components (NewMessageView, RoomInfoButtons, HeaderCallButton, etc.) via mediaSessionInstance.startCall and startCallByRoom methods, directly addressing the VMUX-14 requirement to add 'Start call' triggers.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

The call button should be conditionally rendered based on callEnabled to match other components.

The callEnabled check from useVideoConf is still actively used in RoomInfoButtons.tsx and CallSection.tsx to respect server-side call configuration. Removing it from HeaderCallButton creates 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 passing onChangeText directly.

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: room is required but tests pass null.

The IItem interface declares room: TSubscriptionModel as required, but the test file (line 134) passes null 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 when this.instance is null.

The optional chaining on this.instance?.startCall means 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 when otherUserId is null.

When getUidDirectMessage returns 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 extracting createMockRoom to 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.ts or 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 unused rid prop.

The rid prop 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 room parameter is required per the TypeScript signature, so the if (!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

📥 Commits

Reviewing files that changed from the base of the PR and between 104471c and b2921ab.

⛔ Files ignored due to path filters (3)
  • app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snap is excluded by !**/*.snap
  • app/containers/SearchBox/__snapshots__/SearchBox.test.tsx.snap is excluded by !**/*.snap
  • app/views/NewMessageView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (19)
  • app/containers/MediaCallHeader/components/Subtitle.tsx
  • app/containers/MediaCallHeader/components/Title.tsx
  • app/containers/SearchBox/index.tsx
  • app/i18n/locales/en.json
  • app/lib/services/voip/MediaSessionInstance.ts
  • app/lib/services/voip/MediaSessionStore.ts
  • app/lib/services/voip/useCallStore.ts
  • app/views/NewMessageView/ButtonCreate.tsx
  • app/views/NewMessageView/HeaderNewMessage.tsx
  • app/views/NewMessageView/Item.stories.tsx
  • app/views/NewMessageView/Item.test.tsx
  • app/views/NewMessageView/Item.tsx
  • app/views/NewMessageView/index.tsx
  • app/views/RoomActionsView/components/CallSection.tsx
  • app/views/RoomActionsView/index.tsx
  • app/views/RoomInfoView/components/RoomInfoButtons.tsx
  • app/views/RoomView/RightButtons.tsx
  • app/views/RoomView/components/HeaderCallButton.tsx
  • app/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. The features: ['audio'] option is supported and correctly implemented.

The @rocket.chat/media-signaling version 0.1.1 explicitly defines features: CallFeature[] as part of MediaSignalingSessionConfig. CallFeature is a literal type limited to "audio", making features: ['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 MediaSessionInstance

The 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 allItems array with ButtonConfig type is clean and maintainable. The overflow logic (showing "More" button when >4 items) is well-implemented.


104-109: The Voice_call i18n key is properly defined. The key exists in app/i18n/locales/en.json and translates to "Voice call". No issues found.


85-85: No action needed. The React Compiler is already enabled and properly configured with compilationMode: 'annotation' in babel.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 room is undefined prevents calling startCallByRoom with invalid data.

app/views/RoomView/RightButtons.tsx (1)

527-532: LGTM: Room prop correctly passed to HeaderCallButton.

The change properly passes this.room to enable room-based call initiation. Since HeaderCallButton accepts room as 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 room object instead of rid, aligning with the updated CallSection API that now uses room-based call initiation via mediaSessionInstance.startCallByRoom(room).

Comment thread app/lib/services/voip/useCallStore.ts
Comment thread app/views/NewMessageView/index.tsx
Comment thread app/views/NewMessageView/Item.tsx Outdated
Comment thread app/views/RoomActionsView/components/CallSection.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
app/views/NewMessageView/index.tsx (1)

92-99: ⚠️ Potential issue | 🟠 Major

Guard call target derivation to user/DM entries only.

At Line 92, userId is derived from itemSearch._id for any search hit. Since search runs with filterRooms: 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: room is typed as non-optional.

The guard if (!room) return; is unreachable if the type room: TSubscriptionModel is accurate. If callers might pass undefined, update the type to TSubscriptionModel | 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

📥 Commits

Reviewing files that changed from the base of the PR and between b2921ab and 6498323.

📒 Files selected for processing (3)
  • app/views/NewMessageView/Item.tsx
  • app/views/NewMessageView/index.tsx
  • app/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.tsx
  • app/views/RoomActionsView/components/CallSection.tsx
🔇 Additional comments (4)
app/views/RoomActionsView/components/CallSection.tsx (3)

8-15: LGTM on the API change to accept room object.

The component now accepts the full TSubscriptionModel instead of just rid, which aligns with the refactoring to use mediaSessionInstance.startCallByRoom(room). The room.rid extraction for useVideoConf is correct.


34-46: Correct conditional for Video_call visibility.

The condition {callEnabled ? ( now correctly shows the video call option only when the feature is enabled, consistent with RoomInfoButtons.tsx.


25-27: The List.Item component already handles i18n translation internally. The translateTitle prop defaults to true, which causes the component to automatically call I18n.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.

accessibilityLabel and accessibilityRole='button' on the call action are good additions for screen-reader usability.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6498323 and d5445fc.

⛔ Files ignored due to path filters (3)
  • app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snap is excluded by !**/*.snap
  • app/views/NewMessageView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
  • app/views/RoomInfoView/components/__snapshots__/RoomInfoButtons.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (12)
  • app/lib/hooks/useMediaCallPermission/index.ts
  • app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts
  • app/lib/hooks/useMediaCallPermission/useMediaCallPermission.ts
  • app/views/NewMessageView/Item.stories.tsx
  • app/views/NewMessageView/Item.test.tsx
  • app/views/NewMessageView/Item.tsx
  • app/views/RoomActionsView/components/CallSection.tsx
  • app/views/RoomInfoView/components/RoomInfoABAC.stories.tsx
  • app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx
  • app/views/RoomInfoView/components/RoomInfoButtons.test.tsx
  • app/views/RoomInfoView/components/RoomInfoButtons.tsx
  • app/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.tsx
  • app/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 RoomInfoView without 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 null only 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.

Comment thread app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx Outdated
Comment thread app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx (1)

48-54: Drop unused showActionSheet from story handlers.

RoomInfoButtons does not accept this prop, so keeping it in defaultHandlers adds 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

📥 Commits

Reviewing files that changed from the base of the PR and between d5445fc and 10e8fe0.

⛔ Files ignored due to path filters (3)
  • app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snap is excluded by !**/*.snap
  • app/views/NewMessageView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
  • app/views/RoomInfoView/components/__snapshots__/RoomInfoButtons.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • app/lib/hooks/useMediaCallPermission/useMediaCallPermission.test.ts
  • app/views/NewMessageView/Item.stories.tsx
  • app/views/NewMessageView/Item.test.tsx
  • app/views/RoomInfoView/components/RoomInfoButtons.stories.tsx
  • app/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.

Comment thread app/views/RoomInfoView/components/RoomInfoButtons.tsx Outdated
@diegolmello diegolmello had a problem deploying to approve_e2e_testing March 3, 2026 16:53 — with GitHub Actions Failure
@diegolmello diegolmello merged commit 10a8b19 into feat.voip-lib-new Mar 3, 2026
2 of 6 checks passed
@diegolmello diegolmello deleted the feat.start-call branch March 3, 2026 16:54
@coderabbitai coderabbitai Bot mentioned this pull request Mar 6, 2026
10 tasks
diegolmello added a commit that referenced this pull request Apr 22, 2026
…/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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant