stream-chat-react-native-core 5.23.0-beta.1 → 5.23.0-beta.2

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 (55) hide show
  1. package/lib/commonjs/components/Channel/Channel.js +587 -384
  2. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  3. package/lib/commonjs/components/MessageList/MessageList.js +170 -179
  4. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  5. package/lib/commonjs/components/MessageList/hooks/useMessageList.js +6 -1
  6. package/lib/commonjs/components/MessageList/hooks/useMessageList.js.map +1 -1
  7. package/lib/commonjs/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.js +36 -0
  8. package/lib/commonjs/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.js.map +1 -0
  9. package/lib/commonjs/i18n/en.json +1 -1
  10. package/lib/commonjs/i18n/fr.json +54 -54
  11. package/lib/commonjs/i18n/hi.json +52 -52
  12. package/lib/commonjs/i18n/it.json +52 -52
  13. package/lib/commonjs/i18n/nl.json +52 -52
  14. package/lib/commonjs/i18n/ru.json +54 -54
  15. package/lib/commonjs/i18n/tr.json +54 -54
  16. package/lib/commonjs/version.json +1 -1
  17. package/lib/module/components/Channel/Channel.js +587 -384
  18. package/lib/module/components/Channel/Channel.js.map +1 -1
  19. package/lib/module/components/MessageList/MessageList.js +170 -179
  20. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  21. package/lib/module/components/MessageList/hooks/useMessageList.js +6 -1
  22. package/lib/module/components/MessageList/hooks/useMessageList.js.map +1 -1
  23. package/lib/module/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.js +36 -0
  24. package/lib/module/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.js.map +1 -0
  25. package/lib/module/i18n/en.json +1 -1
  26. package/lib/module/i18n/fr.json +54 -54
  27. package/lib/module/i18n/hi.json +52 -52
  28. package/lib/module/i18n/it.json +52 -52
  29. package/lib/module/i18n/nl.json +52 -52
  30. package/lib/module/i18n/ru.json +54 -54
  31. package/lib/module/i18n/tr.json +54 -54
  32. package/lib/module/version.json +1 -1
  33. package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts +6 -1
  34. package/lib/typescript/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.d.ts +4 -0
  35. package/lib/typescript/i18n/en.json +1 -1
  36. package/lib/typescript/i18n/fr.json +54 -54
  37. package/lib/typescript/i18n/hi.json +52 -52
  38. package/lib/typescript/i18n/it.json +52 -52
  39. package/lib/typescript/i18n/nl.json +52 -52
  40. package/lib/typescript/i18n/ru.json +54 -54
  41. package/lib/typescript/i18n/tr.json +54 -54
  42. package/package.json +1 -1
  43. package/src/components/Channel/Channel.tsx +237 -61
  44. package/src/components/MessageList/MessageList.tsx +190 -180
  45. package/src/components/MessageList/__tests__/useMessageList.test.tsx +5 -2
  46. package/src/components/MessageList/hooks/useMessageList.ts +8 -1
  47. package/src/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.ts +44 -0
  48. package/src/i18n/en.json +1 -1
  49. package/src/i18n/fr.json +54 -54
  50. package/src/i18n/hi.json +52 -52
  51. package/src/i18n/it.json +52 -52
  52. package/src/i18n/nl.json +52 -52
  53. package/src/i18n/ru.json +54 -54
  54. package/src/i18n/tr.json +54 -54
  55. package/src/version.json +1 -1
@@ -588,14 +588,13 @@ const ChannelWithContext = <
588
588
  */
589
589
  const [hasNoMoreRecentMessagesToLoad, setHasNoMoreRecentMessagesToLoad] = useState(true);
590
590
 
591
+ const { setTargetedMessage, targetedMessage } = useTargetedMessage();
592
+
591
593
  /**
592
- * messages array is tracked in ref so that
593
- * functions can be passed to message list context without any change due to dependency
594
+ * If we loaded a channel around message
595
+ * We may have moved latest message to a new message set in that case mark this ref to avoid fetching
594
596
  */
