Skip to content

chore: upgrade reanimated to v4#6720

Open
Rohit3523 wants to merge 118 commits intodevelopfrom
reanimated-v4
Open

chore: upgrade reanimated to v4#6720
Rohit3523 wants to merge 118 commits intodevelopfrom
reanimated-v4

Conversation

@Rohit3523
Copy link
Contributor

@Rohit3523 Rohit3523 commented Oct 12, 2025

Proposed changes

This PR upgrades react-native-reanimated from v3 to v4.

Short Summary of the changes

  • Updated react-native-reanimated from 3.17.1 to 4.1.3
  • Added react-native-worklets (0.6.1), which is now a separate package required by Reanimated v4
  • Migrated all animation callback APIs to use the new scheduleOnRN function (replaces the deprecated runOnJS)
  • Update audio player seek bar gesture handler to use the current Gesture API because useAnimatedGestureHandler is removed in v4

Issue(s)

https://rocketchat.atlassian.net/browse/COMM-46

How to test or reproduce

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

Summary by CodeRabbit

  • Refactor

    • Improved gesture handling and event synchronization mechanisms across interactive components for enhanced responsiveness and smoother animations.
  • Dependencies

    • Updated react-native-reanimated to v4.1.3 for improved gesture performance and API stability.
    • Added react-native-worklets v0.6.1 to enhance scheduling and worklet capabilities.
    • Updated @gorhom/bottom-sheet to v5.2.6.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 12, 2025

Walkthrough

This PR upgrades React Native Reanimated from v3 to v4 and integrates React Native Worklets, replacing the deprecated runOnJS API with scheduleOnRN across multiple components, updating gesture handling to the new Gesture API, modifying type imports, and adjusting build configuration and dependencies accordingly.

Changes

Cohort / File(s) Summary
Animation Scheduling Migration
app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx, app/containers/RoomItem/Actions.tsx, app/containers/RoomItem/Touchable.tsx, app/views/RoomView/List/components/List.tsx
Replaced runOnJS with scheduleOnRN for state updates and callback scheduling on the React Native thread. Updated imports to use scheduleOnRN from react-native-worklets instead of runOnJS from react-native-reanimated.
Gesture Handler Refactor
app/containers/AudioPlayer/Seek.tsx
Migrated from PanGestureHandler/useAnimatedGestureHandler to Gesture.Pan() with GestureDetector. Refactored pan lifecycle: start captures initial position and animates scale, update computes clamped position, end schedules time change via scheduleOnRN, finalize resets state and scale.
Type Definition Updates
app/containers/RoomItem/interfaces.ts
Changed transX field type from Animated.SharedValue<number> to SharedValue<number> in ILeftActionsProps and IRightActionsProps interfaces.
Build & Test Configuration
babel.config.js, jest.setup.js, package.json
Swapped Babel plugin from react-native-reanimated/plugin to react-native-worklets/plugin. Updated dependency versions: Reanimated ^3.17.1 → ^4.1.3, added react-native-worklets ^0.6.1, replaced @discord/bottom-sheet with @gorhom/bottom-sheet ^5.2.6. Removed runOnJS mock from test setup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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 'chore: upgrade reanimated to v4' accurately and specifically describes the main change—upgrading react-native-reanimated from v3 to v4.
Linked Issues check ✅ Passed The PR fulfills the COMM-46 objective by upgrading reanimated to v4, adding react-native-worklets, migrating runOnJS to scheduleOnRN, and updating gesture handlers.
Out of Scope Changes check ✅ Passed All changes are directly related to the reanimated v4 upgrade: dependency updates, API migrations, gesture handler refactoring, and build config changes align with the stated objective.
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.

@Rohit3523 Rohit3523 temporarily deployed to approve_e2e_testing October 12, 2025 22:05 — with GitHub Actions Inactive
@Rohit3523 Rohit3523 changed the title chore: Reanimated v4 chore: reanimated v4 Oct 12, 2025
@Rohit3523 Rohit3523 temporarily deployed to experimental_android_build October 12, 2025 22:09 — with GitHub Actions Inactive
@Rohit3523 Rohit3523 had a problem deploying to official_android_build October 12, 2025 22:09 — with GitHub Actions Failure
@Rohit3523 Rohit3523 had a problem deploying to experimental_ios_build October 12, 2025 22:09 — with GitHub Actions Failure
@Rohit3523 Rohit3523 had a problem deploying to upload_experimental_android October 12, 2025 22:49 — with GitHub Actions Failure
@github-actions
Copy link

Android Build Available

Rocket.Chat Experimental 4.66.0.107555

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNTOBmiQlkEe-OeA3KvdurMOyXgF0YILJjAwuhDsPx5-LdCpHYt19pvHOiftS6iCycG0q6ZpQ_pgK-UBglwa

@Rohit3523 Rohit3523 changed the title chore: reanimated v4 chore: upgrade reanimated to v4 Oct 15, 2025
@OtavioStasiak OtavioStasiak marked this pull request as ready for review January 16, 2026 21:59
Copy link
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

