From 3ff23eddc6e611a8753b16be5dada972cb9a1a5a Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 16 Feb 2022 20:42:05 -0800 Subject: [PATCH 1/5] Add onStartReached and onStartReachedThreshold props --- Libraries/Lists/FlatList.d.ts | 13 -- Libraries/Lists/VirtualizedList.d.ts | 27 +++ Libraries/Lists/VirtualizedList.js | 104 ++++++++--- Libraries/Lists/VirtualizedListProps.js | 23 ++- .../Lists/__tests__/VirtualizedList-test.js | 162 ++++++++++++++++++ .../examples/FlatList/BaseFlatListExample.js | 10 +- .../FlatList/FlatList-onStartReached.js | 56 ++++++ .../examples/FlatList/FlatListExampleIndex.js | 2 + 8 files changed, 355 insertions(+), 42 deletions(-) create mode 100644 packages/rn-tester/js/examples/FlatList/FlatList-onStartReached.js diff --git a/Libraries/Lists/FlatList.d.ts b/Libraries/Lists/FlatList.d.ts index 0cb4302f1ee0..6899c22f7d88 100644 --- a/Libraries/Lists/FlatList.d.ts +++ b/Libraries/Lists/FlatList.d.ts @@ -146,19 +146,6 @@ export interface FlatListProps extends VirtualizedListProps { */ numColumns?: number | undefined; - /** - * Called once when the scroll position gets within onEndReachedThreshold of the rendered content. - */ - onEndReached?: ((info: {distanceFromEnd: number}) => void) | null | undefined; - - /** - * How far from the end (in units of visible length of the list) the bottom edge of the - * list must be from the end of the content to trigger the `onEndReached` callback. - * Thus a value of 0.5 will trigger `onEndReached` when the end of the content is - * within half the visible length of the list. - */ - onEndReachedThreshold?: number | null | undefined; - /** * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. * Make sure to also set the refreshing prop correctly. diff --git a/Libraries/Lists/VirtualizedList.d.ts b/Libraries/Lists/VirtualizedList.d.ts index 29c040a236ca..a0a530b7f07b 100644 --- a/Libraries/Lists/VirtualizedList.d.ts +++ b/Libraries/Lists/VirtualizedList.d.ts @@ -232,8 +232,18 @@ export interface VirtualizedListWithoutRenderItemProps */ maxToRenderPerBatch?: number | undefined; + /** + * Called once when the scroll position gets within within `onEndReachedThreshold` + * from the logical end of the list. + */ onEndReached?: ((info: {distanceFromEnd: number}) => void) | null | undefined; + /** + * How far from the end (in units of visible length of the list) the trailing edge of the + * list must be from the end of the content to trigger the `onEndReached` callback. + * Thus, a value of 0.5 will trigger `onEndReached` when the end of the content is + * within half the visible length of the list. + */ onEndReachedThreshold?: number | null | undefined; onLayout?: ((event: LayoutChangeEvent) => void) | undefined; @@ -257,6 +267,23 @@ export interface VirtualizedListWithoutRenderItemProps }) => void) | undefined; + /** + * Called once when the scroll position gets within within `onStartReachedThreshold` + * from the logical start of the list. + */ + onStartReached?: + | ((info: {distanceFromStart: number}) => void) + | null + | undefined; + + /** + * How far from the start (in units of visible length of the list) the leading edge of the + * list must be from the start of the content to trigger the `onStartReached` callback. + * Thus, a value of 0.5 will trigger `onStartReached` when the start of the content is + * within half the visible length of the list. + */ + onStartReachedThreshold?: number | null | undefined; + /** * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 4e46e1458c3f..6dba6cd99dd7 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -50,7 +50,7 @@ import * as React from 'react'; export type {RenderItemProps, RenderItemType, Separators}; -const ON_END_REACHED_EPSILON = 0.001; +const ON_EDGE_REACHED_EPSILON = 0.001; let _usedIndexForKey = false; let _keylessItemComponentName: string = ''; @@ -90,11 +90,21 @@ function maxToRenderPerBatchOrDefault(maxToRenderPerBatch: ?number) { return maxToRenderPerBatch ?? 10; } +// onStartReachedThresholdOrDefault(this.props.onStartReachedThreshold) +function onStartReachedThresholdOrDefault(onStartReachedThreshold: ?number) { + return onStartReachedThreshold ?? 2; +} + // onEndReachedThresholdOrDefault(this.props.onEndReachedThreshold) function onEndReachedThresholdOrDefault(onEndReachedThreshold: ?number) { return onEndReachedThreshold ?? 2; } +// getScrollingThreshold(visibleLength, onEndReachedThreshold) +function getScrollingThreshold(threshold: number, visibleLength: number) { + return (threshold * visibleLength) / 2; +} + // scrollEventThrottleOrDefault(this.props.scrollEventThrottle) function scrollEventThrottleOrDefault(scrollEventThrottle: ?number) { return scrollEventThrottle ?? 50; @@ -1112,6 +1122,7 @@ export default class VirtualizedList extends StateSafePureComponent< zoomScale: 1, }; _scrollRef: ?React.ElementRef = null; + _sentStartForContentLength = 0; _sentEndForContentLength = 0; _totalCellLength = 0; _totalCellsMeasured = 0; @@ -1299,7 +1310,7 @@ export default class VirtualizedList extends StateSafePureComponent< } this.props.onLayout && this.props.onLayout(e); this._scheduleCellsToRenderUpdate(); - this._maybeCallOnEndReached(); + this._maybeCallOnEdgeReached(); }; _onLayoutEmpty = (e: LayoutEvent) => { @@ -1408,35 +1419,78 @@ export default class VirtualizedList extends StateSafePureComponent< return !horizontalOrDefault(this.props.horizontal) ? metrics.y : metrics.x; } - _maybeCallOnEndReached() { - const {data, getItemCount, onEndReached, onEndReachedThreshold} = - this.props; + _maybeCallOnEdgeReached() { + const { + data, + getItemCount, + onStartReached, + onStartReachedThreshold, + onEndReached, + onEndReachedThreshold, + initialScrollIndex, + } = this.props; const {contentLength, visibleLength, offset} = this._scrollMetrics; + let distanceFromStart = offset; let distanceFromEnd = contentLength - visibleLength - offset; - // Especially when oERT is zero it's necessary to 'floor' very small distanceFromEnd values to be 0 + // Especially when oERT is zero it's necessary to 'floor' very small distance values to be 0 // since debouncing causes us to not fire this event for every single "pixel" we scroll and can thus - // be at the "end" of the list with a distanceFromEnd approximating 0 but not quite there. - if (distanceFromEnd < ON_END_REACHED_EPSILON) { + // be at the edge of the list with a distance approximating 0 but not quite there. + if (distanceFromStart < ON_EDGE_REACHED_EPSILON) { + distanceFromStart = 0; + } + if (distanceFromEnd < ON_EDGE_REACHED_EPSILON) { distanceFromEnd = 0; } // TODO: T121172172 Look into why we're "defaulting" to a threshold of 2 when oERT is not present - const threshold = - onEndReachedThreshold != null ? onEndReachedThreshold * visibleLength : 2; + const startThreshold = + onStartReachedThresholdOrDefault(onStartReachedThreshold) * visibleLength; + const endThreshold = + onEndReachedThresholdOrDefault(onEndReachedThreshold) * visibleLength; + const isWithinStartThreshold = distanceFromStart <= startThreshold; + const isWithinEndThreshold = distanceFromEnd <= endThreshold; + + // First check if the user just scrolled within the end threshold + // and call onEndReached only once for a given content length, + // and only if onStartReached is not being executed if ( onEndReached && this.state.cellsAroundViewport.last === getItemCount(data) - 1 && - distanceFromEnd <= threshold && + isWithinEndThreshold && this._scrollMetrics.contentLength !== this._sentEndForContentLength ) { - // Only call onEndReached once for a given content length this._sentEndForContentLength = this._scrollMetrics.contentLength; onEndReached({distanceFromEnd}); - } else if (distanceFromEnd > threshold) { - // If the user scrolls away from the end and back again cause - // an onEndReached to be triggered again - this._sentEndForContentLength = 0; + } + + // Next check if the user just scrolled within the start threshold + // and call onStartReached only once for a given content length, + // and only if onEndReached is not being executed + else if ( + onStartReached && + this.state.cellsAroundViewport.first === 0 && + isWithinStartThreshold && + this._scrollMetrics.contentLength !== this._sentStartForContentLength && + // On initial mount when using initialScrollIndex the offset will be 0 initially + // and will trigger an unexpected onStartReached. To avoid this we can use + // timestamp to differentiate between the initial scroll metrics and when we actually + // received the first scroll event. + (!initialScrollIndex || this._scrollMetrics.timestamp !== 0) + ) { + this._sentStartForContentLength = this._scrollMetrics.contentLength; + onStartReached({distanceFromStart}); + } + + // If the user scrolls away from the start or end and back again, + // cause onStartReached or onEndReached to be triggered again + else { + this._sentStartForContentLength = isWithinStartThreshold + ? this._sentStartForContentLength + : 0; + this._sentEndForContentLength = isWithinEndThreshold + ? this._sentEndForContentLength + : 0; } } @@ -1461,7 +1515,7 @@ export default class VirtualizedList extends StateSafePureComponent< } this._scrollMetrics.contentLength = this._selectLength({height, width}); this._scheduleCellsToRenderUpdate(); - this._maybeCallOnEndReached(); + this._maybeCallOnEdgeReached(); }; /* Translates metrics from a scroll event in a parent VirtualizedList into @@ -1549,7 +1603,7 @@ export default class VirtualizedList extends StateSafePureComponent< if (!this.props) { return; } - this._maybeCallOnEndReached(); + this._maybeCallOnEdgeReached(); if (velocity !== 0) { this._fillRateHelper.activate(); } @@ -1562,17 +1616,23 @@ export default class VirtualizedList extends StateSafePureComponent< const {offset, visibleLength, velocity} = this._scrollMetrics; const itemCount = this.props.getItemCount(this.props.data); let hiPri = false; + const onStartReachedThreshold = onStartReachedThresholdOrDefault( + this.props.onStartReachedThreshold, + ); const onEndReachedThreshold = onEndReachedThresholdOrDefault( this.props.onEndReachedThreshold, ); - const scrollingThreshold = (onEndReachedThreshold * visibleLength) / 2; // Mark as high priority if we're close to the start of the first item // But only if there are items before the first rendered item if (first > 0) { const distTop = offset - this.__getFrameMetricsApprox(first, this.props).offset; hiPri = - hiPri || distTop < 0 || (velocity < -2 && distTop < scrollingThreshold); + hiPri || + distTop < 0 || + (velocity < -2 && + distTop < + getScrollingThreshold(onStartReachedThreshold, visibleLength)); } // Mark as high priority if we're close to the end of the last item // But only if there are items after the last rendered item @@ -1583,7 +1643,9 @@ export default class VirtualizedList extends StateSafePureComponent< hiPri = hiPri || distBottom < 0 || - (velocity > 2 && distBottom < scrollingThreshold); + (velocity > 2 && + distBottom < + getScrollingThreshold(onEndReachedThreshold, visibleLength)); } // Only trigger high-priority updates if we've actually rendered cells, // and with that size estimate, accurately compute how many cells we should render. diff --git a/Libraries/Lists/VirtualizedListProps.js b/Libraries/Lists/VirtualizedListProps.js index 59e57f2bb6df..f4d497b1d467 100644 --- a/Libraries/Lists/VirtualizedListProps.js +++ b/Libraries/Lists/VirtualizedListProps.js @@ -170,16 +170,15 @@ type OptionalProps = {| */ maxToRenderPerBatch?: ?number, /** - * Called once when the scroll position gets within `onEndReachedThreshold` of the rendered - * content. + * Called once when the scroll position gets within within `onEndReachedThreshold` + * from the logical end of the list. */ onEndReached?: ?(info: {distanceFromEnd: number, ...}) => void, /** - * How far from the end (in units of visible length of the list) the bottom edge of the + * How far from the end (in units of visible length of the list) the trailing edge of the * list must be from the end of the content to trigger the `onEndReached` callback. - * Thus a value of 0.5 will trigger `onEndReached` when the end of the content is - * within half the visible length of the list. A value of 0 will not trigger until scrolling - * to the very end of the list. + * Thus, a value of 0.5 will trigger `onEndReached` when the end of the content is + * within half the visible length of the list. */ onEndReachedThreshold?: ?number, /** @@ -198,6 +197,18 @@ type OptionalProps = {| averageItemLength: number, ... }) => void, + /** + * Called once when the scroll position gets within within `onStartReachedThreshold` + * from the logical start of the list. + */ + onStartReached?: ?(info: {distanceFromStart: number, ...}) => void, + /** + * How far from the start (in units of visible length of the list) the leading edge of the + * list must be from the start of the content to trigger the `onStartReached` callback. + * Thus, a value of 0.5 will trigger `onStartReached` when the start of the content is + * within half the visible length of the list. + */ + onStartReachedThreshold?: ?number, /** * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index 969d72b90142..fa0c40b3b5d6 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -356,6 +356,168 @@ describe('VirtualizedList', () => { expect(scrollRef.measureLayout).toBeInstanceOf(jest.fn().constructor); expect(scrollRef.measureInWindow).toBeInstanceOf(jest.fn().constructor); }); + + it('calls onStartReached when near the start', () => { + const ITEM_HEIGHT = 40; + const layout = {width: 300, height: 600}; + let data = Array(40) + .fill() + .map((_, index) => ({key: `key-${index}`})); + const onStartReached = jest.fn(); + const props = { + data, + initialNumToRender: 10, + onStartReachedThreshold: 1, + windowSize: 10, + renderItem: ({item}) => , + getItem: (items, index) => items[index], + getItemCount: items => items.length, + getItemLayout: (items, index) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, + }), + onStartReached, + initialScrollIndex: data.length - 1, + }; + + const component = ReactTestRenderer.create(); + + const instance = component.getInstance(); + + instance._onLayout({nativeEvent: {layout, zoomScale: 1}}); + instance._onContentSizeChange(300, data.length * ITEM_HEIGHT); + + // Make sure onStartReached is not called initially when initialScrollIndex is set. + performAllBatches(); + expect(onStartReached).not.toHaveBeenCalled(); + + // Scroll for a small amount and make sure onStartReached is not called. + instance._onScroll({ + timeStamp: 1000, + nativeEvent: { + contentOffset: {y: (data.length - 2) * ITEM_HEIGHT, x: 0}, + layoutMeasurement: layout, + contentSize: {...layout, height: data.length * ITEM_HEIGHT}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }, + }); + performAllBatches(); + expect(onStartReached).not.toHaveBeenCalled(); + + // Scroll to start and make sure onStartReached is called. + instance._onScroll({ + timeStamp: 1000, + nativeEvent: { + contentOffset: {y: 0, x: 0}, + layoutMeasurement: layout, + contentSize: {...layout, height: data.length * ITEM_HEIGHT}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }, + }); + performAllBatches(); + expect(onStartReached).toHaveBeenCalled(); + }); + + it('calls onStartReached initially', () => { + const ITEM_HEIGHT = 40; + const layout = {width: 300, height: 600}; + let data = Array(40) + .fill() + .map((_, index) => ({key: `key-${index}`})); + const onStartReached = jest.fn(); + const props = { + data, + initialNumToRender: 10, + onStartReachedThreshold: 1, + windowSize: 10, + renderItem: ({item}) => , + getItem: (items, index) => items[index], + getItemCount: items => items.length, + getItemLayout: (items, index) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, + }), + onStartReached, + }; + + const component = ReactTestRenderer.create(); + + const instance = component.getInstance(); + + instance._onLayout({nativeEvent: {layout, zoomScale: 1}}); + instance._onContentSizeChange(300, data.length * ITEM_HEIGHT); + + performAllBatches(); + expect(onStartReached).toHaveBeenCalled(); + }); + + it('calls onEndReached when near the end', () => { + const ITEM_HEIGHT = 40; + const layout = {width: 300, height: 600}; + let data = Array(40) + .fill() + .map((_, index) => ({key: `key-${index}`})); + const onEndReached = jest.fn(); + const props = { + data, + initialNumToRender: 10, + onEndReachedThreshold: 1, + windowSize: 10, + renderItem: ({item}) => , + getItem: (items, index) => items[index], + getItemCount: items => items.length, + getItemLayout: (items, index) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, + }), + onEndReached, + }; + + const component = ReactTestRenderer.create(); + + const instance = component.getInstance(); + + instance._onLayout({nativeEvent: {layout, zoomScale: 1}}); + instance._onContentSizeChange(300, data.length * ITEM_HEIGHT); + + // Make sure onEndReached is not called initially. + performAllBatches(); + expect(onEndReached).not.toHaveBeenCalled(); + + // Scroll for a small amount and make sure onEndReached is not called. + instance._onScroll({ + timeStamp: 1000, + nativeEvent: { + contentOffset: {y: ITEM_HEIGHT, x: 0}, + layoutMeasurement: layout, + contentSize: {...layout, height: data.length * ITEM_HEIGHT}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }, + }); + performAllBatches(); + expect(onEndReached).not.toHaveBeenCalled(); + + // Scroll to end and make sure onEndReached is called. + instance._onScroll({ + timeStamp: 1000, + nativeEvent: { + contentOffset: {y: data.length * ITEM_HEIGHT, x: 0}, + layoutMeasurement: layout, + contentSize: {...layout, height: data.length * ITEM_HEIGHT}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }, + }); + performAllBatches(); + expect(onEndReached).toHaveBeenCalled(); + }); + it('does not call onEndReached when onContentSizeChange happens after onLayout', () => { const ITEM_HEIGHT = 40; const layout = {width: 300, height: 600}; diff --git a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js index 0f07b9847bac..d1649a702466 100644 --- a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js +++ b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js @@ -103,11 +103,17 @@ export default (BaseFlatListExample: React.AbstractComponent< FlatList, >); +const ITEM_INNER_HEIGHT = 70; +const ITEM_MARGIN = 8; +export const ITEM_HEIGHT: number = ITEM_INNER_HEIGHT + ITEM_MARGIN * 2; + const styles = StyleSheet.create({ item: { backgroundColor: 'pink', - padding: 20, - marginVertical: 8, + paddingHorizontal: 20, + height: ITEM_INNER_HEIGHT, + marginVertical: ITEM_MARGIN, + justifyContent: 'center', }, header: { fontSize: 32, diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-onStartReached.js b/packages/rn-tester/js/examples/FlatList/FlatList-onStartReached.js new file mode 100644 index 000000000000..82ec8e2aae61 --- /dev/null +++ b/packages/rn-tester/js/examples/FlatList/FlatList-onStartReached.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; +import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; +import BaseFlatListExample, {ITEM_HEIGHT} from './BaseFlatListExample'; +import * as React from 'react'; + +export function FlatList_onStartReached(): React.Node { + const [output, setOutput] = React.useState(''); + const exampleProps = { + onStartReached: (info: {distanceFromStart: number, ...}) => + setOutput('onStartReached'), + onStartReachedThreshold: 0, + initialScrollIndex: 5, + getItemLayout: (data: any, index: number) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, + }), + }; + const ref = React.useRef(null); + + const onTest = () => { + const scrollResponder = ref?.current?.getScrollResponder(); + if (scrollResponder != null) { + scrollResponder.scrollTo({y: 0}); + } + }; + + return ( + + ); +} + +export default ({ + title: 'onStartReached', + name: 'onStartReached', + description: + 'Scroll to start of list or tap Test button to see `onStartReached` triggered.', + render: function (): React.Element { + return ; + }, +}: RNTesterModuleExample); diff --git a/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js b/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js index 2fc61e2f55fb..bdfbb8a237ff 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js +++ b/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js @@ -10,6 +10,7 @@ import type {RNTesterModule} from '../../types/RNTesterTypes'; import BasicExample from './FlatList-basic'; +import OnStartReachedExample from './FlatList-onStartReached'; import OnEndReachedExample from './FlatList-onEndReached'; import ContentInsetExample from './FlatList-contentInset'; import InvertedExample from './FlatList-inverted'; @@ -28,6 +29,7 @@ export default ({ showIndividualExamples: true, examples: [ BasicExample, + OnStartReachedExample, OnEndReachedExample, ContentInsetExample, InvertedExample, From 43544585dd6b677e2f4c9401d6ece637780f2f0d Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Thu, 1 Dec 2022 11:27:30 -0500 Subject: [PATCH 2/5] Fix VirtualizedList with maintainVisibleContentPosition --- Libraries/Lists/VirtualizedList.js | 131 ++++++++++++++---- .../js/components/ListExampleShared.js | 48 +++++-- .../js/examples/FlatList/FlatList-basic.js | 71 ++++++++-- .../examples/FlatList/FlatList-multiColumn.js | 4 +- .../SectionList/SectionList-scrollable.js | 4 +- 5 files changed, 207 insertions(+), 51 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 6dba6cd99dd7..1790d96f70c0 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -68,6 +68,11 @@ type ViewabilityHelperCallbackTuple = { type State = { renderMask: CellRenderMask, cellsAroundViewport: {first: number, last: number}, + // Used to track items added at the start of the list for maintainVisibleContentPosition. + firstItemKey: ?string, + // When using maintainVisibleContentPosition we need to adjust the window to make sure + // make sure that the visible elements are still rendered. + maintainVisibleContentPositionAdjustment: ?number, }; /** @@ -128,6 +133,47 @@ function findLastWhere( return null; } +function extractKey( + props: { + keyExtractor?: ?(item: Item, index: number) => string, + ... + }, + item: Item, + index: number, +): string { + if (props.keyExtractor != null) { + return props.keyExtractor(item, index); + } + + const key = defaultKeyExtractor(item, index); + if (key === String(index)) { + _usedIndexForKey = true; + if (item.type && item.type.displayName) { + _keylessItemComponentName = item.type.displayName; + } + } + return key; +} + +function findItemIndexWithKey(props: Props, key: string): ?number { + for (let ii = 0; ii < props.getItemCount(props.data); ii++) { + const item = props.getItem(props.data, ii); + const curKey = extractKey(props, item, ii); + if (curKey === key) { + return ii; + } + } + return null; +} + +function getItemKey(props: Props, index: number): ?string { + const item = props.getItem(props.data, index); + if (item == null) { + return null; + } + return extractKey(props, item, 0); +} + /** * Base implementation for the more convenient [``](https://reactnative.dev/docs/flatlist) * and [``](https://reactnative.dev/docs/sectionlist) components, which are also better @@ -463,13 +509,20 @@ export default class VirtualizedList extends StateSafePureComponent< this.state = { cellsAroundViewport: initialRenderRegion, - renderMask: VirtualizedList._createRenderMask(props, initialRenderRegion), + renderMask: VirtualizedList._createRenderMask( + props, + initialRenderRegion, + null, + ), + firstItemKey: getItemKey(this.props, 0), + maintainVisibleContentPositionAdjustment: null, }; } static _createRenderMask( props: Props, cellsAroundViewport: {first: number, last: number}, + maintainVisibleContentPositionAdjustment: ?number, additionalRegions?: ?$ReadOnlyArray<{first: number, last: number}>, ): CellRenderMask { const itemCount = props.getItemCount(props.data); @@ -506,6 +559,16 @@ export default class VirtualizedList extends StateSafePureComponent< renderMask, cellsAroundViewport.first, ); + + if (maintainVisibleContentPositionAdjustment != null) { + renderMask.addCells({ + first: + cellsAroundViewport.first + + maintainVisibleContentPositionAdjustment, + last: + cellsAroundViewport.last + maintainVisibleContentPositionAdjustment, + }); + } } return renderMask; @@ -673,6 +736,21 @@ export default class VirtualizedList extends StateSafePureComponent< return prevState; } + let maintainVisibleContentPositionAdjustment = + prevState.maintainVisibleContentPositionAdjustment; + const newFirstItemKey = getItemKey(newProps, 0); + if ( + newProps.maintainVisibleContentPosition != null && + maintainVisibleContentPositionAdjustment == null && + prevState.firstItemKey != null && + newFirstItemKey != null + ) { + maintainVisibleContentPositionAdjustment = + newFirstItemKey !== prevState.firstItemKey + ? findItemIndexWithKey(newProps, prevState.firstItemKey) + : null; + } + const constrainedCells = VirtualizedList._constrainToItemCount( prevState.cellsAroundViewport, newProps, @@ -680,7 +758,13 @@ export default class VirtualizedList extends StateSafePureComponent< return { cellsAroundViewport: constrainedCells, - renderMask: VirtualizedList._createRenderMask(newProps, constrainedCells), + renderMask: VirtualizedList._createRenderMask( + newProps, + constrainedCells, + maintainVisibleContentPositionAdjustment, + ), + firstItemKey: newFirstItemKey, + maintainVisibleContentPositionAdjustment, }; } @@ -711,7 +795,7 @@ export default class VirtualizedList extends StateSafePureComponent< last = Math.min(end, last); for (let ii = first; ii <= last; ii++) { const item = getItem(data, ii); - const key = this._keyExtractor(item, ii, this.props); + const key = extractKey(this.props, item, ii); this._indicesToKeys.set(ii, key); if (stickyIndicesFromProps.has(ii + stickyOffset)) { stickyHeaderIndices.push(cells.length); @@ -780,29 +864,6 @@ export default class VirtualizedList extends StateSafePureComponent< _getSpacerKey = (isVertical: boolean): string => isVertical ? 'height' : 'width'; - _keyExtractor( - item: Item, - index: number, - props: { - keyExtractor?: ?(item: Item, index: number) => string, - ... - }, - // $FlowFixMe[missing-local-annot] - ) { - if (props.keyExtractor != null) { - return props.keyExtractor(item, index); - } - - const key = defaultKeyExtractor(item, index); - if (key === String(index)) { - _usedIndexForKey = true; - if (item.type && item.type.displayName) { - _keylessItemComponentName = item.type.displayName; - } - } - return key; - } - render(): React.Node { if (__DEV__) { const flatStyles = flattenStyle(this.props.contentContainerStyle); @@ -1223,6 +1284,7 @@ export default class VirtualizedList extends StateSafePureComponent< const renderMask = VirtualizedList._createRenderMask( this.props, this.state.cellsAroundViewport, + this.state.maintainVisibleContentPositionAdjustment, this._getNonViewportRenderRegions(this.props), ); @@ -1429,6 +1491,9 @@ export default class VirtualizedList extends StateSafePureComponent< onEndReachedThreshold, initialScrollIndex, } = this.props; + if (this.state.maintainVisibleContentPositionAdjustment != null) { + return; + } const {contentLength, visibleLength, offset} = this._scrollMetrics; let distanceFromStart = offset; let distanceFromEnd = contentLength - visibleLength - offset; @@ -1599,6 +1664,11 @@ export default class VirtualizedList extends StateSafePureComponent< visibleLength, zoomScale, }; + if (this.state.maintainVisibleContentPositionAdjustment != null) { + this.setState({ + maintainVisibleContentPositionAdjustment: null, + }); + } this._updateViewableItems(this.props, this.state.cellsAroundViewport); if (!this.props) { return; @@ -1612,6 +1682,9 @@ export default class VirtualizedList extends StateSafePureComponent< }; _scheduleCellsToRenderUpdate() { + if (this.state.maintainVisibleContentPositionAdjustment != null) { + return; + } const {first, last} = this.state.cellsAroundViewport; const {offset, visibleLength, velocity} = this._scrollMetrics; const itemCount = this.props.getItemCount(this.props.data); @@ -1718,6 +1791,7 @@ export default class VirtualizedList extends StateSafePureComponent< const renderMask = VirtualizedList._createRenderMask( props, cellsAroundViewport, + state.maintainVisibleContentPositionAdjustment, this._getNonViewportRenderRegions(props), ); @@ -1744,7 +1818,7 @@ export default class VirtualizedList extends StateSafePureComponent< return { index, item, - key: this._keyExtractor(item, index, props), + key: extractKey(props, item, index), isViewable, }; }; @@ -1811,7 +1885,8 @@ export default class VirtualizedList extends StateSafePureComponent< 'Tried to get frame for out of range index ' + index, ); const item = getItem(data, index); - const frame = item && this._frames[this._keyExtractor(item, index, props)]; + const frame = + item != null ? this._frames[extractKey(props, item, index)] : undefined; if (!frame || frame.index !== index) { if (getItemLayout) { /* $FlowFixMe[prop-missing] (>=0.63.0 site=react_native_fb) This comment diff --git a/packages/rn-tester/js/components/ListExampleShared.js b/packages/rn-tester/js/components/ListExampleShared.js index 1acad0e6f5a5..c970c6921bbc 100644 --- a/packages/rn-tester/js/components/ListExampleShared.js +++ b/packages/rn-tester/js/components/ListExampleShared.js @@ -13,6 +13,7 @@ const React = require('react'); const { + ActivityIndicator, Animated, Image, Platform, @@ -33,16 +34,28 @@ export type Item = { ... }; -function genItemData(count: number, start: number = 0): Array { +function genItemData(i): Item { + const itemHash = Math.abs(hashCode('Item ' + i)); + return { + title: 'Item ' + i, + text: LOREM_IPSUM.substr(0, (itemHash % 301) + 20), + key: String(i), + pressed: false, + }; +} + +function genNewerItems(count: number, start: number = 0): Array { + const dataBlob = []; + for (let i = start; i < count + start; i++) { + dataBlob.push(genItemData(i)); + } + return dataBlob; +} + +function genOlderItems(count: number, start: number = 0): Array { const dataBlob = []; - for (let ii = start; ii < count + start; ii++) { - const itemHash = Math.abs(hashCode('Item ' + ii)); - dataBlob.push({ - title: 'Item ' + ii, - text: LOREM_IPSUM.substr(0, (itemHash % 301) + 20), - key: String(ii), - pressed: false, - }); + for (let i = count; i > 0; i--) { + dataBlob.push(genItemData(start - i)); } return dataBlob; } @@ -147,6 +160,12 @@ class SeparatorComponent extends React.PureComponent<{...}> { } } +const LoadingComponent: React.ComponentType<{}> = React.memo(() => ( + + + +)); + class ItemSeparatorComponent extends React.PureComponent<$FlowFixMeProps> { render(): React.Node { const style = this.props.highlighted @@ -352,6 +371,13 @@ const styles = StyleSheet.create({ text: { flex: 1, }, + loadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 100, + borderTopWidth: 1, + borderTopColor: 'rgb(200, 199, 204)', + }, }); module.exports = { @@ -362,8 +388,10 @@ module.exports = { ItemSeparatorComponent, PlainInput, SeparatorComponent, + LoadingComponent, Spindicator, - genItemData, + genNewerItems, + genOlderItems, getItemLayout, pressItem, renderSmallSwitchOption, diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-basic.js b/packages/rn-tester/js/examples/FlatList/FlatList-basic.js index 64f2800a3554..f34616d7ac46 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-basic.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-basic.js @@ -35,8 +35,10 @@ import { ItemSeparatorComponent, PlainInput, SeparatorComponent, + LoadingComponent, Spindicator, - genItemData, + genNewerItems, + genOlderItems, getItemLayout, pressItem, renderSmallSwitchOption, @@ -44,6 +46,11 @@ import { import type {Item} from '../../components/ListExampleShared'; +const PAGE_SIZE = 100; +const NUM_PAGES = 10; +const INITIAL_PAGE_OFFSET = Math.floor(NUM_PAGES / 2); +const LOAD_TIME = 2000; + const VIEWABILITY_CONFIG = { minimumViewTime: 3000, viewAreaCoveragePercentThreshold: 100, @@ -53,6 +60,8 @@ const VIEWABILITY_CONFIG = { type Props = $ReadOnly<{||}>; type State = {| data: Array, + first: number, + last: number, debug: boolean, horizontal: boolean, inverted: boolean, @@ -66,13 +75,18 @@ type State = {| onPressDisabled: boolean, textSelectable: boolean, isRTL: boolean, + maintainVisibleContentPosition: boolean, + previousLoading: boolean, + nextLoading: boolean, |}; const IS_RTL = I18nManager.isRTL; class FlatListExample extends React.PureComponent { state: State = { - data: genItemData(100), + data: genNewerItems(PAGE_SIZE, PAGE_SIZE * INITIAL_PAGE_OFFSET), + first: PAGE_SIZE * INITIAL_PAGE_OFFSET, + last: PAGE_SIZE + PAGE_SIZE * INITIAL_PAGE_OFFSET, debug: false, horizontal: false, inverted: false, @@ -86,6 +100,9 @@ class FlatListExample extends React.PureComponent { onPressDisabled: false, textSelectable: true, isRTL: IS_RTL, + maintainVisibleContentPosition: true, + previousLoading: false, + nextLoading: false, }; /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's @@ -209,6 +226,11 @@ class FlatListExample extends React.PureComponent { this.state.isRTL, this._setIsRTL, )} + {renderSmallSwitchOption( + 'Maintain content position', + this.state.maintainVisibleContentPosition, + this._setBooleanValue('maintainVisibleContentPosition'), + )} {Platform.OS === 'android' && ( { } - ListFooterComponent={FooterComponent} + ListHeaderComponent={ + this.state.previousLoading ? LoadingComponent : HeaderComponent + } + ListFooterComponent={ + this.state.nextLoading ? LoadingComponent : FooterComponent + } ListEmptyComponent={ListEmptyComponent} data={this.state.empty ? [] : filteredData} debug={this.state.debug} @@ -249,6 +275,8 @@ class FlatListExample extends React.PureComponent { keyboardShouldPersistTaps="always" keyboardDismissMode="on-drag" numColumns={1} + onStartReached={this._onStartReached} + initialScrollIndex={Math.floor(PAGE_SIZE / 2)} onEndReached={this._onEndReached} onRefresh={this._onRefresh} onScroll={ @@ -259,6 +287,11 @@ class FlatListExample extends React.PureComponent { refreshing={false} contentContainerStyle={styles.list} viewabilityConfig={VIEWABILITY_CONFIG} + maintainVisibleContentPosition={ + this.state.maintainVisibleContentPosition + ? {minIndexForVisible: 2} + : undefined + } {...flatListItemRendererProps} /> @@ -279,13 +312,33 @@ class FlatListExample extends React.PureComponent { _getItemLayout = (data: any, index: number) => { return getItemLayout(data, index, this.state.horizontal); }; + _onStartReached = () => { + if (this.state.first <= 0 || this.state.previousLoading) { + return; + } + + this.setState({previousLoading: true}); + setTimeout(() => { + this.setState(state => ({ + previousLoading: false, + data: genOlderItems(PAGE_SIZE, state.first).concat(state.data), + first: state.first - PAGE_SIZE, + })); + }, LOAD_TIME); + }; _onEndReached = () => { - if (this.state.data.length >= 1000) { + if (this.state.last >= PAGE_SIZE * NUM_PAGES || this.state.nextLoading) { return; } - this.setState(state => ({ - data: state.data.concat(genItemData(100, state.data.length)), - })); + + this.setState({nextLoading: true}); + setTimeout(() => { + this.setState(state => ({ + nextLoading: false, + data: state.data.concat(genNewerItems(PAGE_SIZE, state.last)), + last: state.last + PAGE_SIZE, + })); + }, LOAD_TIME); }; // $FlowFixMe[missing-local-annot] _onPressCallback = () => { @@ -342,7 +395,7 @@ class FlatListExample extends React.PureComponent { _pressItem = (key: string) => { this._listRef?.recordInteraction(); - const index = Number(key); + const index = this.state.data.findIndex(item => item.key === key); const itemState = pressItem(this.state.data[index]); this.setState(state => ({ ...state, diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-multiColumn.js b/packages/rn-tester/js/examples/FlatList/FlatList-multiColumn.js index c92c688840fc..3d8c8256dccb 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-multiColumn.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-multiColumn.js @@ -23,7 +23,7 @@ const { ItemComponent, PlainInput, SeparatorComponent, - genItemData, + genNewerItems, getItemLayout, pressItem, renderSmallSwitchOption, @@ -46,7 +46,7 @@ class MultiColumnExample extends React.PureComponent< numColumns: number, virtualized: boolean, |} = { - data: genItemData(1000), + data: genNewerItems(1000), filterText: '', fixedHeight: true, logViewable: false, diff --git a/packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js b/packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js index aa8ea52ec989..58e3c2f5ca1f 100644 --- a/packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js +++ b/packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js @@ -22,7 +22,7 @@ const { PlainInput, SeparatorComponent, Spindicator, - genItemData, + genNewerItems, pressItem, renderSmallSwitchOption, renderStackedItem, @@ -170,7 +170,7 @@ export function SectionList_scrollable(Props: { const [logViewable, setLogViewable] = React.useState(false); const [debug, setDebug] = React.useState(false); const [inverted, setInverted] = React.useState(false); - const [data, setData] = React.useState(genItemData(1000)); + const [data, setData] = React.useState(genNewerItems(1000)); const filterRegex = new RegExp(String(filterText), 'i'); const filter = (item: Item) => From 2028fb8332a58b020ed8a07ae1d9ce081c5eb470 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 5 Oct 2022 18:24:14 -0400 Subject: [PATCH 3/5] Add fabric support for maintainVisibleContentPosition on iOS --- .../ScrollView/RCTScrollViewComponentView.mm | 96 ++ .../components/scrollview/ScrollViewProps.cpp | 14 + .../components/scrollview/ScrollViewProps.h | 3 + .../components/scrollview/conversions.h | 35 + .../components/scrollview/primitives.h | 18 + .../renderer/debug/DebugStringConvertible.h | 9 + packages/rn-tester/Podfile.lock | 1208 ++++++++--------- .../RNTesterPods.xcodeproj/project.pbxproj | 4 +- .../examples/ScrollView/ScrollViewExample.js | 2 +- 9 files changed, 782 insertions(+), 607 deletions(-) diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 2ebbb0e82ac9..a9638f650a91 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -23,6 +23,7 @@ #import "RCTConversions.h" #import "RCTEnhancedScrollView.h" #import "RCTFabricComponentsPlugins.h" +#import "RCTPullToRefreshViewComponentView.h" using namespace facebook::react; @@ -100,6 +101,11 @@ @implementation RCTScrollViewComponentView { BOOL _shouldUpdateContentInsetAdjustmentBehavior; CGPoint _contentOffsetWhenClipped; + + __weak UIView *_contentView; + + CGRect _prevFirstVisibleFrame; + __weak UIView *_firstVisibleView; } + (RCTScrollViewComponentView *_Nullable)findScrollViewComponentViewForView:(UIView *)view @@ -149,10 +155,17 @@ - (void)dealloc #pragma mark - RCTMountingTransactionObserving +- (void)mountingTransactionWillMount:(const facebook::react::MountingTransaction &)transaction + withSurfaceTelemetry:(const facebook::react::SurfaceTelemetry &)surfaceTelemetry +{ + [self _prepareForMaintainVisibleScrollPosition]; +} + - (void)mountingTransactionDidMount:(MountingTransaction const &)transaction withSurfaceTelemetry:(facebook::react::SurfaceTelemetry const &)surfaceTelemetry { [self _remountChildren]; + [self _adjustForMaintainVisibleContentPosition]; } #pragma mark - RCTComponentViewProtocol @@ -337,11 +350,23 @@ - (void)_preserveContentOffsetIfNeededWithBlock:(void (^)())block - (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index { [_containerView insertSubview:childComponentView atIndex:index]; + if ([childComponentView isKindOfClass:RCTPullToRefreshViewComponentView.class]) { + // Ignore the pull to refresh component. + } else { + RCTAssert(_contentView == nil, @"RCTScrollView may only contain a single subview."); + _contentView = childComponentView; + } } - (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index { [childComponentView removeFromSuperview]; + if ([childComponentView isKindOfClass:RCTPullToRefreshViewComponentView.class]) { + // Ignore the pull to refresh component. + } else { + RCTAssert(_contentView == childComponentView, @"Attempted to remove non-existent subview"); + _contentView = nil; + } } /* @@ -404,6 +429,9 @@ - (void)prepareForRecycle CGRect oldFrame = self.frame; self.frame = CGRectZero; self.frame = oldFrame; + _contentView = nil; + _prevFirstVisibleFrame = CGRectZero; + _firstVisibleView = nil; [super prepareForRecycle]; } @@ -684,6 +712,74 @@ - (void)removeScrollListener:(NSObject *)scrollListener [self.scrollViewDelegateSplitter removeDelegate:scrollListener]; } +#pragma mark - Maintain visible content position + +- (void)_prepareForMaintainVisibleScrollPosition +{ + const auto &props = *std::static_pointer_cast(_props); + if (!props.maintainVisibleContentPosition) { + return; + } + + BOOL horizontal = _scrollView.contentSize.width > self.frame.size.width; + int minIdx = props.maintainVisibleContentPosition.value().minIndexForVisible; + for (NSUInteger ii = minIdx; ii < _contentView.subviews.count; ++ii) { + // Find the first entirely visible view. + UIView *subview = _contentView.subviews[ii]; + BOOL hasNewView = NO; + if (horizontal) { + hasNewView = subview.frame.origin.x > _scrollView.contentOffset.x; + } else { + hasNewView = subview.frame.origin.y > _scrollView.contentOffset.y; + } + if (hasNewView || ii == _contentView.subviews.count - 1) { + _prevFirstVisibleFrame = subview.frame; + _firstVisibleView = subview; + break; + } + } +} + +- (void)_adjustForMaintainVisibleContentPosition +{ + const auto &props = *std::static_pointer_cast(_props); + if (!props.maintainVisibleContentPosition) { + return; + } + + std::optional autoscrollThreshold = props.maintainVisibleContentPosition.value().autoscrollToTopThreshold; + BOOL horizontal = _scrollView.contentSize.width > self.frame.size.width; + // TODO: detect and handle/ignore re-ordering + if (horizontal) { + CGFloat deltaX = _firstVisibleView.frame.origin.x - _prevFirstVisibleFrame.origin.x; + if (ABS(deltaX) > 0.5) { + CGFloat x = _scrollView.contentOffset.x; + [self _forceDispatchNextScrollEvent]; + _scrollView.contentOffset = CGPointMake(_scrollView.contentOffset.x + deltaX, _scrollView.contentOffset.y); + if (autoscrollThreshold) { + // If the offset WAS within the threshold of the start, animate to the start. + if (x <= autoscrollThreshold.value()) { + [self scrollToOffset:CGPointMake(0, _scrollView.contentOffset.y) animated:YES]; + } + } + } + } else { + CGRect newFrame = _firstVisibleView.frame; + CGFloat deltaY = newFrame.origin.y - _prevFirstVisibleFrame.origin.y; + if (ABS(deltaY) > 0.5) { + CGFloat y = _scrollView.contentOffset.y; + [self _forceDispatchNextScrollEvent]; + _scrollView.contentOffset = CGPointMake(_scrollView.contentOffset.x, _scrollView.contentOffset.y + deltaY); + if (autoscrollThreshold) { + // If the offset WAS within the threshold of the start, animate to the start. + if (y <= autoscrollThreshold.value()) { + [self scrollToOffset:CGPointMake(_scrollView.contentOffset.x, 0) animated:YES]; + } + } + } + } +} + @end Class RCTScrollViewCls(void) diff --git a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp index 4eff278fdfc1..18fda68ec695 100644 --- a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp +++ b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp @@ -127,6 +127,15 @@ ScrollViewProps::ScrollViewProps( "keyboardDismissMode", sourceProps.keyboardDismissMode, {})), + maintainVisibleContentPosition( + CoreFeatures::enablePropIteratorSetter + ? sourceProps.maintainVisibleContentPosition + : convertRawProp( + context, + rawProps, + "maintainVisibleContentPosition", + sourceProps.maintainVisibleContentPosition, + {})), maximumZoomScale( CoreFeatures::enablePropIteratorSetter ? sourceProps.maximumZoomScale @@ -336,6 +345,7 @@ void ScrollViewProps::setProp( RAW_SET_PROP_SWITCH_CASE_BASIC(directionalLockEnabled, {}); RAW_SET_PROP_SWITCH_CASE_BASIC(indicatorStyle, {}); RAW_SET_PROP_SWITCH_CASE_BASIC(keyboardDismissMode, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(maintainVisibleContentPosition, {}); RAW_SET_PROP_SWITCH_CASE_BASIC(maximumZoomScale, (Float)1.0); RAW_SET_PROP_SWITCH_CASE_BASIC(minimumZoomScale, (Float)1.0); RAW_SET_PROP_SWITCH_CASE_BASIC(scrollEnabled, true); @@ -413,6 +423,10 @@ SharedDebugStringConvertibleList ScrollViewProps::getDebugProps() const { "keyboardDismissMode", keyboardDismissMode, defaultScrollViewProps.keyboardDismissMode), + debugStringConvertibleItem( + "maintainVisibleContentPosition", + maintainVisibleContentPosition, + defaultScrollViewProps.maintainVisibleContentPosition), debugStringConvertibleItem( "maximumZoomScale", maximumZoomScale, diff --git a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h index bd53bc4bb22d..3e30c30cfe4b 100644 --- a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h +++ b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h @@ -11,6 +11,8 @@ #include #include +#include + namespace facebook { namespace react { @@ -43,6 +45,7 @@ class ScrollViewProps final : public ViewProps { bool directionalLockEnabled{}; ScrollViewIndicatorStyle indicatorStyle{}; ScrollViewKeyboardDismissMode keyboardDismissMode{}; + std::optional maintainVisibleContentPosition{}; Float maximumZoomScale{1.0f}; Float minimumZoomScale{1.0f}; bool scrollEnabled{true}; diff --git a/ReactCommon/react/renderer/components/scrollview/conversions.h b/ReactCommon/react/renderer/components/scrollview/conversions.h index 4605f08ea203..97da851fdb82 100644 --- a/ReactCommon/react/renderer/components/scrollview/conversions.h +++ b/ReactCommon/react/renderer/components/scrollview/conversions.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -98,6 +99,28 @@ inline void fromRawValue( abort(); } +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + ScrollViewMaintainVisibleContentPosition &result) { + auto map = (butter::map)value; + + auto minIndexForVisible = map.find("minIndexForVisible"); + if (minIndexForVisible != map.end()) { + fromRawValue( + context, minIndexForVisible->second, result.minIndexForVisible); + } + auto autoscrollToTopThreshold = map.find("autoscrollToTopThreshold"); + if (autoscrollToTopThreshold != map.end()) { + fromRawValue( + context, + autoscrollToTopThreshold->second, + result.autoscrollToTopThreshold); + } +} + +#if RN_DEBUG_STRING_CONVERTIBLE + inline std::string toString(const ScrollViewSnapToAlignment &value) { switch (value) { case ScrollViewSnapToAlignment::Start: @@ -144,5 +167,17 @@ inline std::string toString(const ContentInsetAdjustmentBehavior &value) { } } +inline std::string toString( + const std::optional &value) { + if (!value) { + return "null"; + } + return "{minIndexForVisible: " + toString(value.value().minIndexForVisible) + + ", autoscrollToTopThreshold: " + + toString(value.value().autoscrollToTopThreshold) + "}"; +} + +#endif + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/components/scrollview/primitives.h b/ReactCommon/react/renderer/components/scrollview/primitives.h index fe8a60e21d7c..f05f1125c3d0 100644 --- a/ReactCommon/react/renderer/components/scrollview/primitives.h +++ b/ReactCommon/react/renderer/components/scrollview/primitives.h @@ -7,6 +7,9 @@ #pragma once +#include +#include + namespace facebook { namespace react { @@ -23,5 +26,20 @@ enum class ContentInsetAdjustmentBehavior { Always }; +class ScrollViewMaintainVisibleContentPosition final { + public: + int minIndexForVisible{0}; + std::optional autoscrollToTopThreshold{}; + + bool operator==(const ScrollViewMaintainVisibleContentPosition &rhs) const { + return std::tie(this->minIndexForVisible, this->autoscrollToTopThreshold) == + std::tie(rhs.minIndexForVisible, rhs.autoscrollToTopThreshold); + } + + bool operator!=(const ScrollViewMaintainVisibleContentPosition &rhs) const { + return !(*this == rhs); + } +}; + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/debug/DebugStringConvertible.h b/ReactCommon/react/renderer/debug/DebugStringConvertible.h index a9a1ef02b4e3..7df17f01e39a 100644 --- a/ReactCommon/react/renderer/debug/DebugStringConvertible.h +++ b/ReactCommon/react/renderer/debug/DebugStringConvertible.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,14 @@ std::string toString(float const &value); std::string toString(double const &value); std::string toString(void const *value); +template +std::string toString(const std::optional &value) { + if (!value) { + return "null"; + } + return toString(value.value()); +} + /* * *Informal* `DebugStringConvertible` interface. * diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index db2169f72cc9..e8be0a701678 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -2,14 +2,14 @@ PODS: - boost (1.76.0) - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - - FBLazyVector (1000.0.0) - - FBReactNativeSpec (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-Core (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) + - FBLazyVector (0.71.0-rc.0) + - FBReactNativeSpec (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Core (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) - Flipper (0.125.0): - Flipper-Folly (~> 2.6) - Flipper-RSocket (~> 1.4) @@ -104,650 +104,650 @@ PODS: - fmt (~> 6.2.1) - glog - libevent - - RCTRequired (1000.0.0) - - RCTTypeSafety (1000.0.0): - - FBLazyVector (= 1000.0.0) - - RCTRequired (= 1000.0.0) - - React-Core (= 1000.0.0) - - React (1000.0.0): - - React-Core (= 1000.0.0) - - React-Core/DevSupport (= 1000.0.0) - - React-Core/RCTWebSocket (= 1000.0.0) - - React-RCTActionSheet (= 1000.0.0) - - React-RCTAnimation (= 1000.0.0) - - React-RCTBlob (= 1000.0.0) - - React-RCTImage (= 1000.0.0) - - React-RCTLinking (= 1000.0.0) - - React-RCTNetwork (= 1000.0.0) - - React-RCTSettings (= 1000.0.0) - - React-RCTText (= 1000.0.0) - - React-RCTVibration (= 1000.0.0) - - React-bridging (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - React-jsi (= 1000.0.0) - - React-callinvoker (1000.0.0) - - React-Codegen (1000.0.0): - - FBReactNativeSpec (= 1000.0.0) - - hermes-engine (= 1000.0.0) - - RCT-Folly (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-Core (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-rncore (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Core (1000.0.0): + - RCTRequired (0.71.0-rc.0) + - RCTTypeSafety (0.71.0-rc.0): + - FBLazyVector (= 0.71.0-rc.0) + - RCTRequired (= 0.71.0-rc.0) + - React-Core (= 0.71.0-rc.0) + - React (0.71.0-rc.0): + - React-Core (= 0.71.0-rc.0) + - React-Core/DevSupport (= 0.71.0-rc.0) + - React-Core/RCTWebSocket (= 0.71.0-rc.0) + - React-RCTActionSheet (= 0.71.0-rc.0) + - React-RCTAnimation (= 0.71.0-rc.0) + - React-RCTBlob (= 0.71.0-rc.0) + - React-RCTImage (= 0.71.0-rc.0) + - React-RCTLinking (= 0.71.0-rc.0) + - React-RCTNetwork (= 0.71.0-rc.0) + - React-RCTSettings (= 0.71.0-rc.0) + - React-RCTText (= 0.71.0-rc.0) + - React-RCTVibration (= 0.71.0-rc.0) + - React-bridging (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - React-jsi (= 0.71.0-rc.0) + - React-callinvoker (0.71.0-rc.0) + - React-Codegen (0.71.0-rc.0): + - FBReactNativeSpec (= 0.71.0-rc.0) + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Core (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-rncore (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Core (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-Core/Default (= 0.71.0-rc.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/CoreModulesHeaders (1000.0.0): + - React-Core/CoreModulesHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/Default (1000.0.0): + - React-Core/Default (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/DevSupport (1000.0.0): + - React-Core/DevSupport (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 1000.0.0) - - React-Core/RCTWebSocket (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-jsinspector (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-Core/Default (= 0.71.0-rc.0) + - React-Core/RCTWebSocket (= 0.71.0-rc.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-jsinspector (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTActionSheetHeaders (1000.0.0): + - React-Core/RCTActionSheetHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTAnimationHeaders (1000.0.0): + - React-Core/RCTAnimationHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTBlobHeaders (1000.0.0): + - React-Core/RCTBlobHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTImageHeaders (1000.0.0): + - React-Core/RCTImageHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTLinkingHeaders (1000.0.0): + - React-Core/RCTLinkingHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTNetworkHeaders (1000.0.0): + - React-Core/RCTNetworkHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTPushNotificationHeaders (1000.0.0): + - React-Core/RCTPushNotificationHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTSettingsHeaders (1000.0.0): + - React-Core/RCTSettingsHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTTextHeaders (1000.0.0): + - React-Core/RCTTextHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTVibrationHeaders (1000.0.0): + - React-Core/RCTVibrationHeaders (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-Core/RCTWebSocket (1000.0.0): + - React-Core/RCTWebSocket (0.71.0-rc.0): - glog - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-perflogger (= 1000.0.0) + - React-Core/Default (= 0.71.0-rc.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) - Yoga - - React-CoreModules (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 1000.0.0) - - React-Codegen (= 1000.0.0) - - React-Core/CoreModulesHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-RCTImage (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-cxxreact (1000.0.0): + - React-CoreModules (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/CoreModulesHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-RCTImage (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-cxxreact (0.71.0-rc.0): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.07.22.00) - - React-callinvoker (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsinspector (= 1000.0.0) - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - React-runtimeexecutor (= 1000.0.0) - - React-Fabric (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-Fabric/animations (= 1000.0.0) - - React-Fabric/attributedstring (= 1000.0.0) - - React-Fabric/butter (= 1000.0.0) - - React-Fabric/componentregistry (= 1000.0.0) - - React-Fabric/componentregistrynative (= 1000.0.0) - - React-Fabric/components (= 1000.0.0) - - React-Fabric/config (= 1000.0.0) - - React-Fabric/core (= 1000.0.0) - - React-Fabric/debug_core (= 1000.0.0) - - React-Fabric/debug_renderer (= 1000.0.0) - - React-Fabric/imagemanager (= 1000.0.0) - - React-Fabric/leakchecker (= 1000.0.0) - - React-Fabric/mapbuffer (= 1000.0.0) - - React-Fabric/mounting (= 1000.0.0) - - React-Fabric/runtimescheduler (= 1000.0.0) - - React-Fabric/scheduler (= 1000.0.0) - - React-Fabric/telemetry (= 1000.0.0) - - React-Fabric/templateprocessor (= 1000.0.0) - - React-Fabric/textlayoutmanager (= 1000.0.0) - - React-Fabric/uimanager (= 1000.0.0) - - React-Fabric/utils (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/animations (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/attributedstring (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/butter (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/componentregistry (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/componentregistrynative (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-Fabric/components/activityindicator (= 1000.0.0) - - React-Fabric/components/image (= 1000.0.0) - - React-Fabric/components/inputaccessory (= 1000.0.0) - - React-Fabric/components/legacyviewmanagerinterop (= 1000.0.0) - - React-Fabric/components/modal (= 1000.0.0) - - React-Fabric/components/root (= 1000.0.0) - - React-Fabric/components/safeareaview (= 1000.0.0) - - React-Fabric/components/scrollview (= 1000.0.0) - - React-Fabric/components/slider (= 1000.0.0) - - React-Fabric/components/text (= 1000.0.0) - - React-Fabric/components/textinput (= 1000.0.0) - - React-Fabric/components/unimplementedview (= 1000.0.0) - - React-Fabric/components/view (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/activityindicator (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/image (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/inputaccessory (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/legacyviewmanagerinterop (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/modal (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/root (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/safeareaview (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/scrollview (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/slider (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/text (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/textinput (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/unimplementedview (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/components/view (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) + - React-callinvoker (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsinspector (= 0.71.0-rc.0) + - React-logger (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) + - React-runtimeexecutor (= 0.71.0-rc.0) + - React-Fabric (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Fabric/animations (= 0.71.0-rc.0) + - React-Fabric/attributedstring (= 0.71.0-rc.0) + - React-Fabric/butter (= 0.71.0-rc.0) + - React-Fabric/componentregistry (= 0.71.0-rc.0) + - React-Fabric/componentregistrynative (= 0.71.0-rc.0) + - React-Fabric/components (= 0.71.0-rc.0) + - React-Fabric/config (= 0.71.0-rc.0) + - React-Fabric/core (= 0.71.0-rc.0) + - React-Fabric/debug_core (= 0.71.0-rc.0) + - React-Fabric/debug_renderer (= 0.71.0-rc.0) + - React-Fabric/imagemanager (= 0.71.0-rc.0) + - React-Fabric/leakchecker (= 0.71.0-rc.0) + - React-Fabric/mapbuffer (= 0.71.0-rc.0) + - React-Fabric/mounting (= 0.71.0-rc.0) + - React-Fabric/runtimescheduler (= 0.71.0-rc.0) + - React-Fabric/scheduler (= 0.71.0-rc.0) + - React-Fabric/telemetry (= 0.71.0-rc.0) + - React-Fabric/templateprocessor (= 0.71.0-rc.0) + - React-Fabric/textlayoutmanager (= 0.71.0-rc.0) + - React-Fabric/uimanager (= 0.71.0-rc.0) + - React-Fabric/utils (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/animations (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/attributedstring (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/butter (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/componentregistry (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/componentregistrynative (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Fabric/components/activityindicator (= 0.71.0-rc.0) + - React-Fabric/components/image (= 0.71.0-rc.0) + - React-Fabric/components/inputaccessory (= 0.71.0-rc.0) + - React-Fabric/components/legacyviewmanagerinterop (= 0.71.0-rc.0) + - React-Fabric/components/modal (= 0.71.0-rc.0) + - React-Fabric/components/root (= 0.71.0-rc.0) + - React-Fabric/components/safeareaview (= 0.71.0-rc.0) + - React-Fabric/components/scrollview (= 0.71.0-rc.0) + - React-Fabric/components/slider (= 0.71.0-rc.0) + - React-Fabric/components/text (= 0.71.0-rc.0) + - React-Fabric/components/textinput (= 0.71.0-rc.0) + - React-Fabric/components/unimplementedview (= 0.71.0-rc.0) + - React-Fabric/components/view (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/activityindicator (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/image (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/inputaccessory (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/legacyviewmanagerinterop (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/modal (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/root (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/safeareaview (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/scrollview (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/slider (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/text (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/textinput (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/unimplementedview (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/components/view (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) - Yoga - - React-Fabric/config (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/core (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsidynamic (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/debug_core (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/debug_renderer (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/imagemanager (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-RCTImage (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/leakchecker (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/mapbuffer (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/mounting (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/runtimescheduler (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/scheduler (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/telemetry (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/templateprocessor (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/textlayoutmanager (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) + - React-Fabric/config (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/core (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsidynamic (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/debug_core (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/debug_renderer (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/imagemanager (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-RCTImage (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/leakchecker (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/mapbuffer (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/mounting (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/runtimescheduler (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/scheduler (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/telemetry (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/templateprocessor (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/textlayoutmanager (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) - React-Fabric/uimanager - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/uimanager (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsidynamic (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-Fabric/utils (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-graphics (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-graphics (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - React-Core/Default (= 1000.0.0) - - React-hermes (1000.0.0): + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/uimanager (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsidynamic (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-Fabric/utils (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - RCTRequired (= 0.71.0-rc.0) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-graphics (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-graphics (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - React-Core/Default (= 0.71.0-rc.0) + - React-hermes (0.71.0-rc.0): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - RCT-Folly/Futures (= 2021.07.22.00) - - React-cxxreact (= 1000.0.0) - - React-jsidynamic (= 1000.0.0) - - React-jsiexecutor (= 1000.0.0) - - React-jsinspector (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - React-jsi (1000.0.0): + - React-cxxreact (= 0.71.0-rc.0) + - React-jsidynamic (= 0.71.0-rc.0) + - React-jsiexecutor (= 0.71.0-rc.0) + - React-jsinspector (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) + - React-jsi (0.71.0-rc.0): - hermes-engine - - React-jsidynamic (1000.0.0): + - React-jsidynamic (0.71.0-rc.0): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.07.22.00) - - React-jsi (= 1000.0.0) - - React-jsiexecutor (1000.0.0): + - React-jsi (= 0.71.0-rc.0) + - React-jsiexecutor (0.71.0-rc.0): - DoubleConversion - glog - RCT-Folly (= 2021.07.22.00) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsidynamic (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - React-jsinspector (1000.0.0) - - React-logger (1000.0.0): + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsidynamic (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) + - React-jsinspector (0.71.0-rc.0) + - React-logger (0.71.0-rc.0): - glog - - React-perflogger (1000.0.0) - - React-RCTActionSheet (1000.0.0): - - React-Core/RCTActionSheetHeaders (= 1000.0.0) - - React-RCTAnimation (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 1000.0.0) - - React-Codegen (= 1000.0.0) - - React-Core/RCTAnimationHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTAppDelegate (1000.0.0): + - React-perflogger (0.71.0-rc.0) + - React-RCTActionSheet (0.71.0-rc.0): + - React-Core/RCTActionSheetHeaders (= 0.71.0-rc.0) + - React-RCTAnimation (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTAnimationHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTAppDelegate (0.71.0-rc.0): - RCT-Folly - RCTRequired - RCTTypeSafety - React-Core - ReactCommon/turbomodule/core - - React-RCTBlob (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - React-Codegen (= 1000.0.0) - - React-Core/RCTBlobHeaders (= 1000.0.0) - - React-Core/RCTWebSocket (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-RCTNetwork (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTFabric (1000.0.0): - - RCT-Folly/Fabric (= 2021.07.22.00) - - React-Core (= 1000.0.0) - - React-Fabric (= 1000.0.0) - - React-RCTImage (= 1000.0.0) - - React-RCTImage (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 1000.0.0) - - React-Codegen (= 1000.0.0) - - React-Core/RCTImageHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-RCTNetwork (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTLinking (1000.0.0): - - React-Codegen (= 1000.0.0) - - React-Core/RCTLinkingHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTNetwork (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 1000.0.0) - - React-Codegen (= 1000.0.0) - - React-Core/RCTNetworkHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTPushNotification (1000.0.0): - - RCTTypeSafety (= 1000.0.0) - - React-Codegen (= 1000.0.0) - - React-Core/RCTPushNotificationHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTSettings (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 1000.0.0) - - React-Codegen (= 1000.0.0) - - React-Core/RCTSettingsHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTTest (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - React-Core (= 1000.0.0) - - React-CoreModules (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-RCTText (1000.0.0): - - React-Core/RCTTextHeaders (= 1000.0.0) - - React-RCTVibration (1000.0.0): - - RCT-Folly (= 2021.07.22.00) - - React-Codegen (= 1000.0.0) - - React-Core/RCTVibrationHeaders (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - - React-rncore (1000.0.0) - - React-runtimeexecutor (1000.0.0): - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (1000.0.0): + - React-RCTBlob (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTBlobHeaders (= 0.71.0-rc.0) + - React-Core/RCTWebSocket (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-RCTNetwork (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTFabric (0.71.0-rc.0): + - RCT-Folly/Fabric (= 2021.07.22.00) + - React-Core (= 0.71.0-rc.0) + - React-Fabric (= 0.71.0-rc.0) + - React-RCTImage (= 0.71.0-rc.0) + - React-RCTImage (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTImageHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-RCTNetwork (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTLinking (0.71.0-rc.0): + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTLinkingHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTNetwork (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTNetworkHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTPushNotification (0.71.0-rc.0): + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTPushNotificationHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTSettings (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.0-rc.0) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTSettingsHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTTest (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - React-Core (= 0.71.0-rc.0) + - React-CoreModules (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-RCTText (0.71.0-rc.0): + - React-Core/RCTTextHeaders (= 0.71.0-rc.0) + - React-RCTVibration (0.71.0-rc.0): + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.71.0-rc.0) + - React-Core/RCTVibrationHeaders (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) + - React-rncore (0.71.0-rc.0) + - React-runtimeexecutor (0.71.0-rc.0): + - React-jsi (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (0.71.0-rc.0): - DoubleConversion - glog - RCT-Folly (= 2021.07.22.00) - - React-bridging (= 1000.0.0) - - React-callinvoker (= 1000.0.0) - - React-Core (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-jsidynamic (= 1000.0.0) - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - ReactCommon/turbomodule/samples (1000.0.0): + - React-bridging (= 0.71.0-rc.0) + - React-callinvoker (= 0.71.0-rc.0) + - React-Core (= 0.71.0-rc.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-jsidynamic (= 0.71.0-rc.0) + - React-logger (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) + - ReactCommon/turbomodule/samples (0.71.0-rc.0): - DoubleConversion - glog - RCT-Folly (= 2021.07.22.00) - - React-bridging (= 1000.0.0) - - React-callinvoker (= 1000.0.0) - - React-Core (= 1000.0.0) - - React-cxxreact (= 1000.0.0) - - React-jsi (= 1000.0.0) - - React-logger (= 1000.0.0) - - React-perflogger (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) + - React-bridging (= 0.71.0-rc.0) + - React-callinvoker (= 0.71.0-rc.0) + - React-Core (= 0.71.0-rc.0) + - React-cxxreact (= 0.71.0-rc.0) + - React-jsi (= 0.71.0-rc.0) + - React-logger (= 0.71.0-rc.0) + - React-perflogger (= 0.71.0-rc.0) + - ReactCommon/turbomodule/core (= 0.71.0-rc.0) - ScreenshotManager (0.0.1): - RCT-Folly (= 2021.07.22.00) - React-Core @@ -938,8 +938,8 @@ SPEC CHECKSUMS: boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 - FBLazyVector: 19e408e76fa9258dd32191a50d60c41444f52d29 - FBReactNativeSpec: 9761d52cf2d3727e2557fbf4014c514909d76b6b + FBLazyVector: a96ec3f59b8c90499d34335be5009ff98173c820 + FBReactNativeSpec: cc510e23a28b11ed502454de6acae06e90006deb Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 @@ -951,47 +951,47 @@ SPEC CHECKSUMS: FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b - hermes-engine: 445a2267b04cb39ca4a0b2d6758b5a0e5a58ccad + hermes-engine: 3f5d2c36c59dea8cefb53ecb075ec4d9627cb740 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 - RCTRequired: 1c8808cf84569265784a6c33984bbb506ada8c6e - RCTTypeSafety: b6dcb5036a808864ee8cad66ca15f263c24661cc - React: 8d809d414723bb5763093ddec7658066a21ccabc - React-bridging: c8806159f8ef90f27443857eed1efdb8c85940e1 - React-callinvoker: 5f16202ad4e45f0607b1fae0f6955a8f7c87eef1 - React-Codegen: d2434d5e4d238bceef25f40c4f58b199eb981ad0 - React-Core: 3965263aa4b4e1ebf7b4fdb50d2f49ce7bf28f63 - React-CoreModules: 675170bccf156da3a3348e04e2036ce401b2010d - React-cxxreact: ebed982230716c3515ab2f435cb13aec8a56af02 - React-Fabric: 141459e61c825acf02d26ece099acbd9cbd87b99 - React-graphics: 2dda97baebb0082bb85499c862c3f269a194f416 - React-hermes: 4912383b4f062173cb623e570ead70ab380f7bef - React-jsi: c24dbcfdf7ea075138b73372387c7f17c0db56ef - React-jsidynamic: 2b14ac1b6d3a1b7daa1e5a424b98de87da981698 - React-jsiexecutor: 14e899380e3fe9ca74c4e19727540a03e7574721 - React-jsinspector: 7733dd522d044aef87caa39f3eda77593358a7eb - React-logger: a2b4f0f204e45e25f493ca4ce245a336217c923e - React-perflogger: c4fdd48988c2d3047186fc1bc1772d634cfca2ea - React-RCTActionSheet: 166fd1df85ac10219466b45d12a5884d3eaceac1 - React-RCTAnimation: d6127046c6bb44bd3e67b7503c4ad7f91131b58e - React-RCTAppDelegate: e427b692bf829e40f9b318e2a29dca2ab5f36cf6 - React-RCTBlob: 68675c89ebe6edf310dddd0774ba07b685f090a9 - React-RCTFabric: a98a6effece6719669b8c6b4d2c33fb0edddc613 - React-RCTImage: 6de9f0f4402af859849e97cc73a56a52f400f4c9 - React-RCTLinking: 21bb4675c3ec8fe704672ea511e817aeb3bf8370 - React-RCTNetwork: a865deadacbf6b3d863f0496e7d2c2e81c269317 - React-RCTPushNotification: 7f678a88147254ede5d21a1e1e71e8a964dd0051 - React-RCTSettings: 23ce1aa52ddf5db44c973bb5cc93713e871e09b6 - React-RCTTest: be92171ef0a1818f96324eac3be0356f4fa08844 - React-RCTText: a861fbf2835299d3cc4189697cddd8bd8602afb9 - React-RCTVibration: 0386f50996a153b3f39cecbe7d139763ac9a9fdf - React-rncore: 665c70690f404bbfa3948148de72689672a906d2 - React-runtimeexecutor: 97dca9247f4d3cfe0733384b189c6930fbd402b7 - ReactCommon: b1f213aa09e3dfd0a89389b5023fdb1cd6528e96 - ScreenshotManager: 06cb3d1794c8082d92b3e91813d1678d0977a4fb + RCTRequired: 2e752d0602620b58ca34b8fdb206c5ea2299a1b6 + RCTTypeSafety: 043f37f7679d28a42e48391a88a6baa665aaa875 + React: b880ae6be1584cb9dc58a5833c692b9fc6645767 + React-bridging: 932a3cad7cfaea6792efc6ed565578ca3f94818b + React-callinvoker: e364d27799930b492e3197b3e9aae472f861aa65 + React-Codegen: f9ea818b18e56e5223c17aa0d220e33d409a99b8 + React-Core: bf34ffba62b57bc481578c2e381ccb8084823a8c + React-CoreModules: 08072bc4fc72d77d6f6476642a0976068f8a3f91 + React-cxxreact: 4d9cdb74e6f9156b5c63a58bc8cbc22b6b718332 + React-Fabric: 90859ecf3b1b93fa00d39695e1677e099f14b672 + React-graphics: c32f3e87a5dd23b3d653d57e2972d170e48900ee + React-hermes: 76843a3f2613caba1dad537d990aa15cfbcff7fb + React-jsi: f4af16a8fc555f49953deb4ce0b0f48dbc6c043f + React-jsidynamic: 55422c39884324bf1e4b04ab4be164bcc064fff9 + React-jsiexecutor: 6e0893f69e4a169f81d904192490447ee0f3a170 + React-jsinspector: a4ad2370d57f0150f21f4684b8931b8226580353 + React-logger: 95b3d56468b4968ecce58951e756f03082eb6bed + React-perflogger: 9533f63024f91cb527178f236ae3317c94fdc15f + React-RCTActionSheet: 0e2efddd25f88028c8b1df202afe7a0aeeeb170b + React-RCTAnimation: 1868f537cfd001f2183550199a3fb44a7921d1f0 + React-RCTAppDelegate: 6ae05562a28eb400a39a7d7d20c4dbc5a3604871 + React-RCTBlob: d6905b10424f7dbd244253dbb15193ac2bf8a073 + React-RCTFabric: 1ea60620417e316457e9bde0bf86aa8ed99aef48 + React-RCTImage: 5b1194ff3ebba402ff0e286719b341081786e21a + React-RCTLinking: 7cc81f96011a7979f610793c510ad75dbcc32727 + React-RCTNetwork: 13455ef64bbd0dbe58b0d7bd511bf9702550b64b + React-RCTPushNotification: e1b6f9e6d400eb40e96b29e6855eb8e32b02b203 + React-RCTSettings: 4ad5cbbda0c58f57e416d23a27d927c312194864 + React-RCTTest: a98da24c11276d12eee4dc590e5a9bf058f44a37 + React-RCTText: d7fb841e8dd730ef1633dfe92674248e7106f775 + React-RCTVibration: 64be373eed57a9fd53cf8e0d0bfd781e60814ec3 + React-rncore: cd0d14bf29f01491f49d1f19fd1a044d4b43e754 + React-runtimeexecutor: 7a1ac2b06a997a985f9fbdc6abbac38dd9e9aa48 + ReactCommon: d30ce0c7a6d0df825870d4706f2370f57bbc2b38 + ScreenshotManager: cf552c19152e3357f08875fc2f85adb2dee6a66b SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 - Yoga: 1b1a12ff3d86a10565ea7cbe057d42f5e5fb2a07 + Yoga: 167c6bcfbcd203adef48d2310491e85c51daeaf2 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a PODFILE CHECKSUM: 20298ecd3f30aa788ad491637e593ed0d8c100ca diff --git a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj index 73488e795ff4..e2d30db744f7 100644 --- a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj +++ b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj @@ -911,7 +911,7 @@ ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -996,7 +996,7 @@ ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js index 33f467a49231..5b71700c7f66 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js @@ -75,7 +75,7 @@ class AppendingList extends React.Component< From dd6fc603419f7dcf462a354f2b4fa29fa5ec3816 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Tue, 14 Jun 2022 17:48:11 -0400 Subject: [PATCH 4/5] Add maintainVisibleContentPosition prop for android scroll view --- Libraries/Components/ScrollView/ScrollView.js | 1 - .../MaintainVisibleScrollPositionHelper.java | 186 ++++++++++++++++++ .../scroll/ReactHorizontalScrollView.java | 66 ++++++- .../ReactHorizontalScrollViewManager.java | 10 + .../react/views/scroll/ReactScrollView.java | 34 +++- .../views/scroll/ReactScrollViewHelper.java | 4 + .../views/scroll/ReactScrollViewManager.java | 10 + .../examples/ScrollView/ScrollViewExample.js | 22 ++- 8 files changed, 322 insertions(+), 11 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 0c2ecf2f1684..a2bccc86b736 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -279,7 +279,6 @@ type IOSProps = $ReadOnly<{| * visibility. Occlusion, transforms, and other complexity won't be taken into account as to * whether content is "visible" or not. * - * @platform ios */ maintainVisibleContentPosition?: ?$ReadOnly<{| minIndexForVisible: number, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java new file mode 100644 index 000000000000..dee73198e93a --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.views.scroll; + +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; + +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.UIManager; +import com.facebook.react.bridge.UIManagerListener; +import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.common.ViewUtil; +import com.facebook.react.views.view.ReactViewGroup; +import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; + +import java.lang.ref.WeakReference; + +/** + * Manage state for the maintainVisibleContentPosition prop. + * + * This uses UIManager to listen to updates and capture position of items before and after layout. + */ +public class MaintainVisibleScrollPositionHelper implements UIManagerListener { + private final ScrollViewT mScrollView; + private final boolean mHorizontal; + private @Nullable Config mConfig; + private @Nullable WeakReference mFirstVisibleView = null; + private @Nullable Rect mPrevFirstVisibleFrame = null; + private boolean mListening = false; + + public static class Config { + public final int minIndexForVisible; + public final @Nullable Integer autoScrollToTopThreshold; + + Config(int minIndexForVisible, @Nullable Integer autoScrollToTopThreshold) { + this.minIndexForVisible = minIndexForVisible; + this.autoScrollToTopThreshold = autoScrollToTopThreshold; + } + + static Config fromReadableMap(ReadableMap value) { + int minIndexForVisible = value.getInt("minIndexForVisible"); + Integer autoScrollToTopThreshold = + value.hasKey("autoscrollToTopThreshold") + ? value.getInt("autoscrollToTopThreshold") + : null; + return new Config(minIndexForVisible, autoScrollToTopThreshold); + } + } + + public MaintainVisibleScrollPositionHelper(ScrollViewT scrollView, boolean horizontal) { + mScrollView = scrollView; + mHorizontal = horizontal; + } + + public void setConfig(@Nullable Config config) { + mConfig = config; + } + + /** + * Start listening to view hierarchy updates. Should be called when this is created. + */ + public void start() { + if (mListening) { + return; + } + mListening = true; + getUIManagerModule().addUIManagerEventListener(this); + } + + /** + * Stop listening to view hierarchy updates. Should be called before this is destroyed. + */ + public void stop() { + if (!mListening) { + return; + } + mListening = false; + getUIManagerModule().removeUIManagerEventListener(this); + } + + /** + * Update the scroll position of the managed ScrollView. This should be called after layout + * has been updated. + */ + public void updateScrollPosition() { + if (mConfig == null + || mFirstVisibleView == null + || mPrevFirstVisibleFrame == null) { + return; + } + + View firstVisibleView = mFirstVisibleView.get(); + Rect newFrame = new Rect(); + firstVisibleView.getHitRect(newFrame); + + if (mHorizontal) { + int deltaX = newFrame.left - mPrevFirstVisibleFrame.left; + if (deltaX != 0) { + int scrollX = mScrollView.getScrollX(); + mScrollView.scrollTo(scrollX + deltaX, mScrollView.getScrollY()); + mPrevFirstVisibleFrame = newFrame; + if (mConfig.autoScrollToTopThreshold != null && scrollX <= mConfig.autoScrollToTopThreshold) { + mScrollView.reactSmoothScrollTo(0, mScrollView.getScrollY()); + } + } + } else { + int deltaY = newFrame.top - mPrevFirstVisibleFrame.top; + if (deltaY != 0) { + int scrollY = mScrollView.getScrollY(); + mScrollView.scrollTo(mScrollView.getScrollX(), scrollY + deltaY); + mPrevFirstVisibleFrame = newFrame; + if (mConfig.autoScrollToTopThreshold != null && scrollY <= mConfig.autoScrollToTopThreshold) { + mScrollView.reactSmoothScrollTo(mScrollView.getScrollX(), 0); + } + } + } + } + + private @Nullable ReactViewGroup getContentView() { + return (ReactViewGroup) mScrollView.getChildAt(0); + } + + private UIManager getUIManagerModule() { + return Assertions.assertNotNull( + UIManagerHelper.getUIManager( + (ReactContext) mScrollView.getContext(), + ViewUtil.getUIManagerType(mScrollView.getId()))); + } + + private void computeTargetView() { + if (mConfig == null) { + return; + } + ReactViewGroup contentView = getContentView(); + if (contentView == null) { + return; + } + + int currentScroll = mHorizontal ? mScrollView.getScrollX() : mScrollView.getScrollY(); + for (int i = mConfig.minIndexForVisible; i < contentView.getChildCount(); i++) { + View child = contentView.getChildAt(i); + float position = mHorizontal ? child.getX() : child.getY(); + if (position > currentScroll || i == contentView.getChildCount() - 1) { + mFirstVisibleView = new WeakReference<>(child); + Rect frame = new Rect(); + child.getHitRect(frame); + mPrevFirstVisibleFrame = frame; + break; + } + } + } + + // UIManagerListener + + @Override + public void willDispatchViewUpdates(final UIManager uiManager) { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + computeTargetView(); + } + }); + } + + @Override + public void didDispatchMountItems(UIManager uiManager) { + // noop + } + + @Override + public void didScheduleMountItems(UIManager uiManager) { + // noop + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 3c962f03565a..3f38f42ff268 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -45,6 +45,7 @@ import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState; +import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState; import com.facebook.react.views.view.ReactViewBackgroundManager; import java.lang.reflect.Field; @@ -54,11 +55,14 @@ /** Similar to {@link ReactScrollView} but only supports horizontal scrolling. */ public class ReactHorizontalScrollView extends HorizontalScrollView implements ReactClippingViewGroup, + ViewGroup.OnHierarchyChangeListener, + View.OnLayoutChangeListener, FabricViewStateManager.HasFabricViewStateManager, ReactOverflowViewWithInset, HasScrollState, HasFlingAnimator, - HasScrollEventThrottle { + HasScrollEventThrottle, + HasSmoothScroll { private static boolean DEBUG_MODE = false && ReactBuildConfig.DEBUG; private static String TAG = ReactHorizontalScrollView.class.getSimpleName(); @@ -107,6 +111,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private PointerEvents mPointerEvents = PointerEvents.AUTO; private long mLastScrollDispatchTime = 0; private int mScrollEventThrottle = 0; + private @Nullable View mContentView; + private @Nullable MaintainVisibleScrollPositionHelper mMaintainVisibleContentPositionHelper; private final Rect mTempRect = new Rect(); @@ -127,6 +133,8 @@ public ReactHorizontalScrollView(Context context, @Nullable FpsListener fpsListe I18nUtil.getInstance().isRTL(context) ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR); + + setOnHierarchyChangeListener(this); } public boolean getScrollEnabled() { @@ -243,6 +251,19 @@ public void setOverflow(String overflow) { invalidate(); } + public void setMaintainVisibleContentPosition(@Nullable MaintainVisibleScrollPositionHelper.Config config) { + if (config != null && mMaintainVisibleContentPositionHelper == null) { + mMaintainVisibleContentPositionHelper = new MaintainVisibleScrollPositionHelper(this, true); + mMaintainVisibleContentPositionHelper.start(); + } else if (config == null && mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.stop(); + mMaintainVisibleContentPositionHelper = null; + } + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.setConfig(config); + } + } + @Override public @Nullable String getOverflow() { return mOverflow; @@ -635,6 +656,17 @@ protected void onAttachedToWindow() { if (mRemoveClippedSubviews) { updateClippingRect(); } + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.start(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.stop(); + } } @Override @@ -714,6 +746,18 @@ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolea super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); } + @Override + public void onChildViewAdded(View parent, View child) { + mContentView = child; + mContentView.addOnLayoutChangeListener(this); + } + + @Override + public void onChildViewRemoved(View parent, View child) { + mContentView.removeOnLayoutChangeListener(this); + mContentView = null; + } + private void enableFpsListener() { if (isScrollPerfLoggingEnabled()) { Assertions.assertNotNull(mFpsListener); @@ -1237,6 +1281,26 @@ private void setPendingContentOffsets(int x, int y) { } } + @Override + public void onLayoutChange( + View v, + int left, + int top, + int right, + int bottom, + int oldLeft, + int oldTop, + int oldRight, + int oldBottom) { + if (mContentView == null) { + return; + } + + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.updateScrollPosition(); + } + } + @Override public FabricViewStateManager getFabricViewStateManager() { return mFabricViewStateManager; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index aada6a7c4079..4c29d92fc866 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -328,6 +328,16 @@ public void setContentOffset(ReactHorizontalScrollView view, ReadableMap value) } } + @ReactProp(name = "maintainVisibleContentPosition") + public void setMaintainVisibleContentPosition(ReactHorizontalScrollView view, ReadableMap value) { + if (value != null) { + view.setMaintainVisibleContentPosition( + MaintainVisibleScrollPositionHelper.Config.fromReadableMap(value)); + } else { + view.setMaintainVisibleContentPosition(null); + } + } + @ReactProp(name = ViewProps.POINTER_EVENTS) public void setPointerEvents(ReactHorizontalScrollView view, @Nullable String pointerEventsStr) { view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr)); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index ae0a51274e71..128af8535ef7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -45,8 +45,10 @@ import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState; +import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState; import com.facebook.react.views.view.ReactViewBackgroundManager; + import java.lang.reflect.Field; import java.util.List; @@ -65,7 +67,8 @@ public class ReactScrollView extends ScrollView ReactOverflowViewWithInset, HasScrollState, HasFlingAnimator, - HasScrollEventThrottle { + HasScrollEventThrottle, + HasSmoothScroll { private static @Nullable Field sScrollerField; private static boolean sTriedToGetScrollerField = false; @@ -109,6 +112,7 @@ public class ReactScrollView extends ScrollView private PointerEvents mPointerEvents = PointerEvents.AUTO; private long mLastScrollDispatchTime = 0; private int mScrollEventThrottle = 0; + private @Nullable MaintainVisibleScrollPositionHelper mMaintainVisibleContentPositionHelper = null; public ReactScrollView(Context context) { this(context, null); @@ -240,6 +244,19 @@ public void setOverflow(String overflow) { invalidate(); } + public void setMaintainVisibleContentPosition(@Nullable MaintainVisibleScrollPositionHelper.Config config) { + if (config != null && mMaintainVisibleContentPositionHelper == null) { + mMaintainVisibleContentPositionHelper = new MaintainVisibleScrollPositionHelper(this, false); + mMaintainVisibleContentPositionHelper.start(); + } else if (config == null && mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.stop(); + mMaintainVisibleContentPositionHelper = null; + } + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.setConfig(config); + } + } + @Override public @Nullable String getOverflow() { return mOverflow; @@ -290,6 +307,17 @@ protected void onAttachedToWindow() { if (mRemoveClippedSubviews) { updateClippingRect(); } + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.start(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.stop(); + } } /** @@ -1071,6 +1099,10 @@ public void onLayoutChange( return; } + if (mMaintainVisibleContentPositionHelper != null) { + mMaintainVisibleContentPositionHelper.updateScrollPosition(); + } + int currentScrollY = getScrollY(); int maxScrollY = getMaxScrollY(); if (currentScrollY > maxScrollY) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index b574cb501b04..81d2382b212f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -602,4 +602,8 @@ public interface HasScrollEventThrottle { /** Get the scroll view dispatch time for throttling */ long getLastScrollDispatchTime(); } + + public interface HasSmoothScroll { + void reactSmoothScrollTo(int x, int y); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index 933455e8056a..e98a9c8c0f9d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -329,6 +329,16 @@ public void setContentOffset(ReactScrollView view, ReadableMap value) { } } + @ReactProp(name = "maintainVisibleContentPosition") + public void setMaintainVisibleContentPosition(ReactScrollView view, ReadableMap value) { + if (value != null) { + view.setMaintainVisibleContentPosition( + MaintainVisibleScrollPositionHelper.Config.fromReadableMap(value)); + } else { + view.setMaintainVisibleContentPosition(null); + } + } + @Override public Object updateState( ReactScrollView view, ReactStylesDiffMap props, StateWrapper stateWrapper) { diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js index 5b71700c7f66..a7d7a8f82588 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js @@ -35,6 +35,7 @@ class EnableDisableList extends React.Component<{}, {scrollEnabled: boolean}> { {ITEMS.map(createItemRow)} @@ -78,6 +79,7 @@ class AppendingList extends React.Component< minIndexForVisible: 0, autoscrollToTopThreshold: 10, }} + nestedScrollEnabled style={styles.scrollView}> {this.state.items.map(item => React.cloneElement(item, {key: item.props.msg}), @@ -169,7 +171,10 @@ class AppendingList extends React.Component< function CenterContentList(): React.Node { return ( - + This should be in center. ); @@ -208,6 +213,7 @@ const examples = ([ _scrollView = scrollView; }} automaticallyAdjustContentInsets={false} + nestedScrollEnabled onScroll={() => { console.log('onScroll!'); }} @@ -397,10 +403,7 @@ const examples = ([ return ; }, }, -]: Array); - -if (Platform.OS === 'ios') { - examples.push({ + { title: ' smooth bi-directional content loading\n', description: 'The `maintainVisibleContentPosition` prop allows insertions to either end of the content ' + @@ -408,7 +411,10 @@ if (Platform.OS === 'ios') { render: function () { return ; }, - }); + }, +]: Array); + +if (Platform.OS === 'ios') { examples.push({ title: ' (centerContent = true)\n', description: @@ -491,6 +497,7 @@ const AndroidScrollBarOptions = () => { {ITEMS.map(createItemRow)} @@ -1219,8 +1226,7 @@ const BouncesExampleHorizontal = () => { style={[styles.scrollView, {height: 200}]} horizontal={true} alwaysBounceHorizontal={bounce} - contentOffset={{x: 100, y: 0}} - nestedScrollEnabled> + contentOffset={{x: 100, y: 0}}> {ITEMS.map(createItemRow)} From 32819e63af5253e13a002bccbef6663e2b96be86 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 12 Oct 2022 23:40:34 -0400 Subject: [PATCH 5/5] Add fabric support for maintainVisibleContentPosition on Android --- .../react/animated/NativeAnimatedModule.java | 10 +++++++ .../react/bridge/UIManagerListener.java | 27 +++++++++++++++++-- .../react/fabric/FabricUIManager.java | 14 ++++++++++ .../fabric/mounting/MountItemDispatcher.java | 7 +++++ .../MaintainVisibleScrollPositionHelper.java | 19 +++++++++++++ 5 files changed, 75 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index 0f52b73c6162..cf3ca7f04f72 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -299,6 +299,16 @@ public void didScheduleMountItems(UIManager uiManager) { mCurrentFrameNumber++; } + @Override + public void willMountItems(UIManager uiManager) { + // noop + } + + @Override + public void didMountItems(UIManager uiManager) { + // noop + } + // For FabricUIManager only @Override @UiThread diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java index 984bcf15848e..72837cbc2e6b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java @@ -12,10 +12,33 @@ public interface UIManagerListener { /** * Called right before view updates are dispatched at the end of a batch. This is useful if a * module needs to add UIBlocks to the queue before it is flushed. + * + * This is called by Paper only. */ void willDispatchViewUpdates(UIManager uiManager); - /* Called right after view updates are dispatched for a frame. */ + /** + * Called on UIThread right before view updates are executed. + * + * This is called by Fabric only. + */ + void willMountItems(UIManager uiManager); + /** + * Called on UIThread right after view updates are executed. + * + * This is called by Fabric only. + */ + void didMountItems(UIManager uiManager); + /** + * Called on UIThread right after view updates are dispatched for a frame. Note that this will be called + * for every frame even if there are no updates. + * + * This is called by Fabric only. + */ void didDispatchMountItems(UIManager uiManager); - /* Called right after scheduleMountItems is called in Fabric, after a new tree is committed. */ + /** + * Called right after scheduleMountItems is called in Fabric, after a new tree is committed. + * + * This is called by Fabric only. + */ void didScheduleMountItems(UIManager uiManager); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index e0947264fd95..f8f11e8578e2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -1202,6 +1202,20 @@ public Map getPerformanceCounters() { } private class MountItemDispatchListener implements MountItemDispatcher.ItemDispatchListener { + @Override + public void willMountItems() { + for (UIManagerListener listener : mListeners) { + listener.willMountItems(FabricUIManager.this); + } + } + + @Override + public void didMountItems() { + for (UIManagerListener listener : mListeners) { + listener.didMountItems(FabricUIManager.this); + } + } + @Override public void didDispatchMountItems() { for (UIManagerListener listener : mListeners) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java index a14cc858c5d3..d0bea5e8da48 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java @@ -193,6 +193,8 @@ private boolean dispatchMountItems() { return false; } + mItemDispatchListener.willMountItems(); + // As an optimization, execute all ViewCommands first // This should be: // 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always @@ -295,6 +297,9 @@ private boolean dispatchMountItems() { } mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime; } + + mItemDispatchListener.didMountItems(); + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); return true; @@ -411,6 +416,8 @@ private static void printMountItem(MountItem mountItem, String prefix) { } public interface ItemDispatchListener { + void willMountItems(); + void didMountItems(); void didDispatchMountItems(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java index dee73198e93a..2c3d343e2474 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.UIManagerListener; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.views.view.ReactViewGroup; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; @@ -94,6 +95,14 @@ public void stop() { * has been updated. */ public void updateScrollPosition() { + // On Fabric this will be called internally in `didMountItems`. + if (ViewUtil.getUIManagerType(mScrollView.getId()) == UIManagerType.FABRIC) { + return; + } + updateScrollPositionInternal(); + } + + private void updateScrollPositionInternal() { if (mConfig == null || mFirstVisibleView == null || mPrevFirstVisibleFrame == null) { @@ -174,6 +183,16 @@ public void run() { }); } + @Override + public void willMountItems(UIManager uiManager) { + computeTargetView(); + } + + @Override + public void didMountItems(UIManager uiManager) { + updateScrollPositionInternal(); + } + @Override public void didDispatchMountItems(UIManager uiManager) { // noop