Skip to content

refactor(voip): decouple peer autocomplete from Redux; simplify NewMediaCall#7175

Merged
diegolmello merged 6 commits into
feat.voip-lib-newfrom
refactor/voip-pr2-peer-autocomplete-newmediacall
Apr 20, 2026
Merged

refactor(voip): decouple peer autocomplete from Redux; simplify NewMediaCall#7175
diegolmello merged 6 commits into
feat.voip-lib-newfrom
refactor/voip-pr2-peer-autocomplete-newmediacall

Conversation

@diegolmello

@diegolmello diegolmello commented Apr 17, 2026

Copy link
Copy Markdown
Member

Proposed changes

Decouples getPeerAutocompleteOptions from Redux (auxStore), trims the peer-autocomplete Zustand API, inlines the single-use NewMediaCall container, and hardens async autocomplete against stale responses.

Key changes

  • getPeerAutocompleteOptions takes username / sipEnabled (via TPeerAutocompleteAuth) instead of reading the aux store. Service is pure; filter is trimmed once at the store boundary; peerInfo narrowed via discriminated-union on type.
  • FilterHeader uses named selectors (selectSipEnabled, selectUsername) for login username + SIP setting and passes auth into fetchOptions.
  • Removed setFilter / clearSelection from the store; filter updates use setState; clear chip uses setSelectedPeer(null) and clears options.
  • fetchOptions: last-write-wins. Monotonic sequence counter lives in the store closure, incremented at the very top of fetchOptions (before the empty-filter early return) and bumped on reset(). Single seq stale-guard in both success and catch paths — overlapping requests and late errors cannot clobber newer UI state.
  • Inlined former Container.tsx into NewMediaCall.tsx; removed Container + Container.stories.tsx.
  • PeerList: setSelectedPeer(option) directly; keyExtractor uses ${type}:${value}.
  • Dropped the dead commented setIceServers line in MediaSessionInstance.ts.

Tests

  • New getPeerAutocompleteOptions.test.ts covering trimming, current-username exclusion, empty-filter shortcut, SIP-disabled routing, and canonical typed mocks (mockUsersAutoCompleteResponse helper; failure shape carries error).
  • Extended usePeerAutocompleteStore.test.tsreset, stale fetch, setSelectedPeer(null) clears options, username forwarding, trim-at-store-boundary.
  • Updated FilterHeader / SelectedPeer tests; refreshed SelectedPeer story snapshots.

Issue(s)

How to test or reproduce

  • TZ=UTC yarn test --testPathPattern='getPeerAutocompleteOptions|usePeerAutocompleteStore|FilterHeader|SelectedPeer|PeerList|NewMediaCall|MediaSessionInstance'
  • Manual: New call sheet — type to search, select user/SIP, clear chip; confirm list and field stay coherent under quick typing.

Screenshots

N/A (behavioral refactor)

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

Branched from feat.voip-lib-new.

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes

    • Prevented stale autocomplete results from replacing newer ones; failing requests no longer clear options unless current.
  • Refactor

    • Autocomplete requests now include caller identity and SIP enablement; clearing a selection also clears options.
    • Simplified media-call layout and applied consistent theming.
    • Peer list keys are now type-aware to avoid collisions.
  • Tests

    • Expanded tests for autocomplete, SIP branches, trimming, errors, and race conditions.
  • Chores

    • Removed an obsolete Storybook story and unused container.

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

Deleted the Container component and its Storybook story; NewMediaCall now renders a themed View. Autocomplete/service APIs were refactored to accept explicit auth ({username?, sipEnabled}); the autocomplete store gained sequencing to ignore stale async results. Tests added/updated across related modules.

Changes

Cohort / File(s) Summary
Container & Story
app/containers/NewMediaCall/Container.tsx, app/containers/NewMediaCall/Container.stories.tsx, app/containers/NewMediaCall/NewMediaCall.tsx
Removed Container component and its Storybook story; NewMediaCall now inlines a themed View and styling instead of importing Container.
FilterHeader & tests
app/containers/NewMediaCall/FilterHeader.tsx, app/containers/NewMediaCall/FilterHeader.test.tsx
FilterHeader now derives username and sipEnabled via typed selectors and calls debounced fetchOptions(filter, { username, sipEnabled }); removed direct store clearSelection usage. Tests updated to assert state and service-call args.
Peer selection & tests
app/containers/NewMediaCall/PeerList.tsx, app/containers/NewMediaCall/SelectedPeer.tsx, app/containers/NewMediaCall/SelectedPeer.test.tsx
Selection simplified to call setSelectedPeer(item) or setSelectedPeer(null); keyExtractor changed to ${item.type}:${item.value} to avoid key collisions. Tests now validate store state transitions instead of mocked action calls.
Autocomplete service, store & tests
app/lib/services/voip/getPeerAutocompleteOptions.ts, app/lib/services/voip/getPeerAutocompleteOptions.test.ts, app/lib/services/voip/usePeerAutocompleteStore.ts, app/lib/services/voip/usePeerAutocompleteStore.test.ts
Added exported TPeerAutocompleteAuth and changed APIs to accept { username?, sipEnabled }; removed internal Redux reads; fetchOptions now requires auth and uses a sequence counter to ignore stale responses; tests expanded for auth propagation, SIP branching, error handling, reset, and race conditions.
Misc small change
app/lib/services/voip/MediaSessionInstance.ts
Removed a commented-out call; no runtime behavior change.

