Skip to content

feat(voip): navigate to call DM from message button and header#7082

Merged
diegolmello merged 15 commits intofeat.voip-lib-newfrom
feat.call-message
Apr 6, 2026
Merged

feat(voip): navigate to call DM from message button and header#7082
diegolmello merged 15 commits intofeat.voip-lib-newfrom
feat.call-message

Conversation

@diegolmello
Copy link
Copy Markdown
Member

@diegolmello diegolmello commented Mar 31, 2026

Proposed changes

Implements navigation from the VoIP call UI to the DM for the active user-to-user call (VMUX-23):

  • roomId on the Zustand call store — Persists the DM room id for the active call; cleared on reset().
  • getDMSubscriptionByUsername — WatermelonDB lookup for a direct subscription by username (with early return for falsy usernames); JSDoc added for docstring coverage.
  • MediaSessionInstance — Sets roomId when starting from a room; resolves DM by username on outbound/inbound paths when needed; skips SIP contacts. Async resolution uses .catch(...) instead of void to satisfy no-void ESLint.
  • navigateToCallRoom — Shared helper: guards SIP / missing roomId / missing username, minimizes call UI when focused (toggleFocus), then navigates via existing goRoom + Redux isMasterDetail.
  • UI wiringMessage in CallView (CallButtons) and the tappable area in MediaCallHeader Content call the helper; both stay disabled for SIP or when roomId is missing (no on-the-fly room creation).

Navigation-context fix: If the user opens the DM from Profile, Accessibility, or Settings (outside the chats stack), the helper navigates to ChatsStackNavigator first when the current route is not RoomsListView or RoomView, so goRoom runs with the correct stack. Covered in navigateToCallRoom.test.ts.

Follow-up (review / CI): Addressed CodeRabbit and ESLint feedback: removed void for floating promises in CallButtons, Content, and MediaSessionInstance; prefer-destructuring for contact.username; import grouping in MediaSessionInstance.test.ts; unit test for falsy username on getDMSubscriptionByUsername; Prettier/format pass.

Issue(s)

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

How to test or reproduce

  1. Start or answer a user-to-user VoIP call with an existing DM; confirm roomId is set once the call is active (may be async on incoming paths).
  2. From full CallView, tap Message — call minimizes and the DM opens.
  3. From the minimized header, tap the caller content — same DM opens.
  4. On a SIP call, confirm Message and header tap do not navigate (disabled / no-op).
  5. With no DM subscription for the peer, confirm Message stays disabled.
  6. Regression: With an active call, open Profile (or Settings / Accessibility), then Message (or header) — you should land in the DM, not stay on profile/settings.
  7. On tablet master–detail, confirm the room still opens in the detail pane as expected.

Screenshots

Add if you have before/after or screen recordings.

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

  • Base branch: feat.voip-lib-new.
  • Design alignment: Async roomId on incoming calls, no DM creation from the call UI, reuse of goRoom / toggleFocus — per PRD / progress notes for this workstream.
  • Tests touched (non-exhaustive): useCallStore.test.ts, Subscription.test.ts, MediaSessionInstance.test.ts, navigateToCallRoom.test.ts, CallButtons.test.tsx, CallView / MediaCallHeader tests and stories as needed for enabled/disabled states and snapshots.

Summary by CodeRabbit

  • New Features

    • Message button on active calls opens the direct-message room when available.
    • Call state now stores per-call roomId so DM navigation can be populated automatically.
  • Bug Fixes

    • Message navigation is disabled for SIP calls or when no conversation context exists.
    • Navigation flow now ensures chat view is reached before opening the DM.
  • Tests

    • Added and expanded tests for DM navigation, roomId population, SIP/no-room edge cases.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds per-call roomId to the VoIP call store, a new navigateToCallRoom() service, DB lookup to resolve DM rid from contacts, updates MediaSession flows to populate roomId, and disables/invokes messaging UI based on SIP/roomId; tests and stories updated accordingly.

Changes

Cohort / File(s) Summary
Call store & tests
app/lib/services/voip/useCallStore.ts, app/lib/services/voip/useCallStore.test.ts
Add `roomId: string
Navigation service & tests
app/lib/services/voip/navigateToCallRoom.ts, app/lib/services/voip/navigateToCallRoom.test.ts
New exported navigateToCallRoom() with guards (missing roomId, SIP contact, missing username), optional focus toggle, route checks, and goRoom() invocation; tests cover routing and guard branches.
Media session & tests
app/lib/services/voip/MediaSessionInstance.ts, app/lib/services/voip/MediaSessionInstance.test.ts
Resolve DM rid via getDMSubscriptionByUsername and populate roomId in startCallByRoom, incoming-call, and answerCall flows; tests assert setRoomId calls and conditional lookups.
DB subscription lookup & tests
app/lib/database/services/Subscription.ts, app/lib/database/services/Subscription.test.ts
Add getDMSubscriptionByUsername(username) querying WatermelonDB for direct-message subscriptions with input short-circuiting; tests cover found, not-found, and empty-input cases.
In-call UI, stories & tests
app/containers/MediaCallHeader/components/Content.tsx, app/containers/MediaCallHeader/MediaCallHeader.test.tsx, app/containers/MediaCallHeader/MediaCallHeader.stories.tsx, app/views/CallView/components/CallButtons.tsx, app/views/CallView/components/CallButtons.test.tsx, app/views/CallView/index.test.tsx
Replace alert placeholders with navigateToCallRoom() calls; compute disabled state as `Boolean(contact.sipExtension)
Test setup & incidental mocks
jest.setup.js, various test files...app/lib/services/voip/navigateToCallRoom.test.ts, app/lib/services/voip/MediaSessionInstance.test.ts, others
Add/adjust mocks for react-native-incall-manager, navigateToCallRoom, getDMSubscriptionByUsername, call-store roomId, and navigation to exercise new behavior in tests.

Sequence Diagrams

sequenceDiagram
    participant User
    participant CallUI as Call UI
    participant Store as Call Store
    participant DB as Database
    participant Nav as Navigation

    rect rgba(100, 150, 200, 0.5)
    Note over CallUI,Store: Incoming non‑SIP call handling
    CallUI->>Store: persist incoming call (with contact)
    alt store.roomId is null and contact.username exists
        Store->>DB: getDMSubscriptionByUsername(username)
        DB-->>Store: return subscription {rid}
        Store->>Store: setRoomId(rid)
    end
    end

    rect rgba(150, 100, 200, 0.5)
    Note over User,Nav: User presses Message button
    User->>CallUI: press message button
    CallUI->>Store: read contact & roomId
    alt contact.sipExtension present or roomId null
        CallUI->>CallUI: no action (button disabled)
    else valid DM call
        CallUI->>Nav: navigateToCallRoom()
        Nav->>Nav: ensure stack routing (maybe navigate to ChatsStackNavigator)
        Nav->>Nav: goRoom({ rid, name, t: DIRECT }, isMasterDetail)
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: navigating to call DMs from message button and header components in the VoIP feature.
Linked Issues check ✅ Passed The pull request implements the Message button functionality required by VMUX-23, adding message navigation from both CallView and MediaCallHeader components.
Out of Scope Changes check ✅ Passed Changes introduce roomId persistence, DM subscription lookup, and navigateToCallRoom helper—all supporting the core objective. Jest setup consolidation is a minor cleanup aligned with test refactoring.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
app/lib/services/voip/MediaSessionInstance.ts (1)

171-171: Use object destructuring per ESLint prefer-destructuring.

🔧 Proposed fix
-const username = contact.username;
+const { username } = contact;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/services/voip/MediaSessionInstance.ts` at line 171, In
MediaSessionInstance, replace the direct property access "const username =
contact.username" by using object destructuring to satisfy ESLint
prefer-destructuring; e.g., destructure username from contact (const { username
} = contact) where the variable is declared, ensuring subsequent references
still use the new username binding.
app/lib/database/services/Subscription.test.ts (1)

27-43: Consider adding a test for the falsy username early return.

The getDMSubscriptionByUsername function has an early return when username is falsy (empty string, null, undefined). Adding a test for this branch would improve coverage.

📝 Suggested test case
it('returns null when username is empty', async () => {
	const result = await getDMSubscriptionByUsername('');

	expect(result).toBeNull();
	expect(mockGet).not.toHaveBeenCalled();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/database/services/Subscription.test.ts` around lines 27 - 43, Add a
