Skip to content

fix(android): correct accessibility focus order in inverted FlatList#6934

Open
OtavioStasiak wants to merge 29 commits intodevelopfrom
test.flatlist-inverted-content
Open

fix(android): correct accessibility focus order in inverted FlatList#6934
OtavioStasiak wants to merge 29 commits intodevelopfrom
test.flatlist-inverted-content

Conversation

@OtavioStasiak
Copy link
Contributor

@OtavioStasiak OtavioStasiak commented Jan 20, 2026

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

  • Open the app;
  • Navigate to RoomView;
  • Turn TalkBack on;
  • Navigate to the list and move down;
  • Scroll up using a three-finger gesture;
  • Try to focus on the older messages;

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

  • Bug Fixes

    • Fixed Android screen-reader (TalkBack) traversal for inverted (newest-first) lists so accessibility follows visual order.
  • New Features

    • Added Android-native inverted-scroll support with an Android-only scroll wrapper component.
    • Non-iOS list rendering now uses the new inverted-scroll wrapper for correct inverted-list behavior, improved scrolling, and sticky header handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

Walkthrough

Adds Android-native inverted scroll views, view managers, and a React Native package; introduces an Android-only TSX wrapper and wires FlatList to use the native inverted scroll on non-iOS platforms. Accessibility traversal is reversed to match the visual newest-first order.

Changes

Cohort / File(s) Summary
Android Native Views
android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollView.java, android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentView.java
New native view classes extending ReactScrollView/ReactViewGroup. They override addChildrenForAccessibility(...) to reverse accessibility child order when rendering inverted (newest-first) lists.
Android View Managers
android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollViewManager.java, android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentViewManager.java
New ReactModule view managers exposing REACT_CLASS, getName(), and createViewInstance(...) to instantiate the inverted native views.
Android Package & Registration
android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollPackage.java, android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt
New InvertedScrollPackage registering the view managers; package added to the app’s native package list in MainApplication.kt.
React Native wrapper & usage
app/views/RoomView/List/components/InvertedScrollView.tsx, app/views/RoomView/List/components/List.tsx
New Android-only TSX component that wires the native views via requireNativeComponent, merges/forwards refs and scroll methods, handles layout/content-size/sticky headers; List.tsx uses it for renderScrollComponent on non-iOS platforms.

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)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • diegolmello

Poem

🐰 I flipped the feed with nimble feet,
TalkBack now hops where new messages meet,
Children march from fresh to old,
Accessibility sings bold,
Hooray — the chat feels neat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing a fix for accessibility focus order in inverted FlatList on Android.
Linked Issues check ✅ Passed The PR addresses MA-96 by implementing native Android components to reverse accessibility children order in inverted scrollable lists, ensuring TalkBack traversal matches visual order.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing accessibility focus order in inverted FlatList on Android through native components and their integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test.flatlist-inverted-content

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.

@github-actions
Copy link

Android Build Available

Rocket.Chat Experimental 4.69.0.108196

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNTlK_aaCydNoR8tL0LU9ha91x4SUn6F8LQ_l7193fkFssxP1kKCqvtZvuPhoyE9s7WolKu01pF8dITARNJC

@OtavioStasiak OtavioStasiak temporarily deployed to approve_e2e_testing January 27, 2026 18:34 — with GitHub Actions Inactive
@github-actions
Copy link

Android Build Available

Rocket.Chat Experimental 4.69.0.108208

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNRkEKjr0nv7NcI6xLOU1Hf33CN6fGAImxz2dxN5rwCG938UBKpFBFtPdDKYUQ0167PNBAipwJsTvABYyrAj

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@app/views/RoomView/List/components/InvertedScrollView.tsx`:
- Around line 106-114: The ref callback setRef assigns the external ref (casting
node to InvertedScrollViewRef) but the ScrollableMethods are attached later in
useLayoutEffect, so synchronous calls to methods like scrollTo from a consumer's
ref callback may fail; update the file to add a short inline comment near setRef
(and reference InvertedScrollViewRef, setRef, and
useLayoutEffect/ScrollableMethods) explaining that methods are attached in
useLayoutEffect and thus consumers should not call instance methods
synchronously in the ref callback, and recommend accessing the methods from an
effect or event handler instead.
- Around line 55-103: The useLayoutEffect in InvertedScrollView is missing a
dependency array causing repeated re-patching of methods; update the
useLayoutEffect call so it only runs when the internal node changes (e.g., add a
dependency array such as [internalRef.current] or [] per desired behavior) to
avoid reassigning patchedNode.scrollTo, scrollToEnd, flashScrollIndicators,
getScrollRef and setNativeProps on every render; ensure the dependency array is
added to the useLayoutEffect invocation that wraps the patching logic.
- Around line 157-158: The InvertedScrollView component is destructuring
maintainVisibleContentPosition, snapToAlignment, and stickyHeaderIndices but not
passing them to the rendered ScrollView, so their native behaviors are lost;
update the return in InvertedScrollView (the JSX that renders <ScrollView ...
/>) to forward these props (maintainVisibleContentPosition, snapToAlignment,
stickyHeaderIndices) along with restWithoutStyle/style/ref/horizontal so the
native view receives them (also ensure TypeScript types/interfaces for
InvertedScrollView accept these props if needed).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/views/RoomView/List/components/InvertedScrollView.tsx`:
- Around line 77-83: The fallback for node.setNativeProps is recursive because
it overwrites the method and then calls the same property; fix this in
InvertedScrollView.tsx by first capturing the original method (e.g., const
origSetNativeProps = (node as any).setNativeProps) before assigning the
fallback, then assign node.setNativeProps to a wrapper that calls
origSetNativeProps if it's a function, otherwise becomes a safe no-op; ensure
you reference node.setNativeProps and origSetNativeProps (or similar) so the
wrapper never invokes itself.

@OtavioStasiak OtavioStasiak temporarily deployed to approve_e2e_testing January 29, 2026 20:47 — with GitHub Actions Inactive
@github-actions
Copy link

Android Build Available

Rocket.Chat Experimental 4.69.0.108209

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNT-qSf1sj6yCWFc7EBUnCX7872g8JTAnZzbGkVwQo0KMctVry7qMTkO4_EipaKXVvueHGOoPpO8rG9jHbly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant