Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1/5 Avoid using transform scaleY or scaleX in inverted flatlist when TalkBack and enabledTalkbackCompatibleInvertedList prop are enabled #34141

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
5b2cb47
draft - invert flatlist without using transform
fabOnReact Jul 6, 2022
06fb1dd
adding comments to scroll to top/bottom optimization
fabOnReact Jul 6, 2022
f72cac2
draft
fabOnReact Jul 8, 2022
e53aa54
workaround scrollToEnd issues for inverted flatlist
fabOnReact Jul 8, 2022
4c08d7c
adding flatlist nested example
fabOnReact Jul 8, 2022
24c612c
improve scrollToTop functionality when new item added
fabOnReact Jul 8, 2022
6215dc9
adding test case remove an item
fabOnReact Jul 8, 2022
85f3a42
fix circleci failures
fabOnReact Jul 8, 2022
389487b
remove line fullScroll
fabOnReact Jul 11, 2022
050a629
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Jul 11, 2022
c43c832
trigger scrollToEnd on not-nested flatlist
fabOnReact Jul 11, 2022
0eb74c6
Last item added/deleted in vertical flatlist does not adjust scroll p…
fabOnReact Jul 11, 2022
98bea1b
fix circle ci errors
fabOnReact Jul 11, 2022
6271850
re-introduce cellStyle flexDirection row
fabOnReact Jul 11, 2022
e640998
fix delete item inverted flatlist scrollToEnd
fabOnReact Jul 13, 2022
c47a75a
add/remove button for inverted flatlist example
fabOnReact Jul 13, 2022
c793c3b
flatlist-inverted ex add buttons
fabOnReact Jul 14, 2022
c281788
draft - when items are appended to the end of the list, the view need…
fabOnReact Jul 15, 2022
acddd08
draft - logic only used with ScreenReader and Inverted Flatlist
fabOnReact Jul 18, 2022
94d1246
use custom inversion logic for TalkBack (no android condition yet added)
fabOnReact Jul 18, 2022
543a9e4
adding explanatory comments
fabOnReact Jul 18, 2022
d627812
fix typo - isScreenReaderEnabled instead of screenreaderEnabled
fabOnReact Jul 18, 2022
87c88bd
adapt logic to horizontal scroll view
fabOnReact Jul 18, 2022
5e81716
when items are appended to the end of the list, the view needs to sta…
fabOnReact Jul 18, 2022
509931d
adding check on Platform Android
fabOnReact Jul 18, 2022
f779f9d
remove event listener on AccessibilityInfo
fabOnReact Jul 18, 2022
0c75c4d
adding EventSubcription type to this._screenreaderEventListener
fabOnReact Jul 18, 2022
8e58058
adding screenreaderEnabled to State type
fabOnReact Jul 18, 2022
6cb1dec
fix return type from method getDerivedStateFromProps
fabOnReact Jul 18, 2022
a506b51
remove changes to ScrollProps
fabOnReact Jul 18, 2022
f9b64bd
run yarn test -u
fabOnReact Jul 18, 2022
3cfe035
adding log message in case AccessibilityInfo.isScreenReaderEnabled fails
fabOnReact Jul 18, 2022
c163c54
TalkBack inverted FlatList _maybeCallOnEndReached custom behaviour
fabOnReact Jul 19, 2022
adff29f
when items appended, view stays in same position
fabOnReact Jul 19, 2022
88c68b2
draft - TalkBack onEndReached scroll Position
fabOnReact Jul 19, 2022
9bdaa47
fix onEndReached callback triggering multiple times
fabOnReact Jul 19, 2022
36e0ace
fix scrolling top does not trigger after long time
fabOnReact Jul 19, 2022
0d1cf47
remove state.height and resetScrollPosition
fabOnReact Jul 19, 2022
f670f19
Test Case for issue scrollview triggers a scrollToTop when scrolling …
fabOnReact Jul 20, 2022
193e9f4
fix scrollTo bottom/top when scolling fast down
fabOnReact Jul 20, 2022
ffe959e
remove console.log statements added for debugging
fabOnReact Jul 20, 2022
f2768ab
remove setTimeout when calling scrollToOffset
fabOnReact Jul 20, 2022
0912944
onMomentumScrollEnd does not work with talkback
fabOnReact Jul 20, 2022
3843514
scrollToEnd TalkBack Inverted Flatlist
fabOnReact Jul 20, 2022
b4abedd
adding scroll to end to example
fabOnReact Jul 20, 2022
3b6fe66
keep scroll position infinite horizontal flatlist
fabOnReact Jul 20, 2022
880fd80
upating logic that triggers first scroll to the end
fabOnReact Jul 20, 2022
72aaa2b
componentWillUnmount - clear variables
fabOnReact Jul 20, 2022
079fbfa
inverted example - adding header and footer
fabOnReact Jul 20, 2022
a3b8e1c
initialize variables in constructor
fabOnReact Jul 20, 2022
4b4c7a5
remove changes to flatlist-basic
fabOnReact Jul 20, 2022
3df9a90
fix issue lastTimeCalled not numberic and ref type
fabOnReact Jul 20, 2022
dfef050
fix regression in initialScrollIndex and scrollToIndex
fabOnReact Jul 21, 2022
239d31d
remove already decl variables
fabOnReact Jul 21, 2022
891c4f1
remove bottomHeight from state
fabOnReact Jul 21, 2022
6451aab
fix check on TalkBack Inverted FlatList
fabOnReact Jul 21, 2022
9cbc554
adding prop enableTalkbackCompatibleInvertedList
fabOnReact Jul 21, 2022
7b307d5
rename variables
fabOnReact Jul 21, 2022
a4ef645
adding test for scrollToIndex with TalkBack
fabOnReact Jul 21, 2022
c41e1c8
adding info about not supported initialScrollIndes
fabOnReact Jul 21, 2022
3d2067c
removing not relevant code from rn-tester
fabOnReact Jul 21, 2022
9d89b60
minor changes
fabOnReact Jul 21, 2022
c3c2a1b
remove setTimeout in scrollToOffset for infinite scroll
fabOnReact Jul 21, 2022
405d9fc
reintroduce setTimeout required for infinite scroll
fabOnReact Jul 21, 2022
9a894e9
improving conditions to enable functionality
fabOnReact Jul 21, 2022
e9f9715
adding test to press on inverted flatlist item
fabOnReact Jul 21, 2022
b0d2521
adding screenreaderEnabled to getDerivedStateFromProps
fabOnReact Jul 21, 2022
c96f635
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Jul 21, 2022
879c93f
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Jul 22, 2022
bf78345
adding clearTimeout to clear timer on componentWillUnmount
fabOnReact Jul 22, 2022
30960cf
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Jul 25, 2022
f7e5efc
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Jul 28, 2022
56e7be1
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Jul 29, 2022
6214e29
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Aug 3, 2022
d5c845e
Props are moved to VirtualizedListProps
fabOnReact Aug 3, 2022
436fbed
remove import for Platform
fabOnReact Aug 3, 2022
c7b2200
remove changes to flatlist-inverted
fabOnReact Aug 3, 2022
93e4e88
pullbot issues VirtualizedList
fabOnReact Aug 3, 2022
ce9b0aa
minor changes
fabOnReact Aug 3, 2022
48b4a4b
pullbot issues VirtualizedList
fabOnReact Aug 3, 2022
a8640c3
trying to fix circleci errors
fabOnReact Aug 4, 2022
5f7dbc6
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Aug 4, 2022
ceddbcd
VirList circleci errors
fabOnReact Aug 4, 2022
4e7bb10
avoid changing VirtUtils shared types with VirtList Experimental
fabOnReact Aug 4, 2022
a903479
remove change from VirtListExperimental
fabOnReact Aug 4, 2022
9943970
minor changes
fabOnReact Aug 4, 2022
4091984
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Sep 12, 2022
c7d0d22
draft example
fabOnReact Sep 14, 2022
b5ff683
adding talkback mocked value to example
fabOnReact Sep 14, 2022
4266625
updating example to use column-reverse
fabOnReact Sep 15, 2022
496cc49
adding column-reverse to FlatList contentContainerStyle and style
fabOnReact Sep 15, 2022
b3050bd
commit updates to the example
fabOnReact Sep 19, 2022
f43ac61
WIP - column-reverse, handle short list, fix issues
fabOnReact Sep 20, 2022
777e282
remove comments from github actions
fabOnReact Sep 20, 2022
6e3aff2
flatlist example without infinite list
fabOnReact Sep 21, 2022
aaada22
Test functionality with the infinite list (commit b3050bd637d)
fabOnReact Sep 21, 2022
5546b72
The infinite list does not work when TalkBack is enabled
fabOnReact Sep 21, 2022
0a588d5
vertical and horizontal infinite list
fabOnReact Sep 21, 2022
ed87a1c
trigger initial scroll position in Java
fabOnReact Sep 23, 2022
ba904fb
fix issue with scrolling in infinite list
fabOnReact Sep 23, 2022
b4ef1a3
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Sep 23, 2022
3b4ca19
minor changes
fabOnReact Sep 23, 2022
8f1a234
minor changes
fabOnReact Sep 23, 2022
ffe5c81
Revert "trigger initial scroll position in Java"
fabOnReact Sep 26, 2022
0064acc
remove platform checks
fabOnReact Sep 26, 2022
82768f7
_maybeCallOnEndReached from PR #26444
fabOnReact Sep 26, 2022
3242de0
adapting #26444 to solve issue #30373
fabOnReact Sep 26, 2022
0617ce4
Revert "Revert "trigger initial scroll position in Java""
fabOnReact Sep 26, 2022
36776e9
working solution (with some issues) on Android and iOS
fabOnReact Sep 26, 2022
b3b3096
working implementation for iOS and Android
fabOnReact Sep 26, 2022
4cf5c16
move onEndReached logic for inverted list to example
fabOnReact Sep 26, 2022
b708ed9
review Java functionalities
fabOnReact Sep 27, 2022
8abdfb7
Code Review JavaScript Functionalities
fabOnReact Sep 27, 2022
5919a6e
fixing issues with _hasTriggeredInitialScrollToIndex race condition
fabOnReact Sep 27, 2022
036f088
circle ci eslint checks on example
fabOnReact Sep 27, 2022
7206a4d
Merge branch 'main' into inverted-flatlist-accessibility
fabOnReact Sep 27, 2022
6690eeb
rename component in example
fabOnReact Sep 27, 2022
e18ac41
disabled contentOffset if talkback is not enabled
fabOnReact Sep 27, 2022
8c8e68e
fix flow errors circleci
fabOnReact Sep 27, 2022
099b806
quick fix flow errors example
fabOnReact Sep 27, 2022
66be76f
avoid over-riding props.style and props.contentContainerStyle
fabOnReact Sep 27, 2022
81e6116
inverted flatlist start from the end
fabOnReact Sep 27, 2022
8a1df99
using destructured variables
fabOnReact Sep 27, 2022
7f543e6
re-enable _scheduleCellsToRenderUpdate for TalkBack
fabOnReact Sep 27, 2022
9202414
adding log warning for not compatible functionalities
fabOnReact Sep 27, 2022
9b141b9
eslint fix check
fabOnReact Sep 28, 2022
10056e4
minor fix to example
fabOnReact Sep 28, 2022
a93d8c1
avoid trigger scroll to bottom with contentOffset after first scroll
fabOnReact Sep 28, 2022
1106350
move variable declaration
fabOnReact Sep 28, 2022
223da5d
avoid setting contentOffset if contentLength == 0
fabOnReact Sep 28, 2022
6bb091c
move contentOffset to rn-tester example
fabOnReact Sep 28, 2022
7ee21a5
fix circleci
fabOnReact Sep 28, 2022
94db868
fix circleci
fabOnReact Sep 28, 2022
80d2343
avoid trigger multiple time contentOffset in example
fabOnReact Sep 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 131 additions & 10 deletions Libraries/Lists/VirtualizedList.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
computeWindowedRenderLimits,
keyExtractor as defaultKeyExtractor,
} from './VirtualizeUtils';
import AccessibilityInfo from '../Components/AccessibilityInfo/AccessibilityInfo';
import type {EventSubscription} from '../vendor/emitter/EventEmitter';
import * as VirtualizedListInjection from './VirtualizedListInjection';
import CellRenderer from './VirtualizedListCellRenderer';
import * as React from 'react';
Expand All @@ -47,6 +49,7 @@ const StyleSheet = require('../StyleSheet/StyleSheet');
const infoLog = require('../Utilities/infoLog');
const FillRateHelper = require('./FillRateHelper');
const ViewabilityHelper = require('./ViewabilityHelper');
const Platform = require('../Utilities/Platform');
const invariant = require('invariant');

