stream-chat-react-native-core 8.13.13 → 8.13.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/lib/commonjs/components/AutoCompleteInput/AutoCompleteSuggestionList.js +3 -2
  2. package/lib/commonjs/components/AutoCompleteInput/AutoCompleteSuggestionList.js.map +1 -1
  3. package/lib/commonjs/components/Channel/Channel.js +18 -2
  4. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  5. package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js +2 -0
  6. package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
  7. package/lib/commonjs/components/MessageList/MessageFlashList.js +44 -34
  8. package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
  9. package/lib/commonjs/contexts/channelContext/ChannelContext.js.map +1 -1
  10. package/lib/commonjs/version.json +1 -1
  11. package/lib/module/components/AutoCompleteInput/AutoCompleteSuggestionList.js +3 -2
  12. package/lib/module/components/AutoCompleteInput/AutoCompleteSuggestionList.js.map +1 -1
  13. package/lib/module/components/Channel/Channel.js +18 -2
  14. package/lib/module/components/Channel/Channel.js.map +1 -1
  15. package/lib/module/components/Channel/hooks/useCreateChannelContext.js +2 -0
  16. package/lib/module/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
  17. package/lib/module/components/MessageList/MessageFlashList.js +44 -34
  18. package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
  19. package/lib/module/contexts/channelContext/ChannelContext.js.map +1 -1
  20. package/lib/module/version.json +1 -1
  21. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  22. package/lib/typescript/components/Channel/hooks/useCreateChannelContext.d.ts +1 -1
  23. package/lib/typescript/components/Channel/hooks/useCreateChannelContext.d.ts.map +1 -1
  24. package/lib/typescript/components/MessageList/MessageFlashList.d.ts +1 -1
  25. package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
  26. package/lib/typescript/contexts/channelContext/ChannelContext.d.ts +6 -0
  27. package/lib/typescript/contexts/channelContext/ChannelContext.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/__tests__/offline-support/offline-feature.js +10 -2
  30. package/src/components/AutoCompleteInput/AutoCompleteSuggestionList.tsx +2 -1
  31. package/src/components/Channel/Channel.tsx +21 -6
  32. package/src/components/Channel/hooks/useCreateChannelContext.ts +2 -0
  33. package/src/components/MessageList/MessageFlashList.tsx +31 -26
  34. package/src/contexts/channelContext/ChannelContext.tsx +6 -0
  35. package/src/version.json +1 -1
@@ -834,6 +834,18 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
834
834
  channel,
835
835
  });
836
836
 
837
+ const shouldLoadInitialChannelAtFirstUnreadMessage = useStableCallback((unreadCount?: number) => {
838
+ if (messageId || !initialScrollToFirstUnreadMessage || !client.user) {
839
+ return false;
840
+ }
841
+
842
+ return (unreadCount ?? channel.countUnread()) > scrollToFirstUnreadThreshold;
843
+ });
844
+
845
+ const hasPendingInitialTargetLoad = useStableCallback(() => {
846
+ return !!messageId || shouldLoadInitialChannelAtFirstUnreadMessage();
847
+ });
848
+
837
849
  const { setMessages: copyMessagesStateFromChannel, viewabilityChangedCallback } =
838
850
  usePrunableMessageList({ maximumMessageLimit, setMessages: rawCopyMessagesStateFromChannel });
839
851
 