Sequence Diagram(s)

sequenceDiagram
    participant UI as "FilterHeader / Input" rect rgba(100,150,240,0.5)
    participant Store as "usePeerAutocompleteStore" rect rgba(120,200,140,0.5)
    participant Service as "getPeerAutocompleteOptions" rect rgba(240,180,80,0.5)
    participant API as "usersAutoComplete (external)" rect rgba(200,120,200,0.5)

    UI->>Store: setFilter(value) -> clears selectedPeer/options, seq++
    UI->>UI: debounce timer
    UI->>Store: fetchOptions(filter, { username, sipEnabled })
    Store->>Store: seq++, capture currentSeq
    Store->>Service: getPeerAutocompleteOptions({ filter: trimmed, peerInfo, username, sipEnabled })
    Service->>API: usersAutoComplete(requestParams)
    API-->>Service: response(items / success)
    Service-->>Store: resolved options
    Store->>Store: if currentSeq == latest seq -> set options else discard
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the primary change: decoupling peer autocomplete from Redux and simplifying NewMediaCall by inlining the Container component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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: 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/lib/services/voip/getPeerAutocompleteOptions.test.ts`:
- Around line 38-44: Test fixtures for mockedUsersAutoComplete don't match
usersAutoComplete's typed return: remove the non-IUser field freeSwitchExtension
from synthetic user objects and stop placing items on failure responses; instead
create a small test helper (e.g., makeSuccessResponse<T>(payload):
ReturnTypeOfUsersAutoCompleteSuccess) to cast synthetic success responses into
the exact success union shape used by usersAutoComplete, use that helper when
calling mockedUsersAutoComplete.mockResolvedValue(...) for the success case
(refer to mockedUsersAutoComplete and usersAutoComplete), and change the failure
fixture to return the failure union shape with an error property (not items) to
satisfy the type union.

In `@app/lib/services/voip/usePeerAutocompleteStore.ts`:
- Around line 26-35: The fetchOptions handler clears options for an empty filter
but does not increment the peerAutocompleteFetchSeq guard, so older in-flight
requests can still win; update fetchOptions to bump/invalidate
peerAutocompleteFetchSeq before the early return (i.e., increment the shared seq
used as the last-write-wins guard) and then call set({ options: [] }), ensuring
any prior async responses will be ignored; reference the fetchOptions function,
peerAutocompleteFetchSeq variable, and existing set/get/selectedPeer usage when
making the change.
🪄 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: 4f44adab-f3e5-4fe6-8076-1db65033e382

📥 Commits

Reviewing files that changed from the base of the PR and between 9c84d4c and 587cc27.

⛔ Files ignored due to path filters (1)
  • app/containers/NewMediaCall/__snapshots__/SelectedPeer.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (12)
  • app/containers/NewMediaCall/Container.stories.tsx
  • app/containers/NewMediaCall/Container.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
💤 Files with no reviewable changes (2)
  • app/containers/NewMediaCall/Container.tsx
  • app/containers/NewMediaCall/Container.stories.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line

Files:

  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins

Files:

  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Files:

  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/containers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable UI components in app/containers/ directory

Files:

  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/containers/NewMediaCall/FilterHeader.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/lib/services/voip/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Files:

  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Applied to files:

  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/containers/**/*.{ts,tsx} : Place reusable UI components in app/containers/ directory

Applied to files:

  • app/containers/NewMediaCall/NewMediaCall.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/NewMediaCall/NewMediaCall.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/AppContainer.tsx : Implement root navigation container logic in app/AppContainer.tsx to switch between authentication states

Applied to files:

  • app/containers/NewMediaCall/NewMediaCall.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/theme.tsx : Configure theming context in app/theme.tsx

Applied to files:

  • app/containers/NewMediaCall/NewMediaCall.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/index.tsx : Configure Redux provider, theme, navigation, and notifications in app/index.tsx

Applied to files:

  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
🪛 GitHub Check: ESLint and Test / run-eslint-and-test
app/lib/services/voip/getPeerAutocompleteOptions.test.ts

[failure] 151-151:
Object literal may only specify known properties, and 'items' does not exist in type '{ success: false; error: null; stack: null; errorType: null; details: null; } | { success: false; error: null; } | Promise<Serialized<ResultFor<"GET", "users.autocomplete">>>'.


[failure] 41-41:
Argument of type '{ success: true; items: ({ _id: string; name: string; username: string; freeSwitchExtension: string; } | { _id: string; name: string; username: string; freeSwitchExtension: undefined; })[]; }' is not assignable to parameter of type 'Serialized<ResultFor<"GET", "users.autocomplete">> | Promise<Serialized<ResultFor<"GET", "users.autocomplete">>>'.

🔇 Additional comments (13)
app/containers/NewMediaCall/NewMediaCall.tsx (3)

2-12: LGTM — wrapper dependencies are cleanly inlined.

View, StyleSheet, and useTheme are appropriate replacements for the removed local container wrapper here.


17-22: LGTM — screen layout remains straightforward.

The new View preserves the existing child composition while applying the theme background directly.


26-32: LGTM — static layout styles are properly extracted.

Using StyleSheet.create for the fixed screen spacing keeps the render path tidy.

app/containers/NewMediaCall/SelectedPeer.test.tsx (1)

72-82: LGTM — the test follows the new clear-selection flow.

The assertion now verifies the actual Zustand state after pressing the clear button, which matches the removed clearSelection API.

app/containers/NewMediaCall/SelectedPeer.tsx (1)

18-27: LGTM — clearing now uses the canonical store action.

Calling setSelectedPeer(null) keeps the component aligned with the trimmed peer-autocomplete API.

app/containers/NewMediaCall/PeerList.tsx (1)

15-25: LGTM — direct selection and type-aware keys are safer.

This preserves the full peer payload and prevents FlatList key collisions across peer types.

app/containers/NewMediaCall/FilterHeader.test.tsx (1)

56-70: LGTM — the debounce test matches the new auth-aware API.

The test now verifies the immediate store update separately from the delayed fetchOptions call.

app/containers/NewMediaCall/FilterHeader.tsx (1)

19-35: LGTM — auth context is now passed explicitly into autocomplete fetching.

This keeps Redux access at the container boundary while preserving the debounced store fetch flow.

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

41-188: LGTM — the store tests cover the important new invariants.

The added cases validate reset behavior, null selection semantics, auth forwarding, and stale async response protection.

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

17-77: LGTM — the autocomplete helper is now properly parameterized.

Passing username and sipEnabled explicitly removes the hidden Redux dependency while keeping the selector construction straightforward.

app/lib/services/voip/usePeerAutocompleteStore.ts (3)

3-5: Good decoupling of autocomplete auth from Redux.

Threading TPeerAutocompleteAuth into the store keeps this VoIP slice Zustand-owned and makes stale-request handling explicit.


37-59: Stale-response guards look solid.

The sequence check plus filter/selectedPeer snapshot validation prevents out-of-order autocomplete responses from overwriting newer UI state, and the catch path applies the same guard before clearing options.


62-73: Selection clear and reset behavior match the new store contract.

setSelectedPeer(null) now clears stale options, and reset() invalidates in-flight fetches before restoring initial state.

Comment thread app/lib/services/voip/getPeerAutocompleteOptions.test.ts Outdated
Comment thread app/lib/services/voip/usePeerAutocompleteStore.ts Outdated
@diegolmello diegolmello force-pushed the refactor/voip-pr2-peer-autocomplete-newmediacall branch from 587cc27 to f713d52 Compare April 20, 2026 19:05

@diegolmello diegolmello left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Consolidated review — VoIP PR2: peer autocomplete + NewMediaCall

Three parallel review passes (architect / anti-slop / simplifier) plus two existing CodeRabbit comments. Overall direction is a net improvement — findings ordered by severity.


Critical

1. app/lib/services/voip/getPeerAutocompleteOptions.test.ts:38-44 — typed mocks use fields not in IUser (CodeRabbit)

Fixture freeSwitchExtension is not part of IUser, and the failure response shape { success: false } is missing the required error field. The typed mock fails type-checks once consumed. Introduce a mockUsersAutoCompleteResponse helper returning the canonical shape, and use Awaited<ReturnType<typeof ...>> on the mocked fn so the compiler enforces the real signature.


Major

2. app/lib/services/voip/usePeerAutocompleteStore.ts:26-56fetchSeq race: increment must precede the empty-filter early return (CodeRabbit)

++peerAutocompleteFetchSeq lives below the if (!trimmedFilter) early return. Typing → clearing the filter fast enough lets a stale in-flight response resolve and overwrite cleared state. Move const seq = ++peerAutocompleteFetchSeq; to the very top of fetchOptions so every invocation (including the early return) invalidates prior requests.

3. app/lib/services/voip/usePeerAutocompleteStore.ts:30 — sequence counter at module scope (simplifier)

let peerAutocompleteFetchSeq = 0 is module-level mutable state. It leaks across store resets and across tests / hot-reload. Scope it to the store closure (let seq = 0 just before create(...)). Same semantics, proper boundary.

4. app/lib/services/voip/getPeerAutocompleteOptions.ts:2,42,47,50 — Redux reads inside the VoIP service layer (architect)

The service reaches into reduxStore.getState().login.user.username and reduxStore.getState().settings.VoIP_TeamCollab_SIP_Integration_For_Internal_Calls. Per project convention the VoIP layer is Zustand-only and should stay pure; pulling Redux in here creates hidden coupling and forces unit tests to mock Redux (see #6). Inject currentUsername and sipEnabled as arguments from the caller — the React layer already has both via useSelector.

5. app/lib/services/voip/getPeerAutocompleteOptions.ts — service has zero unit tests for key branches (anti-slop)

Filter trimming, current-username exclusion, empty-filter shortcut, SIP-disabled routing. Once dependencies are injected per #4 these branches become trivially testable.

6. app/lib/services/voip/usePeerAutocompleteStore.test.ts:110 — Redux store not mocked; filter branches silently untested (architect)

currentUsername resolves to undefined in the test harness, so the "exclude self" filter is a no-op in every assertion. Either mock the Redux store, or (preferred) inject the username per #4.

7. app/containers/NewMediaCall/CreateCall.tsx:8,22 — direct mediaSessionInstance.startCall bypasses the Zustand store boundary (architect)

Every other VoIP call site goes through the store. Directly importing the singleton here breaks the pattern and makes the component harder to test in isolation. Route through the store action.


Minor

8. app/lib/services/voip/usePeerAutocompleteStore.ts:45-55 — redundant double stale-guard (simplifier)

After await, both the seq check and the filter/selectedPeer check guard staleness. seq alone is sufficient — any filter change has already bumped the counter. The same redundant guard is duplicated in the catch block. Keep only the seq guard.

9. app/lib/services/voip/getPeerAutocompleteOptions.ts — double-trim overlap with store fetchOptions (anti-slop)

Both the store and the service trim the filter string. Trim once, at the store boundary.

10. app/containers/NewMediaCall/FilterHeader.tsx:15-18,20 — four separate usePeerAutocompleteStore subscriptions + 500ms debounce (architect)

Four separate selectors on the same store per render. Collapse with a single selector + shallow compare. Separately, 500ms debounce is not sufficient on its own to prevent the race fixed by #2/#3 — the sequence counter is the real fix.

11. app/containers/NewMediaCall/FilterHeader.tsx:17-27useCallback inside useDebounce adds indirection with no benefit (simplifier)

The inner useCallback is not observed by anything; useDebounce already produces a stable ref. Pass the inline function directly.


Nits

12. app/lib/services/voip/MediaSessionInstance.ts:235 — commented-out // this.instance?.setIceServers(this.iceServers); (anti-slop)

Pre-existing but worth cleaning: either delete or add a one-line note on why it's disabled.

13. app/lib/services/voip/getPeerAutocompleteOptions.ts:28-34 — repeated 'callerId' in peerInfo guards (simplifier)

Inline ternaries all use peerInfo && 'x' in peerInfo ? peerInfo.x : undefined. TPeerItem is already a discriminated union on type — narrow on peerInfo?.type === '...' for type-safety and readability.

14. app/containers/NewMediaCall/PeerList.tsx:13-16handleSelectOption is a one-line wrapper (simplifier)

If PeerItem's onSelectOption prop matches (peer: TPeerItem | null) => void, pass setSelectedPeer directly.

15. app/containers/NewMediaCall/FilterHeader.tsx:14-17 — two useSelector calls (simplifier)

Boolean(settings.VoIP_TeamCollab_SIP_Integration_For_Internal_Calls) inline is fine but a named selector (selectSipEnabled) reads better.

16. app/containers/NewMediaCall/NewMediaCall.tsx — inlining Container is the right call (simplifier)

Confirm styles.screen matches the values previously in Container.


Summary: fix #1, #2, #3 before merge. #4#7 and #10 are architectural — worth this PR or a fast follow-up. Rest is polish.

@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 (2)
app/lib/services/voip/usePeerAutocompleteStore.ts (1)

26-31: ⚠️ Potential issue | 🟠 Major

Empty-filter early return still bypasses the seq guard.

fetchOptions('', auth) sets options: [] without incrementing peerAutocompleteFetchSeq, so a previously dispatched in-flight fetch can still satisfy seq === peerAutocompleteFetchSeq on resolution and overwrite the cleared state. Bump the sequence before the early return (as suggested in the prior review) so blank searches also invalidate older fetches.

🛡️ Proposed fix
 	fetchOptions: async (filter: string, auth: TPeerAutocompleteAuth) => {
 		const term = filter.trim();
+		const seq = ++peerAutocompleteFetchSeq;
 		if (!term) {
 			set({ options: [] });
 			return;
 		}
 
 		const peerSnapshot = get().selectedPeer;
-		const seq = ++peerAutocompleteFetchSeq;
 
 		try {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/services/voip/usePeerAutocompleteStore.ts` around lines 26 - 31, The
