Skip to content

feat(voip): tap-to-hide call controls with animations#7078

Open
diegolmello wants to merge 9 commits intofeat.voip-lib-newfrom
feat.controls-animation
Open

feat(voip): tap-to-hide call controls with animations#7078
diegolmello wants to merge 9 commits intofeat.voip-lib-newfrom
feat.controls-animation

Conversation

@diegolmello
Copy link
Copy Markdown
Member

@diegolmello diegolmello commented Mar 31, 2026

Proposed changes

This PR adds tap-to-hide (and tap-to-show) VoIP call controls on the in-call screen, with Reanimated opacity and translation transitions and a single source of truth in the call Zustand store.

User-facing behavior

  • Tapping the caller info area (Pressable around avatar + name) toggles whether secondary controls are visible.
  • Caller name row fades and slides slightly; the avatar stays visible and centered so the user always sees who is on the call.
  • Call action buttons (mute, hold, speaker, dialpad, message, end) sit in an animated container that fades and slides down when hidden. When hidden, pointerEvents is set to none so taps do not hit invisible controls.
  • Media call header (expanded call view only): when the call surface is focused and controls are hidden, the header slides up and fades out; when the call is collapsed (not focused), the header stays visible and interactive regardless of controlsVisible, so the mini header bar remains usable.

Store and lifecycle

  • New controlsVisible flag (default true) with toggleControlsVisible, showControls, and selector useControlsVisible for components that animate or gate interaction.
  • showControls() is the single path for forcing controls back on after important events: stateChange and trackStateChange on the media call emitter, and toggleFocus when switching between expanded and collapsed call UI. That keeps “force visible” in one place and avoids scattering controlsVisible patches next to unrelated set({ … }) updates.
  • reset() restores initial store state (including controlsVisible) like other call UI fields.

Shared animation timing

  • CONTROLS_ANIMATION_DURATION (300 ms) is defined once in CallView styles and reused by CallButtons, CallerInfo, and MediaCallHeader so timing stays consistent.

Tests

  • Store tests cover default visibility, toggle, reset, focus toggle, and auto-reveal on simulated stateChange / trackStateChange. The in-memory media-call mock’s emit forwards variadic arguments to listeners (aligned with real emitters) with a test that locks that behavior in.
  • Component and integration tests were updated for the caller-info-toggle testID, pointerEvents when controls are hidden vs visible (CallButtons and MediaCallHeader), and existing CallView flows.

Architecture note (Zustand vs Reanimated shared values)

  • controlsVisible stays in Zustand. Subscribers re-render when it changes; useAnimatedStyle reads the boolean on those updates. This PR does not mirror that flag into a Reanimated shared value for these animations—intentionally, to avoid duplicating state and to keep the feature easy to follow in one store.

Issue(s)

How to test or reproduce

  • Run yarn test. Pay particular attention to app/lib/services/voip/useCallStore.test.ts, CallView-related tests, CallerInfo, CallButtons, and MediaCallHeader tests.
  • Manual smoke: start a VoIP call, expand the call view, tap the caller info area to hide controls—confirm buttons and (when focused) the media header animate out and do not accept stray taps. Tap again to show. Collapse the call—confirm the header stays usable. Trigger mute/hold or other track/state changes and expand/collapse focus—controls should auto-show when those events fire, matching previous expectations for visibility on important updates.

Screenshots

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

Suggested reply if a reviewer asks to drive these animations only from Reanimated shared values synced via useEffect: We keep controlsVisible in Zustand; components that subscribe re-render when it changes, which updates useAnimatedStyle. We are not mirroring this flag into shared values for this feature.

Add toggle/show controls visibility for tap-to-hide animation support
in CallView. Includes convenience selector and animation duration constant.
Wrap CallerInfo with Pressable to toggle controlsVisible on tap.
Animate caller name row with fade + slide using Reanimated.
Avatar remains always visible and centered.
Wrap CallButtons in Animated.View with opacity and translateY animations
driven by controlsVisible store state. Set pointerEvents to 'none' when
hidden to block ghost taps.
Update 'caller-info' to 'caller-info-toggle' to match the testID
rename from the CallerInfo toggle implementation.
Replace active-call View with Animated.View that slides up and fades
out when controlsVisible is false. Animation only applies when focused
(expanded call view); collapsed header bar remains always visible.
Show controls automatically when stateChange, trackStateChange events
fire or when toggling focus, so users never miss important state updates.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Walkthrough