595
- const messagesRef = useRef(messages);
596
- messagesRef.current = messages;
597
-
598
- const { setTargetedMessage, targetedMessage } = useTargetedMessage();
597
+ const hasOverlappingRecentMessagesRef = useRef(false);
599
598
 
600
599
  /**
601
600
  * This ref will hold the abort controllers for
@@ -605,6 +604,7 @@ const ChannelWithContext = <
605
604
  const uploadAbortControllerRef = useRef<Map<string, AbortController>>(new Map());
606
605
 
607
606
  const channelId = channel?.id || '';
607
+
608
608
  useEffect(() => {
609
609
  const initChannel = () => {
610
610
  if (!channel || !shouldSyncChannel || channel.offlineMode) return;
@@ -707,7 +707,9 @@ const ChannelWithContext = <
707
707
  throttle(
708
708
  () => {
709
709
  if (channel) {
710
+ clearInterval(mergeSetsIntervalRef.current);
710
711
  setMessages([...channel.state.messages]);
712
+ restartSetsMergeFuncRef.current();
711
713
  }
712
714
  },
713
715
  newMessageStateUpdateThrottleInterval,
@@ -813,48 +815,69 @@ const ChannelWithContext = <
813
815
  return unsubscribe;
814
816
  }, [channelId]);
815
817
 
818
+ useEffect(() => {
819
+ const handleEvent: EventHandler<StreamChatGenerics> = (event) => {
820
+ if (channel.cid === event.cid) copyChannelState();
821
+ };
822
+
823
+ const { unsubscribe } = client.on('notification.mark_read', handleEvent);
824
+ return unsubscribe;
825
+ }, []);
826
+
816
827
  const channelQueryCallRef = useRef(
817
828
  async (
818
829
  queryCall: () => Promise<void>,
819
830
  onAfterQueryCall: (() => void) | undefined = undefined,
820
- // if we are targeting a message after the query, pass it here
821
- targetMessageId: string | undefined = undefined,
831
+ // if we are scrolling to a message after the query, pass it here
832
+ scrollToMessageId: string | (() => string | undefined) | undefined = undefined,
822
833
  ) => {
823
834
  setError(false);
824
835
  try {
836
+ clearInterval(mergeSetsIntervalRef.current);
825
837
  await queryCall();
826
838
  setLastRead(new Date());
827
839
  setHasMore(true);
828
- if (channel && targetMessageId) {
829
- // 30 is the maxToRenderPerBatch in MessageList
830
- const limit = 30 * 3; // we allow 3 batches of messages to be rendered
831
- const currentMessages = channel.state.messages;
832
- // number of messages are over the limit, limit the length of messages
833
- if (currentMessages.length > limit) {
834
- const targetMessageIndex = currentMessages.findIndex(
835
- ({ id }) => id === targetMessageId,
836
- );
837
- let startIndex = Math.max(targetMessageIndex - Math.floor(limit / 2), 0);
838
- const endIndex = targetMessageIndex + Math.floor(limit / 2);
839
- if (endIndex > currentMessages.length) {
840
- startIndex = Math.max(startIndex - (endIndex - currentMessages.length - 1) - 1, 0);
840
+ const currentMessages = channel.state.messages;
841
+ const hadCurrentLatestMessages =
842
+ currentMessages.length > 0 && currentMessages === channel.state.latestMessages;
843
+ if (typeof scrollToMessageId === 'function') {
844
+ scrollToMessageId = scrollToMessageId();
845
+ }
846
+ const scrollToMessageIndex = scrollToMessageId
847
+ ? currentMessages.findIndex(({ id }) => id === scrollToMessageId)
848
+ : -1;
849
+ if (channel && scrollToMessageIndex !== -1) {
850
+ // We assume that on average user sees 5 messages on screen
851
+ // We dont want new renders to happen while scrolling to the targeted message
852
+ // hence we limit the number of messages to be rendered after the targeted message to 5 - 1 = 4
853
+ // NOTE: we have one drawback here, if there were already a split latest and current message set
854
+ // the previous latest message set will be thrown away as we cannot merge it with the current message set after the target message is set
855
+ const limitAfter = 4;
856
+ const currentLength = currentMessages.length;
857
+ if (scrollToMessageIndex !== -1) {
858
+ const noOfMessagesAfter = currentLength - scrollToMessageIndex - 1;
859
+ // number of messages are over the limit, limit the length of messages
860
+ if (noOfMessagesAfter > limitAfter) {
861
+ const endIndex = scrollToMessageIndex + limitAfter;
862
+ channel.state.clearMessages();
863
+ channel.state.messages = currentMessages.slice(0, endIndex + 1);
864
+ splitLatestCurrentMessageSetRef.current();
865
+ const restOfMessages = currentMessages.slice(endIndex + 1);
866
+ if (hadCurrentLatestMessages) {
867
+ const latestSet = channel.state.messageSets.find((set) => set.isLatest);
868
+ if (latestSet) {
869
+ latestSet.messages = restOfMessages;
870
+ hasOverlappingRecentMessagesRef.current = true;
871
+ }
872
+ }
841
873
  }
842
- const hadLatestMessages = channel.state.messages === channel.state.latestMessages;
843
- const recentMessage = currentMessages[currentMessages.length - 1];
844
- channel.state.clearMessages();
845
- channel.state.messages = currentMessages.slice(startIndex, endIndex);
846
- const stillHasLatestMessages =
847
- hadLatestMessages &&
848
- channel.state.messages[channel.state.messages.length - 1] === recentMessage;
849
- setHasNoMoreRecentMessagesToLoad(stillHasLatestMessages);
850
- channel.state.setIsUpToDate(stillHasLatestMessages);
851
874
  }
852
- } else {
853
- const areLatestMessages = channel.state.messages === channel.state.latestMessages;
854
- setHasNoMoreRecentMessagesToLoad(areLatestMessages);
855
- channel.state.setIsUpToDate(areLatestMessages);
856
875
  }
876
+ const hasLatestMessages = channel.state.latestMessages.length > 0;
877
+ channel.state.setIsUpToDate(hasLatestMessages);
878
+ setHasNoMoreRecentMessagesToLoad(hasLatestMessages);
857
879
  copyChannelState();
880
+ restartSetsMergeFuncRef.current();
858
881
  onAfterQueryCall?.();
859
882
  } catch (err) {
860
883
  if (err instanceof Error) {
@@ -877,35 +900,46 @@ const ChannelWithContext = <
877
900
  // query for messages around the last read date
878
901
  return channelQueryCallRef.current(
879
902
  async () => {
880
- setLoading(true);
903
+ const unreadCount = channel.countUnread();
904
+ if (unreadCount === 0) return;
905
+ const isLatestMessageSetShown = !!channel.state.messageSets.find(
906
+ (set) => set.isCurrent && set.isLatest,
907
+ );
908
+ if (isLatestMessageSetShown && unreadCount <= channel.state.messages.length) {
909
+ unreadMessageIdToScrollTo =
910
+ channel.state.messages[channel.state.messages.length - unreadCount].id;
911
+ return;
912
+ }
881
913
  const lastReadDate = channel.lastRead();
914
+
882
915
  // if last read date is present we can just fetch messages around that date
883
916
  // last read date not being present is an edge case if somewhere the user of SDK deletes the read state (this will usually never happen)
884
917
  if (lastReadDate) {
918
+ setLoading(true);
885
919
  // get totally 30 messages... max 15 before last read date and max 15 after last read date
886
920
  // ref: https://github.com/GetStream/chat/pull/2588
887
- await channel.query(
921
+ const res = await channel.query(
888
922
  {
889
923
  messages: {
890
924
  created_at_around: lastReadDate,
891
925
  limit: 30,
892
926
  },
927
+ watch: true,
893
928
  },
894
929
  'new',
895
930
  );
896
- unreadMessageIdToScrollTo = channel.state.messages.find(
897
- (m) => lastReadDate < m.created_at,
931
+ unreadMessageIdToScrollTo = res.messages.find(
932
+ (m) => lastReadDate < (m.created_at ? new Date(m.created_at) : new Date()),
898
933
  )?.id;
934
+ if (unreadMessageIdToScrollTo) {
935
+ channel.state.loadMessageIntoState(unreadMessageIdToScrollTo);
936
+ }
899
937
  } else {
900
- // we just load the latest messages (25 is the default) and we cant scroll to first unread message
901
- await channel.state.loadMessageIntoState('latest');
902
- }
903
- },
904
- () => {
905
- if (unreadMessageIdToScrollTo) {
906
- setTargetedMessage(unreadMessageIdToScrollTo);
938
+ await loadLatestMessagesRef.current();
907
939
  }
908
940
  },
941
+ undefined,
942
+ () => unreadMessageIdToScrollTo,
909
943
  );
910
944
  };
911
945
 
@@ -920,9 +954,25 @@ const ChannelWithContext = <
920
954
  async () => {
921
955
  setLoading(true);
922
956
  if (messageIdToLoadAround) {
957
+ setMessages([]);
923
958
  await channel.state.loadMessageIntoState(messageIdToLoadAround);
959
+ const currentMessageSet = channel.state.messageSets.find((set) => set.isCurrent);
960
+ if (currentMessageSet && !currentMessageSet?.isLatest) {
961
+ // if the current message set is not the latest, we will throw away the latest messages
962
+ // in order to attempt to not throw away, will attempt to merge it by loading 25 more messages
963
+ const recentCurrentSetMsgId =
964
+ currentMessageSet.messages[currentMessageSet.messages.length - 1].id;
965
+ await channel.query({
966
+ messages: {
967
+ id_gte: recentCurrentSetMsgId,
968
+ limit: 25,
969
+ },
970
+ watch: true,
971
+ });
972
+ // if the gap is more than 25, we will unfortunately have to throw away the latest messages
973
+ }
924
974
  } else {
925
- await channel.state.loadMessageIntoState('latest');
975
+ await loadLatestMessagesRef.current();
926
976
  }
927
977
  },
928
978
  () => {
@@ -955,16 +1005,103 @@ const ChannelWithContext = <
955
1005
  }
956
1006
  });
957
1007
 
1008
+ /**
1009
+ * Utility method to mark that current set if latest into two.
1010
+ * With an empty latest set
1011
+ * This is useful when we know that we dont know the latest messages anymore
1012
+ * Or if we are loading a channel around a message
1013
+ */
1014
+ const splitLatestCurrentMessageSetRef = useRef(() => {
1015
+ const currentLatestSet = channel.state.messageSets.find((set) => set.isCurrent && set.isLatest);
1016
+ if (!currentLatestSet) return;
1017
+ // unmark the current latest set
1018
+ currentLatestSet.isLatest = false;
1019
+ // create a new set with empty latest messages
1020
+ channel.state.messageSets.push({
1021
+ isCurrent: false,
1022
+ isLatest: true,
1023
+ messages: [],
1024
+ });
1025
+ });
1026
+
1027
+ /**
1028
+ * Utility method to merge current and latest message set.
1029
+ * Returns true if merge was successful, false otherwise.
1030
+ */
1031
+ const mergeOverlappingMessageSetsRef = useRef((limitToMaxRenderPerBatch = false) => {
1032
+ if (hasOverlappingRecentMessagesRef.current) {
1033
+ const limit = 30; // 30 is the maxToRenderPerBatch
1034
+ // merge current and latest sets
1035
+ const latestMessageSet = channel.state.messageSets.find((set) => set.isLatest);
1036
+ const currentMessageSet = channel.state.messageSets.find((set) => set.isCurrent);
1037
+ if (latestMessageSet && currentMessageSet && latestMessageSet !== currentMessageSet) {
1038
+ if (limitToMaxRenderPerBatch && latestMessageSet.messages.length > limit) {
1039
+ currentMessageSet.messages = currentMessageSet.messages.concat(
1040
+ latestMessageSet.messages.slice(0, limit),
1041
+ );
1042
+ latestMessageSet.messages = latestMessageSet.messages.slice(limit);
1043
+ } else {
1044
+ channel.state.messageSets = channel.state.messageSets.filter((set) => !set.isLatest);
1045
+ currentMessageSet.messages = currentMessageSet.messages.concat(latestMessageSet.messages);
1046
+ currentMessageSet.isLatest = true;
1047
+ hasOverlappingRecentMessagesRef.current = false;
1048
+ clearInterval(mergeSetsIntervalRef.current);
1049
+ }
1050
+ return true;
1051
+ }
1052
+ }
1053
+ return false;
1054
+ });
1055
+
1056
+ const mergeSetsIntervalRef = useRef<NodeJS.Timeout>();
1057
+
1058
+ // clear the interval on unmount
1059
+ useEffect(
1060
+ () => () => {
1061
+ clearInterval(mergeSetsIntervalRef.current);
1062
+ },
1063
+ [],
1064
+ );
1065
+
1066
+ // if we had split the latest and current message set, we try to merge them back
1067
+ const restartSetsMergeFuncRef = useRef(() => {
1068
+ clearInterval(mergeSetsIntervalRef.current);
1069
+ if (!hasOverlappingRecentMessagesRef.current) return;
1070
+ mergeSetsIntervalRef.current = setInterval(() => {
1071
+ const currentLength = channel.state.messages.length || 0;
1072
+ const didMerge = mergeOverlappingMessageSetsRef.current(true);
1073
+ if (didMerge && channel.state.messages.length !== currentLength) {
1074
+ setMessages([...channel.state.messages]);
1075
+ }
1076
+ }, 1000);
1077
+ });
1078
+
1079
+ /**
1080
+ * Shows the latest messages from the channel state
1081
+ * If recent messages are empty, fetches new
1082
+ * @param clearLatest If true, clears the latest messages before loading (useful for complete refresh)
1083
+ */
1084
+ const loadLatestMessagesRef = useRef(async (clearLatest = false) => {
1085
+ mergeOverlappingMessageSetsRef.current();
1086
+ if (clearLatest) {
1087
+ const latestSet = channel.state.messageSets.find((set) => set.isLatest);
1088
+ if (latestSet) latestSet.messages = [];
1089
+ }
1090
+ if (channel.state.latestMessages.length === 0) {
1091
+ await channel.query({}, 'latest');
1092
+ }
1093
+ await channel.state.loadMessageIntoState('latest');
1094
+ });
1095
+
958
1096
  const loadChannel = () =>