early-return branch in fetchOptions bypasses the seq guard—before returning for
an empty term you must increment peerAutocompleteFetchSeq to invalidate any
in-flight responses; update the fetchOptions implementation so it increments the
shared peerAutocompleteFetchSeq counter (the same one used to compare seq in
async fetch completions) then calls set({ options: [] }) and returns, ensuring
older fetches cannot overwrite the cleared state.
app/lib/services/voip/getPeerAutocompleteOptions.test.ts (1)

38-44: ⚠️ Potential issue | 🔴 Critical

CI still failing on typed mock fixtures.

Per static analysis: line 41 uses freeSwitchExtension, which is not part of IUser, and line 151 passes items on a success: false response whose union only permits an error field. Both must be addressed for type-check to pass — see the earlier review for the helper-cast pattern and the failure-response fix.

Also applies to: 150-151

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

In `@app/lib/services/voip/getPeerAutocompleteOptions.test.ts` around lines 38 -
44, The test's mockedUsersAutoComplete fixture includes a non‑IUser property
(freeSwitchExtension) and later returns items on a failure response, which
breaks the IUser/response unions; update the mock to either remove non‑IUser
fields from the item objects or explicitly cast each item to IUser using the
project's helper-cast pattern (e.g., wrap the item object with the "as unknown
as IUser" helper used elsewhere) so the success:true shape matches IUser[], and
change the failure case for mockedUsersAutoComplete (success: false) to only
include the allowed error field (remove items) so the mock matches the union
type for error responses.
🤖 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/lib/services/voip/getPeerAutocompleteOptions.test.ts`:
- Around line 38-44: The test's mockedUsersAutoComplete fixture includes a
non‑IUser property (freeSwitchExtension) and later returns items on a failure
response, which breaks the IUser/response unions; update the mock to either
remove non‑IUser fields from the item objects or explicitly cast each item to
IUser using the project's helper-cast pattern (e.g., wrap the item object with
the "as unknown as IUser" helper used elsewhere) so the success:true shape
matches IUser[], and change the failure case for mockedUsersAutoComplete
(success: false) to only include the allowed error field (remove items) so the
mock matches the union type for error responses.

In `@app/lib/services/voip/usePeerAutocompleteStore.ts`:
- Around line 26-31: The early-return branch in fetchOptions bypasses the seq
guard—before returning for an empty term you must increment
peerAutocompleteFetchSeq to invalidate any in-flight responses; update the
fetchOptions implementation so it increments the shared peerAutocompleteFetchSeq
counter (the same one used to compare seq in async fetch completions) then calls
set({ options: [] }) and returns, ensuring older fetches cannot overwrite the
cleared state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0dd32905-0631-42d2-be7c-8f64b9dd1dc9

📥 Commits

Reviewing files that changed from the base of the PR and between 587cc27 and f713d52.

⛔ Files ignored due to path filters (1)
  • app/containers/NewMediaCall/__snapshots__/SelectedPeer.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (12)
  • app/containers/NewMediaCall/Container.stories.tsx
  • app/containers/NewMediaCall/Container.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/PeerList.tsx
  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
💤 Files with no reviewable changes (2)
  • app/containers/NewMediaCall/Container.tsx
  • app/containers/NewMediaCall/Container.stories.tsx
✅ Files skipped from review due to trivial changes (2)
  • app/containers/NewMediaCall/SelectedPeer.test.tsx
  • app/containers/NewMediaCall/FilterHeader.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/containers/NewMediaCall/SelectedPeer.tsx
  • app/containers/NewMediaCall/NewMediaCall.tsx
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line

Files:

  • app/containers/NewMediaCall/PeerList.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins

Files:

  • app/containers/NewMediaCall/PeerList.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Files:

  • app/containers/NewMediaCall/PeerList.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/containers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable UI components in app/containers/ directory

Files:

  • app/containers/NewMediaCall/PeerList.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/containers/NewMediaCall/PeerList.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/lib/services/voip/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Files:

  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/AppContainer.tsx : Implement root navigation container logic in app/AppContainer.tsx to switch between authentication states
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Applied to files:

  • app/containers/NewMediaCall/PeerList.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
🪛 GitHub Check: ESLint and Test / run-eslint-and-test
app/lib/services/voip/getPeerAutocompleteOptions.test.ts

[failure] 151-151:
Object literal may only specify known properties, and 'items' does not exist in type '{ success: false; error: null; stack: null; errorType: null; details: null; } | { success: false; error: null; } | Promise<Serialized<ResultFor<"GET", "users.autocomplete">>>'.


[failure] 41-41:
Argument of type '{ success: true; items: ({ _id: string; name: string; username: string; freeSwitchExtension: string; } | { _id: string; name: string; username: string; freeSwitchExtension: undefined; })[]; }' is not assignable to parameter of type 'Serialized<ResultFor<"GET", "users.autocomplete">> | Promise<Serialized<ResultFor<"GET", "users.autocomplete">>>'.

🔇 Additional comments (2)
app/containers/NewMediaCall/PeerList.tsx (1)

15-25: LGTM.

Passing the TPeerItem through to setSelectedPeer removes the previous reshape branching, and the composite ${type}:${value} key correctly disambiguates entries across peer types.

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

168-189: Race-condition coverage looks correct.

The test properly sequences the slow in-flight request, a newer fetch that wins, and a late stale resolution — matching the seq-guard semantics in the store. Good addition.

diegolmello added a commit that referenced this pull request Apr 20, 2026
Apply review feedback from PR #7175:
- Move fetchSeq increment above empty-filter early return so every invocation invalidates in-flight requests; scope seq to store closure.
- Drop redundant filter/selectedPeer post-await stale guards; rely on seq alone.
- Keep service pure: getPeerAutocompleteOptions takes username/sipEnabled via TPeerAutocompleteAuth instead of reading Redux.
- Trim filter once at the store boundary.
- Narrow peerInfo via discriminated-union on type.
- Add tests for service trimming, current-username exclusion, SIP-disabled routing, and store-boundary trim.
- FilterHeader: named selectSipEnabled/selectUsername, drop useCallback wrapper around useDebounce.
- Typed mocks: canonical mockUsersAutoCompleteResponse helper; failure shape carries error.
- Drop dead commented setIceServers line in MediaSessionInstance.
@diegolmello diegolmello had a problem deploying to official_android_build April 20, 2026 19:28 — with GitHub Actions Error
@diegolmello diegolmello had a problem deploying to experimental_android_build April 20, 2026 19:28 — with GitHub Actions Error
@diegolmello diegolmello had a problem deploying to experimental_ios_build April 20, 2026 19:28 — with GitHub Actions Error
@diegolmello diegolmello changed the title refactor(voip): peer autocomplete slice + NewMediaCall trims (PR-2) refactor(voip): decouple peer autocomplete from Redux; simplify NewMediaCall Apr 20, 2026

@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

🤖 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/NewMediaCall/FilterHeader.tsx`:
- Around line 26-33: The debounced callback debouncedFetchOptions closes over
stale username and sipEnabled values and will call
usePeerAutocompleteStore.getState().fetchOptions with outdated context; wrap the
callback in useCallback with [username, sipEnabled] and pass that into
useDebounce so the debounced function captures current values (reference
debouncedFetchOptions, useDebounce, and useCallback). Also, when updating the
filter in handleChangeText (usePeerAutocompleteStore.setState({ filter: value,
selectedPeer: null })), clear or reset options immediately (e.g., set options:
[] or a loading flag) to avoid showing stale peer options during the debounce
window (reference handleChangeText and usePeerAutocompleteStore.setState).
🪄 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: 0583e151-6ac6-4b03-aed6-67a3dffde9d4