unit test for the early-return branch in getDMSubscriptionByUsername that
verifies a falsy username (e.g., empty string) immediately returns null and does
not call the database; specifically, add a test case that calls
getDMSubscriptionByUsername('') (or null/undefined) and asserts the result is
null and that mockGet (the mocked SUBSCRIPTIONS_TABLE accessor used in other
tests) was not invoked.
app/views/CallView/components/CallButtons.tsx (1)

31-31: Consider !roomId instead of roomId == null for consistency.

While roomId == null works correctly (covers both null and undefined), using !roomId would be more consistent with the Boolean(contact.sipExtension) pattern on the same line. Both are valid; this is a minor style consideration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/views/CallView/components/CallButtons.tsx` at line 31, The expression
computing messageDisabled uses mixed styles—Boolean(contact.sipExtension) vs
roomId == null—so change the roomId check to use the falsy check (!roomId) for
consistency; update the messageDisabled assignment (symbol: messageDisabled) to
Boolean(contact.sipExtension) || !roomId (symbols: contact.sipExtension, roomId)
ensuring behavior remains the same (covers null/undefined/empty falsy values).
app/lib/services/voip/navigateToCallRoom.ts (1)

31-34: Consider potential timing between toggleFocus and Navigation.navigate.

When focused is true, toggleFocus() is called synchronously, but the UI animation may still be in progress when Navigation.navigate('ChatsStackNavigator') executes. If users report visual glitches, you may need to introduce a small delay or await the focus toggle completion.

This is a minor observation - the current implementation should work in most cases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/services/voip/navigateToCallRoom.ts` around lines 31 - 34, The
current code calls toggleFocus() synchronously then immediately uses
Navigation.navigate('ChatsStackNavigator'), which can cause visual glitches if
the focus animation is still running; in navigateToCallRoom, ensure navigation
waits until the focus toggle completes by either awaiting toggleFocus() if it
returns a Promise or deferring Navigation.navigate using
InteractionManager.runAfterInteractions (or a short
setTimeout/requestAnimationFrame) so the UI animation finishes before calling
Navigation.navigate; update the logic around toggleFocus,
Navigation.getCurrentRoute and the Navigation.navigate('ChatsStackNavigator')
call accordingly.
🤖 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/containers/MediaCallHeader/components/Content.tsx`:
- Line 31: The onPress handler in Content.tsx uses the `void` operator
(`onPress={() => void navigateToCallRoom()}`) which violates ESLint's no-void
rule; remove the `void` and either call `navigateToCallRoom()` directly in the
arrow handler (`onPress={() => navigateToCallRoom()}`) or convert the handler to
an async function and `await navigateToCallRoom()` inside it (e.g.,
`onPress={async () => { await navigateToCallRoom(); }}`), referencing the
`navigateToCallRoom` function and the `onPress` handler in the Content
component.

In `@app/lib/services/voip/MediaSessionInstance.test.ts`:
- Around line 1-6: ESLint wants a blank line separating type-only imports (e.g.,
IClientMediaCall, IDDPMessage) from value imports (e.g., waitFor,
getDMSubscriptionByUsername, getUidDirectMessage, mediaSessionStore); fix by
grouping all "import type" lines together and inserting a single blank line
before the first non-type import (ensure the imports for IClientMediaCall and
IDDPMessage remain as type imports and that waitFor,
getDMSubscriptionByUsername, getUidDirectMessage, and mediaSessionStore are in
the following group).

In `@app/lib/services/voip/MediaSessionInstance.ts`:
- Around line 96-98: The current fire-and-forget call uses the banned `void`
operator on resolveRoomIdFromContact; replace it with a promise-catch pattern so
the promise is started and any errors are handled. Specifically, in the block
that checks useCallStore.getState().roomId == null, call
this.resolveRoomIdFromContact(call.contact).catch(err => { /* log or ignore
error */ }) so you remove the `void` operator and handle errors from
resolveRoomIdFromContact(call.contact).
- Line 126: Replace the "void this.resolveRoomIdFromContact(mainCall.contact)"
call to avoid the ESLint no-void violation by calling
this.resolveRoomIdFromContact(mainCall.contact).catch(...) instead; locate the
call in MediaSessionInstance (where resolveRoomIdFromContact is invoked with
mainCall.contact) and append a .catch handler that logs or handles the error
appropriately (use the existing logger or error handling pattern in the class)
so the promise rejection is handled without using void.

In `@app/views/CallView/components/CallButtons.tsx`:
- Around line 33-35: The handleMessage function currently uses the void operator
(void navigateToCallRoom()) which violates the no-void ESLint rule; update
handleMessage to either call navigateToCallRoom() and attach an explicit no-op
catch (e.g., navigateToCallRoom().catch(() => {})) or make handleMessage async
and await navigateToCallRoom() (i.e., async function handleMessage() { await
navigateToCallRoom(); }) so that unhandled promise rejections are handled and
the linter error is resolved; locate handleMessage and navigateToCallRoom in
CallButtons.tsx to apply the change.

---

Nitpick comments:
In `@app/lib/database/services/Subscription.test.ts`:
- Around line 27-43: Add a unit test for the early-return branch in
getDMSubscriptionByUsername that verifies a falsy username (e.g., empty string)
immediately returns null and does not call the database; specifically, add a
test case that calls getDMSubscriptionByUsername('') (or null/undefined) and
asserts the result is null and that mockGet (the mocked SUBSCRIPTIONS_TABLE
accessor used in other tests) was not invoked.

In `@app/lib/services/voip/MediaSessionInstance.ts`:
- Line 171: In MediaSessionInstance, replace the direct property access "const
username = contact.username" by using object destructuring to satisfy ESLint
prefer-destructuring; e.g., destructure username from contact (const { username
} = contact) where the variable is declared, ensuring subsequent references
still use the new username binding.

In `@app/lib/services/voip/navigateToCallRoom.ts`:
- Around line 31-34: The current code calls toggleFocus() synchronously then
immediately uses Navigation.navigate('ChatsStackNavigator'), which can cause
visual glitches if the focus animation is still running; in navigateToCallRoom,
ensure navigation waits until the focus toggle completes by either awaiting
toggleFocus() if it returns a Promise or deferring Navigation.navigate using
InteractionManager.runAfterInteractions (or a short
setTimeout/requestAnimationFrame) so the UI animation finishes before calling
Navigation.navigate; update the logic around toggleFocus,
Navigation.getCurrentRoute and the Navigation.navigate('ChatsStackNavigator')
call accordingly.

In `@app/views/CallView/components/CallButtons.tsx`:
- Line 31: The expression computing messageDisabled uses mixed
styles—Boolean(contact.sipExtension) vs roomId == null—so change the roomId
check to use the falsy check (!roomId) for consistency; update the
messageDisabled assignment (symbol: messageDisabled) to
Boolean(contact.sipExtension) || !roomId (symbols: contact.sipExtension, roomId)
ensuring behavior remains the same (covers null/undefined/empty falsy values).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 68498b5f-5670-4ad7-9669-ea6b0aeb5ebd

📥 Commits

Reviewing files that changed from the base of the PR and between 81dba28 and 88b08b9.

⛔ Files ignored due to path filters (2)
  • app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snap is excluded by !**/*.snap
  • app/views/CallView/__snapshots__/index.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (14)
  • app/containers/MediaCallHeader/MediaCallHeader.stories.tsx
  • app/containers/MediaCallHeader/MediaCallHeader.test.tsx
  • app/containers/MediaCallHeader/components/Content.tsx
  • app/lib/database/services/Subscription.test.ts
  • app/lib/database/services/Subscription.ts
  • app/lib/services/voip/MediaSessionInstance.test.ts
  • app/lib/services/voip/MediaSessionInstance.ts
  • app/lib/services/voip/navigateToCallRoom.test.ts
  • app/lib/services/voip/navigateToCallRoom.ts
  • app/lib/services/voip/useCallStore.test.ts
  • app/lib/services/voip/useCallStore.ts
  • app/views/CallView/components/CallButtons.test.tsx
  • app/views/CallView/components/CallButtons.tsx
  • app/views/CallView/index.test.tsx
📜 Review details
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2026-03-15T13:55:42.038Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6911
File: app/containers/markdown/Markdown.stories.tsx:104-104
Timestamp: 2026-03-15T13:55:42.038Z
Learning: In Rocket.Chat React Native, the markdown parser requires a space between the underscore wrapping italic text and a mention sigil (_ mention _ instead of _mention_). Ensure stories and tests that include italic-wrapped mentions follow this form to guarantee proper parsing. Specifically, for files like app/containers/markdown/Markdown.stories.tsx, and any test/content strings that exercise italic-mentions, use the pattern _ mention _ (with spaces) to prevent the mention from being treated as plain text. Validate any test strings or story content accordingly.

Applied to files:

  • app/containers/MediaCallHeader/MediaCallHeader.stories.tsx
📚 Learning: 2026-03-10T15:21:45.098Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7046
File: app/containers/InAppNotification/NotifierComponent.stories.tsx:46-75
Timestamp: 2026-03-10T15:21:45.098Z
Learning: In `app/containers/InAppNotification/NotifierComponent.tsx` (React Native, Rocket.Chat), `NotifierComponent` is exported as a Redux-connected component via `connect(mapStateToProps)`. The `isMasterDetail` prop is automatically injected from `state.app.isMasterDetail` and does not need to be passed explicitly at call sites or in Storybook stories that use the default (connected) export.

Applied to files:

  • app/containers/MediaCallHeader/MediaCallHeader.test.tsx
  • app/views/CallView/index.test.tsx
  • app/containers/MediaCallHeader/components/Content.tsx
📚 Learning: 2026-03-30T15:49:30.957Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: app/containers/RoomItem/Actions.tsx:12-12
Timestamp: 2026-03-30T15:49:30.957Z
Learning: In RocketChat/Rocket.Chat.ReactNative, `react-native-worklets` version 0.6.1 does NOT export a built-in Jest mock (e.g., no `react-native-worklets/lib/module/mock`). The correct Jest mock approach for this version is to add a manual mock in `jest.setup.js`: `jest.mock('react-native-worklets', () => ({ scheduleOnRN: jest.fn((fn, ...args) => fn(...args)) }))`.

Applied to files:

  • app/views/CallView/index.test.tsx
  • app/lib/services/voip/MediaSessionInstance.test.ts
📚 Learning: 2026-03-17T19:15:30.463Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6970
File: .maestro/tests/room/share-message.yaml:77-79
Timestamp: 2026-03-17T19:15:30.463Z
Learning: In `.maestro/tests/room/share-message.yaml` (Rocket.Chat React Native), the `tapOn: point: 5%,10%` step is intentional: it taps the empty area above the bottom sheet and keyboard to dismiss both simultaneously. Using `action-sheet-handle` instead would only close the sheet but not the keyboard. This pattern is acceptable when both need to be dismissed together in a single step.

Applied to files:

  • app/views/CallView/index.test.tsx
🪛 ESLint
app/views/CallView/components/CallButtons.tsx

[error] 34-34: Expected 'undefined' and instead saw 'void'.

(no-void)

app/lib/services/voip/MediaSessionInstance.ts

[error] 97-97: Expected 'undefined' and instead saw 'void'.

(no-void)


[error] 126-126: Expected 'undefined' and instead saw 'void'.

(no-void)


[error] 171-171: Use object destructuring.

(prefer-destructuring)

app/containers/MediaCallHeader/components/Content.tsx

[error] 31-31: Expected 'undefined' and instead saw 'void'.

(no-void)

app/lib/services/voip/MediaSessionInstance.test.ts

[error] 2-2: There should be at least one empty line between import groups

(import/order)

🪛 GitHub Actions: Format Code with Prettier
app/containers/MediaCallHeader/components/Content.tsx

[error] 31-31: no-void: Expected 'undefined' and instead saw 'void'.

🪛 GitHub Check: format
app/views/CallView/components/CallButtons.tsx

[failure] 34-34:
Expected 'undefined' and instead saw 'void'

app/lib/services/voip/MediaSessionInstance.ts

[failure] 97-97:
Expected 'undefined' and instead saw 'void'


[failure] 126-126:
Expected 'undefined' and instead saw 'void'

app/containers/MediaCallHeader/components/Content.tsx

[failure] 31-31:
Expected 'undefined' and instead saw 'void'

🔇 Additional comments (14)
app/lib/database/services/Subscription.ts (1)

10-18: LGTM!

The implementation is clean and handles edge cases properly:

  • Early return for falsy username prevents unnecessary DB queries
  • Q.take(1) efficiently limits results
  • Nullish coalescing (??) correctly returns null when no rows match
app/containers/MediaCallHeader/MediaCallHeader.stories.tsx (1)

24-49: LGTM!

The story mock data is correctly updated to reflect an enabled navigation scenario:

  • sipExtension: '' indicates a non-SIP call
  • roomId: 'story-room-rid' provides a valid room identifier

This aligns with the navigation gating logic where the message action is enabled only for non-SIP calls with a valid roomId.

app/lib/services/voip/useCallStore.test.ts (1)

53-69: LGTM!

The test suite correctly verifies:

  1. setRoomId updates the store value
  2. reset() clears roomId back to null

The test isolation via beforeEach reset is appropriate.

app/views/CallView/index.test.tsx (2)

12-16: LGTM!

The mock setup is correct. Although jest.mocked(navigateToCallRoom) appears before jest.mock(...) in the source, Jest hoists jest.mock() calls to the top of the file during execution, so the mock is in place when jest.mocked() runs.


249-259: LGTM!

The test correctly verifies that pressing the message button triggers navigateToCallRoom. The store state setup with sipExtension: '' and roomId: 'test-room-rid' ensures the button is enabled.

app/lib/services/voip/MediaSessionInstance.ts (1)

167-179: LGTM on the resolveRoomIdFromContact implementation.

The logic correctly:

  1. Skips SIP extension contacts (no DM room exists)
  2. Returns early for missing username
  3. Queries the DM subscription and sets roomId if found

The async handling is appropriate for a fire-and-forget pattern where navigation is enabled only after resolution.

app/lib/services/voip/useCallStore.ts (1)

73-74: LGTM!

The roomId state addition follows the existing store patterns:

  • Proper TypeScript typing with string | null
  • JSDoc documentation explains the field's purpose and lifecycle
  • Initialized to null in initialState
  • Automatically cleared on reset() via the spread of initialState
  • Simple setter implementation is appropriate

Also applies to: 94-94, 112-113, 253-255

app/containers/MediaCallHeader/MediaCallHeader.test.tsx (2)

12-22: LGTM! Well-structured mock setup.

The mock for react-native-incall-manager and navigateToCallRoom are correctly placed before they're imported by the component under test. Using jest.mocked after the mock declaration is the correct pattern.


190-232: Good test coverage for navigation behavior.

The tests comprehensively cover:

  1. Navigation enabled (non-SIP, roomId present)
  2. Navigation disabled for SIP calls
  3. Navigation disabled when roomId is null

This aligns well with the component's conditional logic.

app/views/CallView/components/CallButtons.test.tsx (1)

1-82: LGTM! Focused test coverage for message button navigation.

The test file is well-structured with:

  • Proper mock setup for dependencies
  • Clean test isolation via beforeEach reset
  • Coverage for all three message button states (enabled, SIP disabled, roomId null)

The tests correctly verify that navigateToCallRoom is called only when the button should be active.

app/containers/MediaCallHeader/components/Content.tsx (1)

21-38: Logic implementation looks correct.

The component properly:

  • Reads roomId and contact from the call store
  • Disables interaction for SIP calls (contact.sipExtension truthy) or missing room (roomId == null)
  • Applies visual feedback (opacity 0.5) when disabled
app/lib/services/voip/navigateToCallRoom.ts (1)

11-44: Well-structured navigation helper with appropriate guards.

The implementation correctly:

  • Guards against SIP calls and missing roomId/username
  • Minimizes the call UI before navigating
  • Ensures navigation context is correct before calling goRoom
app/lib/services/voip/navigateToCallRoom.test.ts (1)

1-182: Excellent test coverage for navigation logic.

The test suite comprehensively covers:

  • All early-exit conditions (null roomId, SIP contact, missing username)
  • Focus toggle behavior (both focused and unfocused states)
  • Navigation context handling (multiple non-chat screens + RoomView)

The mock setup is clean and the assertions correctly verify the expected behavior and call order.

app/lib/services/voip/MediaSessionInstance.test.ts (1)

349-475: Comprehensive test coverage for roomId population logic.

The new roomId population test block thoroughly covers:

  • startCallByRoom sets roomId before startCall (verified via execution order tracking)
  • newCall caller triggers/skips DM lookup based on existing roomId and SIP status
  • answerCall resolves/skips DM lookup based on SIP status

The use of waitFor for async assertions and order tracking via mock implementations are good testing patterns.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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/lib/services/voip/navigateToCallRoom.test.ts (1)

55-99: Also assert no stack navigation on early-return branches.

In no-op scenarios, asserting Navigation.navigate is untouched makes the guard behavior explicit and prevents accidental side effects.

Suggested assertion additions
@@
 		await navigateToCallRoom();

 		expect(mockGoRoom).not.toHaveBeenCalled();
 		expect(toggleFocus).not.toHaveBeenCalled();
+		expect(mockNavigation.navigate).not.toHaveBeenCalled();
 	});
@@
 		await navigateToCallRoom();

 		expect(mockGoRoom).not.toHaveBeenCalled();
+		expect(mockNavigation.navigate).not.toHaveBeenCalled();
 	});
@@
 		await navigateToCallRoom();

 		expect(mockGoRoom).not.toHaveBeenCalled();
+		expect(mockNavigation.navigate).not.toHaveBeenCalled();
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/services/voip/navigateToCallRoom.test.ts` around lines 55 - 99, Add
assertions to explicitly verify that no stack/navigation happens in the
early-return branches by asserting that Navigation.navigate (or mockGoRoom
wrapper) is not called. In the tests for navigateToCallRoom ('does not navigate
when roomId is null', 'does not navigate for SIP contact', and 'does not
navigate when username is missing') update the expectations to include an
assertion that Navigation.navigate (or the mock navigation function used in this
test file, e.g., mockGoRoom) was not called and where appropriate that
toggleFocus was not called; reference the navigateToCallRoom function,
mockGetState, mockCallStoreState, mockGoRoom, and toggleFocus to locate and add
these assertions.
🤖 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/navigateToCallRoom.test.ts`:
- Around line 101-118: The tests currently only assert that toggleFocus,
Navigation.navigate and mockGoRoom were called, but not their order; update the
tests in navigateToCallRoom.test.ts (including the other cases around 139-191)
to assert call sequencing: after calling navigateToCallRoom() check the mock
invocation order numbers (e.g., toggleFocus.mock.invocationCallOrder[0] and
Navigation.navigate.mock.invocationCallOrder[0]) and assert each is less than
mockGoRoom.mock.invocationCallOrder[0] so you guarantee minimize/Navigation
happen before goRoom; use the existing mock symbols toggleFocus,
Navigation.navigate and mockGoRoom to locate where to add these ordering
assertions.

---

Nitpick comments:
In `@app/lib/services/voip/navigateToCallRoom.test.ts`:
- Around line 55-99: Add assertions to explicitly verify that no
stack/navigation happens in the early-return branches by asserting that
Navigation.navigate (or mockGoRoom wrapper) is not called. In the tests for
navigateToCallRoom ('does not navigate when roomId is null', 'does not navigate
for SIP contact', and 'does not navigate when username is missing') update the
expectations to include an assertion that Navigation.navigate (or the mock
navigation function used in this test file, e.g., mockGoRoom) was not called and
where appropriate that toggleFocus was not called; reference the
navigateToCallRoom function, mockGetState, mockCallStoreState, mockGoRoom, and
toggleFocus to locate and add these assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3dff821f-82b1-48c6-b7fa-260888aee7b5

📥 Commits

Reviewing files that changed from the base of the PR and between b11885f and 3bc0654.

📒 Files selected for processing (1)
  • app/lib/services/voip/navigateToCallRoom.test.ts
📜 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

diegolmello and others added 12 commits April 6, 2026 15:25
Add roomId and setRoomId to the Zustand VoIP call store so DM
navigation can reference the active call room. roomId defaults to
null and is cleared by reset() via initialState spread.

Tests cover setRoomId and reset clearing roomId.

Made-with: Cursor
Query WatermelonDB for a direct subscription by username and room type.
Includes unit tests with a mocked DB collection.

Made-with: Cursor
Set roomId in startCallByRoom before dialing. For outbound calls that
still lack roomId after setCall, resolve DM subscription by username.
For inbound answerCall, resolve the same way and skip SIP contacts.

Extend MediaSessionInstance tests for ordering, newCall, and answerCall
paths; stub CallKeep setCurrentCallActive for answerCall tests.

Made-with: Cursor
Read call store roomId, contact, and focused state; skip SIP and missing
data; minimize CallView when focused; navigate via goRoom with Redux
isMasterDetail. Unit tests cover early exits and focus ordering.

Made-with: Cursor
Disable message when SIP extension is set or roomId is missing. Add
CallButtons tests; mock navigation helper in CallView tests and refresh
story snapshots for the enabled message control.

Made-with: Cursor
Replace placeholder alert with navigateToCallRoom; disable press and dim
when SIP or roomId is missing. Align stories and tests with DM fixtures
(roomId, non-SIP); add coverage for enabled vs SIP vs missing room.

Made-with: Cursor
…awers

When the message button is pressed from Profile, Accessibility, or Settings
screens, explicitly navigate to ChatsStackNavigator first. This ensures goRoom
has the correct navigation context to reset routes properly.

Fixes navigation staying on the same screen when message is pressed from
Profile/A11y/Settings while a call is active.
- Replace void operator with promise catch handlers (no-void)
- Use prefer-destructuring for contact.username in MediaSessionInstance
- Fix import order in MediaSessionInstance.test.ts
- Add getDMSubscriptionByUsername falsy-username test with mockClear
- Document getDMSubscriptionByUsername with JSDoc

Made-with: Cursor
Move the InCallManager stub to setupFilesAfterEnv and remove duplicate
jest.mock blocks from VoIP-related tests.

Made-with: Cursor
Use mockCallStoreState helper with unknown double assertion so partial
mocks satisfy @typescript-eslint/consistent-type-assertions (CI failure
on navigateToCallRoom.test.ts).

Made-with: Cursor
- Assert Navigation.navigate and toggleFocus stay untouched on early exits
- Assert toggleFocus and ChatsStackNavigator navigation run before goRoom

Made-with: Cursor
… tests

- Remove incorrect IClientMediaCall return type from createMockCall (returns {call, emit})
- Add missing setRoomId/roomId fields to mockUseCallStoreGetState.mockReturnValue calls
@diegolmello diegolmello requested a deployment to approve_e2e_testing April 6, 2026 19:15 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to official_android_build April 6, 2026 19:18 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to official_ios_build April 6, 2026 19:18 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to experimental_ios_build April 6, 2026 19:18 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to experimental_android_build April 6, 2026 19:18 — with GitHub Actions Waiting
@diegolmello diegolmello merged commit 58e91f1 into feat.voip-lib-new Apr 6, 2026
6 of 11 checks passed
@diegolmello diegolmello deleted the feat.call-message branch April 6, 2026 19:28
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