Skip to content

chore: upgrade to react native 81 and expo 54#6875

Open
Rohit3523 wants to merge 193 commits intodevelopfrom
react-native-81
Open

chore: upgrade to react native 81 and expo 54#6875
Rohit3523 wants to merge 193 commits intodevelopfrom
react-native-81

Conversation

@Rohit3523
Copy link
Copy Markdown
Contributor

@Rohit3523 Rohit3523 commented Jan 3, 2026

Proposed changes

This PR updates the React Native version from 0.79 to 0.81.5 and Expo 53 to Expo 54

Few Changes

  1. I tired overwriting the react-is version but it failed
  2. Using solution provided in [Bug]: Snapshot tests failing for React 19 jestjs/jest#15402 (comment)

Depends on:

  1. chore: upgrade reanimated to v4 #6720
  2. feat: Migrate to react-native-true-sheet #6970

Issue(s)

https://rocketchat.atlassian.net/browse/CORE-1578
Closes #6874
Closes #6803

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

Release Notes

  • New Features

    • Upgraded to React 19, React Native 0.81, and Expo 54 for improved stability and performance
    • Enabled New Architecture support on iOS
    • Improved gesture handling and animation performance
  • Bug Fixes

    • Enhanced file system operations handling
    • Optimized animation callbacks for better responsiveness
  • Chores

    • Updated Android build tools (version 36) and Kotlin (2.1)
    • Updated Gradle wrapper and dependencies across platforms
    • Improved test coverage with enhanced element identification

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 3, 2026

Walkthrough

This PR upgrades the project to React Native 0.81 and Expo 54, updates Android build tooling to version 36 and Kotlin to 2.1.20, migrates from react-native-reanimated's runOnJS to react-native-worklets' scheduleOnRN across multiple gesture handlers, updates gesture-handler implementations, and converts expo-file-system imports to legacy endpoints throughout the codebase.

Changes