📥 Commits

Reviewing files that changed from the base of the PR and between f713d52 and d1acd30.

📒 Files selected for processing (6)
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/MediaSessionInstance.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
💤 Files with no reviewable changes (1)
  • app/lib/services/voip/MediaSessionInstance.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/lib/services/voip/getPeerAutocompleteOptions.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/lib/services/voip/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/containers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable UI components in app/containers/ directory

Files:

  • app/containers/NewMediaCall/FilterHeader.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Learnt from: 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.
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Applied to files:

  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/getPeerAutocompleteOptions.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/selectors/**/*.{ts,tsx} : Place Redux selectors in app/selectors/ directory and memoize them using reselect

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/store/**/*.{ts,tsx} : Configure Redux store in app/lib/store/ with middleware for saga, app state, and internet state

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/index.tsx : Configure Redux provider, theme, navigation, and notifications in app/index.tsx

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
🔇 Additional comments (3)
app/lib/services/voip/getPeerAutocompleteOptions.test.ts (1)

1-233: LGTM — test coverage looks solid.

Good end-to-end coverage of the service's new { filter, peerInfo, username, sipEnabled } shape: empty-filter short-circuit, untrimmed-at-service boundary, current-username exclusion, SIP-enabled $exists, peer callerId $ne exclusion both with and without sipEnabled, and the success: false path. The mockUsersAutoCompleteResponse helper cleanly resolves the previously flagged typed-union mismatch.

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

