@@ -47,6 +47,7 @@ const {findNodeHandle} = require('../ReactNative/RendererProxy');
4747const  flattenStyle  =  require ( '../StyleSheet/flattenStyle' ) ; 
4848const  StyleSheet  =  require ( '../StyleSheet/StyleSheet' ) ; 
4949const  infoLog  =  require ( '../Utilities/infoLog' ) ; 
50+ const  Platform  =  require ( '../Utilities/Platform' ) ; 
5051const  FillRateHelper  =  require ( './FillRateHelper' ) ; 
5152const  ViewabilityHelper  =  require ( './ViewabilityHelper' ) ; 
5253const  invariant  =  require ( 'invariant' ) ; 
@@ -1034,6 +1035,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
10341035  _totalCellsMeasured  =  0 ; 
10351036  _updateCellsToRenderBatcher : Batchinator ; 
10361037  _viewabilityTuples : Array < ViewabilityHelperCallbackTuple >  =  [ ] ; 
1038+   _hasDoneFirstScroll  =  false ; 
10371039
10381040  /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's 
10391041   * LTI update could not be added via codemod */ 
@@ -1320,71 +1322,62 @@ class VirtualizedList extends React.PureComponent<Props, State> {
13201322      initialScrollIndex, 
13211323      enabledTalkbackCompatibleInvertedList, 
13221324    }  =  this . props ; 
1323-     const  { contentLength,  visibleLength,  offset}  =  this . _scrollMetrics ; 
1325+     const  { contentLength,  visibleLength,  offset,  dOffset }  =  this . _scrollMetrics ; 
13241326    const  talkbackCompatibility  = 
13251327      this . state . screenreaderEnabled  && 
13261328      initialScrollIndex  ==  null  && 
13271329      inverted  && 
13281330      enabledTalkbackCompatibleInvertedList ; 
13291331    let  distanceFromEnd ; 
1332+     let  endPositionReached ; 
1333+     let  startPositionReached ; 
1334+     let  isScrollingForward  =  dOffset  <  0 ; 
1335+     // in case of inverted flatlist with talkback enabled 
1336+     // replace 2 with threeshold 
1337+     const  THRESHOLD  =  Platform . OS  ===  'android'  ? 2  : 30 ; 
13301338    if  ( talkbackCompatibility  &&  this . _hasTriggeredInitialScrollToIndex )  { 
13311339      distanceFromEnd =  offset ; 
1340+       startPositionReached  = 
1341+         Math . abs ( visibleLength  +  offset  -  contentLength )  <  THRESHOLD ; 
1342+       endPositionReached  =  Math . abs ( offset )  <  THRESHOLD ; 
13321343    }  else  { 
13331344      distanceFromEnd =  contentLength  -  visibleLength  -  offset ; 
1345+       endPositionReached  = 
1346+         Math . abs ( visibleLength  +  offset  -  contentLength )  <  THRESHOLD ; 
1347+       startPositionReached  =  Math . abs ( offset )  <  THRESHOLD ; 
13341348    } 
13351349
1336-     // Especially when oERT is zero it's necessary to 'floor' very small distanceFromEnd values to be 0 
1337-     // since debouncing causes us to not fire this event for every single "pixel" we scroll and can thus 
1338-     // be at the "end" of the list with a distanceFromEnd approximating 0 but not quite there. 
1339-     if  ( ! talkbackCompatibility  &&  distanceFromEnd  <  ON_END_REACHED_EPSILON ) { 
1340-       distanceFromEnd  =  0 ; 
1341-     } 
1342- 
1343-     // TODO: T121172172 Look into why we're "defaulting" to a threshold of 2 when oERT is not present
1344-     const threshold =
1345-       onEndReachedThreshold != null ? onEndReachedThreshold * visibleLength : 2;
1346-     const canTriggerOnEndReachedWithTalkback =
1347-       typeof this._lastTimeOnEndReachedCalled === 'number'
1348-         ? Math.abs(this._lastTimeOnEndReachedCalled - Date.now()) >  500 
1349-         : true ; 
13501350    if  ( 
1351-       onEndReached  && 
1352-       this . _beginningReached  && 
13531351      talkbackCompatibility  && 
1354-       distanceFromEnd  ===  0  && 
1355-       this . _hasTriggeredInitialScrollToIndex  && 
1356-       this . state . last  ===  getItemCount ( data )  -  1  && 
1357-       this . _scrollMetrics . contentLength  !==  this . _sentEndForContentLength  && 
1358-       canTriggerOnEndReachedWithTalkback 
1352+       startPositionReached  && 
1353+       ! this . _hasDoneFirstScroll 
13591354    )  { 
1360-       // save the last position in the flastlist to restore it after animation to Top 
1361-       this . _lastOffsetFromBottomOfScreen  =  this . _offsetFromBottomOfScreen ; 
1362-       // Only call onEndReached once for a given content length 
1363-       this . _sentEndForContentLength  =  this . _scrollMetrics . contentLength ; 
1364-       // wait 100 ms to call again onEndReached (TalkBack scrolling is slower) 
1365-       this . _lastTimeOnEndReachedCalled  =  Date . now ( ) ; 
1366-       onEndReached ( { distanceFromEnd} ) ; 
1367-     }  else { 
1368-       this . _lastOffsetFromBottomOfScreen  =  undefined ; 
1355+       return; 
13691356    } 
1370-     if (
1371-       onEndReached && 
1372-       this . state . last  ===  getItemCount ( data )  -  1  && 
1373-       distanceFromEnd  <=  threshold  && 
1374-       this . _scrollMetrics . contentLength  !==  this . _sentEndForContentLength 
1375-     )  { 
1376-       if  ( talkbackCompatibility ) { 
1377-         this . _beginningReached  =  true ; 
1378-       }  else { 
1379-         // Only call onEndReached once for a given content length 
1380-         this . _sentEndForContentLength  =  this . _scrollMetrics . contentLength ; 
1381-         onEndReached ( { distanceFromEnd} ) ; 
1382-       } 
1383-     }  else  if  ( distanceFromEnd  >  threshold )  { 
1384-       // If the user scrolls away from the end and back again cause 
1385-       // an onEndReached to be triggered again 
1386-       this . _sentEndForContentLength  =  0 ; 
1357+ 
1358+     // If scrolled up in the vertical list 
1359+     if  ( dOffset  <  0 ) { 
1360+       return ; 
1361+     } 
1362+ 
1363+     // If contentLength has not changed
1364+     if (contentLength === this._sentEndForContentLength) { 
1365+       return ; 
13871366    } 
1367+ 
1368+     // If the distance is so farther than the area shown on the screen
1369+     if (distanceFromEnd >=  visibleLength  *  1.5 )  { 
1370+       return ; 
1371+     } 
1372+ 
1373+     // $FlowFixMe
1374+     const minimumDistanceFromEnd = onEndReachedThreshold * visibleLength;
1375+     if (distanceFromEnd >=  minimumDistanceFromEnd )  { 
1376+       return ; 
1377+     } 
1378+ 
1379+     this._sentEndForContentLength = contentLength;
1380+     onEndReached({ distanceFromEnd } );
13881381  } 
13891382
13901383  _onContentSizeChange  =  ( width : number ,  height : number )  =>  { 
@@ -1445,12 +1438,15 @@ class VirtualizedList extends React.PureComponent<Props, State> {
14451438      this._lastScrollPosition === undefined ||
14461439      this._lastItem === undefined ||
14471440      (lastItemDidNotChange &&  scrollPositionChanged ) ; 
1441+     console . log ( 'triggerTalkbackScrollToEnd :',  triggerTalkbackScrollToEnd ) ; 
1442+     console . log ( 'talkbackCompatibility :',  talkbackCompatibility ) ; 
14481443    if  ( 
14491444      width  >  0  && 
14501445      height  >  0  && 
14511446      triggerTalkbackScrollToEnd  && 
14521447      talkbackCompatibility 
14531448    )  { 
1449+       console . log ( 'scroll to the end' ) ; 
14541450      // onMomentumScrollEnd does not work with TalkBack gestures 
14551451      // estrapolate this to a method talkbackScrollTo compatible with 
14561452      // TalkBack gestures. 
@@ -1613,6 +1609,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
16131609    if  ( ! this . props )  { 
16141610      return; 
16151611    } 
1612+     this . _hasDoneFirstScroll  =  true ; 
16161613    this . _maybeCallOnEndReached ( ) ; 
16171614    if  ( velocity  !==  0 )  { 
16181615      this . _fillRateHelper . activate ( ) ; 
0 commit comments