fix(iOS): message press effect is not visible#7042
fix(iOS): message press effect is not visible#7042
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe Touch component is renamed from Touchable and reorganized into a local file structure. The Message component is updated to import and use the renamed Touch component from its new local location. Internal variable naming is consolidated for consistency. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/containers/Touchable.tsx`:
- Around line 81-82: The iOS branch currently sets activeOpacity: 1 (in
touchableProps) which suppresses press feedback and hard-codes underlayColor to
colors.surfaceNeutral causing incorrect contrast on non-neutral backgrounds;
change the component to accept an optional underlayColor prop (falling back
intelligently to the component's background or colors.surfaceNeutral when none
provided) and remove or avoid forcing activeOpacity: 1 for iOS so the child can
become transparent and the underlay shows through; update references in the
isIOS/touchableProps branch and the TouchableHighlight usage to use the passed
underlayColor fallback and to not override activeOpacity to 1.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 83337ddb-3a1b-4b01-9317-954794898544
⛔ Files ignored due to path filters (2)
app/containers/message/__snapshots__/Message.test.tsx.snapis excluded by!**/*.snapapp/views/RoomView/LoadMore/__snapshots__/LoadMore.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (1)
app/containers/Touchable.tsx
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
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.
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.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/containers/Touchable.tsx (1)
10-10: WidenunderlayColorto React Native’s color type.Typing the new prop as
stringnarrowsTouchableHighlight’s API and prevents callers from usingPlatformColor/DynamicColorIOS. PreferColorValue(orTouchableHighlightProps['underlayColor']) for this wrapper prop.♻️ Suggested diff
import { View, StyleSheet, + type ColorValue, type ViewStyle, type StyleProp, type AccessibilityActionEvent, type AccessibilityActionInfo, TouchableNativeFeedback, TouchableHighlight, type TouchableWithoutFeedbackProps } from 'react-native'; @@ - underlayColor?: string; + underlayColor?: ColorValue;Also applies to: 28-28
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/containers/Touchable.tsx` at line 10, The underlayColor prop in the Touchable wrapper is typed as string which prevents callers from passing PlatformColor/DynamicColorIOS; update the prop type to accept React Native color types by changing the type of underlayColor (in the Touchable component / its props interface) to ColorValue or TouchableHighlightProps['underlayColor'] so it matches TouchableHighlight’s API and allows PlatformColor/DynamicColorIOS values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/containers/Touchable.tsx`:
- Line 10: The underlayColor prop in the Touchable wrapper is typed as string
which prevents callers from passing PlatformColor/DynamicColorIOS; update the
prop type to accept React Native color types by changing the type of
underlayColor (in the Touchable component / its props interface) to ColorValue
or TouchableHighlightProps['underlayColor'] so it matches TouchableHighlight’s
API and allows PlatformColor/DynamicColorIOS values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 372e580e-7bad-477e-b955-02239b5bf424
📒 Files selected for processing (1)
app/containers/Touchable.tsx
📜 Review details
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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.
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.
📚 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/containers/Touchable.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/Touchable.tsx
🔇 Additional comments (1)
app/containers/Touchable.tsx (1)
31-31: Nice platform split for restoring iOS feedback.Switching the iOS branch to
TouchableHighlightand threadingunderlayColorthrough here is a clean way to restore visible press feedback without changing the Android ripple path.Also applies to: 83-85
There was a problem hiding this comment.
🧹 Nitpick comments (2)
app/containers/Touchable.tsx (2)
84-86: Consider using the extractedbackgroundColorin the fallback chain.The current fallback
underlayColor ?? colors.surfaceNeutralworks for most cases, but callers with custom backgrounds (e.g.,CallButtonwithbadgeBackgroundLevel2,ReactionswithsurfaceRoom,CollapsibleQuotewith dynamic colors) will always getsurfaceNeutralas the underlay unless they explicitly passunderlayColor.Since
backgroundColoris already extracted from the flattened style (line 59), using it in the fallback would provide better visual consistency without requiring callers to pass an additional prop:💡 Optional: Use backgroundColor in fallback chain
const touchableProps = isIOS - ? { underlayColor: underlayColor ?? colors.surfaceNeutral, activeOpacity: 1 } + ? { underlayColor: underlayColor ?? backgroundColor ?? colors.surfaceNeutral, activeOpacity: 1 } : { background: TouchableNativeFeedback.Ripple(android_rippleColor ?? colors.surfaceNeutral, false) };This is optional since the current implementation achieves the PR objective and the
surfaceNeutraldefault provides consistent grey feedback matching Android's ripple behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/containers/Touchable.tsx` around lines 84 - 86, The underlay/ripple fallback should prefer the extracted backgroundColor instead of always falling back to colors.surfaceNeutral; update the touchableProps construction (symbol: touchableProps) so the iOS underlayColor uses underlayColor ?? backgroundColor ?? colors.surfaceNeutral and the Android ripple uses android_rippleColor ?? backgroundColor ?? colors.surfaceNeutral (TouchableNativeFeedback.Ripple), keeping existing isIOS branching and other props intact.
54-55: Minor: Comment may be slightly outdated.The comment references "touch opacity animation" which was relevant for
TouchableOpacity. WithTouchableHighlight, the mechanism is now underlay-based rather than opacity-based. Consider updating for clarity.📝 Optional: Update comment
// The background color must be applied to the RectButton, not the View. - // If set on the View, the touch opacity animation won't work properly. + // If set on the View, the touch feedback (underlay/ripple) won't work properly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/containers/Touchable.tsx` around lines 54 - 55, Update the outdated comment in Touchable.tsx that mentions "touch opacity animation" to reflect the current implementation: note that the background color must be applied to RectButton (not the wrapper View) because the touch effect is underlay-based with TouchableHighlight/RectButton rather than opacity-based as with TouchableOpacity; update the text to explicitly reference "underlay/press effect" or "underlay-based highlight" and keep the guidance about applying background color to RectButton (referencing RectButton and the surrounding wrapper View) for clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/containers/Touchable.tsx`:
- Around line 84-86: The underlay/ripple fallback should prefer the extracted
backgroundColor instead of always falling back to colors.surfaceNeutral; update
the touchableProps construction (symbol: touchableProps) so the iOS
underlayColor uses underlayColor ?? backgroundColor ?? colors.surfaceNeutral and
the Android ripple uses android_rippleColor ?? backgroundColor ??
colors.surfaceNeutral (TouchableNativeFeedback.Ripple), keeping existing isIOS
branching and other props intact.
- Around line 54-55: Update the outdated comment in Touchable.tsx that mentions
"touch opacity animation" to reflect the current implementation: note that the
background color must be applied to RectButton (not the wrapper View) because
the touch effect is underlay-based with TouchableHighlight/RectButton rather
than opacity-based as with TouchableOpacity; update the text to explicitly
reference "underlay/press effect" or "underlay-based highlight" and keep the
guidance about applying background color to RectButton (referencing RectButton
and the surrounding wrapper View) for clarity.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 686da0bc-88c8-4b71-b655-3a84b2f9047d
📒 Files selected for processing (1)
app/containers/Touchable.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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.
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.
📚 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/containers/Touchable.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/Touchable.tsx
🔇 Additional comments (2)
app/containers/Touchable.tsx (2)
10-12: Imports look correct for the TouchableHighlight approach.The added imports (
TouchableHighlight,TouchableWithoutFeedbackProps,ColorValue) are appropriate for the iOS touch feedback fix.
29-29: API surface addition is well-typed.The
underlayColor?: ColorValueprop addition allows callers to customize iOS press feedback color when needed, while the component selection logic correctly routes toTouchableHighlightfor iOS.Also applies to: 32-32, 40-40
There was a problem hiding this comment.
I don't understand where this component is being used or why you had to copy/paste logic from containers/Touch.
There was a problem hiding this comment.
containers/Touch use RectButton from RNGH with it's props and we can't use for message component because of gesture conflicts, so we copied the logic from it and created a new component and tried to keep the props same and tried to keep minimum changes in message component.
This component is being used in Message
There was a problem hiding this comment.
If it's only used on Message, it should be placed on app/containers/message https://kentcdodds.com/blog/colocation
There was a problem hiding this comment.
Thanks for the reference, I have moved the file into /containers/message
app/containers/Touchable.tsx
Outdated
| } | ||
|
|
||
| const Component = isIOS ? TouchableOpacity : TouchableNativeFeedback; | ||
| const Component = isIOS ? TouchableHighlight : TouchableNativeFeedback; |
There was a problem hiding this comment.
TouchableHighlight is not an iOS UI.
You should aim to style opacity.
| "opacity": 1, | ||
| } | ||
| [ | ||
| undefined, |
| "opacity": 1, | ||
| } | ||
| [ | ||
| undefined, |
There was a problem hiding this comment.
If it's only used on Message, it should be placed on app/containers/message https://kentcdodds.com/blog/colocation
| import Quote from './Components/Attachments/Quote'; | ||
| import translationLanguages from '../../lib/constants/translationLanguages'; | ||
| import Touchable from '../Touchable'; | ||
| import Touch from './Touch'; |
There was a problem hiding this comment.
This is done because we have a Touchable in Message which uses RectButton from RNGH and to keep it different from it, we are using Touch.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/containers/message/Touch.tsx (1)
81-92:⚠️ Potential issue | 🟠 MajorApply wrapper styles to a separate View on Android, not directly to
TouchableNativeFeedback.Line 89 passes
backgroundColor,borderRadius, and margins directly toComponent, which on Android resolves toTouchableNativeFeedback. React Native'sTouchableNativeFeedbackdoes not accept or respect astyleprop—it only replaces its childView. This causes edited/highlighted message backgrounds to disappear on Android.Split the render paths: on iOS, apply styles to
TouchableOpacityas-is; on Android, wrapTouchableNativeFeedbackin an outerViewforbackgroundColor,borderRadius, and margin, and apply remaining styles to the innerView.Example approach
+ if (isIOS) { + return ( + <TouchableOpacity + ref={ref} + onPress={onPress} + style={[rectButtonStyle, marginStyles, { backgroundColor, borderRadius }]} + disabled={!enabled} + {...props}> + <View + accessible={accessible} + accessibilityRole={props.accessibilityRole} + accessibilityLabel={accessibilityLabel} + accessibilityHint={accessibilityHint} + accessibilityActions={accessibilityActions} + onAccessibilityAction={onAccessibilityAction} + style={viewStyle}> + {children} + </View> + </TouchableOpacity> + ); + } + + return ( + <View style={[rectButtonStyle, marginStyles, { backgroundColor, borderRadius }]}> + <TouchableNativeFeedback + ref={ref} + onPress={onPress} + disabled={!enabled} + background={TouchableNativeFeedback.Ripple(android_rippleColor ?? colors.surfaceNeutral, false)} + {...props}> + <View + accessible={accessible} + accessibilityRole={props.accessibilityRole} + accessibilityLabel={accessibilityLabel} + accessibilityHint={accessibilityHint} + accessibilityActions={accessibilityActions} + onAccessibilityAction={onAccessibilityAction} + style={viewStyle}> + {children} + </View> + </TouchableNativeFeedback> + </View> + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/containers/message/Touch.tsx` around lines 81 - 92, The Android path is applying visual styles (backgroundColor, borderRadius, marginStyles) directly to Component which becomes TouchableNativeFeedback (which ignores style); fix by splitting iOS vs Android render: keep current single-View styling for iOS/TouchableOpacity, but for Android/TouchableNativeFeedback wrap it in an outer View that receives [rectButtonStyle, marginStyles, { backgroundColor, borderRadius }] and pass only touchableProps plus remaining props/ref/onPress to TouchableNativeFeedback, then render an inner View for any child content/styles; update references: Component, touchableProps, rectButtonStyle, marginStyles, backgroundColor, borderRadius, ref, onPress, props.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@app/containers/message/Touch.tsx`:
- Around line 81-92: The Android path is applying visual styles
(backgroundColor, borderRadius, marginStyles) directly to Component which
becomes TouchableNativeFeedback (which ignores style); fix by splitting iOS vs
Android render: keep current single-View styling for iOS/TouchableOpacity, but
for Android/TouchableNativeFeedback wrap it in an outer View that receives
[rectButtonStyle, marginStyles, { backgroundColor, borderRadius }] and pass only
touchableProps plus remaining props/ref/onPress to TouchableNativeFeedback, then
render an inner View for any child content/styles; update references: Component,
touchableProps, rectButtonStyle, marginStyles, backgroundColor, borderRadius,
ref, onPress, props.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 26ecfbf8-f5c4-4eec-8841-8abe134ddf50
📒 Files selected for processing (2)
app/containers/message/Message.tsxapp/containers/message/Touch.tsx
📜 Review details
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
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.
📚 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

Proposed changes
Regression introduced on #6997
On iOS, after pressing or long-pressing a message, the press opacity effect is missing.
On Android, it is working fine as expected
Issue(s)
https://rocketchat.atlassian.net/browse/CORE-1931
How to test or reproduce
Screenshots
Before
Screen.Recording.2026-03-06.at.10.42.14.PM.mov
After
Screen.Recording.2026-03-07.at.2.29.33.AM.mov
Types of changes
Checklist
Further comments
Summary by CodeRabbit