19-71: LGTM — last-write-wins guard is correctly implemented.

Moving seq inside the factory closure scopes the counter to the store instance, ++seq before the empty-filter short-circuit invalidates earlier in-flight fetches, reset() bumps it too, and both the success and catch branches re-check currentSeq !== seq before mutating options. The setSelectedPeer(null) vs setSelectedPeer(peer) branch split (keep vs clear filter) matches the UI contract exercised by the tests.

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

41-219: LGTM — tests accurately exercise the new contract.

The reset / setSelectedPeer branches, auth forwarding, trimming at the store boundary, and the stale-response race (line 198–219) all correspond to the store's sequence-based guard. The race test is particularly valuable since it validates that a late-resolving earlier request cannot overwrite a later request's options.

Comment thread app/containers/NewMediaCall/FilterHeader.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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/containers/NewMediaCall/FilterHeader.tsx`:
- Around line 36-38: The current handleChangeText clears UI options but doesn't
invalidate in-flight fetches, so create a new store action (e.g.,
clearAutocompleteForInputChange) on usePeerAutocompleteStore that sets filter,
selectedPeer=null, options=[], and increments the autocomplete request
sequence/nonce used by fetchOptions; then call clearAutocompleteForInputChange
from handleChangeText before scheduling debouncedFetchOptions. Also ensure
fetchOptions/debouncedFetchOptions reads the current sequence when issued and
that any resolved responses compare their response sequence against the store's
current sequence and discard results if stale.
🪄 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: b4edc5ce-6aa7-4489-9c26-831a0b782f6d

📥 Commits

Reviewing files that changed from the base of the PR and between d1acd30 and 21ada25.

📒 Files selected for processing (1)
  • app/containers/NewMediaCall/FilterHeader.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line

Files:

  • app/containers/NewMediaCall/FilterHeader.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins

Files:

  • app/containers/NewMediaCall/FilterHeader.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Files:

  • app/containers/NewMediaCall/FilterHeader.tsx
app/containers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable UI components in app/containers/ directory

Files:

  • app/containers/NewMediaCall/FilterHeader.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/containers/NewMediaCall/FilterHeader.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/selectors/**/*.{ts,tsx} : Place Redux selectors in app/selectors/ directory and memoize them using reselect

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/store/**/*.{ts,tsx} : Configure Redux store in app/lib/store/ with middleware for saga, app state, and internet state

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/index.tsx : Configure Redux provider, theme, navigation, and notifications in app/index.tsx

Applied to files:

  • app/containers/NewMediaCall/FilterHeader.tsx
🔇 Additional comments (1)
app/containers/NewMediaCall/FilterHeader.tsx (1)

26-34: LGTM — auth closure is kept fresh.

Wrapping the debounced fetch callback with [username, sipEnabled] keeps the auth context aligned with Redux changes.

Comment thread app/containers/NewMediaCall/FilterHeader.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

🤖 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/usePeerAutocompleteStore.ts`:
- Around line 64-70: setSelectedPeer currently clears options but doesn't
increment the fetch sequence, so in-flight fetchOptions can still repopulate
options; update both branches in setSelectedPeer to increment the sequence
(e.g., set(s => ({ selectedPeer: null/peer, filter: '', options: [], seq: s.seq
+ 1 }))) so any pending fetchOptions observing seq will be invalidated when
selection changes.
🪄 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: 5e231176-53f9-404f-b267-56bdf95c0b9c

