fix(android): correct accessibility focus order in inverted FlatList#6934
fix(android): correct accessibility focus order in inverted FlatList#6934OtavioStasiak merged 35 commits intodevelopfrom
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:
WalkthroughAdds Android-native inverted scroll views, content view, view managers, and a React Native package; registers the package in MainApplication. Introduces an Android-only TSX wrapper component and uses it as the FlatList scroll component on non-iOS platforms. Accessibility traversal order is reversed to match the visual newest-first order. Changes
Sequence Diagram(s)sequenceDiagram
participant JS as React (FlatList)
participant RN as React Native Bridge
participant Native as InvertedScrollView / InvertedScrollContentView
participant A11y as Android TalkBack
JS->>RN: renderScrollComponent -> InvertedScrollView
RN->>Native: createViewInstance(ThemedReactContext)
Native->>Native: mount children in inverted newest-first order
A11y->>Native: request accessibility children traversal
Native-->>A11y: return reversed children list to match visual order
A11y-->>JS: user navigates (TalkBack follows visual order)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
|
Android Build Available Rocket.Chat Experimental 4.69.0.108196 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/views/RoomView/List/components/InvertedScrollView.tsx (1)
54-92: Method patching logic is sound; minor nit on nullish coalescing.The
useLayoutEffectwith[]deps correctly patches methods once after mount. ThesetNativePropsfallback is now safe (no recursive call — good fix from prior feedback).One small note on Lines 62–63:
options?.x || 0uses logical OR, which coerces all falsy values (including0) to the default. Since0is also the desired default here, behavior is correct, but?? 0is more semantically precise and avoids surprising readers who expect||to swallow valid0values.Suggested diff
- const x = options?.x || 0; - const y = options?.y || 0; + const x = options?.x ?? 0; + const y = options?.y ?? 0;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/RoomView/List/components/InvertedScrollView.tsx` around lines 54 - 92, Update the nullish-coalescing for scroll coordinates in the patched scroll methods: in the node.scrollTo implementation change the x and y fallbacks from using logical OR to nullish coalescing (replace uses of options?.x || 0 and options?.y || 0 with options?.x ?? 0 and options?.y ?? 0) so that explicit 0 values are preserved; leave other logic in scrollTo, scrollToEnd, flashScrollIndicators, getScrollRef, and the setNativeProps fallback unchanged.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/views/RoomView/List/components/InvertedScrollView.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
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.
🧬 Code graph analysis (1)
app/views/RoomView/List/components/InvertedScrollView.tsx (2)
jest.setup.js (1)
React(179-179)android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollView.java (1)
InvertedScrollView(13-36)
⏰ 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 comments (2)
app/views/RoomView/List/components/InvertedScrollView.tsx (2)
1-49: Types and native component wiring look good.The type definitions, command constants, and
requireNativeComponentusage are well-structured. TheIScrollableMethodsinterface cleanly defines the scroll API surface, and the exportedInvertedScrollViewRefintersection type provides a correct public API.
135-152: Render path and content view wiring look correct.The conditional
removeClippedSubviewshandling for sticky headers (Line 145), thecollapsableChildrenflag driven bypreserveChildren(Line 147), and the content-size-change proxy viaonLayout(Lines 121–129) are all implemented correctly. The null guard at Line 135 is defensive —requireNativeComponentwon't return null at runtime, but it doesn't hurt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@app/views/RoomView/List/components/InvertedScrollView.tsx`:
- Around line 94-103: The setRef callback currently forwards the native node to
externalRef immediately, but the IScrollableMethods (scrollTo, scrollToEnd,
etc.) are only attached in useLayoutEffect, so consumers who call those methods
synchronously from their own ref callback may see undefined; add a concise
inline comment next to setRef (referencing setRef, internalRef, externalRef,
InvertedScrollViewRef, and useLayoutEffect) explaining that the node is exposed
during commit and that scroll methods are attached in useLayoutEffect so callers
should access them in effects/handlers (not synchronously) to avoid undefined.
- Around line 105-114: The component destructures
maintainVisibleContentPosition, snapToAlignment, and stickyHeaderIndices out of
props and uses them for local flags (preserveChildren, hasStickyHeaders) but
then omits them from restWithoutStyle and never forwards them to the underlying
<ScrollView> in InvertedScrollView component; update the props forwarding so
that maintainVisibleContentPosition, snapToAlignment, and stickyHeaderIndices
are included in the props passed to ScrollView (or merged into restWithoutStyle)
so the native InvertedScrollView/ReactScrollView receives and honors these
behaviors (refer to the props named maintainVisibleContentPosition,
snapToAlignment, stickyHeaderIndices, the local flags preserveChildren and
hasStickyHeaders, and the restWithoutStyle/rest spread used when rendering
ScrollView).
---
Nitpick comments:
In `@app/views/RoomView/List/components/InvertedScrollView.tsx`:
- Around line 54-92: Update the nullish-coalescing for scroll coordinates in the
patched scroll methods: in the node.scrollTo implementation change the x and y
fallbacks from using logical OR to nullish coalescing (replace uses of
options?.x || 0 and options?.y || 0 with options?.x ?? 0 and options?.y ?? 0) so
that explicit 0 values are preserved; leave other logic in scrollTo,
scrollToEnd, flashScrollIndicators, getScrollRef, and the setNativeProps
fallback unchanged.
|
Android Build Available Rocket.Chat Experimental 4.70.0.108306 |
|
Android Build Available Rocket.Chat 4.70.0.108308 |
|
iOS Build Available Rocket.Chat Experimental 4.70.0.108309 |
|
Android Build Available Rocket.Chat 4.70.0.108308 |
|
iOS Build Available Rocket.Chat 4.70.0.108307 |
Proposed changes
create a native module to use a native ScrollView component that uses a11y children in the right order.
Issue(s)
https://rocketchat.atlassian.net/browse/MA-96
How to test or reproduce
Screenshots
Types of changes
Checklist
Further comments
Summary by CodeRabbit
Bug Fixes
New Features