959
1097
  channelQueryCallRef.current(async () => {
960
1098
  if (!channel?.initialized || !channel.state.isUpToDate) {
961
1099
  await channel?.watch();
962
- setHasNoMoreRecentMessagesToLoad(true);
963
- channel?.state.setIsUpToDate(true);
964
1100
  } else {
965
- await channel.state.loadMessageIntoState('latest');
1101
+ await loadLatestMessagesRef.current(true);
966
1102
  }
967
- return;
1103
+ channel?.state.setIsUpToDate(true);
1104
+ setHasNoMoreRecentMessagesToLoad(true);
968
1105
  });
969
1106
 
970
1107
  const reloadThread = async () => {
@@ -1002,6 +1139,8 @@ const ChannelWithContext = <
1002
1139
 
1003
1140
  const resyncChannel = async () => {
1004
1141
  if (!channel || syncingChannel) return;
1142
+ hasOverlappingRecentMessagesRef.current = false;
1143
+ clearInterval(mergeSetsIntervalRef.current);
1005
1144
  setSyncingChannel(true);
1006
1145
 
1007
1146
  setError(false);
@@ -1081,11 +1220,11 @@ const ChannelWithContext = <
1081
1220
  finalMessages = state.messages;
1082
1221
  }
1083
1222
 
1084
- setHasNoMoreRecentMessagesToLoad(true);
1085
1223
  channel.state.setIsUpToDate(true);
1086
1224
  channel.state.clearMessages();
1087
1225
  channel.state.addMessagesSorted(finalMessages);
1088
1226
  channel.state.addPinnedMessages(state.pinned_messages);
1227
+ setHasNoMoreRecentMessagesToLoad(true);
1089
1228
  setHasMore(true);
1090
1229
  copyChannelState();
1091
1230
 
@@ -1141,10 +1280,10 @@ const ChannelWithContext = <
1141
1280
  const reloadChannel = () =>
1142
1281
  channelQueryCallRef.current(async () => {
1143
1282
  setLoading(true);
1144
- await channel.state.loadMessageIntoState('latest');
1283
+ await loadLatestMessagesRef.current(true);
1145
1284
  setLoading(false);
1146
- setHasNoMoreRecentMessagesToLoad(true);
1147
1285
  channel?.state.setIsUpToDate(true);
1286
+ setHasNoMoreRecentMessagesToLoad(true);
1148
1287
  });
1149
1288
 
1150
1289
  /**
@@ -1162,8 +1301,10 @@ const ChannelWithContext = <
1162
1301
  }: Parameters<ChannelContextValue<StreamChatGenerics>['loadChannelAtMessage']>[0]) => {
1163
1302
  if (!channel) return;
1164
1303
  channel.state.setIsUpToDate(false);
1304
+ hasOverlappingRecentMessagesRef.current = false;
1305
+ clearInterval(mergeSetsIntervalRef.current);
1165
1306
  channel.state.clearMessages();
1166
- setMessages([...channel.state.messages]);
1307
+ setMessages([]);
1167
1308
  if (!messageId) {
1168
1309
  await channel.query({
1169
1310
  messages: {
@@ -1219,9 +1360,18 @@ const ChannelWithContext = <
1219
1360
  });
1220
1361
 
1221
1362
  if (state.messages.length < limit) {
1363
+ // make current set as the latest
1364
+ const currentSet = channel.state.messageSets.find((set) => set.isCurrent);
1365
+ if (currentSet && !currentSet.isLatest) {
1366
+ channel.state.messageSets = channel.state.messageSets.filter((set) => !set.isLatest);
1367
+ currentSet.isLatest = true;
1368
+ }
1222
1369
  channel.state.setIsUpToDate(true);
1370
+ setHasNoMoreRecentMessagesToLoad(true);
1223
1371
  } else {
1372
+ splitLatestCurrentMessageSetRef.current();
1224
1373
  channel.state.setIsUpToDate(false);
1374
+ setHasNoMoreRecentMessagesToLoad(false);
1225
1375
  }
1226
1376
  };
1227
1377
 
@@ -1494,6 +1644,8 @@ const ChannelWithContext = <
1494
1644
  attachments: message.attachments || [],
1495
1645
  });
1496
1646
 
1647
+ mergeOverlappingMessageSetsRef.current();
1648
+
1497
1649
  if (!channel?.state.isUpToDate) {
1498
1650
  await reloadChannel();
1499
1651
  }
@@ -1533,6 +1685,7 @@ const ChannelWithContext = <
1533
1685
  const loadMoreFinished = useRef(
1534
1686
  debounce(
1535
1687
  (updatedHasMore: boolean, newMessages: ChannelState<StreamChatGenerics>['messages']) => {
1688
+ setLoading(false);
1536
1689
  setLoadingMore(false);
1537
1690
  setError(false);
1538
1691
  setHasMore(updatedHasMore);
@@ -1551,9 +1704,8 @@ const ChannelWithContext = <
1551
1704
  if (loadingMore || hasMore === false) {
1552
1705
  return;
1553
1706
  }
1554
- setLoadingMore(true);
1555
1707
 
1556
- const currentMessages = messagesRef.current;
1708
+ const currentMessages = channel.state.messages;
1557
1709
 
1558
1710
  if (!currentMessages.length) {
1559
1711
  return setLoadingMore(false);
@@ -1565,6 +1717,8 @@ const ChannelWithContext = <
1565
1717
  return setLoadingMore(false);
1566
1718
  }
1567
1719
 
1720
+ setLoadingMore(true);
1721
+
1568
1722
  const oldestID = oldestMessage && oldestMessage.id;
1569
1723
 
1570
1724
  try {
@@ -1601,18 +1755,31 @@ const ChannelWithContext = <
1601
1755
  PaginatedMessageListContextValue<StreamChatGenerics>['loadMoreRecent']
1602
1756
  >(
1603
1757
  async (limit = 5) => {
1604
- if (hasNoMoreRecentMessagesToLoad) {
1758
+ const latestMessageSet = channel.state.messageSets.find((set) => set.isLatest);
1759
+ const latestLengthBeforeMerge = latestMessageSet?.messages.length || 0;
1760
+ const didMerge = mergeOverlappingMessageSetsRef.current(true);
1761
+ if (didMerge) {
1762
+ if (latestMessageSet && latestLengthBeforeMerge >= limit) {
1763
+ setLoadingMoreRecent(true);
1764
+ channel.state.setIsUpToDate(true);
1765
+ setHasNoMoreRecentMessagesToLoad(true);
1766
+ loadMoreRecentFinished(channel.state.messages);
1767
+ restartSetsMergeFuncRef.current();
1768
+ return;
1769
+ }
1770
+ }
1771
+ if (channel.state.isUpToDate) {
1772
+ setLoadingMoreRecent(false);
1605
1773
  return;
1606
1774
  }
1607
- setLoadingMoreRecent(true);
1608
- const currentMessages = messagesRef.current;
1775
+ const currentMessages = channel.state.messages;
1609
1776
  const recentMessage = currentMessages[currentMessages.length - 1];
1610
1777
 
1611
1778
  if (recentMessage?.status !== MessageStatusTypes.RECEIVED) {
1612
1779
  setLoadingMoreRecent(false);
1613
1780
  return;
1614
1781
  }
1615
-
1782
+ setLoadingMoreRecent(true);
1616
1783
  try {
1617
1784
  if (channel) {
1618
1785
  const queryResponse = await channel.query({
@@ -1622,7 +1789,15 @@ const ChannelWithContext = <
1622
1789
  },
1623
1790
  watch: true,
1624
1791
  });
1625
- setHasNoMoreRecentMessagesToLoad(queryResponse.messages.length < limit);
1792
+ const gotAllRecentMessages = queryResponse.messages.length < limit;
1793
+ const currentSet = channel.state.messageSets.find((set) => set.isCurrent);
1794
+ if (gotAllRecentMessages && currentSet && !currentSet.isLatest) {
1795
+ channel.state.messageSets = channel.state.messageSets.filter((set) => !set.isLatest);
1796
+ // make current set as the latest
1797
+ currentSet.isLatest = true;
1798
+ }
1799
+ channel.state.setIsUpToDate(gotAllRecentMessages);
1800
+ setHasNoMoreRecentMessagesToLoad(gotAllRecentMessages);
1626
1801
  loadMoreRecentFinished(channel.state.messages);
1627
1802
  }
1628
1803
  } catch (err) {
@@ -2190,6 +2365,7 @@ export const Channel = <
2190
2365
 
2191
2366
  return (
2192
2367
  <ChannelWithContext<StreamChatGenerics>
2368
+ key={props.channel?.cid}
2193
2369
  {...{
2194
2370
  client,
2195
2371
  enableOfflineSupport,