Adds a controlsVisible boolean to the call store with actions to toggle/show it, surfaces a useControlsVisible selector, and updates CallerInfo, CallButtons, and MediaCallHeader to animate visibility and gate interactions based on that state; controls auto-show on call/track state changes and focus toggles.

Changes

Cohort / File(s) Summary
Store State & Tests
app/lib/services/voip/useCallStore.ts, app/lib/services/voip/useCallStore.test.ts
Added controlsVisible: boolean (default true), actions toggleControlsVisible and showControls, selector useControlsVisible; handlers now call showControls() on stateChange/trackStateChange and after toggleFocus. Tests updated and new coverage for controls visibility and mock emitter behavior.
Animation Constant
app/views/CallView/styles.ts
Exported CONTROLS_ANIMATION_DURATION = 300 for shared timing.
CallerInfo & Tests
app/views/CallView/components/CallerInfo.tsx, app/views/CallView/components/CallerInfo.test.tsx
Converted container to Pressable that calls toggleControlsVisible; animated caller name row using Reanimated (opacity/translateY) driven by useControlsVisible; tests assert toggle behavior and new caller-info-toggle testID.
CallButtons & Tests
app/views/CallView/components/CallButtons.tsx, app/views/CallView/components/CallButtons.test.tsx
Wrapped root in Animated.View with animated style (opacity/translateY) driven by useControlsVisible; sets pointerEvents to 'none' when hidden; tests assert pointerEvents based on store state.
MediaCallHeader & Tests
app/containers/MediaCallHeader/MediaCallHeader.tsx, app/containers/MediaCallHeader/MediaCallHeader.test.tsx
Switched header to Animated.View; computes shouldHide = focused && !controlsVisible; animates slide/fade when hidden and sets pointerEvents 'none' when hidden; tests added for pointerEvents across focused/controlsVisible combos.
Integration Tests
app/views/CallView/index.test.tsx
Updated expectations to use caller-info-toggle testID where caller-info was previously asserted.
Docs
progress-controls-animation.md
New documentation describing the six-step progress plan and implementation details for tap-to-hide controls and auto-show behavior.

Sequence Diagram

sequenceDiagram
    actor User
    participant CallerInfo as CallerInfo<br/>(Pressable)
    participant Store as useCallStore
    participant CallButtons as CallButtons<br/>(Animated)
    participant Header as MediaCallHeader<br/>(Animated)
    participant AnimSys as Reanimated

    User->>CallerInfo: Tap
    CallerInfo->>Store: toggleControlsVisible()
    Store->>Store: controlsVisible = !controlsVisible
    Store-->>CallButtons: notify controlsVisible
    Store-->>Header: notify controlsVisible
    CallButtons->>AnimSys: update animated style (opacity, translateY)
    AnimSys-->>CallButtons: animate (~300ms)
    CallButtons->>CallButtons: set pointerEvents (auto/none)
    Header->>AnimSys: update animated style if focused && !controlsVisible
    AnimSys-->>Header: animate (~300ms)
    Header->>Header: set pointerEvents (auto/none)

    Note over Store: On call state/track change or toggleFocus
    Store->>Store: showControls() -> controlsVisible = true
    Store-->>CallButtons: notify controlsVisible
    Store-->>Header: notify controlsVisible
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding tap-to-hide functionality for call controls with animations throughout the VoIP call UI components.
Linked Issues check ✅ Passed The PR implements logic to hide/show call controls with new controlsVisible state, toggle actions, animations, and interactive tap handling in CallerInfo, aligning with VMUX-19's objective.
Out of Scope Changes check ✅ Passed All changes directly support the controls hide/show mechanism: store state/actions, UI animations, interaction gates via pointerEvents, test coverage, and documentation are all in-scope.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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: 2

🧹 Nitpick comments (2)
app/lib/services/voip/useCallStore.ts (1)

208-210: Consider whether showControls is needed.

The showControls action is defined but the auto-reveal logic uses inline set({ controlsVisible: true }) instead. This action may be useful for external consumers or future enhancements. If it's not needed externally, you could simplify by removing it, or alternatively refactor the inline sets to use this action for consistency.

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

