Skip to content

Commit a87b095

Browse files
fix: double check whether we scroll to an existing index when messageId is… (#2148)
* Double check whether we scroll to an existing index when messageId is filled in by MessageList * catch and retry * catch and retry * dont throw but log * don;t throw but log and go out * Update package/src/components/MessageList/MessageList.tsx Co-authored-by: Santhosh Vaiyapuri <[email protected]> * reset after retries failure * clearing when busy * clearing when busy --------- Co-authored-by: Santhosh Vaiyapuri <[email protected]>
1 parent 3b5e2ff commit a87b095

File tree

1 file changed

+45
-11
lines changed

1 file changed

+45
-11
lines changed

package/src/components/MessageList/MessageList.tsx

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ import {
5555

5656
import type { DefaultStreamChatGenerics } from '../../types/types';
5757

58+
const WAIT_FOR_SCROLL_TO_OFFSET_TIMEOUT = 150;
59+
const MAX_RETRIES_AFTER_SCROLL_FAILURE = 10;
5860
const 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

Comments
 (0)