@@ -55,6 +55,8 @@ import {
5555
5656import type { DefaultStreamChatGenerics } from '../../types/types' ;
5757
58+ const WAIT_FOR_SCROLL_TO_OFFSET_TIMEOUT = 150 ;
59+ const MAX_RETRIES_AFTER_SCROLL_FAILURE = 10 ;
5860const styles = StyleSheet . create ( {
5961 container : {
6062 alignItems : 'center' ,
@@ -550,6 +552,7 @@ const MessageListWithContext = <
550552 }
551553 return false ;
552554 }
555+
553556 const isCurrentMessageUnread = isMessageUnread ( index ) ;
554557 const showUnreadUnderlay = isCurrentMessageUnread && scrollToBottomButtonVisible ;
555558 const insertInlineUnreadIndicator = showUnreadUnderlay && ! isMessageUnread ( index + 1 ) ; // show only if previous message is read
@@ -783,19 +786,22 @@ const MessageListWithContext = <
783786 }
784787 } ;
785788
789+ const scrollToIndexFailedRetryCountRef = useRef < number > ( 0 ) ;
790+ const failScrollTimeoutId = useRef < NodeJS . Timeout > ( ) ;
786791 const onScrollToIndexFailedRef = useRef <
787792 FlatListProps < MessageType < StreamChatGenerics > > [ 'onScrollToIndexFailed' ]
788793 > ( ( info ) => {
789794 // We got a failure as we tried to scroll to an item that was outside the render length
790- if ( flatListRef . current ) {
791- // we dont know the actual size of all items but we can see the average, so scroll to the closest offset
792- flatListRef . current . scrollToOffset ( {
793- animated : false ,
794- offset : info . averageItemLength * info . index ,
795- } ) ;
796- // since we used only an average offset.. we wont go to the center of the item yet
797- // with a little delay to wait for scroll to offset to complete, we can then scroll to the index
798- setTimeout ( ( ) => {
795+ if ( ! flatListRef . current ) return ;
796+ // we don't know the actual size of all items but we can see the average, so scroll to the closest offset
797+ flatListRef . current . scrollToOffset ( {
798+ animated : false ,
799+ offset : info . averageItemLength * info . index ,
800+ } ) ;
801+ // since we used only an average offset... we won't go to the center of the item yet
802+ // with a little delay to wait for scroll to offset to complete, we can then scroll to the index
803+ failScrollTimeoutId . current = setTimeout ( ( ) => {
804+ try {
799805 flatListRef . current ?. scrollToIndex ( {
800806 animated : false ,
801807 index : info . index ,
@@ -804,8 +810,30 @@ const MessageListWithContext = <
804810 if ( messageIdLastScrolledToRef . current ) {
805811 setTargetedMessage ( messageIdLastScrolledToRef . current ) ;
806812 }
807- } , 150 ) ;
808- }
813+ scrollToIndexFailedRetryCountRef . current = 0 ;
814+ } catch ( e ) {
815+ if (
816+ ! onScrollToIndexFailedRef . current ||
817+ scrollToIndexFailedRetryCountRef . current > MAX_RETRIES_AFTER_SCROLL_FAILURE
818+ ) {
819+ console . log (
820+ `Scrolling to index failed after ${ MAX_RETRIES_AFTER_SCROLL_FAILURE } retries` ,
821+ e ,
822+ ) ;
823+ scrollToIndexFailedRetryCountRef . current = 0 ;
824+ return ;
825+ }
826+ // At some cases the index we're trying to scroll to, doesn't exist yet in the messageList
827+ // Scrolling to an index not in range of the Flatlist's data will result in a crash that
828+ // won't call onScrollToIndexFailed.
829+ // By catching this error we retry scrolling by calling onScrollToIndexFailedRef
830+ scrollToIndexFailedRetryCountRef . current += 1 ;
831+ onScrollToIndexFailedRef . current ( info ) ;
832+ }
833+ } , WAIT_FOR_SCROLL_TO_OFFSET_TIMEOUT ) ;
834+
835+ // Only when index is greater than 0 and in range of items in FlatList
836+ // this onScrollToIndexFailed will be called again
809837 } ) ;
810838
811839 const goToMessage = useCallback (
@@ -814,6 +842,8 @@ const MessageListWithContext = <
814842 ( message ) => message ?. id === messageId ,
815843 ) ;
816844 if ( indexOfParentInMessageList !== - 1 && flatListRef . current ) {
845+ clearTimeout ( failScrollTimeoutId . current ) ;
846+ scrollToIndexFailedRetryCountRef . current = 0 ;
817847 flatListRef . current . scrollToIndex ( {
818848 animated : true ,
819849 index : indexOfParentInMessageList ,
@@ -855,6 +885,9 @@ const MessageListWithContext = <
855885 ( message ) => message ?. id === messageIdToScroll ,
856886 ) ;
857887 if ( indexOfParentInMessageList !== - 1 && flatListRef . current ) {
888+ // By a fresh scroll we should clear the retries for the previous failed scroll
889+ clearTimeout ( failScrollTimeoutId . current ) ;
890+ scrollToIndexFailedRetryCountRef . current = 0 ;
858891 flatListRef . current . scrollToIndex ( {
859892 animated : false ,
860893 index : indexOfParentInMessageList ,
@@ -867,6 +900,7 @@ const MessageListWithContext = <
867900 }
868901 } , 150 ) ;
869902 return ( ) => {
903+ clearTimeout ( failScrollTimeoutId . current ) ;
870904 clearTimeout ( scrollToDebounceTimeoutRef . current ) ;
871905 clearTimeout ( initialScrollSettingTimeoutRef . current ) ;
872906 } ;
0 commit comments