📥 Commits

Reviewing files that changed from the base of the PR and between 21ada25 and 3ceb430.

📒 Files selected for processing (3)
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.test.ts
  • app/lib/services/voip/usePeerAutocompleteStore.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/containers/NewMediaCall/FilterHeader.tsx
  • app/lib/services/voip/usePeerAutocompleteStore.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). (1)
  • GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Configure Prettier with tabs, single quotes, 130 character width, no trailing commas, arrow parens avoid, and bracket same line

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @rocket.chat/eslint-config base configuration including React, React Native, TypeScript, and Jest plugins

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript with strict mode enabled and configure baseUrl to app/ for import resolution

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
app/lib/services/voip/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate

Applied to files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to @(app/sagas/videoConf.ts|app/lib/methods/videoConf.ts) : Manage video conferencing via Redux actions/reducers/sagas in app/sagas/videoConf.ts and app/lib/methods/videoConf.ts using server-managed Jitsi integration; do not conflate with VoIP

Applied to files:

  • app/lib/services/voip/usePeerAutocompleteStore.ts
🔇 Additional comments (4)
app/lib/services/voip/usePeerAutocompleteStore.ts (4)

3-15: Auth-forwarding API looks good.

The store contract now makes the auth dependency explicit and keeps autocomplete decoupled from Redux.


