Skip to content

perf(call): cut HeaderCallButton mount cost by consolidating call hooks#7361

Merged
diegolmello merged 1 commit into
developfrom
roomview-header-perf
Jun 2, 2026
Merged

perf(call): cut HeaderCallButton mount cost by consolidating call hooks#7361
diegolmello merged 1 commit into
developfrom
roomview-header-perf

Conversation

@diegolmello

@diegolmello diegolmello commented May 29, 2026

Copy link
Copy Markdown
Member

Proposed changes

HeaderCallButton (rendered in the RoomView navigation header) ran a heavy hook stack on every mount: ~12 redux useSelector calls (8 of them one-per-setting), a native expo-camera permission hook, and a duplicated zustand selector — none of which is needed in the first frame. Because React Navigation renders the header twice during the push transition, this work runs ×2 in the critical room-open commit.

This PR consolidates that per-mount work in the shared call hooks (behavior-preserving — same return values; every consumer benefits: RoomView header, Room Info, Room Actions, Sidebar, VideoConferenceEnded):

  • useVideoConfCall: 8 separate useSetting reads → 1 useAppSelector(..., shallowEqual) (−7 store subscriptions)
  • useVideoConf: reactive useCameraPermissions() → imperative Camera.getCameraPermissionsAsync() / requestCameraPermissionsAsync() at press time (removes a native-backed hook from every render; reads fresh permission at press instead of a reactive snapshot)
  • HeaderCallButton: drop the duplicate useIsInActiveVoipCall() (reuse isInActiveCall from useNewMediaCall)

Benchmark (iOS sim, Argent React Profiler; warm, interleaved A/B) — HeaderCallButton self-time in the critical room-open commit, with MessageTouchable as a warmth control (flat across runs):

develop (n=3) this PR (n=2)
HeaderCallButton self 6.41 / 5.88 / 6.14 → mean 6.14ms 1.25 / 2.68 → mean 1.97ms

−68% (~4.2ms warm) in the component's own self-time, on the critical path; larger absolute saving on cold opens. (Profiling warm-up varies the commit total ~2.4× run-to-run, so per-component self-time is the trustworthy metric.)

Issue(s)

https://rocketchat.atlassian.net/browse/NATIVE-1195

Follow-up to NATIVE-1184 (markdown perf, #7348) from the same RoomView cold-open profiling.

How to test or reproduce

  • Open a room and verify the call button still appears in the header and opens the call action sheet (VoIP "New call" / videoconf start-call sheet).
  • Start a videoconf call and confirm the camera permission is still requested when not yet granted.
  • Exercise the other consumers of these hooks: Room Info, Room Actions, Sidebar, VideoConferenceEnded.

Screenshots

No visual change — header/call button render identically.

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

The hooks are shared, so the change is internal and preserves each hook's public API and return values — verified by the existing CallSection and RoomInfoButtons snapshot tests (no snapshot changes) and on-device (VoIP call sheet still opens).

An alternative was considered: deferring HeaderCallButton's mount past the room-open transition with InteractionManager. That only relocated the work (no net CPU saving) and added a late-appearing button plus an extra re-render commit, with a warm benefit within measurement noise. Consolidation was chosen instead because it reduces the work itself, stays on the critical path, and benefits every consumer.

Summary by CodeRabbit

  • Refactor
    • Improved camera permission flow during video calls for more reliable, async permission prompts.
    • Consolidated video-call settings loading into a single subscription to reduce delays and ensure consistency.
    • Streamlined active-call state handling in call controls for more accurate enable/disable behavior.

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d9d7c90-1c37-4aa6-978b-4315a7d6601a

📥 Commits

Reviewing files that changed from the base of the PR and between 1ca2f38 and 918d393.

📒 Files selected for processing (3)
  • app/lib/hooks/useVideoConf/index.tsx
  • app/lib/hooks/useVideoConf/useVideoConfCall.ts
  • app/views/RoomView/components/HeaderCallButton.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/lib/hooks/useVideoConf/index.tsx
  • app/views/RoomView/components/HeaderCallButton.tsx
  • app/lib/hooks/useVideoConf/useVideoConfCall.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: ESLint and Test / run-eslint-and-test
  • GitHub Check: format

Walkthrough

Refactors video call hooks to use the direct Camera API for permissions, consolidate Jitsi/VideoConf settings into one selector, and switch HeaderCallButton to use the unified isInActiveCall state.

Changes

Video Call Hook Infrastructure Refactor

Layer / File(s) Summary
Camera permission flow using direct API
app/lib/hooks/useVideoConf/index.tsx
useVideoConf replaces the useCameraPermissions hook with direct Camera.getCameraPermissionsAsync() and Camera.requestCameraPermissionsAsync() calls for permission acquisition during call initialization.
Settings aggregation with consolidated selector
app/lib/hooks/useVideoConf/useVideoConfCall.ts
useVideoConfCall consolidates separate useSetting calls into a single useAppSelector subscription with shallowEqual, then derives enabledDMs, enabledChannel, enabledTeams, enabledGroups, and enabledLiveChat from explicit-false checks on the aggregated settings state.
Unified active call state usage
app/views/RoomView/components/HeaderCallButton.tsx
HeaderCallButton removes the useIsInActiveVoipCall hook and uses isInActiveCall from useNewMediaCall to determine the disabled state of the call-init header button.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

type: chore

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'perf(call): cut HeaderCallButton mount cost by consolidating call hooks' accurately summarizes the main objective of reducing HeaderCallButton performance by consolidating hooks, which is the primary focus of the PR changes across all three modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (2)
  • NATIVE-1195: Request failed with status code 401
  • NATIVE-1184: Request failed with status code 401

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.

Comment thread app/lib/hooks/useVideoConf/useVideoConfCall.ts

@OtavioStasiak OtavioStasiak 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.

lgtm

HeaderCallButton ran ~12 useSelector calls (8 one-per-setting), a native
expo-camera permission hook, and a duplicated zustand selector on every
mount. Consolidate this per-mount work (behavior-preserving) in the shared
call hooks:

- useVideoConfCall: 8 useSetting -> 1 useAppSelector(..., shallowEqual)
- useVideoConf: reactive useCameraPermissions() -> imperative
  Camera.get/requestCameraPermissionsAsync() at press time
- HeaderCallButton: drop duplicate useIsInActiveVoipCall()

Argent React Profiler (warm interleaved A/B), HeaderCallButton self-time in
the critical room-open commit: develop ~6.14ms -> consolidated ~1.97ms (-68%),
on the critical path; larger absolute saving on cold opens.
@diegolmello diegolmello force-pushed the roomview-header-perf branch from 1ca2f38 to 918d393 Compare June 2, 2026 20:03
@diegolmello diegolmello requested a deployment to approve_e2e_testing June 2, 2026 20:04 — with GitHub Actions Waiting
@diegolmello diegolmello merged commit 91fa530 into develop Jun 2, 2026
6 of 9 checks passed
@diegolmello diegolmello deleted the roomview-header-perf branch June 2, 2026 20:32
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.

2 participants