const ON_END_REACHED_EPSILON = 0.001;
Expand All @@ -67,6 +70,7 @@ type ViewabilityHelperCallbackTuple = {
type State = {
first: number,
last: number,
screenreaderEnabled: ?boolean,
};

/**
Expand Down Expand Up @@ -138,6 +142,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {

// scrollToEnd may be janky without getItemLayout prop
scrollToEnd(params?: ?{animated?: ?boolean, ...}) {
if (__DEV__ && this.props.enabledTalkbackCompatibleInvertedList) {
console.log(
'scrollToEnd is not compatible with enabledTalkbackCompatibleInvertedList,' +
'use instead scrollToOffset. An example implementation is available in rn-tester flatlist-inverted.',
);
}
const animated = params ? params.animated : true;
const veryLast = this.props.getItemCount(this.props.data) - 1;
const frame = this.__getFrameMetricsApprox(veryLast);
Expand Down Expand Up @@ -424,6 +434,19 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}
}

if (Platform.OS === 'android') {
this._screenreaderEventListener = AccessibilityInfo.addEventListener(
'screenReaderChanged',
screenreaderEnabled => {
fabOnReact marked this conversation as resolved.
Show resolved Hide resolved
if (
typeof screenreaderEnabled === 'boolean' &&
screenreaderEnabled !== this.state.screenreaderEnabled
) {
this.setState({screenreaderEnabled});
}
},
);
}
invariant(
!this.context,
'Unexpectedly saw VirtualizedListContext available in ctor',
Expand All @@ -437,6 +460,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
(this.props.initialScrollIndex || 0) +
initialNumToRenderOrDefault(this.props.initialNumToRender),
) - 1,
screenreaderEnabled: undefined,
};
}

Expand All @@ -447,6 +471,31 @@ class VirtualizedList extends React.PureComponent<Props, State> {
cellKey: this.context.cellKey,
});
}

// updates the initial state of the screenreaderReader
if (
Platform.OS === 'android' &&
this.state.screenreaderEnabled === undefined
) {
AccessibilityInfo.isScreenReaderEnabled().then(
screenreaderEnabled => {
if (
typeof screenreaderEnabled === 'boolean' &&
screenreaderEnabled !== this.state.screenreaderEnabled
) {
this.setState({screenreaderEnabled});
}
},
e => {
if (__DEV__) {
console.log(
'isScreenReaderEnabled() raised an error, in this case the default inverted FlatList will be used with Talkback. ' +
e.toString(),
);
}
},
);
}
}

componentWillUnmount() {
Expand All @@ -459,6 +508,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
tuple.viewabilityHelper.dispose();
});
this._fillRateHelper.deactivateAndFlush();
if (this._screenreaderEventListener) {
this._screenreaderEventListener.remove();
}
}

static getDerivedStateFromProps(newProps: Props, prevState: State): State {
Expand All @@ -474,6 +526,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
Math.min(prevState.first, getItemCount(data) - 1 - maxToRenderPerBatch),
),
last: Math.max(0, Math.min(prevState.last, getItemCount(data) - 1)),
screenreaderEnabled: prevState.screenreaderEnabled,
};
}

Expand Down Expand Up @@ -587,13 +640,48 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}
const {ListEmptyComponent, ListFooterComponent, ListHeaderComponent} =
this.props;
const {data, horizontal} = this.props;
const {data, horizontal, inverted, enabledTalkbackCompatibleInvertedList} =
this.props;
const {first, last, screenreaderEnabled} = this.state;
const {contentLength, visibleLength} = this._scrollMetrics;
const talkbackCompatibility =
screenreaderEnabled &&
inverted &&
Platform.OS === 'android' &&
enabledTalkbackCompatibleInvertedList;
const isVirtualizationDisabled = this._isVirtualizationDisabled();
const inversionStyle = this.props.inverted
? horizontalOrDefault(this.props.horizontal)
let inversionStyle = inverted
? horizontalOrDefault(horizontal)
? styles.horizontallyInverted
: styles.verticallyInverted
: null;
let style = inversionStyle
? [inversionStyle, this.props.style]
: this.props.style;
let contentContainerStyle = this.props.contentContainerStyle;
if (
talkbackCompatibility &&
contentLength != null &&
visibleLength != null
) {
const diff = visibleLength - contentLength;
if (diff > 0) {
style = horizontalOrDefault(horizontal)
? [{flexDirection: 'row-reverse'}, this.props.style]
: [{flexDirection: 'column-reverse'}, this.props.style];
} else {
style = this.props.style;
}
}
if (talkbackCompatibility) {
contentContainerStyle = horizontalOrDefault(horizontal)
? [{flexDirection: 'row-reverse'}, this.props.contentContainerStyle]
: [{flexDirection: 'column-reverse'}, this.props.contentContainerStyle];
inversionStyle = null;
if (contentLength == null || visibleLength == null) {
style = this.props.style;
}
}
const cells = [];
const stickyIndicesFromProps = new Set(this.props.stickyHeaderIndices);
const stickyHeaderIndices = [];
Expand Down Expand Up @@ -634,7 +722,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
const lastInitialIndex = this.props.initialScrollIndex
? -1
: initialNumToRenderOrDefault(this.props.initialNumToRender) - 1;
const {first, last} = this.state;
// scroll to top optimization. The first page is always rendered.
this._pushCells(
cells,
stickyHeaderIndices,
Expand Down Expand Up @@ -788,9 +876,8 @@ class VirtualizedList extends React.PureComponent<Props, State> {
? this.props.invertStickyHeaders
: this.props.inverted,
stickyHeaderIndices,
style: inversionStyle
? [inversionStyle, this.props.style]
: this.props.style,
style,
contentContainerStyle,
};

this._hasMore =
Expand Down Expand Up @@ -921,6 +1008,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
_totalCellsMeasured = 0;
_updateCellsToRenderBatcher: Batchinator;
_viewabilityTuples: Array<ViewabilityHelperCallbackTuple> = [];
_screenreaderEventListener: EventSubscription;

/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
Expand Down Expand Up @@ -1191,9 +1279,26 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}

_maybeCallOnEndReached() {
const {data, getItemCount, onEndReached, onEndReachedThreshold} =
this.props;
const {
data,
getItemCount,
onEndReached,
onEndReachedThreshold,
inverted,
enabledTalkbackCompatibleInvertedList,
} = this.props;
const {contentLength, visibleLength, offset} = this._scrollMetrics;
if (
__DEV__ &&
inverted &&
enabledTalkbackCompatibleInvertedList &&
onEndReached != null
) {
console.log(
'The prop enabledTalkbackCompatibleInvertedList is not compatible with onEndReached. ' +
'An example implementation is available in rn-tester flatlist-inverted.',
);
}
let distanceFromEnd = contentLength - visibleLength - offset;

// Especially when oERT is zero it's necessary to 'floor' very small distanceFromEnd values to be 0
Expand Down Expand Up @@ -1223,6 +1328,21 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}

_onContentSizeChange = (width: number, height: number) => {
const {
initialScrollIndex,
inverted,
enabledTalkbackCompatibleInvertedList,
} = this.props;
if (
__DEV__ &&
initialScrollIndex != null &&
inverted &&
enabledTalkbackCompatibleInvertedList
) {
console.error(
'The prop enabledTalkbackCompatibleInvertedList can not be used with initialScrollIndex.',
);
}
if (
width > 0 &&
height > 0 &&
Expand Down Expand Up @@ -1464,6 +1584,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
// - initialScrollIndex > 0 AND scrolling is complete
// - initialScrollIndex > 0 AND the end of the list is visible (this handles the case
// where the list is shorter than the visible area)
const {first, last} = state;
if (
!this.props.initialScrollIndex ||
this._scrollMetrics.offset ||
Expand All @@ -1473,7 +1594,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
this.props,
maxToRenderPerBatchOrDefault(this.props.maxToRenderPerBatch),
windowSizeOrDefault(this.props.windowSize),
state,
{first, last},
this.__getFrameMetricsApprox,
this._scrollMetrics,
);
Expand Down
10 changes: 10 additions & 0 deletions Libraries/Lists/VirtualizedListProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ type OptionalProps = {|
* this for debugging purposes. Defaults to false.
*/
disableVirtualization?: ?boolean,

/**
* Enable TalkBack support for inverted FlatList
* The default implementation of inverted FlatList uses transform scaleX or scaleY and is not compatible
* with TalkBack. This implementation manually inverts the order of the items, but does not yet support all
* FlatList functionalities, by default is disabled.
* Experimental - Example of implementation available in rn-tester flatlist-inverted.
*/
enabledTalkbackCompatibleInvertedList?: ?boolean,

/**
* A marker property for telling the list to re-render (since it implements `PureComponent`). If
* any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the
Expand Down
Loading