🤖 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/AudioPlayer/Seek.tsx`:
- Around line 76-80: The scale-up animation is currently run inside onUpdate
causing it to restart every frame; move the withTiming(1.3, { duration: 150 })
call into the onStart handler so it runs once when the gesture begins. Update
the gesture handlers around onStart, onUpdate, and onEnd: in onStart call
scale.value = withTiming(1.3, { duration: 150 }) (and set any initial
contextX.value if used), in onUpdate keep only the translation logic using
contextX, translateX and maxWidth (clamp newX), and in onEnd revert scale with
withTiming(1, { duration: 150 }) to restore the original size. Ensure you
reference the existing scale, translateX, contextX, maxWidth,
onStart/onUpdate/onEnd functions so behavior and state remain consistent.
- Line 84: The call to scheduleOnRN(onChangeTime, Math.round(currentTime.value *
1000)) from the gesture handler can cause iOS crashes when it bridges into the
main thread and calls AudioManager.setPositionAsync; update Seek.tsx to detect
iOS and avoid a single-shot scheduleOnRN call: add a platform check (iOS) around
scheduleOnRN, and implement a safe fallback such as queuing the position update
on the RN main thread with a short retry/backoff (e.g., try up to N times with
small delays) or call a native-safe bridge method instead of directly invoking
AudioManager.setPositionAsync from the worklet; ensure you reference
scheduleOnRN, onChangeTime, currentTime.value and the code path that ultimately
calls AudioManager.setPositionAsync when adding the workaround.
🪄 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: 4049f5e3-0a17-4646-b519-d0591066f65a

📥 Commits

Reviewing files that changed from the base of the PR and between d3f516b and 4ff6637.

📒 Files selected for processing (3)
  • app/containers/AudioPlayer/Seek.tsx
  • app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx
  • app/containers/RoomItem/Actions.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/containers/RoomItem/Actions.tsx
  • app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx
📜 Review details
🔇 Additional comments (3)
app/containers/AudioPlayer/Seek.tsx (3)

3-12: Imports look correct for the Reanimated v4 migration.

The migration to Gesture/GestureDetector API and scheduleOnRN from react-native-worklets aligns with the PR objectives. The removal of runOnJS import is expected as it's replaced by scheduleOnRN.


52-52: Good addition of contextX shared value.

This correctly replaces the context parameter from useAnimatedGestureHandler to capture the starting position at gesture start.


125-127: Correct usage of GestureDetector wrapper.

The migration from PanGestureHandler component to GestureDetector with the composed gesture object follows the recommended react-native-gesture-handler v2+ pattern.

Copy link
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

🤖 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/AudioPlayer/Seek.tsx`:
- Around line 67-81: The onEnd handler schedules onChangeTime (an async
Promise-returning function) directly via scheduleOnRN which can cause unhandled
rejections; create a RN-scoped wrapper in the component (e.g., seekOnRN) that
calls onChangeTime(Math.round(currentTime.value * 1000)), catches any rejection
and forwards it to the component's existing error path/handler, and then pass
that wrapper to scheduleOnRN in panGesture.onEnd instead of onChangeTime.
- Around line 82-85: onFinalize currently always resets isPanning and scale but
doesn’t restore state when a pan is cancelled; update the onFinalize callback to
check the provided didSucceed flag and if didSucceed === false restore the
pre-pan position by setting translateX back to the captured contextX and
resetting currentTime.value from contextX (the same context captured during
gesture start), otherwise (didSucceed === true) call onChangeTime to commit the
seek; ensure you still set isPanning.value = false and run scale.value =
withTiming(1, { duration: 150 }) in all cases.
🪄 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: 193f2f01-d5af-4b5d-b308-8a4a05d8e640

📥 Commits

Reviewing files that changed from the base of the PR and between d3f516b and a541660.

📒 Files selected for processing (3)
  • app/containers/AudioPlayer/Seek.tsx
  • app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx
  • app/containers/RoomItem/Actions.tsx
✅ Files skipped from review due to trivial changes (1)
  • app/containers/RoomItem/Actions.tsx