@@ -960,6 +972,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
960
972
  const initChannel = async () => {
961
973
  setLastRead(new Date());
962
974
  const unreadCount = channel.countUnread();
975
+ const shouldLoadAtFirstUnread = shouldLoadInitialChannelAtFirstUnreadMessage(unreadCount);
963
976
  if (!channel || !shouldSyncChannel) {
964
977
  return;
965
978
  }
@@ -989,13 +1002,14 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
989
1002
 
990
1003
  if (messageId) {
991
1004
  await loadChannelAroundMessage({ messageId, setTargetedMessage });
992
- } else if (
993
- initialScrollToFirstUnreadMessage &&
994
- client.user &&
995
- unreadCount > scrollToFirstUnreadThreshold
996
- ) {
1005
+ } else if (shouldLoadAtFirstUnread) {
1006
+ const clientUserId = client.user?.id;
1007
+ if (!clientUserId) {
1008
+ return;
1009
+ }
1010
+
997
1011
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
998
- const { user, ...ownReadState } = channel.state.read[client.user.id];
1012
+ const { user, ...ownReadState } = channel.state.read[clientUserId];
999
1013
 
1000
1014
  await loadChannelAtFirstUnreadMessage({
1001
1015
  channelUnreadState: ownReadState,
@@ -1788,6 +1802,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1788
1802
  enableMessageGroupingByUser,
1789
1803
  enforceUniqueReaction,
1790
1804
  error,
1805
+ hasPendingInitialTargetLoad,
1791
1806
  hideDateSeparators,
1792
1807
  hideStickyDateHeader,
1793
1808
  highlightedMessageId,
@@ -31,6 +31,7 @@ export const useCreateChannelContext = ({
31
31
  setChannelUnreadState,
32
32
  setLastRead,
33
33
  setTargetedMessage,
34
+ hasPendingInitialTargetLoad,
34
35
  StickyHeader,
35
36
  targetedMessage,
36
37
  threadList,
@@ -58,6 +59,7 @@ export const useCreateChannelContext = ({
58
59
  enableMessageGroupingByUser,
59
60
  enforceUniqueReaction,
60
61
  error,
62
+ hasPendingInitialTargetLoad,
61
63
  hideDateSeparators,
62
64
  hideStickyDateHeader,
63
65
  highlightedMessageId,
@@ -119,6 +119,7 @@ type MessageFlashListPropsWithContext = Pick<
119
119
  | 'scrollToFirstUnreadThreshold'
120
120
  | 'setChannelUnreadState'
121
121
  | 'setTargetedMessage'
122
+ | 'hasPendingInitialTargetLoad'
122
123
  | 'StickyHeader'
123
124
  | 'targetedMessage'
124
125
  | 'threadList'
@@ -298,6 +299,7 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
298
299
  setMessages,
299
300
  setSelectedPicker,
300
301
  setTargetedMessage,
302
+ hasPendingInitialTargetLoad,
301
303
  StickyHeader,
302
304
  targetedMessage,
303
305
  thread,
@@ -389,11 +391,16 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
389
391
 
390
392
  useEffect(() => {
391
393
  if (autoscrollToRecent && flashListRef.current) {
394
+ if (hasPendingInitialTargetLoad?.()) {
395
+ return;
396
+ }
397
+
398
+ console.log('[STREAM] scrollToEnd effect#1 (autoscrollToRecent) fired');
392
399
  flashListRef.current.scrollToEnd({
393
400
  animated: true,
394
401
  });
395
402
  }
396
- }, [autoscrollToRecent]);
403
+ }, [autoscrollToRecent, hasPendingInitialTargetLoad]);
397
404
 
398
405
  const maintainVisibleContentPosition = useMemo(() => {
399
406
  return {
@@ -409,18 +416,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
409
416
  }
410
417
  }, [disabled]);
411
418
 
412
- const indexToScrollToRef = useRef<number | undefined>(undefined);
413
-
414
- const initialIndexToScrollTo = useMemo(() => {
415
- return targetedMessage
416
- ? processedMessageList.findIndex((message) => message?.id === targetedMessage)
417
- : -1;
418
- }, [processedMessageList, targetedMessage]);
419
-
420
- useEffect(() => {
421
- indexToScrollToRef.current = initialIndexToScrollTo;
422
- }, [initialIndexToScrollTo]);
423
-
424
419
  /**
425
420
  * Check if a messageId needs to be scrolled to after list loads, and scroll to it
426
421
  * Note: This effect fires on every list change with a small debounce so that scrolling isnt abrupted by an immediate rerender
@@ -438,16 +433,30 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
438
433
  if (indexOfParentInMessageList === -1) {
439
434
  loadChannelAroundMessage({ messageId: targetedMessage, setTargetedMessage });
440
435
  } else {
441
- scrollToDebounceTimeoutRef.current = setTimeout(() => {
436
+ scrollToDebounceTimeoutRef.current = setTimeout(async () => {
442
437
  clearTimeout(scrollToDebounceTimeoutRef.current);
443
438
 
444
- // now scroll to it
445
- flashListRef.current?.scrollToIndex({
446
- animated: true,
447
- index: indexOfParentInMessageList,
448
- viewPosition: 0.5,
439
+ const scrollToIndex = async () => {
440
+ const list = flashListRef.current;
441
+
442
+ if (!list) {
443
+ return false;
444
+ }
445
+
446
+ await list.scrollToIndex({
447
+ animated: true,
448
+ index: indexOfParentInMessageList,
449
+ viewPosition: 0.5,
450
+ });
451
+
452
+ return true;
453
+ };
454
+
455
+ await scrollToIndex();
456
+ requestAnimationFrame(async () => {
457
+ await scrollToIndex();
458
+ setTargetedMessage(undefined);
449
459
  });
450
- setTargetedMessage(undefined);
451
460
  }, WAIT_FOR_SCROLL_TIMEOUT);
452
461
  }
453
462
  }, [loadChannelAroundMessage, processedMessageList, setTargetedMessage, targetedMessage]);
@@ -457,8 +466,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
457
466
  (message) => message?.id === messageId,
458
467
  );
459
468
 
460
- indexToScrollToRef.current = indexOfParentInMessageList;
461
-
462
469
  try {
463
470
  if (indexOfParentInMessageList === -1) {
464
471
  clearTimeout(scrollToDebounceTimeoutRef.current);
@@ -530,7 +537,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
530
537
  setScrollToBottomButtonVisible(true);
531
538
  return;
532
539
  } else {
533
- indexToScrollToRef.current = undefined;
534
540
  setAutoscrollToRecent(true);
535
541
  }
536
542
  const latestNonCurrentMessageBeforeUpdate = latestNonCurrentMessageBeforeUpdateRef.current;
@@ -1072,9 +1078,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
1072
1078
  data={processedMessageList}
1073
1079
  drawDistance={800}
1074
1080
  getItemType={getItemTypeInternal}
1075
- initialScrollIndex={
1076
- indexToScrollToRef.current === -1 ? undefined : indexToScrollToRef.current
1077
- }
1078
1081
  keyboardShouldPersistTaps='handled'
1079
1082
  keyExtractor={keyExtractor}
1080
1083
  ListFooterComponent={FooterComponent}
@@ -1151,6 +1154,7 @@ export const MessageFlashList = (props: MessageFlashListProps) => {
1151
1154
  scrollToFirstUnreadThreshold,
1152
1155
  setChannelUnreadState,
1153
1156
  setTargetedMessage,
1157
+ hasPendingInitialTargetLoad,
1154
1158
  StickyHeader,
1155
1159
  targetedMessage,
1156
1160
  threadList,
@@ -1192,6 +1196,7 @@ export const MessageFlashList = (props: MessageFlashListProps) => {
1192
1196
  enableMessageGroupingByUser,
1193
1197
  error,
1194
1198
  FlatList,
1199
+ hasPendingInitialTargetLoad,
1195
1200
  hideStickyDateHeader,
1196
1201
  highlightedMessageId,
1197
1202
  InlineDateSeparator,
@@ -130,6 +130,12 @@ export type ChannelContextValue = {
130
130
  setChannelUnreadState: (data: ChannelUnreadStateStoreType['channelUnreadState']) => void;
131
131
  setLastRead: React.Dispatch<React.SetStateAction<Date | undefined>>;
132
132
  setTargetedMessage: (messageId?: string) => void;
133
+ /**
134
+ * Returns true when Channel is about to load an initial targeted message.
135
+ *
136
+ * @internal
137
+ */
138
+ hasPendingInitialTargetLoad?: () => boolean;
133
139
  /**
134
140
  * Abort controller for cancelling async requests made for uploading images/files
135
141
  * Its a map of filename and AbortController
package/src/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "8.13.13"
2
+ "version": "8.13.15"
3
3
  }