20-57: Stale fetch handling is solid here.

Incrementing the sequence before the empty-filter branch and guarding both success/failure paths prevents older fetches from winning.


59-62: Good invalidation on filter changes.

Bumping seq while clearing peer/options keeps the debounce and async fetch paths coherent.


72-75: Reset correctly invalidates pending autocomplete work.

The sequence bump before clearing state prevents in-flight responses from restoring stale options after reset.

Comment thread app/lib/services/voip/usePeerAutocompleteStore.ts
- Pass username/sipEnabled into getPeerAutocompleteOptions (remove auxStore)
- Thread auth from FilterHeader via useSelector into fetchOptions
- Remove setFilter/clearSelection; use setState/setSelectedPeer(null)
- Async fetch: filter+peer snapshot + monotonic seq; reset invalidates in-flight
- Inline NewMediaCall screen chrome; remove Container + Container story
- PeerList passes option directly; composite FlatList keys
- Add getPeerAutocompleteOptions tests; extend store tests (reset, stale, seq)

Made-with: Cursor
Apply review feedback from PR #7175:
- Move fetchSeq increment above empty-filter early return so every invocation invalidates in-flight requests; scope seq to store closure.
- Drop redundant filter/selectedPeer post-await stale guards; rely on seq alone.
- Keep service pure: getPeerAutocompleteOptions takes username/sipEnabled via TPeerAutocompleteAuth instead of reading Redux.
- Trim filter once at the store boundary.
- Narrow peerInfo via discriminated-union on type.
- Add tests for service trimming, current-username exclusion, SIP-disabled routing, and store-boundary trim.
- FilterHeader: named selectSipEnabled/selectUsername, drop useCallback wrapper around useDebounce.
- Typed mocks: canonical mockUsersAutoCompleteResponse helper; failure shape carries error.
- Drop dead commented setIceServers line in MediaSessionInstance.
- Clear `options: []` in `handleChangeText` so old peer options do not linger during the debounce window.
- Wrap the debounced `fetchOptions` callback in `useCallback` with [username, sipEnabled] deps for explicit closure guarantees.
Add `setFilter` store action that bumps the autocomplete sequence atomically with the filter/options/selectedPeer reset, so an in-flight `fetchOptions` response from a previous keystroke cannot repopulate stale options during the debounce window before the next fetch runs. `FilterHeader` now calls `setFilter` instead of `setState`.
@diegolmello diegolmello force-pushed the refactor/voip-pr2-peer-autocomplete-newmediacall branch from 3ceb430 to 61c896f Compare April 20, 2026 20:17
@diegolmello diegolmello had a problem deploying to official_android_build April 20, 2026 20:20 — with GitHub Actions Error
@diegolmello diegolmello had a problem deploying to experimental_android_build April 20, 2026 20:20 — with GitHub Actions Error
@diegolmello diegolmello had a problem deploying to experimental_ios_build April 20, 2026 20:20 — with GitHub Actions Error
Increment the peer-autocomplete sequence in setSelectedPeer so late
fetchOptions completions cannot repopulate options after the user picks
or clears a peer. Add store tests for both paths.

Made-with: Cursor
…ptions

Normalize with filter.trim() inside the service so callers get consistent
behavior; add whitespace-only coverage and align unit expectations.

Made-with: Cursor
@diegolmello diegolmello had a problem deploying to experimental_android_build April 20, 2026 20:33 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to official_android_build April 20, 2026 20:33 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to experimental_ios_build April 20, 2026 20:33 — with GitHub Actions Failure
@diegolmello diegolmello merged commit 00306b7 into feat.voip-lib-new Apr 20, 2026
5 of 10 checks passed
@diegolmello diegolmello deleted the refactor/voip-pr2-peer-autocomplete-newmediacall branch April 20, 2026 20:35
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