Comment on lines +67 to +81
const panGesture = Gesture.Pan()
.enabled(loaded)
.activeOffsetX([-ACTIVE_OFFSET_X, ACTIVE_OFFSET_X])
.onStart(() => {
isPanning.value = true;
ctx.offsetX = translateX.value;
},
onActive: ({ translationX }, ctx) => {
translateX.value = clamp(ctx.offsetX + translationX, 0, maxWidth.value);
scale.value = 1.3;
},
onFinish() {
scale.value = 1;
contextX.value = translateX.value;
scale.value = withTiming(1.3, { duration: 150 });
})
.onUpdate(event => {
const newX = contextX.value + event.translationX;
translateX.value = clamp(newX, 0, maxWidth.value);
})
.onEnd(() => {
scheduleOnRN(onChangeTime, Math.round(currentTime.value * 1000));
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd app/containers/AudioPlayer && wc -l Seek.tsx

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 90


🏁 Script executed:

cat -n app/containers/AudioPlayer/Seek.tsx | head -100

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 4014


🏁 Script executed:

# Search for ISeek interface definition
rg "interface ISeek" -A 10

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 743


🏁 Script executed:

# Search for scheduleOnRN usage and imports
rg "scheduleOnRN" -B 2 -A 2

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 8010


🏁 Script executed:

# Search for onChangeTime type definition
rg "onChangeTime" -B 2 -A 2

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1552


Wrap the async seek callback before scheduling it.

Line 80 passes onChangeTime directly to scheduleOnRN. Since onChangeTime returns Promise<void> but scheduleOnRN is fire-and-forget (void return), any rejection from the seek operation will result in an unhandled promise rejection that bypasses error handling. Define an RN-scope wrapper in the component body that catches and routes promise rejections through the existing error path, then schedule that wrapper instead. (docs.swmansion.com)

🛡️ Proposed fix
+ const commitChangeTime = React.useCallback((time: number) => {
+ 	void onChangeTime(time).catch(() => {
+ 		// Route this through the existing error/reporting path.
+ 	});
+ }, [onChangeTime]);
+
  const panGesture = Gesture.Pan()
  	.enabled(loaded)
  	.activeOffsetX([-ACTIVE_OFFSET_X, ACTIVE_OFFSET_X])
  	.onStart(() => {
  		isPanning.value = true;
  		contextX.value = translateX.value;
  		scale.value = withTiming(1.3, { duration: 150 });
  	})
  	.onUpdate(event => {
  		const newX = contextX.value + event.translationX;
  		translateX.value = clamp(newX, 0, maxWidth.value);
  	})
  	.onEnd(() => {
- 		scheduleOnRN(onChangeTime, Math.round(currentTime.value * 1000));
+ 		scheduleOnRN(commitChangeTime, Math.round(currentTime.value * 1000));
  	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/AudioPlayer/Seek.tsx` around lines 67 - 81, The onEnd handler
schedules onChangeTime (an async Promise-returning function) directly via
scheduleOnRN which can cause unhandled rejections; create a RN-scoped wrapper in
the component (e.g., seekOnRN) that calls
onChangeTime(Math.round(currentTime.value * 1000)), catches any rejection and
forwards it to the component's existing error path/handler, and then pass that
wrapper to scheduleOnRN in panGesture.onEnd instead of onChangeTime.

Comment on lines +82 to +85
.onFinalize(() => {
isPanning.value = false;
runOnJS(onChangeTime)(Math.round(currentTime.value * 1000));
}
});
scale.value = withTiming(1, { duration: 150 });
});
Copy link
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

🏁 Script executed:

find . -name "Seek.tsx" -type f

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 115


🏁 Script executed:

cat -n ./app/containers/AudioPlayer/Seek.tsx

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 5414


🌐 Web query:

react-native-gesture-handler pan gesture onFinalize didSucceed parameter

💡 Result:

In react-native-gesture-handler, the Pan gesture's onFinalize callback receives a second parameter called didSucceed, which is a boolean indicating whether the gesture was successfully recognized and completed. Signature: onFinalize: (event: PanHandlerData, didSucceed: boolean) => void - didSucceed is true if the gesture reached the END state (successfully completed after being ACTIVE). - didSucceed is false if the gesture FAILED or was CANCELLED. This applies to the modern API (usePanGesture in RNGH 3.x+). The callback is called whenever the handler stops recognizing the gesture, after onDeactivate if the gesture activated.

Citations:


Restore the pre-pan position when the gesture is cancelled.

When a pan gesture is cancelled, onEnd does not run (it only executes for successful gestures), so onChangeTime is never called and the seek is not committed. However, onFinalize always runs and provides didSucceed to indicate the outcome. Since currentTime.value is mutated during panning (line 91), a cancelled gesture leaves the thumb at an uncommitted position until the next external update. Restore the pre-pan translateX and currentTime from the captured contextX value when didSucceed === false:

🔁 Proposed fix
- 		.onFinalize(() => {
+ 		.onFinalize((_, didSucceed) => {
+ 			if (isPanning.value && !didSucceed) {
+ 				translateX.value = contextX.value;
+ 				currentTime.value = (contextX.value * duration.value) / maxWidth.value || 0;
+ 			}
  			isPanning.value = false;
  			scale.value = withTiming(1, { duration: 150 });
  		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/AudioPlayer/Seek.tsx` around lines 82 - 85, onFinalize
currently always resets isPanning and scale but doesn’t restore state when a pan
is cancelled; update the onFinalize callback to check the provided didSucceed
flag and if didSucceed === false restore the pre-pan position by setting
translateX back to the captured contextX and resetting currentTime.value from
contextX (the same context captured during gesture start), otherwise (didSucceed
=== true) call onChangeTime to commit the seek; ensure you still set
isPanning.value = false and run scale.value = withTiming(1, { duration: 150 })
in all cases.

@github-actions
Copy link

iOS Build Available

Rocket.Chat Experimental 4.71.0.108435

@github-actions
Copy link

Android Build Available

Rocket.Chat Experimental 4.71.0.108434

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNS1RtQubN6MMAV6ZUrFc1b0kUreKqDbpdWfbaPXjLGxe63axqkLb1yrncpK7nMv-7rvES64oTVal6fgsv-c

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