In `@app/lib/services/voip/useCallStore.ts` around lines 208 - 210, The store
defines a showControls action but other code uses inline set({ controlsVisible:
true }), causing duplication; either remove the showControls action if it has no
external usage, or replace the inline set({ controlsVisible: true }) calls with
calls to showControls for consistency. Locate the showControls function in
useCallStore and the places that call set({ controlsVisible: true }) and choose
one approach: delete showControls and keep inline sets (and remove its export),
or refactor those inline sets to call showControls so all visibility changes go
through the single action.
app/lib/services/voip/useCallStore.test.ts (1)

33-35: Forward event payloads in the mock emitter.

This helper currently invokes listeners without args, which can mask regressions if store handlers later depend on event payloads.

♻️ Proposed tweak
-	const emit = (ev: string) => {
-		listeners[ev]?.forEach(fn => fn());
-	};
+	const emit = (ev: string, ...args: unknown[]) => {
+		listeners[ev]?.forEach(fn => fn(...args));
+	};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/services/voip/useCallStore.test.ts` around lines 33 - 35, The mock
emitter function emit currently calls listeners without forwarding payloads;
update the emit helper in useCallStore.test.ts so its signature accepts variadic
payloads (e.g., emit(ev: string, ...args)) and invoke each listener with those
payloads (fn(...args)); also adjust the listeners typing if necessary so
listener functions can receive arguments, ensuring tests exercise real event
payload handling used by the store handlers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/views/CallView/components/CallButtons.tsx`:
- Around line 30-33: The animated worklet in containerStyle captures
controlsVisible at creation time so it won't react to zustand updates; convert
the zustand boolean (from useControlsVisible) into a reanimated shared value
(useSharedValue) and sync it in a useEffect when the selector changes, then
reference that shared value inside useAnimatedStyle so opacity/translateY use
withTiming against the shared value (keep CONTROLS_ANIMATION_DURATION as-is) to
restore reactivity.

In `@app/views/CallView/components/CallerInfo.tsx`:
- Around line 17-20: The animated style uses the plain JS boolean
controlsVisible inside useAnimatedStyle which harms Reanimated performance;
convert controlsVisible into a Reanimated shared value and reference that inside
callerRowStyle. Specifically, create a useSharedValue (e.g.,
controlsVisibleShared), update it when the React prop/store changes (via
useEffect or a derived value) and then replace references to controlsVisible in
the useAnimatedStyle callback for callerRowStyle with
controlsVisibleShared.value while keeping CONTROLS_ANIMATION_DURATION and the
same withTiming logic.

---

Nitpick comments:
In `@app/lib/services/voip/useCallStore.test.ts`:
- Around line 33-35: The mock emitter function emit currently calls listeners
without forwarding payloads; update the emit helper in useCallStore.test.ts so
its signature accepts variadic payloads (e.g., emit(ev: string, ...args)) and
invoke each listener with those payloads (fn(...args)); also adjust the
listeners typing if necessary so listener functions can receive arguments,
ensuring tests exercise real event payload handling used by the store handlers.

In `@app/lib/services/voip/useCallStore.ts`:
- Around line 208-210: The store defines a showControls action but other code
uses inline set({ controlsVisible: true }), causing duplication; either remove
the showControls action if it has no external usage, or replace the inline set({
controlsVisible: true }) calls with calls to showControls for consistency.
Locate the showControls function in useCallStore and the places that call set({
controlsVisible: true }) and choose one approach: delete showControls and keep
inline sets (and remove its export), or refactor those inline sets to call
showControls so all visibility changes go through the single action.
🪄 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: 38495b52-7fdb-4b65-86d3-caa69559972d

📥 Commits

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

⛔ Files ignored due to path filters (3)
  • app/containers/MediaCallHeader/__snapshots__/MediaCallHeader.test.tsx.snap is excluded by !**/*.snap
  • app/views/CallView/__snapshots__/index.test.tsx.snap is excluded by !**/*.snap
  • app/views/CallView/components/__snapshots__/CallerInfo.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (11)
  • app/containers/MediaCallHeader/MediaCallHeader.test.tsx
  • app/containers/MediaCallHeader/MediaCallHeader.tsx
  • 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/components/CallerInfo.test.tsx
  • app/views/CallView/components/CallerInfo.tsx
  • app/views/CallView/index.test.tsx
  • app/views/CallView/styles.ts
  • progress-controls-animation.md
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 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/views/CallView/components/CallerInfo.test.tsx
  • app/views/CallView/components/CallButtons.tsx
  • app/views/CallView/components/CallerInfo.tsx