Cohort / File(s) Summary
Android Build Configuration
android/build.gradle, android/gradle.properties, android/gradle/wrapper/gradle-wrapper.properties, android/gradlew, android/gradlew.bat
Updated buildToolsVersion and compileSdkVersion to 36, targetSdkVersion to 36, kotlinVersion to 2.1.20, gradle wrapper to 8.14.3, updated gradlew scripts with new classpath/jar invocation model, added edgeToEdgeEnabled=false property.
Android Manifest & App Initialization
android/app/src/debug/AndroidManifest.xml, android/app/src/main/AndroidManifest.xml, android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt
Deleted debug manifest, added android:usesCleartextTraffic variable to main manifest, replaced DefaultNewArchitectureEntryPoint.load() with loadReactNative() from ReactNativeApplicationEntryPoint in MainApplication.kt, removed SoLoader initialization.
iOS Configuration & Build
ios/NotificationService/Info.plist, ios/RocketChatRN/Info.plist, ios/ShareRocketChatRN/Info.plist, ios/RocketChatRN.xcodeproj/project.pbxproj
Added RCTNewArchEnabled=true to plist files, reordered IS_OFFICIAL and bugsnag entries, added RNCAsyncStorage_resources.bundle references, extended React runtime executor headers, introduced Expo configure project phases with environment inputs and ExpoModulesProvider.swift generation across multiple targets.
Project Configuration
app.json, babel.config.js, metro.config.js
Added plugins field for @react-native-community/datetimepicker and expo-web-browser, replaced react-native-reanimated/plugin with react-native-worklets/plugin in babel config, updated metro config to derive sourceExts from defaultConfig.
Dependency Updates
package.json
Updated React Native to 0.81.x, React to 19.x, Expo to 54-era, updated @expo/\* packages, @react-navigation, gesture-handler, reanimated, worklets, and related tooling to compatible versions; aligned 40+ transitive dependencies.
Patches for External Modules
patches/expo-file-system+19.0.21.patch, patches/expo-image+2.3.2.patch, patches/react-native+0.81.5.patch
Renamed FileSystemModule to FileSystemLegacyModule in legacy path, removed default value from useAppleWebpCodec prop registration, excluded .xcframework and .framework in cocoapods build script.
Gesture Handler & Animation Migration
app/containers/Button/index.tsx, app/containers/AudioPlayer/Seek.tsx, app/containers/ActionSheet/Handle.tsx, app/containers/RoomItem/Touchable.tsx, app/containers/RoomItem/Actions.tsx, app/containers/ServerItem/SwipeableDeleteItem/Touchable.tsx, app/containers/ServerItem/SwipeableDeleteItem/Actions.tsx, app/views/RoomView/List/components/List.tsx, app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx
Replaced Pressable/TouchableNativeFeedback with RectButton, migrated from runOnJS to scheduleOnRN for worklet callbacks, updated gesture handlers from PanGestureHandler + useAnimatedGestureHandler to Gesture.Pan() + GestureDetector, added withTiming animations for scale transitions, updated disabled prop handling.
Expo File System Legacy Migration
android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningTurboModule.java, app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx, app/lib/encryption/encryption.ts, app/lib/methods/handleMediaDownload.ts, app/lib/methods/helpers/fileDownload.ts, app/lib/methods/helpers/fileUpload/Upload.android.ts, app/lib/methods/helpers/sslPinning.ts, app/lib/methods/sendFileMessage/utils.ts, app/views/AttachmentView.tsx, app/views/ShareListView/index.tsx
Updated imports from expo-file-system to expo-file-system/legacy across file operations (getInfoAsync, deleteAsync, createDownloadResumable, etc.), updated SSLPinningTurboModule to reference FileSystemLegacyModule.
Component testID & Ref Refactoring
app/containers/List/ListItem.tsx, app/containers/UIKit/Overflow.tsx, app/containers/message/Touch.tsx, app/views/CreateDiscussionView/index.tsx, app/views/DirectoryView/Options.tsx, app/views/SecurityPrivacyView.tsx, app/views/ShareView/Thumbs.tsx, app/views/UserNotificationPreferencesView/index.tsx, app/views/UserPreferencesView/index.tsx, app/views/RoomItem/interfaces.ts
Moved testID from List.Item to nested Switch/Radio components, consolidated Touch component usage replacing platform-specific touchables, improved ref handling for popover anchors, added directory-switch testIDs, conditionally disabled accessibility for E2E tests on iOS, updated type imports from Animated.SharedValue to SharedValue.
Test Files
.maestro/tests/accessibilityAndAppearance/ToastsAndDialogs.yml, .maestro/tests/assorted/join-from-directory.yaml, .maestro/tests/assorted/profile.yaml, .maestro/tests/room/room-actions.yaml
Updated tapOn selectors from exact matches to regex wildcards, replaced text-based selectors with stable id-based identifiers, added scrollUntilVisible for better element synchronization, consolidated keyboard hiding via helper flow.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Out of Scope Changes check ❓ Inconclusive While most changes are in-scope for the RN/Expo upgrade, multiple unrelated modifications appear present: Maestro test selector updates, Gradle/Android build tool version bumps, Babel plugin swaps, and touch component replacements beyond what the upgrade strictly requires. Clarify whether Maestro test updates, Gradle wrapper changes, Babel plugin switches from reanimated to worklets, and touch component platform refactoring are necessary for RN 0.81/Expo 54 compatibility or represent scope creep.
✅ 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 'chore: upgrade to react native 81 and expo 54' clearly and concisely summarizes the main change, accurately reflecting the primary objective of upgrading React Native and Expo versions.
Linked Issues check ✅ Passed The PR successfully addresses all linked issues: restores Switch layout on iOS 26 (#6874), upgrades React Native to 0.81 (CORE-1578), and improves Android push notification handling by refactoring the New Architecture loader in MainApplication.kt (NATIVE-81).

✏️ 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 January 4, 2026 08:54 — with GitHub Actions Inactive
@Rohit3523 Rohit3523 temporarily deployed to approve_e2e_testing January 4, 2026 08:58 — with GitHub Actions Inactive
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/containers/Button/index.tsx (1)

9-20: ⚠️ Potential issue | 🟠 Major

Prevent enabled from bypassing disabled/loading safeguards.

Because IButtonProps extends RectButtonProps (line 9) and {...otherProps} is spread last (line 97), a caller can pass enabled={true} to override enabled={!isDisabled} (line 93). This allows re-enabling presses while loading or disabled is true.

Apply the proposed fix to exclude problematic props from the interface and ensure explicit props take precedence:

Proposed fix
-interface IButtonProps extends RectButtonProps {
+interface IButtonProps extends Omit<RectButtonProps, 'enabled' | 'onPress' | 'style'> {
 	title: string;
 	onPress: () => void;
 	type?: 'primary' | 'secondary';
@@
 	return (
 		<RectButton
+			{...otherProps}
 			onPress={onPress}
 			enabled={!isDisabled}
 			style={containerStyle}
 			accessibilityLabel={title}
 			accessibilityRole='button'
-			{...otherProps}>
+		>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/Button/index.tsx` around lines 9 - 20, The component allows
callers to override the derived enabled state because IButtonProps extends
RectButtonProps and {...otherProps} is spread after setting enabled, so callers
can pass enabled to bypass disabled/loading; fix by excluding enabled from the
inherited props and/or filtering it out before the spread: change the props type
to extend Omit<RectButtonProps, 'enabled'> (i.e., IButtonProps extends
Omit<RectButtonProps, 'enabled'>) or destructure enabled out of otherProps
(const { enabled, ...rest } = otherProps) and spread rest instead of otherProps,
and ensure the component always sets enabled={!isDisabled} (or similar) when
rendering the RectButton so explicit props cannot override it.
🤖 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 animation is being restarted on every gesture
update because withTiming(1.3, ...) is called inside the .onUpdate handler; move
the scale-up call into the gesture's .onStart handler so it runs once when the
gesture begins (e.g., call scale.value = withTiming(1.3, { duration: 150 }) in
.onStart), keep translateX.value and clamp logic inside .onUpdate using contextX
and maxWidth, and ensure you restore scale (e.g., scale.value = withTiming(1) or
appropriate reset) in .onEnd/.onFinalize so the knob returns to normal after the
gesture.

In `@app/containers/UIKit/Overflow.tsx`:
- Around line 44-57: The module-scoped touchable map (touchable) used in
Overflow leaks refs and can mis-anchor Popover when blockId repeats or is empty;
replace it with an instance-local ref inside the Overflow component (e.g.,
remove touchable and create const touchableRef = useRef<View | null>(null)
inside the Overflow function), attach that ref to the touchable element, and
remove all accesses to the global touchable[blockId] so the ref lifecycle is
tied to the component instance (update any code using touchableRef variable name
or blockId-dependent lookup to use the local touchableRef and, if needed, handle
blockId changes with useEffect).

In `@app/views/SecurityPrivacyView.tsx`:
- Around line 106-112: The "Send_crash_report" List.Item is using the wrong
accessibility label variable; replace the additionalAccessibilityLabel value
currently set to analyticsEventsState with crashReportState so VoiceOver reports
the correct enabled/disabled state for the crash report toggle. Update the
List.Item (title 'Send_crash_report') to use crashReportState for
additionalAccessibilityLabel and keep the existing Switch props
(value={crashReportState}, onValueChange={toggleCrashReport}, testID) unchanged.

---

Outside diff comments:
In `@app/containers/Button/index.tsx`:
- Around line 9-20: The component allows callers to override the derived enabled
state because IButtonProps extends RectButtonProps and {...otherProps} is spread
after setting enabled, so callers can pass enabled to bypass disabled/loading;
fix by excluding enabled from the inherited props and/or filtering it out before
the spread: change the props type to extend Omit<RectButtonProps, 'enabled'>
(i.e., IButtonProps extends Omit<RectButtonProps, 'enabled'>) or destructure
enabled out of otherProps (const { enabled, ...rest } = otherProps) and spread
rest instead of otherProps, and ensure the component always sets
enabled={!isDisabled} (or similar) when rendering the RectButton so explicit
props cannot override it.
🪄 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: bddc7b3f-32f5-4a46-9548-0793d81b218b

📥 Commits

Reviewing files that changed from the base of the PR and between 691fb63 and ee7133d.

⛔ Files ignored due to path filters (25)
  • app/containers/Avatar/__snapshots__/Avatar.test.tsx.snap is excluded by !**/*.snap
  • app/containers/Button/__snapshots__/Button.test.tsx.snap is excluded by !**/*.snap
  • app/containers/Chip/__snapshots__/Chip.test.tsx.snap is excluded by !**/*.snap
  • app/containers/DirectoryItem/__snapshots__/DirectoryItem.test.tsx.snap is excluded by !**/*.snap
  • app/containers/Header/components/HeaderButton/__snapshots__/HeaderButtons.test.tsx.snap is excluded by !**/*.snap
  • app/containers/InAppNotification/__snapshots__/NotifierComponent.test.tsx.snap is excluded by !**/*.snap
  • app/containers/List/__snapshots__/List.test.tsx.snap is excluded by !**/*.snap
  • app/containers/LoginServices/__snapshots__/LoginServices.test.tsx.snap is excluded by !**/*.snap
  • app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap is excluded by !**/*.snap
  • app/containers/ReactionsList/__snapshots__/ReactionsList.test.tsx.snap is excluded by !**/*.snap
  • app/containers/RoomHeader/__snapshots__/RoomHeader.test.tsx.snap is excluded by !**/*.snap
  • app/containers/RoomItem/__snapshots__/RoomItem.test.tsx.snap is excluded by !**/*.snap
  • app/containers/SearchBox/__snapshots__/SearchBox.test.tsx.snap is excluded by !**/*.snap
  • app/containers/ServerItem/__snapshots__/ServerItem.test.tsx.snap is excluded by !**/*.snap
  • app/containers/TextInput/__snapshots__/TextInput.test.tsx.snap is excluded by !**/*.snap
  • app/containers/UIKit/__snapshots__/UiKitMessage.test.tsx.snap is excluded by !**/*.snap
  • app/containers/UIKit/__snapshots__/UiKitModal.test.tsx.snap is excluded by !**/*.snap
  • app/containers/markdown/__snapshots__/Markdown.test.tsx.snap is excluded by !**/*.snap
  • app/containers/message/__snapshots__/Message.test.tsx.snap is excluded by !**/*.snap
  • app/views/CannedResponsesListView/__snapshots__/CannedResponseItem.test.tsx.snap is excluded by !**/*.snap
  • app/views/CreateChannelView/RoomSettings/__snapshots__/SwitchItem.test.tsx.snap is excluded by !**/*.snap
  • app/views/DiscussionsView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
  • app/views/NewServerView/components/ServersHistoryItem/__snapshots__/ServersHistoryItem.test.tsx.snap is excluded by !**/*.snap
  • app/views/RoomView/LoadMore/__snapshots__/LoadMore.test.tsx.snap is excluded by !**/*.snap
  • app/views/ThreadMessagesView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (33)
  • .maestro/tests/accessibilityAndAppearance/ToastsAndDialogs.yml
  • .maestro/tests/assorted/join-from-directory.yaml
  • .maestro/tests/assorted/profile.yaml
  • .maestro/tests/room/room-actions.yaml
  • android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt
  • android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningTurboModule.java
  • app/containers/ActionSheet/Handle.tsx
  • app/containers/AudioPlayer/Seek.tsx
  • app/containers/Button/index.tsx
  • app/containers/List/ListItem.tsx
  • app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx
  • app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx
  • app/containers/RoomItem/Actions.tsx
  • app/containers/RoomItem/Touchable.tsx
  • app/containers/RoomItem/interfaces.ts
  • app/containers/UIKit/Overflow.tsx
  • app/lib/encryption/encryption.ts
  • app/lib/methods/handleMediaDownload.ts
  • app/lib/methods/helpers/fileDownload.ts
  • app/lib/methods/helpers/fileUpload/Upload.android.ts
  • app/lib/methods/helpers/sslPinning.ts
  • app/lib/methods/sendFileMessage/utils.ts
  • app/views/AttachmentView.tsx
  • app/views/CreateDiscussionView/index.tsx
  • app/views/DirectoryView/Options.tsx
  • app/views/RoomView/List/components/List.tsx
  • app/views/SecurityPrivacyView.tsx
  • app/views/ShareListView/index.tsx
  • app/views/ShareView/Thumbs.tsx
  • app/views/UserNotificationPreferencesView/index.tsx
  • app/views/UserPreferencesView/index.tsx
  • babel.config.js
  • ios/NotificationService/Info.plist
✅ Files skipped from review due to trivial changes (10)
  • app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx
  • app/lib/encryption/encryption.ts
  • app/views/AttachmentView.tsx
  • app/lib/methods/helpers/fileDownload.ts
  • .maestro/tests/accessibilityAndAppearance/ToastsAndDialogs.yml
  • app/lib/methods/handleMediaDownload.ts
  • app/containers/ActionSheet/Handle.tsx
  • app/views/UserPreferencesView/index.tsx
  • app/views/UserNotificationPreferencesView/index.tsx
  • app/containers/RoomItem/interfaces.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • babel.config.js
  • android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt
📜 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). (4)
  • GitHub Check: E2E Run Android (12) / Android Tests
  • GitHub Check: E2E Run Android (5) / Android Tests
  • GitHub Check: E2E Run Android (11) / Android Tests
  • GitHub Check: E2E Build iOS / ios-build
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:06.688Z
Learning: The RocketChat/Rocket.Chat.ReactNative repository uses a fork of react-native-image-crop-picker (RocketChat/react-native-image-crop-picker) with custom Android edge-to-edge fixes, not the upstream ivpusic/react-native-image-crop-picker package. Dependencies should reference commit pins from the RocketChat fork.
📚 Learning: 2026-03-17T19:15:26.536Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6970
File: .maestro/tests/room/share-message.yaml:77-79
Timestamp: 2026-03-17T19:15:26.536Z
Learning: In YAML test files under .maestro/tests/room, use tapping the empty area (e.g., tapOn: point: 5%,10%) to dismiss both the bottom sheet and keyboard when needed. Do not rely on action-sheet-handle alone if the keyboard also needs to be dismissed in the same step. This pattern is acceptable for tests where a single tap should close both UI elements.

Applied to files:

  • .maestro/tests/room/room-actions.yaml
📚 Learning: 2026-03-05T14:28:10.004Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6997
File: .maestro/tests/room/message-markdown-click.yaml:28-39
Timestamp: 2026-03-05T14:28:10.004Z
Learning: In Maestro YAML selector fields (text, id) within the Rocket.Chat React Native repository, use the contains pattern '.*keyword.*' (leading and trailing '.*') for matching text. The pattern '.*keyword*.' is incorrect and will fail to match cases where the keyword appears at the end of the element's text. This guideline applies to all Maestro YAML selector fields across the codebase.

Applied to files:

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

Applied to files:

  • .maestro/tests/assorted/profile.yaml
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.

Applied to files:

  • app/views/CreateDiscussionView/index.tsx
  • app/containers/List/ListItem.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/UIKit/Overflow.tsx
  • app/views/ShareListView/index.tsx
📚 Learning: 2026-03-04T20:13:17.288Z
Learnt from: divyanshu-patil
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6957
File: ios/RocketChat Watch App/Views/MessageComposerView.swift:37-55
Timestamp: 2026-03-04T20:13:17.288Z
Learning: In the WatchOS app (ios/RocketChat Watch App) for Rocket.Chat React Native, using SwiftUI `Button` inside a `ScrollView` on WatchOS causes accidental message sends because button tap targets can be triggered during scroll gestures. `Text` with `.onTapGesture` is the preferred pattern for tappable items in scroll views on WatchOS. To preserve accessibility, add `.accessibilityAddTraits(.isButton)` and `.accessibilityLabel()` to the `Text` element instead.

Applied to files:

  • app/containers/List/ListItem.tsx
🔇 Additional comments (20)
app/containers/List/ListItem.tsx (2)

23-25: Good E2E/iOS gating strategy.

This guard is consistent with the codebase pattern and keeps behavior narrowly scoped to iOS E2E runs.


177-177: Conditional accessible usage looks correct.

Switching to accessible={!shouldDisableAccessibility} is a clean way to avoid iOS E2E accessibility interference without affecting normal app behavior.

ios/NotificationService/Info.plist (2)

25-26: LGTM!

The IS_OFFICIAL key reordering is a cosmetic change with no functional impact.


36-37: LGTM — New Architecture flag aligns with RN 0.81 upgrade.

Enabling RCTNewArchEnabled is appropriate for React Native 0.81, where the New Architecture is the default. Adding this flag consistently across all iOS targets (main app, share extension, notification service) ensures uniform build configuration.

Note: If the NotificationService extension is purely native Swift/ObjC code that doesn't use React Native directly, this flag may have no runtime effect but maintains consistency for any shared React Native dependencies.

.maestro/tests/room/room-actions.yaml (1)

124-127: Change from extendedWaitUntil to scrollUntilVisible looks appropriate.

Using scrollUntilVisible after the swipe gesture ensures the 'Star' option is brought into view, which is more robust if the action sheet content varies in length.

However, similar action sheet interactions for 'Unstar' (lines 174-177) and 'Pin' (lines 229-232) still use extendedWaitUntil after the swipe. If the 'Star' option requires scrolling, the same may apply to these other actions depending on their position in the sheet.

[approve_code_changes, request_verification]

#!/bin/bash
# Description: Check for similar patterns in this file and other Maestro tests 
# to verify if other action sheet interactions should also use scrollUntilVisible.

# Find all swipe followed by extendedWaitUntil patterns in this file
echo "=== Patterns in room-actions.yaml ==="
rg -n -A 5 'swipe:' .maestro/tests/room/room-actions.yaml

# Check other Maestro tests for similar action-sheet patterns
echo -e "\n=== Similar patterns in other Maestro test files ==="
rg -n -B 2 -A 5 'action-sheet-handle' .maestro/tests/ --glob '*.yaml' | head -100
.maestro/tests/assorted/profile.yaml (1)

99-100: LGTM!

Good refactor to use the shared hide-keyboard.yaml helper instead of brittle text-matching tapOn commands for dismissing the keyboard after username and nickname input. This improves maintainability and aligns with the project's pattern of centralizing common Maestro interactions.

Also applies to: 115-116

.maestro/tests/assorted/join-from-directory.yaml (1)

100-103: Good move to stable id selectors for directory filters.

Line 100/103 and Line 148/151 now target deterministic element ids, which should reduce Maestro flakiness across locales and UI state changes.

Also applies to: 148-151

app/views/DirectoryView/Options.tsx (2)

50-50: List.Radio testID wiring is correct and matches E2E usage.

Line 50 generates the ids used by the updated Maestro flow (directory-switch-users / directory-switch-teams).


74-74: Nice addition of explicit testID on the global users switch.

Line 74 improves automation targeting for this control and keeps behavior unchanged.

app/views/SecurityPrivacyView.tsx (1)

96-110: LGTM on testID relocation.

Moving testID from List.Item to the nested Switch component is consistent with the PR pattern. E2E tests targeting security-privacy-view-analytics-events and security-privacy-view-crash-report will now interact with the toggle controls directly.

app/views/CreateDiscussionView/index.tsx (1)

172-177: Remove concern about E2E test relocation—testID is newly added, not moved.

The testID='room-actions-encrypt' appears only on the Switch component and does not exist elsewhere in the codebase. This is a new testID addition, not a relocation from the List.Item. Since no existing E2E or Maestro tests reference this testID, there are no tests to update.

			> Likely an incorrect or invalid review comment.
app/containers/RoomItem/Actions.tsx (1)

12-12: LGTM — scheduleOnRN migration is correct.

The migration from runOnJS(triggerHideAnimation)(value) to scheduleOnRN(triggerHideAnimation, value) correctly maintains the same behavior for scheduling JS-thread work from the worklet context. The toValue parameter is passed appropriately for both RTL and LTR swipe threshold transitions.

Also applies to: 84-91

app/views/RoomView/List/components/List.tsx (1)

3-4: LGTM — Scroll handler migration is correct.

The scheduleOnRN(setVisible, boolean) pattern correctly schedules the React state update from the animated scroll handler worklet context.

Also applies to: 26-34

app/containers/RoomItem/Touchable.tsx (1)

9-9: LGTM — Gesture callback migration is correct.

Both long press and pan gesture end callbacks are properly migrated:

  • scheduleOnRN(handleLongPress) correctly handles the no-argument case
  • scheduleOnRN(handleRelease, event) correctly passes the gesture event containing translationX needed for the release logic

Also applies to: 173-189

app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx (1)

4-6: LGTM — Emoji keyboard state synchronization is correctly migrated.

The useAnimatedReaction callbacks properly schedule React state updates via scheduleOnRN(setState, value). The boolean currentValue is passed correctly to synchronize showEmojiKeyboard and showEmojiSearchbar states.

Also applies to: 159-159, 175-175

app/containers/AudioPlayer/Seek.tsx (2)

67-85: LGTM — Gesture API migration from PanGestureHandler to Gesture.Pan() is correct.

The migration correctly maps:

  • Context capture via contextX shared value
  • onUpdate for translation updates with clamping
  • onEnd scheduling onChangeTime with milliseconds (matching expo-av expectations)
  • GestureDetector wrapper replacing PanGestureHandler

Also applies to: 125-127


12-12: The scheduleOnRN API usage is compatible and correctly implemented.

The function signature matches the expected pattern (function reference + arguments). However, be aware there are reported iOS crashes in audio worklet contexts (see react-native-worklets documentation). Test thoroughly on iOS devices to ensure no crashes occur when seeking completes in your audio player implementation.

app/containers/Button/index.tsx (1)

3-3: Import migration looks good.

No concerns on this segment.

app/views/ShareListView/index.tsx (1)

104-104: The code is safe as written. The share extension saves all media files locally to the app's cache directory (iOS) or app group container (Android) and passes only file:// URIs to the React component. According to the Expo legacy FileSystem API documentation, getInfoAsync(uri) returns the size field by default for file:// URIs without requiring the { size: true } option. The { size: true } option is only required for remote URIs like MediaLibrary (ph://) URIs, which do not occur in this code path.

			> Likely an incorrect or invalid review comment.
app/views/ShareView/Thumbs.tsx (1)

96-113: Nice simplification using shared Touch wrapper.

This removes platform branching in Thumb while keeping the interaction flow intact in this component.

Comment on lines +44 to +57
const touchable: { [key: string]: React.RefObject<View | null> } = {};

export const Overflow = ({ element, loading, action, parser }: IOverflow) => {
const { theme } = useTheme();
const options = element?.options || [];
const blockId = element?.blockId || '';
const [show, onShow] = useState(false);

if (!touchable[blockId]) {
touchable[blockId] = React.createRef();
}

const touchableRef = touchable[blockId] as React.RefObject<any>;

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 | 🟠 Major

Avoid module-scoped ref registry for anchors.

The global touchable map can leak refs over time and can collide when blockId repeats/empties, which may anchor Popover to the wrong element. Prefer an instance-local ref.

💡 Suggested fix
-import React, { useState } from 'react';
-import { FlatList, StyleSheet, Text, type View } from 'react-native';
+import React, { useRef, useState } from 'react';
+import { FlatList, StyleSheet, Text } from 'react-native';
@@
-const touchable: { [key: string]: React.RefObject<View | null> } = {};
-
 export const Overflow = ({ element, loading, action, parser }: IOverflow) => {
 	const { theme } = useTheme();
 	const options = element?.options || [];
-	const blockId = element?.blockId || '';
 	const [show, onShow] = useState(false);
-
-	if (!touchable[blockId]) {
-		touchable[blockId] = React.createRef();
-	}
-
-	const touchableRef = touchable[blockId] as React.RefObject<any>;
+	const touchableRef = useRef<React.ElementRef<typeof Touch>>(null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/UIKit/Overflow.tsx` around lines 44 - 57, The module-scoped
touchable map (touchable) used in Overflow leaks refs and can mis-anchor Popover
when blockId repeats or is empty; replace it with an instance-local ref inside
the Overflow component (e.g., remove touchable and create const touchableRef =
useRef<View | null>(null) inside the Overflow function), attach that ref to the
touchable element, and remove all accesses to the global touchable[blockId] so
the ref lifecycle is tied to the component instance (update any code using
touchableRef variable name or blockId-dependent lookup to use the local
touchableRef and, if needed, handle blockId changes with useEffect).

Comment on lines 106 to 112
<List.Item
title='Send_crash_report'
testID='security-privacy-view-crash-report'
right={() => <Switch value={crashReportState} onValueChange={toggleCrashReport} />}
right={() => (
<Switch value={crashReportState} onValueChange={toggleCrashReport} testID='security-privacy-view-crash-report' />
)}
additionalAccessibilityLabel={analyticsEventsState}
/>
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

Pre-existing bug: wrong accessibility label for crash report toggle.

Line 111 uses analyticsEventsState for the "Send_crash_report" item's additionalAccessibilityLabel, but it should use crashReportState. This causes VoiceOver to announce the wrong enabled/disabled state for this toggle.

Since you're modifying adjacent code, consider fixing this while you're here.

🐛 Proposed fix
 				<List.Item
 					title='Send_crash_report'
 					right={() => (
 						<Switch value={crashReportState} onValueChange={toggleCrashReport} testID='security-privacy-view-crash-report' />
 					)}
-					additionalAccessibilityLabel={analyticsEventsState}
+					additionalAccessibilityLabel={crashReportState}
 				/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<List.Item
title='Send_crash_report'
testID='security-privacy-view-crash-report'
right={() => <Switch value={crashReportState} onValueChange={toggleCrashReport} />}
right={() => (
<Switch value={crashReportState} onValueChange={toggleCrashReport} testID='security-privacy-view-crash-report' />
)}
additionalAccessibilityLabel={analyticsEventsState}
/>
<List.Item
title='Send_crash_report'
right={() => (
<Switch value={crashReportState} onValueChange={toggleCrashReport} testID='security-privacy-view-crash-report' />
)}
additionalAccessibilityLabel={crashReportState}
/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/views/SecurityPrivacyView.tsx` around lines 106 - 112, The
"Send_crash_report" List.Item is using the wrong accessibility label variable;
replace the additionalAccessibilityLabel value currently set to
analyticsEventsState with crashReportState so VoiceOver reports the correct
enabled/disabled state for the crash report toggle. Update the List.Item (title
'Send_crash_report') to use crashReportState for additionalAccessibilityLabel
and keep the existing Switch props (value={crashReportState},
onValueChange={toggleCrashReport}, testID) unchanged.

@github-actions
Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.71.0.108438

@github-actions
Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.71.0.108439

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNSuEVKKA0DG3IPkOnoI1S2XrUNX2nUQvp_K0WPo95OnUZaT-J6ggoaqE-ep8RKRmAVkXlFF4ijR5vrlbvFE

# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=false No newline at end of file
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If we enable edge to edge on android, It will hide some view behind navigation bar

Example: TextInput is hiding behind navigation bar
Image

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/views/ShareListView/index.tsx (1)

105-117: ⚠️ Potential issue | 🟠 Major

Filter out null attachments before setting state.

Line 106 returns null for missing files, but Line 117 force-casts the array to IFileToShare[]. This can propagate null into attachments and break downstream consumers expecting only attachment objects.

💡 Proposed fix
-				const attachments = info.map(file => {
-					if (!file.exists) {
-						return null;
-					}
-
-					return {
-						filename: decodeURIComponent(file.uri.substring(file.uri.lastIndexOf('/') + 1)),
-						description: '',
-						size: file.size,
-						mime: mime.lookup(file.uri),
-						path: file.uri
-					};
-				}) as IFileToShare[];
+				const attachments = info
+					.map(file => {
+						if (!file.exists) {
+							return null;
+						}
+						return {
+							filename: decodeURIComponent(file.uri.substring(file.uri.lastIndexOf('/') + 1)),
+							description: '',
+							size: file.size ?? 0,
+							mime: mime.lookup(file.uri),
+							path: file.uri
+						};
+					})
+					.filter((file): file is IFileToShare => file !== null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/views/ShareListView/index.tsx` around lines 105 - 117, The attachments
mapping in ShareListView (const attachments = info.map(... ) as IFileToShare[])
returns null for missing files but is force-cast, so filter out nulls before
setting state: replace the single-step map+cast with map producing nullable
items and then filter to remove nulls (e.g., .filter(Boolean) or a type guard
like .filter((a): a is IFileToShare => a !== null)) so attachments is a true
IFileToShare[] and downstream consumers won’t receive null values.
♻️ Duplicate comments (1)
app/containers/UIKit/Overflow.tsx (1)

44-57: ⚠️ Potential issue | 🟠 Major

Use an instance-local ref for the popover anchor.

Line 44 introduces a module-scoped ref registry, and Line 49 allows blockId to be empty. That combination can retain stale refs and mis-anchor Popover (downstream at Line 72) when keys collide. Prefer a per-instance useRef tied to component lifecycle.

Suggested fix
-import React, { useState } from 'react';
-import { FlatList, StyleSheet, Text, type View } from 'react-native';
+import React, { useRef, useState } from 'react';
+import { FlatList, StyleSheet, Text } from 'react-native';
@@
-const touchable: { [key: string]: React.RefObject<View | null> } = {};
-
 export const Overflow = ({ element, loading, action, parser }: IOverflow) => {
 	const { theme } = useTheme();
 	const options = element?.options || [];
-	const blockId = element?.blockId || '';
 	const [show, onShow] = useState(false);
-
-	if (!touchable[blockId]) {
-		touchable[blockId] = React.createRef();
-	}
-
-	const touchableRef = touchable[blockId] as React.RefObject<any>;
+	const touchableRef = useRef<React.ElementRef<typeof Touch>>(null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/containers/UIKit/Overflow.tsx` around lines 44 - 57, The module-scoped
touchable registry (touchable) can hold stale refs and collide when blockId is
empty; replace it with an instance-local ref inside the Overflow component:
remove usage of the touchable map and instead create const touchableRef =
useRef<View | null>(null) (or useRef<any>(null)) within the Overflow function
and use that ref as the Popover anchor; drop the blockId-based allocation logic
and any casts to touchableRef so the ref is tied to the component lifecycle and
cannot leak or collide across instances.
🧹 Nitpick comments (1)
app/views/RoomView/List/components/List.tsx (1)

27-31: Guard the RN bridge behind a visibility flip.

Right now every scroll event above/below SCROLL_LIMIT posts setVisible back to the RN runtime. scheduleOnRN always schedules work onto that runtime, so this adds avoidable cross-runtime traffic on the room list’s hottest path even when the boolean has not changed. Cache the last threshold in a shared value and only bridge on transitions. (docs.swmansion.com)

♻️ Proposed fix
-import Animated, { useAnimatedScrollHandler } from 'react-native-reanimated';
+import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
 import { scheduleOnRN } from 'react-native-worklets';
@@
 const List = ({ listRef, jumpToBottom, ...props }: IListProps) => {
 	const [visible, setVisible] = useState(false);
+	const visibleRef = useSharedValue(false);
 	const { isAutocompleteVisible } = useRoomContext();
 	const scrollHandler = useAnimatedScrollHandler({
 		onScroll: event => {
-			if (event.contentOffset.y > SCROLL_LIMIT) {
-				scheduleOnRN(setVisible, true);
-			} else {
-				scheduleOnRN(setVisible, false);
+			const nextVisible = event.contentOffset.y > SCROLL_LIMIT;
+			if (nextVisible !== visibleRef.value) {
+				visibleRef.value = nextVisible;
+				scheduleOnRN(setVisible, nextVisible);
 			}
 		}
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/views/RoomView/List/components/List.tsx` around lines 27 - 31, The
onScroll handler is calling scheduleOnRN(setVisible, ...) on every event even
when visibility hasn't changed, causing unnecessary RN bridge traffic; add a
worklet/shared boolean (e.g., lastVisible shared value) inside the same scope as
onScroll and update it in the worklet so you only call scheduleOnRN when the
computed visible state (event.contentOffset.y > SCROLL_LIMIT) differs from
lastVisible, then flip lastVisible and invoke scheduleOnRN(setVisible, newValue)
only on that transition; reference the onScroll handler, scheduleOnRN,
setVisible, SCROLL_LIMIT and the new lastVisible shared value when applying the
change.
🤖 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/List/ListItem.tsx`:
- Around line 23-25: The code in ListItem.tsx computes
shouldDisableAccessibility using process.env.RUNNING_E2E_TESTS which bypasses
the React Native typed env import; replace that usage by importing
RUNNING_E2E_TESTS from '@env' and use it (together with isIOS) when computing
shouldDisableAccessibility and any other occurrences (e.g., around line 177).
Update the top-level imports to include RUNNING_E2E_TESTS and ensure the boolean
check uses the typed value instead of process.env to work with the
babel-plugin/react-native-dotenv setup.

In `@app/containers/RoomItem/Actions.tsx`:
- Line 12: Add a Jest mock for the react-native-worklets package in
jest.setup.js so imports like scheduleOnRN from 'react-native-worklets' in files
such as RoomItem/Actions.tsx resolve during tests; specifically, in
jest.setup.js register a jest.mock that maps 'react-native-worklets' to the
package's built-in mock module (the module at
'react-native-worklets/lib/module/mock'), and ensure this mock registration runs
before any test files import scheduleOnRN or other symbols from
'react-native-worklets'.

---

Outside diff comments:
In `@app/views/ShareListView/index.tsx`:
- Around line 105-117: The attachments mapping in ShareListView (const
attachments = info.map(... ) as IFileToShare[]) returns null for missing files
but is force-cast, so filter out nulls before setting state: replace the
single-step map+cast with map producing nullable items and then filter to remove
nulls (e.g., .filter(Boolean) or a type guard like .filter((a): a is
IFileToShare => a !== null)) so attachments is a true IFileToShare[] and
downstream consumers won’t receive null values.

---

Duplicate comments:
In `@app/containers/UIKit/Overflow.tsx`:
- Around line 44-57: The module-scoped touchable registry (touchable) can hold
stale refs and collide when blockId is empty; replace it with an instance-local
ref inside the Overflow component: remove usage of the touchable map and instead
create const touchableRef = useRef<View | null>(null) (or useRef<any>(null))
within the Overflow function and use that ref as the Popover anchor; drop the
blockId-based allocation logic and any casts to touchableRef so the ref is tied
to the component lifecycle and cannot leak or collide across instances.

---

Nitpick comments:
In `@app/views/RoomView/List/components/List.tsx`:
- Around line 27-31: The onScroll handler is calling scheduleOnRN(setVisible,
...) on every event even when visibility hasn't changed, causing unnecessary RN
bridge traffic; add a worklet/shared boolean (e.g., lastVisible shared value)
inside the same scope as onScroll and update it in the worklet so you only call
scheduleOnRN when the computed visible state (event.contentOffset.y >
SCROLL_LIMIT) differs from lastVisible, then flip lastVisible and invoke
scheduleOnRN(setVisible, newValue) only on that transition; reference the
onScroll handler, scheduleOnRN, setVisible, SCROLL_LIMIT and the new lastVisible
shared value when applying 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: d3e08d3d-d4e6-4858-9e00-736b3eb47c49

📥 Commits

Reviewing files that changed from the base of the PR and between 691fb63 and a393624.

⛔ Files ignored due to path filters (25)
  • app/containers/Avatar/__snapshots__/Avatar.test.tsx.snap is excluded by !**/*.snap
  • app/containers/Button/__snapshots__/Button.test.tsx.snap is excluded by !**/*.snap
  • app/containers/Chip/__snapshots__/Chip.test.tsx.snap is excluded by !**/*.snap
  • app/containers/DirectoryItem/__snapshots__/DirectoryItem.test.tsx.snap is excluded by !**/*.snap
  • app/containers/Header/components/HeaderButton/__snapshots__/HeaderButtons.test.tsx.snap is excluded by !**/*.snap
  • app/containers/InAppNotification/__snapshots__/NotifierComponent.test.tsx.snap is excluded by !**/*.snap
  • app/containers/List/__snapshots__/List.test.tsx.snap is excluded by !**/*.snap
  • app/containers/LoginServices/__snapshots__/LoginServices.test.tsx.snap is excluded by !**/*.snap
  • app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap is excluded by !**/*.snap
  • app/containers/ReactionsList/__snapshots__/ReactionsList.test.tsx.snap is excluded by !**/*.snap
  • app/containers/RoomHeader/__snapshots__/RoomHeader.test.tsx.snap is excluded by !**/*.snap
  • app/containers/RoomItem/__snapshots__/RoomItem.test.tsx.snap is excluded by !**/*.snap
  • app/containers/SearchBox/__snapshots__/SearchBox.test.tsx.snap is excluded by !**/*.snap
  • app/containers/ServerItem/__snapshots__/ServerItem.test.tsx.snap is excluded by !**/*.snap
  • app/containers/TextInput/__snapshots__/TextInput.test.tsx.snap is excluded by !**/*.snap
  • app/containers/UIKit/__snapshots__/UiKitMessage.test.tsx.snap is excluded by !**/*.snap
  • app/containers/UIKit/__snapshots__/UiKitModal.test.tsx.snap is excluded by !**/*.snap
  • app/containers/markdown/__snapshots__/Markdown.test.tsx.snap is excluded by !**/*.snap
  • app/containers/message/__snapshots__/Message.test.tsx.snap is excluded by !**/*.snap
  • app/views/CannedResponsesListView/__snapshots__/CannedResponseItem.test.tsx.snap is excluded by !**/*.snap
  • app/views/CreateChannelView/RoomSettings/__snapshots__/SwitchItem.test.tsx.snap is excluded by !**/*.snap
  • app/views/DiscussionsView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
  • app/views/NewServerView/components/ServersHistoryItem/__snapshots__/ServersHistoryItem.test.tsx.snap is excluded by !**/*.snap
  • app/views/RoomView/LoadMore/__snapshots__/LoadMore.test.tsx.snap is excluded by !**/*.snap
  • app/views/ThreadMessagesView/__snapshots__/Item.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (35)
  • .maestro/tests/accessibilityAndAppearance/ToastsAndDialogs.yml
  • .maestro/tests/assorted/join-from-directory.yaml
  • .maestro/tests/assorted/profile.yaml
  • .maestro/tests/room/room-actions.yaml
  • android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt
  • android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningTurboModule.java
  • app/containers/ActionSheet/Handle.tsx
  • app/containers/AudioPlayer/Seek.tsx
  • app/containers/Button/index.tsx
  • app/containers/List/ListItem.tsx
  • app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx
  • app/containers/MessageComposer/hooks/useEmojiKeyboard.tsx
  • app/containers/RoomItem/Actions.tsx
  • app/containers/RoomItem/Touchable.tsx
  • app/containers/RoomItem/interfaces.ts
  • app/containers/ServerItem/SwipeableDeleteItem/Actions.tsx
  • app/containers/ServerItem/SwipeableDeleteItem/Touchable.tsx
  • app/containers/UIKit/Overflow.tsx
  • app/containers/message/Touch.tsx
  • app/lib/encryption/encryption.ts
  • app/lib/methods/handleMediaDownload.ts
  • app/lib/methods/helpers/fileDownload.ts
  • app/lib/methods/helpers/fileUpload/Upload.android.ts
  • app/lib/methods/helpers/sslPinning.ts
  • app/lib/methods/sendFileMessage/utils.ts
  • app/views/AttachmentView.tsx
  • app/views/CreateDiscussionView/index.tsx
  • app/views/DirectoryView/Options.tsx
  • app/views/RoomView/List/components/List.tsx
  • app/views/SecurityPrivacyView.tsx
  • app/views/ShareListView/index.tsx
  • app/views/ShareView/Thumbs.tsx
  • app/views/UserNotificationPreferencesView/index.tsx
  • app/views/UserPreferencesView/index.tsx
  • babel.config.js
✅ Files skipped from review due to trivial changes (11)
  • app/containers/ActionSheet/Handle.tsx
  • app/lib/methods/helpers/fileDownload.ts
  • .maestro/tests/accessibilityAndAppearance/ToastsAndDialogs.yml
  • app/lib/methods/sendFileMessage/utils.ts
  • app/lib/methods/helpers/fileUpload/Upload.android.ts
  • app/views/AttachmentView.tsx
  • android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningTurboModule.java
  • app/views/UserNotificationPreferencesView/index.tsx
  • app/containers/MessageComposer/components/RecordAudio/RecordAudio.tsx
  • app/lib/encryption/encryption.ts
  • app/views/UserPreferencesView/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • babel.config.js
  • android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt
📜 Review details
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:06.688Z
Learning: The RocketChat/Rocket.Chat.ReactNative repository uses a fork of react-native-image-crop-picker (RocketChat/react-native-image-crop-picker) with custom Android edge-to-edge fixes, not the upstream ivpusic/react-native-image-crop-picker package. Dependencies should reference commit pins from the RocketChat fork.
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.

Applied to files:

  • app/views/CreateDiscussionView/index.tsx
  • app/containers/List/ListItem.tsx
  • app/containers/message/Touch.tsx
  • app/views/SecurityPrivacyView.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/views/ShareListView/index.tsx
  • app/containers/message/Touch.tsx
  • app/containers/UIKit/Overflow.tsx
📚 Learning: 2026-03-17T19:15:30.463Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6970
File: .maestro/tests/room/share-message.yaml:77-79
Timestamp: 2026-03-17T19:15:30.463Z
Learning: In `.maestro/tests/room/share-message.yaml` (Rocket.Chat React Native), the `tapOn: point: 5%,10%` step is intentional: it taps the empty area above the bottom sheet and keyboard to dismiss both simultaneously. Using `action-sheet-handle` instead would only close the sheet but not the keyboard. This pattern is acceptable when both need to be dismissed together in a single step.

Applied to files:

  • .maestro/tests/assorted/profile.yaml
📚 Learning: 2026-03-05T14:28:10.004Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6997
File: .maestro/tests/room/message-markdown-click.yaml:28-39
Timestamp: 2026-03-05T14:28:10.004Z
Learning: In Maestro YAML selector fields (text, id) within the Rocket.Chat React Native repository, use the contains pattern '.*keyword.*' (leading and trailing '.*') for matching text. The pattern '.*keyword*.' is incorrect and will fail to match cases where the keyword appears at the end of the element's text. This guideline applies to all Maestro YAML selector fields across the codebase.

Applied to files:

  • .maestro/tests/assorted/profile.yaml
  • .maestro/tests/room/room-actions.yaml
  • .maestro/tests/assorted/join-from-directory.yaml
📚 Learning: 2026-03-17T19:15:26.536Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6970
File: .maestro/tests/room/share-message.yaml:77-79
Timestamp: 2026-03-17T19:15:26.536Z
Learning: In YAML test files under .maestro/tests/room, use tapping the empty area (e.g., tapOn: point: 5%,10%) to dismiss both the bottom sheet and keyboard when needed. Do not rely on action-sheet-handle alone if the keyboard also needs to be dismissed in the same step. This pattern is acceptable for tests where a single tap should close both UI elements.

Applied to files:

  • .maestro/tests/room/room-actions.yaml
📚 Learning: 2026-03-04T20:13:17.288Z
Learnt from: divyanshu-patil
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6957
File: ios/RocketChat Watch App/Views/MessageComposerView.swift:37-55
Timestamp: 2026-03-04T20:13:17.288Z
Learning: In the WatchOS app (ios/RocketChat Watch App) for Rocket.Chat React Native, using SwiftUI `Button` inside a `ScrollView` on WatchOS causes accidental message sends because button tap targets can be triggered during scroll gestures. `Text` with `.onTapGesture` is the preferred pattern for tappable items in scroll views on WatchOS. To preserve accessibility, add `.accessibilityAddTraits(.isButton)` and `.accessibilityLabel()` to the `Text` element instead.

Applied to files:

  • app/containers/message/Touch.tsx
  • app/views/SecurityPrivacyView.tsx
🔇 Additional comments (15)
app/containers/Button/index.tsx (2)

91-99: LGTM on the RectButton implementation.

The switch from Pressable to RectButton with enabled={!isDisabled} correctly handles the Android production build issue mentioned in the PR. The component properly:

  • Derives isDisabled from disabled || loading
  • Converts to enabled={!isDisabled} for RectButton's expected API
  • Preserves all accessibility attributes

3-3: No breaking changes detected. The components using Pressable-specific props like android_ripple (IconButton, UserItem, Chip) are separate implementations that extend Pressable directly, not callers of the Button component. All actual Button imports pass only compatible props (title, type, onPress, loading, etc.), making this migration safe.

app/views/ShareListView/index.tsx (1)

5-5: No actionable concern in this import-only change.

app/lib/methods/handleMediaDownload.ts (1)

1-1: No actionable concern in this import-only change.

app/lib/methods/helpers/sslPinning.ts (2)

3-3: No actionable concern in this import-only change.


64-64: No actionable concern in this typing-only change.

app/containers/message/Touch.tsx (1)

10-11: Looks good: platform touchable swap is coherent and props are correctly wired.

The Line 30 component selection and Line 81 non-iOS touch props are internally consistent with the rendered wrapper and forwarded ref usage.

Also applies to: 30-31, 81-81

app/views/ShareView/Thumbs.tsx (1)

3-3: LGTM: unified touch wrapper simplifies platform handling without changing intent.

The switch to Touch in Line 96 keeps the thumb press behavior clear while preserving the dedicated remove action button.

Also applies to: 11-12, 96-113

app/views/SecurityPrivacyView.tsx (2)

106-112: Carry-forward: crash report accessibility label still points to the wrong state.

Line 111 is still bound to analyticsEventsState; it should use crashReportState so the spoken enabled/disabled state matches the crash-report toggle.


96-102: Good move: testID is now attached to the actual interactive Switch.

This makes E2E targeting more reliable than attaching the ID on List.Item.

app/views/DirectoryView/Options.tsx (1)

50-50: Good selector hardening with stable IDs.

These testIDs make the filter toggles deterministic for Maestro and reduce text-selector flakiness.

Also applies to: 74-74

.maestro/tests/assorted/profile.yaml (1)

99-100: Nice cleanup using shared keyboard-dismiss flow.

Centralizing this into hide-keyboard.yaml should reduce platform-specific drift in this test.

Also applies to: 115-116

app/views/CreateDiscussionView/index.tsx (1)

174-174: LGTM on moving testID to the encryption Switch.

This is the right locator surface for test interactions.

.maestro/tests/assorted/join-from-directory.yaml (1)

100-100: Great switch from text selectors to stable id selectors.

This should make the directory filter steps much less brittle.

Also applies to: 103-103, 148-148, 151-151

.maestro/tests/room/room-actions.yaml (1)

124-127: Good robustness improvement in the action-sheet flow.

Using scrollUntilVisible here is safer than waiting when the option can be off-screen.

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.

bug(iOS): switch having layout problem after iOS 26 update

3 participants