🔇 Additional comments (13)
app/views/CallView/styles.ts (1)

5-6: LGTM!

Good practice extracting the animation duration as a shared constant. This ensures consistent timing across all animated components (CallButtons, CallerInfo, MediaCallHeader) and makes future adjustments easy.

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

204-210: LGTM - clean state management implementation.

The toggleControlsVisible and showControls actions follow Zustand patterns correctly. Auto-revealing controls on stateChange, trackStateChange, and toggleFocus provides good UX by ensuring users see the controls during important call events.

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

52-55: LGTM for the Animated.View setup.

The pointerEvents toggle is a good practice to prevent accidental taps on hidden controls, and maintaining testID ensures testability.

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

57-70: LGTM - good test coverage for toggle behavior.

The test properly verifies the complete toggle cycle (true → false → true) and directly accesses store state which is appropriate for testing Zustand stores.

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

179-210: LGTM - comprehensive test coverage for pointer events behavior.

The three test cases properly cover the visibility matrix:

  • focused=true, controlsVisible=falsepointerEvents='none'
  • focused=true, controlsVisible=truepointerEvents='auto'
  • focused=falsepointerEvents='auto' (header always interactive when collapsed)

This correctly validates the conditional hiding logic.

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

94-95: LGTM - testID references updated consistently.

All assertions correctly updated from caller-info to caller-info-toggle to match the renamed/restructured CallerInfo component.

Also applies to: 105-106, 135-138, 302-303, 314-315

app/views/CallView/components/CallerInfo.tsx (1)

26-35: LGTM - clean implementation of tap-to-hide with correct separation.

The avatar correctly remains outside the animated view (always visible), while only the caller name row animates. The Pressable provides the expected tap target covering the entire caller info area.

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

33-38: Logic is correct; same animated style note applies.

The shouldHide = focused && !controlsVisible logic correctly ensures:

  • Header hides only when the CallView is focused AND controls are toggled off
  • Header stays visible when collapsed (not focused), regardless of controlsVisible

This matches the PR objective: "collapsed header bar remains visible."


50-61: LGTM - well-structured animated header.

The Animated.View correctly applies the combined styles and manages pointer events. The empty state branch appropriately remains a plain View since no animation is needed when there's no call.

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

57-118: Good coverage for controlsVisible lifecycle.

The suite exercises default state, action transitions, reset behavior, focus toggling, and auto-reveal on call events—this is solid coverage for the new store contract.


174-174: Nice fix in stale-timer setup.

Passing createMockCall('x').call keeps the test aligned with setCall input shape while preserving the helper’s event emitter API.

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

41-63: Targeted interaction-gating tests look good.

Verifying pointerEvents for both hidden and visible states gives clear coverage for preventing ghost taps when controls are hidden.

progress-controls-animation.md (1)

1-123: Well-structured rollout documentation.

The slice breakdown, demos, and decision log are clear and actionable for both implementation tracking and QA validation.

Comment on lines +30 to +33
const containerStyle = useAnimatedStyle(() => ({
opacity: withTiming(controlsVisible ? 1 : 0, { duration: CONTROLS_ANIMATION_DURATION }),
transform: [{ translateY: withTiming(controlsVisible ? 0 : 100, { duration: CONTROLS_ANIMATION_DURATION }) }]
}));
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

react-native-reanimated 3 useAnimatedStyle reactivity with JavaScript values

💡 Result:

In React Native Reanimated 3, useAnimatedStyle primarily reacts to changes in shared values (created with useSharedValue) for automatic UI thread updates without triggering React re-renders. The official documentation states: "Styles are automatically updated whenever an associated shared value or React state changes." However, direct use of plain JavaScript values (like regular React state from useState) inside the useAnimatedStyle worklet does not automatically trigger updates, as the worklet captures dependencies at creation time and plain JS values are not tracked like shared values. To achieve reactivity with JavaScript values: 1. Mirror React state in a shared value: Update the shared value in a useEffect when the JS value changes. Example: const [jsState, setJsState] = useState(0); const sharedValue = useSharedValue(0); useEffect( => { sharedValue.value = jsState; }, [jsState]); const animatedStyle = useAnimatedStyle( => ({ opacity: sharedValue.value, })); This ensures the style updates smoothly on the UI thread. The optional dependencies array is mainly for web without the Babel plugin and does not enable reactivity to plain JS values on mobile. Known issues exist where styles may not update on re-render if shared values are set during render (e.g., GitHub issues #6276, #6298), fixed in later patches like 3.14.0. Always update shared values outside render (e.g., in event handlers or useEffect). For optimal performance, prefer shared values over React state for animation drivers, as plain JS state changes cause re-renders while shared values do not.

Citations:


🏁 Script executed:

# First, let's look at the CallButtons.tsx file around the relevant lines
head -50 app/views/CallView/components/CallButtons.tsx | tail -30

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1079


🏁 Script executed:

# Also check what useControlsVisible returns
rg -n "useControlsVisible" app/views/CallView/components/CallButtons.tsx -A 2 -B 2

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 502


🏁 Script executed:

# Find where useControlsVisible is defined
fd -name "*.ts" -o -name "*.tsx" | xargs rg -l "export.*useControlsVisible" | head -5

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 363


🏁 Script executed:

cat -n app/lib/services/voip/useCallStore.ts | head -100

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 3857


🏁 Script executed:

cat -n app/lib/services/voip/useCallStore.ts | tail -100

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 3160


Animated style should use a shared value for proper reactivity.

In react-native-reanimated 3.x, useAnimatedStyle worklets capture dependencies at creation time. The controlsVisible value comes from a zustand store selector (not a shared value), so changes to it won't automatically trigger the worklet to re-run. Use useSharedValue to sync the zustand state, or extract it to a shared value via useEffect:

const controlsVisibleBool = useControlsVisible();
const controlsVisibleShared = useSharedValue(controlsVisibleBool);

useEffect(() => {
  controlsVisibleShared.value = controlsVisibleBool;
}, [controlsVisibleBool]);

const containerStyle = useAnimatedStyle(() => ({
  opacity: withTiming(controlsVisibleShared.value ? 1 : 0, { duration: CONTROLS_ANIMATION_DURATION }),
  transform: [{ translateY: withTiming(controlsVisibleShared.value ? 0 : 100, { duration: CONTROLS_ANIMATION_DURATION }) }]
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/views/CallView/components/CallButtons.tsx` around lines 30 - 33, The
animated worklet in containerStyle captures controlsVisible at creation time so
it won't react to zustand updates; convert the zustand boolean (from
useControlsVisible) into a reanimated shared value (useSharedValue) and sync it
in a useEffect when the selector changes, then reference that shared value
inside useAnimatedStyle so opacity/translateY use withTiming against the shared
value (keep CONTROLS_ANIMATION_DURATION as-is) to restore reactivity.

Comment on lines +17 to +20
const callerRowStyle = useAnimatedStyle(() => ({
opacity: withTiming(controlsVisible ? 1 : 0, { duration: CONTROLS_ANIMATION_DURATION }),
transform: [{ translateY: withTiming(controlsVisible ? 0 : 10, { duration: CONTROLS_ANIMATION_DURATION }) }]
}));
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.

⚠️ Potential issue | 🟡 Minor

Same animated style reactivity consideration as CallButtons.

The controlsVisible JavaScript value is used directly inside useAnimatedStyle. This may work in practice since the component re-renders when the store updates, but for optimal Reanimated performance, consider using shared values.

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

In `@app/views/CallView/components/CallerInfo.tsx` around lines 17 - 20, The
animated style uses the plain JS boolean controlsVisible inside useAnimatedStyle
which harms Reanimated performance; convert controlsVisible into a Reanimated
shared value and reference that inside callerRowStyle. Specifically, create a
useSharedValue (e.g., controlsVisibleShared), update it when the React
prop/store changes (via useEffect or a derived value) and then replace
references to controlsVisible in the useAnimatedStyle callback for
callerRowStyle with controlsVisibleShared.value while keeping
CONTROLS_ANIMATION_DURATION and the same withTiming logic.

- Call showControls() from stateChange, trackStateChange, and toggleFocus
  instead of inlining controlsVisible in set() patches
- Mock media emitter forwards variadic args in useCallStore tests
- Add test and JSDoc for controls visibility / animation consumers

Made-with: Cursor
@diegolmello diegolmello requested a deployment to approve_e2e_testing April 2, 2026 14:36 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to experimental_ios_build April 2, 2026 14:41 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to official_ios_build April 2, 2026 14:41 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to official_android_build April 2, 2026 14:41 — with GitHub Actions Waiting
@diegolmello diegolmello requested a deployment to experimental_android_build April 2, 2026 14:41 — with GitHub Actions Waiting
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