stream-chat-react-native-core 8.11.0 → 8.12.0-beta.1

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 (197) hide show
  1. package/README.md +1 -1
  2. package/lib/commonjs/components/Channel/Channel.js +26 -17
  3. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  4. package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js +3 -2
  5. package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
  6. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +4 -0
  7. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  8. package/lib/commonjs/components/Message/Message.js +6 -8
  9. package/lib/commonjs/components/Message/Message.js.map +1 -1
  10. package/lib/commonjs/components/Message/MessageSimple/MessageBlocked.js +70 -0
  11. package/lib/commonjs/components/Message/MessageSimple/MessageBlocked.js.map +1 -0
  12. package/lib/commonjs/components/Message/MessageSimple/MessageContent.js +0 -2
  13. package/lib/commonjs/components/Message/MessageSimple/MessageContent.js.map +1 -1
  14. package/lib/commonjs/components/Message/MessageSimple/MessageWrapper.js +126 -0
  15. package/lib/commonjs/components/Message/MessageSimple/MessageWrapper.js.map +1 -0
  16. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js +1 -3
  17. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  18. package/lib/commonjs/components/MessageList/MessageFlashList.js +74 -105
  19. package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
  20. package/lib/commonjs/components/MessageList/MessageList.js +77 -119
  21. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  22. package/lib/commonjs/components/MessageList/UnreadMessagesNotification.js +2 -2
  23. package/lib/commonjs/components/MessageList/UnreadMessagesNotification.js.map +1 -1
  24. package/lib/commonjs/components/MessageList/hooks/useMessageDateSeparator.js +45 -0
  25. package/lib/commonjs/components/MessageList/hooks/useMessageDateSeparator.js.map +1 -0
  26. package/lib/commonjs/components/MessageList/hooks/useMessageGroupStyles.js +46 -0
  27. package/lib/commonjs/components/MessageList/hooks/useMessageGroupStyles.js.map +1 -0
  28. package/lib/commonjs/components/MessageList/hooks/useMessageList.js +49 -29
  29. package/lib/commonjs/components/MessageList/hooks/useMessageList.js.map +1 -1
  30. package/lib/commonjs/components/MessageList/utils/getDateSeparators.js +5 -14
  31. package/lib/commonjs/components/MessageList/utils/getDateSeparators.js.map +1 -1
  32. package/lib/commonjs/components/MessageList/utils/getGroupStyles.js +22 -17
  33. package/lib/commonjs/components/MessageList/utils/getGroupStyles.js.map +1 -1
  34. package/lib/commonjs/components/MessageList/utils/getLastReceivedMessage.js +1 -1
  35. package/lib/commonjs/components/MessageList/utils/getLastReceivedMessage.js.map +1 -1
  36. package/lib/commonjs/components/index.js +33 -0
  37. package/lib/commonjs/components/index.js.map +1 -1
  38. package/lib/commonjs/contexts/channelContext/ChannelContext.js.map +1 -1
  39. package/lib/commonjs/contexts/messageContext/MessageContext.js.map +1 -1
  40. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +3 -0
  41. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  42. package/lib/commonjs/contexts/messageListItemContext/MessageListItemContext.js +29 -0
  43. package/lib/commonjs/contexts/messageListItemContext/MessageListItemContext.js.map +1 -0
  44. package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
  45. package/lib/commonjs/contexts/themeContext/utils/theme.js +6 -0
  46. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  47. package/lib/commonjs/state-store/channel-unread-state.js +31 -0
  48. package/lib/commonjs/state-store/channel-unread-state.js.map +1 -0
  49. package/lib/commonjs/state-store/message-list-prev-next-state.js +46 -0
  50. package/lib/commonjs/state-store/message-list-prev-next-state.js.map +1 -0
  51. package/lib/commonjs/store/apis/getChannelMessages.js +7 -10
  52. package/lib/commonjs/store/apis/getChannelMessages.js.map +1 -1
  53. package/lib/commonjs/utils/utils.js +4 -4
  54. package/lib/commonjs/utils/utils.js.map +1 -1
  55. package/lib/commonjs/version.json +1 -1
  56. package/lib/module/components/Channel/Channel.js +26 -17
  57. package/lib/module/components/Channel/Channel.js.map +1 -1
  58. package/lib/module/components/Channel/hooks/useCreateChannelContext.js +3 -2
  59. package/lib/module/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
  60. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +4 -0
  61. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  62. package/lib/module/components/Message/Message.js +6 -8
  63. package/lib/module/components/Message/Message.js.map +1 -1
  64. package/lib/module/components/Message/MessageSimple/MessageBlocked.js +70 -0
  65. package/lib/module/components/Message/MessageSimple/MessageBlocked.js.map +1 -0
  66. package/lib/module/components/Message/MessageSimple/MessageContent.js +0 -2
  67. package/lib/module/components/Message/MessageSimple/MessageContent.js.map +1 -1
  68. package/lib/module/components/Message/MessageSimple/MessageWrapper.js +126 -0
  69. package/lib/module/components/Message/MessageSimple/MessageWrapper.js.map +1 -0
  70. package/lib/module/components/Message/hooks/useCreateMessageContext.js +1 -3
  71. package/lib/module/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  72. package/lib/module/components/MessageList/MessageFlashList.js +74 -105
  73. package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
  74. package/lib/module/components/MessageList/MessageList.js +77 -119
  75. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  76. package/lib/module/components/MessageList/UnreadMessagesNotification.js +2 -2
  77. package/lib/module/components/MessageList/UnreadMessagesNotification.js.map +1 -1
  78. package/lib/module/components/MessageList/hooks/useMessageDateSeparator.js +45 -0
  79. package/lib/module/components/MessageList/hooks/useMessageDateSeparator.js.map +1 -0
  80. package/lib/module/components/MessageList/hooks/useMessageGroupStyles.js +46 -0
  81. package/lib/module/components/MessageList/hooks/useMessageGroupStyles.js.map +1 -0
  82. package/lib/module/components/MessageList/hooks/useMessageList.js +49 -29
  83. package/lib/module/components/MessageList/hooks/useMessageList.js.map +1 -1
  84. package/lib/module/components/MessageList/utils/getDateSeparators.js +5 -14
  85. package/lib/module/components/MessageList/utils/getDateSeparators.js.map +1 -1
  86. package/lib/module/components/MessageList/utils/getGroupStyles.js +22 -17
  87. package/lib/module/components/MessageList/utils/getGroupStyles.js.map +1 -1
  88. package/lib/module/components/MessageList/utils/getLastReceivedMessage.js +1 -1
  89. package/lib/module/components/MessageList/utils/getLastReceivedMessage.js.map +1 -1
  90. package/lib/module/components/index.js +33 -0
  91. package/lib/module/components/index.js.map +1 -1
  92. package/lib/module/contexts/channelContext/ChannelContext.js.map +1 -1
  93. package/lib/module/contexts/messageContext/MessageContext.js.map +1 -1
  94. package/lib/module/contexts/messageInputContext/MessageInputContext.js +3 -0
  95. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  96. package/lib/module/contexts/messageListItemContext/MessageListItemContext.js +29 -0
  97. package/lib/module/contexts/messageListItemContext/MessageListItemContext.js.map +1 -0
  98. package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
  99. package/lib/module/contexts/themeContext/utils/theme.js +6 -0
  100. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  101. package/lib/module/state-store/channel-unread-state.js +31 -0
  102. package/lib/module/state-store/channel-unread-state.js.map +1 -0
  103. package/lib/module/state-store/message-list-prev-next-state.js +46 -0
  104. package/lib/module/state-store/message-list-prev-next-state.js.map +1 -0
  105. package/lib/module/store/apis/getChannelMessages.js +7 -10
  106. package/lib/module/store/apis/getChannelMessages.js.map +1 -1
  107. package/lib/module/utils/utils.js +4 -4
  108. package/lib/module/utils/utils.js.map +1 -1
  109. package/lib/module/version.json +1 -1
  110. package/lib/typescript/components/Channel/Channel.d.ts +3 -3
  111. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  112. package/lib/typescript/components/Channel/hooks/useCreateChannelContext.d.ts +1 -1
  113. package/lib/typescript/components/Channel/hooks/useCreateChannelContext.d.ts.map +1 -1
  114. package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts +1 -1
  115. package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts.map +1 -1
  116. package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts +2 -2
  117. package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts.map +1 -1
  118. package/lib/typescript/components/Message/Message.d.ts +1 -1
  119. package/lib/typescript/components/Message/Message.d.ts.map +1 -1
  120. package/lib/typescript/components/Message/MessageSimple/MessageBlocked.d.ts +19 -0
  121. package/lib/typescript/components/Message/MessageSimple/MessageBlocked.d.ts.map +1 -0
  122. package/lib/typescript/components/Message/MessageSimple/MessageContent.d.ts.map +1 -1
  123. package/lib/typescript/components/Message/MessageSimple/MessageWrapper.d.ts +7 -0
  124. package/lib/typescript/components/Message/MessageSimple/MessageWrapper.d.ts.map +1 -0
  125. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts +1 -1
  126. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts.map +1 -1
  127. package/lib/typescript/components/MessageList/MessageFlashList.d.ts +1 -1
  128. package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
  129. package/lib/typescript/components/MessageList/MessageList.d.ts +1 -1
  130. package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
  131. package/lib/typescript/components/MessageList/hooks/useMessageDateSeparator.d.ts +16 -0
  132. package/lib/typescript/components/MessageList/hooks/useMessageDateSeparator.d.ts.map +1 -0
  133. package/lib/typescript/components/MessageList/hooks/useMessageGroupStyles.d.ts +15 -0
  134. package/lib/typescript/components/MessageList/hooks/useMessageGroupStyles.d.ts.map +1 -0
  135. package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts +8 -0
  136. package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts.map +1 -1
  137. package/lib/typescript/components/MessageList/utils/getDateSeparators.d.ts +12 -0
  138. package/lib/typescript/components/MessageList/utils/getDateSeparators.d.ts.map +1 -1
  139. package/lib/typescript/components/MessageList/utils/getGroupStyles.d.ts +25 -0
  140. package/lib/typescript/components/MessageList/utils/getGroupStyles.d.ts.map +1 -1
  141. package/lib/typescript/components/MessageList/utils/getLastReceivedMessage.d.ts.map +1 -1
  142. package/lib/typescript/components/index.d.ts +3 -0
  143. package/lib/typescript/components/index.d.ts.map +1 -1
  144. package/lib/typescript/contexts/channelContext/ChannelContext.d.ts +10 -4
  145. package/lib/typescript/contexts/channelContext/ChannelContext.d.ts.map +1 -1
  146. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +4 -1
  147. package/lib/typescript/contexts/messageContext/MessageContext.d.ts.map +1 -1
  148. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
  149. package/lib/typescript/contexts/messageListItemContext/MessageListItemContext.d.ts +37 -0
  150. package/lib/typescript/contexts/messageListItemContext/MessageListItemContext.d.ts.map +1 -0
  151. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +11 -1
  152. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts.map +1 -1
  153. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +6 -0
  154. package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
  155. package/lib/typescript/hooks/useTranslatedMessage.d.ts.map +1 -1
  156. package/lib/typescript/state-store/channel-unread-state.d.ts +12 -0
  157. package/lib/typescript/state-store/channel-unread-state.d.ts.map +1 -0
  158. package/lib/typescript/state-store/message-list-prev-next-state.d.ts +16 -0
  159. package/lib/typescript/state-store/message-list-prev-next-state.d.ts.map +1 -0
  160. package/lib/typescript/store/apis/getChannelMessages.d.ts.map +1 -1
  161. package/lib/typescript/utils/utils.d.ts +1 -2
  162. package/lib/typescript/utils/utils.d.ts.map +1 -1
  163. package/package.json +2 -2
  164. package/src/components/Channel/Channel.tsx +31 -16
  165. package/src/components/Channel/hooks/useCreateChannelContext.ts +2 -2
  166. package/src/components/Channel/hooks/useCreateMessagesContext.ts +4 -0
  167. package/src/components/Message/Message.tsx +6 -15
  168. package/src/components/Message/MessageSimple/MessageBlocked.tsx +67 -0
  169. package/src/components/Message/MessageSimple/MessageContent.tsx +0 -2
  170. package/src/components/Message/MessageSimple/MessageWrapper.tsx +147 -0
  171. package/src/components/Message/MessageSimple/__tests__/MessageStatus.test.js +1 -5
  172. package/src/components/Message/hooks/useCreateMessageContext.ts +0 -3
  173. package/src/components/MessageList/MessageFlashList.tsx +81 -143
  174. package/src/components/MessageList/MessageList.tsx +83 -165
  175. package/src/components/MessageList/UnreadMessagesNotification.tsx +2 -2
  176. package/src/components/MessageList/__tests__/MessageList.test.js +10 -8
  177. package/src/components/MessageList/__tests__/useMessageDateSeparator.test.ts +83 -0
  178. package/src/components/MessageList/hooks/useMessageDateSeparator.ts +66 -0
  179. package/src/components/MessageList/hooks/useMessageGroupStyles.ts +75 -0
  180. package/src/components/MessageList/hooks/useMessageList.ts +68 -36
  181. package/src/components/MessageList/utils/getDateSeparators.ts +16 -20
  182. package/src/components/MessageList/utils/getGroupStyles.ts +44 -33
  183. package/src/components/MessageList/utils/getLastReceivedMessage.ts +1 -4
  184. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +6 -6
  185. package/src/components/index.ts +3 -0
  186. package/src/contexts/channelContext/ChannelContext.tsx +13 -4
  187. package/src/contexts/messageContext/MessageContext.tsx +4 -1
  188. package/src/contexts/messageInputContext/MessageInputContext.tsx +5 -0
  189. package/src/contexts/messageListItemContext/MessageListItemContext.tsx +63 -0
  190. package/src/contexts/messagesContext/MessagesContext.tsx +16 -1
  191. package/src/contexts/themeContext/utils/theme.ts +12 -0
  192. package/src/state-store/channel-unread-state.ts +28 -0
  193. package/src/state-store/message-list-prev-next-state.ts +58 -0
  194. package/src/store/apis/getChannelMessages.ts +9 -12
  195. package/src/utils/utils.ts +9 -8
  196. package/src/version.json +1 -1
  197. package/src/components/MessageList/utils/__tests__/getDateSeparators.test.ts +0 -71
@@ -17,7 +17,6 @@ import { useShouldScrollToRecentOnNewOwnMessage } from './hooks/useShouldScrollT
17
17
  import { InlineLoadingMoreIndicator } from './InlineLoadingMoreIndicator';
18
18
  import { InlineLoadingMoreRecentIndicator } from './InlineLoadingMoreRecentIndicator';
19
19
  import { InlineLoadingMoreRecentThreadIndicator } from './InlineLoadingMoreRecentThreadIndicator';
20
- import { getLastReceivedMessage } from './utils/getLastReceivedMessage';
21
20
 
22
21
  import {
23
22
  AttachmentPickerContextValue,
@@ -33,6 +32,10 @@ import {
33
32
  ImageGalleryContextValue,
34
33
  useImageGalleryContext,
35
34
  } from '../../contexts/imageGalleryContext/ImageGalleryContext';
35
+ import {
36
+ MessageListItemContextValue,
37
+ MessageListItemProvider,
38
+ } from '../../contexts/messageListItemContext/MessageListItemContext';
36
39
  import {
37
40
  MessagesContextValue,
38
41
  useMessagesContext,
@@ -45,11 +48,12 @@ import {
45
48
  PaginatedMessageListContextValue,
46
49
  usePaginatedMessageListContext,
47
50
  } from '../../contexts/paginatedMessageListContext/PaginatedMessageListContext';
48
- import { mergeThemes, ThemeProvider, useTheme } from '../../contexts/themeContext/ThemeContext';
51
+ import { mergeThemes, useTheme } from '../../contexts/themeContext/ThemeContext';
49
52
  import { ThreadContextValue, useThreadContext } from '../../contexts/threadContext/ThreadContext';
50
53
 
51
54
  import { useStableCallback } from '../../hooks';
52
55
  import { FileTypes } from '../../types/types';
56
+ import { MessageWrapper } from '../Message/MessageSimple/MessageWrapper';
53
57
 
54
58
  // This is just to make sure that the scrolling happens in a different task queue.
55
59
  // TODO: Think if we really need this and strive to remove it if we can.
@@ -124,10 +128,10 @@ type MessageListPropsWithContext = Pick<
124
128
  ChannelContextValue,
125
129
  | 'channel'
126
130
  | 'channelUnreadState'
131
+ | 'channelUnreadStateStore'
127
132
  | 'disabled'
128
133
  | 'EmptyStateIndicator'
129
134
  | 'hideStickyDateHeader'
130
- | 'highlightedMessageId'
131
135
  | 'loadChannelAroundMessage'
132
136
  | 'loading'
133
137
  | 'LoadingIndicator'
@@ -150,14 +154,9 @@ type MessageListPropsWithContext = Pick<
150
154
  | 'DateHeader'
151
155
  | 'disableTypingIndicator'
152
156
  | 'FlatList'
153
- | 'InlineDateSeparator'
154
- | 'InlineUnreadIndicator'
155
157
  | 'legacyImageViewerSwipeBehaviour'
156
- | 'Message'
157
158
  | 'ScrollToBottomButton'
158
- | 'MessageSystem'
159
159
  | 'myMessageTheme'
160
- | 'shouldShowUnreadUnderlay'
161
160
  | 'TypingIndicator'
162
161
  | 'TypingIndicatorContainer'
163
162
  | 'UnreadMessagesNotification'
@@ -234,6 +233,10 @@ type MessageListPropsWithContext = Pick<
234
233
  isLiveStreaming?: boolean;
235
234
  };
236
235
 
236
+ const renderItem = ({ item: message }: { item: LocalMessage }) => {
237
+ return <MessageWrapper message={message} />;
238
+ };
239
+
237
240
  /**
238
241
  * The message list component renders a list of messages. It consumes the following contexts:
239
242
  *
@@ -250,7 +253,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
250
253
  const {
251
254
  additionalFlatListProps,
252
255
  channel,
253
- channelUnreadState,
256
+ channelUnreadStateStore,
254
257
  client,
255
258
  closePicker,
256
259
  DateHeader,
@@ -261,9 +264,6 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
261
264
  FooterComponent = InlineLoadingMoreIndicator,
262
265
  HeaderComponent = LoadingMoreRecentIndicator,
263
266
  hideStickyDateHeader,
264
- highlightedMessageId,
265
- InlineDateSeparator,
266
- InlineUnreadIndicator,
267
267
  inverted = true,
268
268
  isListActive = false,
269
269
  isLiveStreaming = false,
@@ -277,8 +277,6 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
277
277
  loadMoreThread,
278
278
  markRead,
279
279
  maximumMessageLimit,
280
- Message,
281
- MessageSystem,
282
280
  myMessageTheme,
283
281
  NetworkDownIndicator,
284
282
  noGroupByUser,
@@ -293,7 +291,6 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
293
291
  setMessages,
294
292
  setSelectedPicker,
295
293
  setTargetedMessage,
296
- shouldShowUnreadUnderlay,
297
294
  StickyHeader,
298
295
  targetedMessage,
299
296
  thread,
@@ -308,8 +305,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
308
305
 
309
306
  const {
310
307
  colors: { white_snow },
311
- messageList: { container, contentContainer, listContainer, messageContainer },
312
- screenPadding,
308
+ messageList: { container, contentContainer, listContainer },
313
309
  } = theme;
314
310
 
315
311
  const myMessageThemeString = useMemo(() => JSON.stringify(myMessageTheme), [myMessageTheme]);
@@ -325,8 +321,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
325
321
  * processedMessageList changes on any state change
326
322
  */
327
323
  const {
328
- dateSeparatorsRef,
329
- messageGroupStylesRef,
324
+ messageListPreviousAndNextMessageStore,
330
325
  processedMessageList,
331
326
  rawMessageList,
332
327
  viewabilityChangedCallback,
@@ -395,10 +390,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
395
390
  */
396
391
  const messageIdLastScrolledToRef = useRef<string>(undefined);
397
392
  const [hasMoved, setHasMoved] = useState(false);
398
- const lastReceivedId = useMemo(
399
- () => getLastReceivedMessage(processedMessageList)?.id,
400
- [processedMessageList],
401
- );
393
+
402
394
  const [scrollToBottomButtonVisible, setScrollToBottomButtonVisible] = useState(false);
403
395
 
404
396
  const [stickyHeaderDate, setStickyHeaderDate] = useState<Date | undefined>();
@@ -437,26 +429,23 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
437
429
  }
438
430
  });
439
431
 
440
- const messagesLength = useRef<number>(processedMessageList.length);
441
-
442
432
  /**
443
433
  * This function should show or hide the unread indicator depending on the
444
434
  */
445
435
  const updateStickyUnreadIndicator = useStableCallback((viewableItems: ViewToken[]) => {
436
+ const channelUnreadState = channelUnreadStateStore.channelUnreadState;
446
437
  // we need this check to make sure that regular list change do not trigger
447
438
  // the unread notification to appear (for example if the old last read messages
448
439
  // go out of the viewport).
449
- if (processedMessageList.length !== messagesLength.current) {
450
- return;
451
- }
452
- messagesLength.current = processedMessageList.length;
440
+ const lastReadMessageId = channelUnreadState?.last_read_message_id;
441
+ const lastReadMessageVisible = viewableItems.some((item) => item.item.id === lastReadMessageId);
453
442
 
454
- if (!viewableItems.length || !readEvents) {
455
- setIsUnreadNotificationOpen(false);
456
- return;
457
- }
458
-
459
- if (selectedPicker === 'images') {
443
+ if (
444
+ !viewableItems.length ||
445
+ !readEvents ||
446
+ lastReadMessageVisible ||
447
+ selectedPicker === 'images'
448
+ ) {
460
449
  setIsUnreadNotificationOpen(false);
461
450
  return;
462
451
  }
@@ -467,7 +456,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
467
456
  const lastItemMessage = lastItem.item;
468
457
  const lastItemCreatedAt = lastItemMessage.created_at;
469
458
 
470
- const unreadIndicatorDate = channelUnreadState?.last_read.getTime();
459
+ const unreadIndicatorDate = channelUnreadState?.last_read?.getTime();
471
460
  const lastItemDate = lastItemCreatedAt.getTime();
472
461
 
473
462
  if (
@@ -547,6 +536,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
547
536
  */
548
537
  useEffect(() => {
549
538
  const shouldMarkRead = () => {
539
+ const channelUnreadState = channelUnreadStateStore.channelUnreadState;
550
540
  return (
551
541
  !channelUnreadState?.first_unread_message_id &&
552
542
  !scrollToBottomButtonVisible &&
@@ -558,23 +548,22 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
558
548
  const handleEvent = async (event: Event) => {
559
549
  const mainChannelUpdated = !event.message?.parent_id || event.message?.show_in_channel;
560
550
  const isMyOwnMessage = event.message?.user?.id === client.user?.id;
551
+ const channelUnreadState = channelUnreadStateStore.channelUnreadState;
561
552
  // When the scrollToBottomButtonVisible is true, we need to manually update the channelUnreadState when its a received message.
562
553
  if (
563
554
  (scrollToBottomButtonVisible || channelUnreadState?.first_unread_message_id) &&
564
555
  !isMyOwnMessage
565
556
  ) {
566
- setChannelUnreadState((prev) => {
567
- const previousUnreadCount = prev?.unread_messages ?? 0;
568
- const previousLastMessage = getPreviousLastMessage(channel.state.messages, event.message);
569
- return {
570
- ...(prev || {}),
571
- last_read:
572
- prev?.last_read ??
573
- (previousUnreadCount === 0 && previousLastMessage?.created_at
574
- ? new Date(previousLastMessage.created_at)
575
- : new Date(0)), // not having information about the last read message means the whole channel is unread,
576
- unread_messages: previousUnreadCount + 1,
577
- };
557
+ const previousUnreadCount = channelUnreadState?.unread_messages ?? 0;
558
+ const previousLastMessage = getPreviousLastMessage(channel.state.messages, event.message);
559
+ setChannelUnreadState({
560
+ ...channelUnreadState,
561
+ last_read:
562
+ channelUnreadState?.last_read ??
563
+ (previousUnreadCount === 0 && previousLastMessage?.created_at
564
+ ? new Date(previousLastMessage.created_at)
565
+ : new Date(0)), // not having information about the last read message means the whole channel is unread,
566
+ unread_messages: previousUnreadCount + 1,
578
567
  });
579
568
  } else if (mainChannelUpdated && shouldMarkRead()) {
580
569
  await markRead();
@@ -588,7 +577,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
588
577
  };
589
578
  }, [
590
579
  channel,
591
- channelUnreadState?.first_unread_message_id,
580
+ channelUnreadStateStore,
592
581
  client.user?.id,
593
582
  markRead,
594
583
  scrollToBottomButtonVisible,
@@ -779,95 +768,20 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
779
768
  // eslint-disable-next-line react-hooks/exhaustive-deps
780
769
  }, [targetedMessage]);
781
770
 
782
- const renderItem = useCallback(
783
- ({ index, item: message }: { index: number; item: LocalMessage }) => {
784
- if (!channel || channel.disconnected) {
785
- return null;
786
- }
787
-
788
- const createdAtTimestamp = message.created_at && new Date(message.created_at).getTime();
789
- const lastReadTimestamp = channelUnreadState?.last_read.getTime();
790
- const isNewestMessage = index === 0;
791
- const isLastReadMessage =
792
- channelUnreadState?.last_read_message_id === message.id ||
793
- (!channelUnreadState?.unread_messages && createdAtTimestamp === lastReadTimestamp);
794
-
795
- const showUnreadSeparator =
796
- isLastReadMessage &&
797
- !isNewestMessage &&
798
- // The `channelUnreadState?.first_unread_message_id` is here for sent messages unread label
799
- (!!channelUnreadState?.first_unread_message_id || !!channelUnreadState?.unread_messages);
800
-
801
- const showUnreadUnderlay = !!shouldShowUnreadUnderlay && showUnreadSeparator;
802
-
803
- const wrapMessageInTheme = client.userID === message.user?.id && !!myMessageTheme;
804
- const renderDateSeperator = dateSeparatorsRef.current[message.id] && (
805
- <InlineDateSeparator date={dateSeparatorsRef.current[message.id]} />
806
- );
807
-
808
- const renderMessage = (
809
- <Message
810
- goToMessage={goToMessage}
811
- groupStyles={messageGroupStylesRef.current[message.id] ?? []}
812
- isTargetedMessage={highlightedMessageId === message.id}
813
- lastReceivedId={
814
- lastReceivedId === message.id || message.quoted_message_id ? lastReceivedId : undefined
815
- }
816
- message={message}
817
- onThreadSelect={onThreadSelect}
818
- showUnreadUnderlay={showUnreadUnderlay}
819
- style={[messageContainer]}
820
- threadList={threadList}
821
- />
822
- );
823
-
824
- return (
825
- <View testID={`message-list-item-${index}`}>
826
- {message.type === 'system' ? (
827
- <MessageSystem
828
- message={message}
829
- style={[{ paddingHorizontal: screenPadding }, messageContainer]}
830
- />
831
- ) : wrapMessageInTheme ? (
832
- <ThemeProvider mergedStyle={modifiedTheme}>
833
- <View testID={`message-list-item-${index}`}>
834
- {renderDateSeperator}
835
- {renderMessage}
836
- </View>
837
- </ThemeProvider>
838
- ) : (
839
- <View testID={`message-list-item-${index}`}>
840
- {renderDateSeperator}
841
- {renderMessage}
842
- </View>
843
- )}
844
- {showUnreadUnderlay && <InlineUnreadIndicator />}
845
- </View>
846
- );
847
- },
771
+ const messageListItemContextValue: MessageListItemContextValue = useMemo(
772
+ () => ({
773
+ goToMessage,
774
+ messageListPreviousAndNextMessageStore,
775
+ modifiedTheme,
776
+ noGroupByUser,
777
+ onThreadSelect,
778
+ }),
848
779
  [
849
- InlineDateSeparator,
850
- InlineUnreadIndicator,
851
- Message,
852
- MessageSystem,
853
- channel,
854
- channelUnreadState?.first_unread_message_id,
855
- channelUnreadState?.last_read,
856
- channelUnreadState?.last_read_message_id,
857
- channelUnreadState?.unread_messages,
858
- client.userID,
859
- dateSeparatorsRef,
860
780
  goToMessage,
861
- highlightedMessageId,
862
- lastReceivedId,
863
- messageContainer,
864
- messageGroupStylesRef,
781
+ messageListPreviousAndNextMessageStore,
865
782
  modifiedTheme,
866
- myMessageTheme,
783
+ noGroupByUser,
867
784
  onThreadSelect,
868
- screenPadding,
869
- shouldShowUnreadUnderlay,
870
- threadList,
871
785
  ],
872
786
  );
873
787
 
@@ -1232,42 +1146,44 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
1232
1146
  {EmptyStateIndicator ? <EmptyStateIndicator listType='message' /> : null}
1233
1147
  </View>
1234
1148
  ) : (
1235
- <FlatList
1236
- contentContainerStyle={flatListContentContainerStyle}
1237
- /** Disables the MessageList UI. Which means, message actions, reactions won't work. */
1238
- data={processedMessageList}
1239
- extraData={disabled}
1240
- inverted={inverted}
1241
- keyboardShouldPersistTaps='handled'
1242
- keyExtractor={keyExtractor}
1243
- ListFooterComponent={FooterComponent}
1244
- ListHeaderComponent={HeaderComponent}
1245
- /**
1149
+ <MessageListItemProvider value={messageListItemContextValue}>
1150
+ <FlatList
1151
+ contentContainerStyle={flatListContentContainerStyle}
1152
+ /** Disables the MessageList UI. Which means, message actions, reactions won't work. */
1153
+ data={processedMessageList}
1154
+ extraData={disabled}
1155
+ inverted={inverted}
1156
+ keyboardShouldPersistTaps='handled'
1157
+ keyExtractor={keyExtractor}
1158
+ ListFooterComponent={FooterComponent}
1159
+ ListHeaderComponent={HeaderComponent}
1160
+ /**
1246
1161
  If autoscrollToTopThreshold is 10, we scroll to recent only if before the update, the list was already at the
1247
1162
  bottom (10 offset or below).
1248
1163
  minIndexForVisible = 1 means that beyond the item at index 1 we will not change the position on list updates,
1249
1164
  however it is not used when autoscrollToTopThreshold = 10.
1250
1165
  */
1251
- maintainVisibleContentPosition={maintainVisibleContentPosition}
1252
- maxToRenderPerBatch={30}
1253
- onMomentumScrollEnd={onUserScrollEvent}
1254
- onScroll={handleScroll}
1255
- onScrollBeginDrag={onScrollBeginDrag}
1256
- onScrollEndDrag={onScrollEndDrag}
1257
- onScrollToIndexFailed={onScrollToIndexFailedRef.current}
1258
- onTouchEnd={dismissImagePicker}
1259
- onViewableItemsChanged={stableOnViewableItemsChanged}
1260
- ref={refCallback}
1261
- renderItem={renderItem}
1262
- scrollEventThrottle={isLiveStreaming ? 16 : undefined}
1263
- showsVerticalScrollIndicator={false}
1264
- // @ts-expect-error react-native internal
1265
- strictMode={isLiveStreaming}
1266
- style={flatListStyle}
1267
- testID='message-flat-list'
1268
- viewabilityConfig={flatListViewabilityConfig}
1269
- {...additionalFlatListPropsExcludingStyle}
1270
- />
1166
+ maintainVisibleContentPosition={maintainVisibleContentPosition}
1167
+ maxToRenderPerBatch={30}
1168
+ onMomentumScrollEnd={onUserScrollEvent}
1169
+ onScroll={handleScroll}
1170
+ onScrollBeginDrag={onScrollBeginDrag}
1171
+ onScrollEndDrag={onScrollEndDrag}
1172
+ onScrollToIndexFailed={onScrollToIndexFailedRef.current}
1173
+ onTouchEnd={dismissImagePicker}
1174
+ onViewableItemsChanged={stableOnViewableItemsChanged}
1175
+ ref={refCallback}
1176
+ renderItem={renderItem}
1177
+ scrollEventThrottle={isLiveStreaming ? 16 : undefined}
1178
+ showsVerticalScrollIndicator={false}
1179
+ // @ts-expect-error react-native internal
1180
+ strictMode={isLiveStreaming}
1181
+ style={flatListStyle}
1182
+ testID='message-flat-list'
1183
+ viewabilityConfig={flatListViewabilityConfig}
1184
+ {...additionalFlatListPropsExcludingStyle}
1185
+ />
1186
+ </MessageListItemProvider>
1271
1187
  )}
1272
1188
  <View style={styles.stickyHeader}>
1273
1189
  {messageListLengthAfterUpdate && StickyHeader ? (
@@ -1299,6 +1215,7 @@ export const MessageList = (props: MessageListProps) => {
1299
1215
  const {
1300
1216
  channel,
1301
1217
  channelUnreadState,
1218
+ channelUnreadStateStore,
1302
1219
  disabled,
1303
1220
  EmptyStateIndicator,
1304
1221
  enableMessageGroupingByUser,
@@ -1347,6 +1264,7 @@ export const MessageList = (props: MessageListProps) => {
1347
1264
  {...{
1348
1265
  channel,
1349
1266
  channelUnreadState,
1267
+ channelUnreadStateStore,
1350
1268
  client,
1351
1269
  closePicker,
1352
1270
  DateHeader,
@@ -21,7 +21,7 @@ export const UnreadMessagesNotification = (props: UnreadMessagesNotificationProp
21
21
  const { onCloseHandler, onPressHandler } = props;
22
22
  const { t } = useTranslationContext();
23
23
  const {
24
- channelUnreadState,
24
+ channelUnreadStateStore,
25
25
  loadChannelAtFirstUnreadMessage,
26
26
  markRead,
27
27
  setChannelUnreadState,
@@ -33,7 +33,7 @@ export const UnreadMessagesNotification = (props: UnreadMessagesNotificationProp
33
33
  await onPressHandler();
34
34
  } else {
35
35
  await loadChannelAtFirstUnreadMessage({
36
- channelUnreadState,
36
+ channelUnreadState: channelUnreadStateStore.channelUnreadState,
37
37
  setChannelUnreadState,
38
38
  setTargetedMessage,
39
39
  });
@@ -382,12 +382,19 @@ describe('MessageList', () => {
382
382
  });
383
383
  });
384
384
 
385
- it("should render the UnreadMessagesIndicator when there's unread messages", async () => {
385
+ it("should render the InlineUnreadIndicator when there's unread messages", async () => {
386
386
  const user1 = generateUser();
387
387
  const user2 = generateUser();
388
388
  const messages = Array.from({ length: 10 }, (_, i) =>
389
389
  generateMessage({ id: `${i}`, text: `message-${i}` }),
390
390
  );
391
+ const read_data = {
392
+ [user1.id]: {
393
+ last_read: new Date(),
394
+ last_read_message_id: '5',
395
+ unread_messages: 5,
396
+ },
397
+ };
391
398
  const mockedChannel = generateChannelResponse({
392
399
  members: [generateMember({ user: user1 }), generateMember({ user: user2 })],
393
400
  });
@@ -397,23 +404,18 @@ describe('MessageList', () => {
397
404
  const channel = chatClient.channel('messaging', mockedChannel.id);
398
405
  await channel.watch();
399
406
 
400
- const channelUnreadState = {
401
- last_read: new Date(),
402
- last_read_message_id: '5',
403
- unread_messages: 5,
404
- };
405
-
406
407
  channel.state = {
407
408
  ...channelInitialState,
408
409
  latestMessages: [],
409
410
  messages,
411
+ read: read_data,
410
412
  };
411
413
 
412
414
  const { queryByLabelText } = render(
413
415
  <OverlayProvider>
414
416
  <Chat client={chatClient}>
415
417
  <Channel channel={channel}>
416
- <MessageList channelUnreadState={channelUnreadState} />
418
+ <MessageList />
417
419
  </Channel>
418
420
  </Chat>
419
421
  </OverlayProvider>,
@@ -0,0 +1,83 @@
1
+ import { renderHook } from '@testing-library/react-native';
2
+
3
+ import { LocalMessage } from 'stream-chat';
4
+
5
+ import { MessagePreviousAndNextMessageStore } from '../../../state-store/message-list-prev-next-state';
6
+ import { useMessageDateSeparator } from '../hooks/useMessageDateSeparator';
7
+
8
+ describe('useMessageDateSeparator', () => {
9
+ let messageListPreviousAndNextMessageStore: MessagePreviousAndNextMessageStore;
10
+ let messages: LocalMessage[];
11
+
12
+ beforeEach(() => {
13
+ messageListPreviousAndNextMessageStore = new MessagePreviousAndNextMessageStore();
14
+ messages = [
15
+ {
16
+ created_at: new Date('2020-01-01T00:00:00.000Z'),
17
+ id: '1',
18
+ text: 'Hello',
19
+ },
20
+ {
21
+ created_at: new Date('2020-01-02T00:00:00.000Z'),
22
+ id: '2',
23
+ text: 'World',
24
+ },
25
+ {
26
+ created_at: new Date('2020-01-03T00:00:00.000Z'),
27
+ id: '3',
28
+ text: 'Hello World',
29
+ },
30
+ ] as LocalMessage[];
31
+ messageListPreviousAndNextMessageStore.setMessageListPreviousAndNextMessage({ messages });
32
+ });
33
+
34
+ it('should return undefined if no message is passed', () => {
35
+ const { result } = renderHook(() =>
36
+ useMessageDateSeparator({ message: undefined, messageListPreviousAndNextMessageStore }),
37
+ );
38
+ expect(result.current).toBeUndefined();
39
+ });
40
+
41
+ it('should return undefined if the hideDateSeparators prop is true', () => {
42
+ const { result } = renderHook(() =>
43
+ useMessageDateSeparator({
44
+ hideDateSeparators: true,
45
+ message: messages[1],
46
+ messageListPreviousAndNextMessageStore,
47
+ }),
48
+ );
49
+ expect(result.current).toBeUndefined();
50
+ });
51
+
52
+ it('should return the date separator for a message if previous message is not the same day', () => {
53
+ const { result } = renderHook(() =>
54
+ useMessageDateSeparator({ message: messages[1], messageListPreviousAndNextMessageStore }),
55
+ );
56
+ expect(result.current).toBe(messages[1].created_at);
57
+ });
58
+
59
+ it('should return undefined if the message is the same day as the previous message', () => {
60
+ const messages = [
61
+ {
62
+ created_at: new Date('2020-01-01T01:00:00.000Z'),
63
+ id: '1',
64
+ text: 'Hello',
65
+ },
66
+ {
67
+ created_at: new Date('2020-01-01T02:00:00.000Z'),
68
+ id: '2',
69
+ text: 'World',
70
+ },
71
+ ] as LocalMessage[];
72
+ const messageListPreviousAndNextMessageStore = new MessagePreviousAndNextMessageStore();
73
+ messageListPreviousAndNextMessageStore.setMessageListPreviousAndNextMessage({ messages });
74
+ const { result: resultOfFirstMessage } = renderHook(() =>
75
+ useMessageDateSeparator({ message: messages[0], messageListPreviousAndNextMessageStore }),
76
+ );
77
+ expect(resultOfFirstMessage.current).toBe(messages[0].created_at);
78
+ const { result: resultOfSecondMessage } = renderHook(() =>
79
+ useMessageDateSeparator({ message: messages[1], messageListPreviousAndNextMessageStore }),
80
+ );
81
+ expect(resultOfSecondMessage.current).toBeUndefined();
82
+ });
83
+ });
@@ -0,0 +1,66 @@
1
+ import { useCallback, useMemo } from 'react';
2
+
3
+ import { LocalMessage } from 'stream-chat';
4
+
5
+ import { useStateStore } from '../../../hooks/useStateStore';
6
+ import {
7
+ MessagePreviousAndNextMessageStore,
8
+ MessagePreviousAndNextMessageStoreType,
9
+ } from '../../../state-store/message-list-prev-next-state';
10
+
11
+ export const getDateSeparatorValue = ({
12
+ hideDateSeparators,
13
+ message,
14
+ previousMessage,
15
+ }: {
16
+ hideDateSeparators?: boolean;
17
+ message?: LocalMessage;
18
+ previousMessage?: LocalMessage;
19
+ }) => {
20
+ if (hideDateSeparators) {
21
+ return undefined;
22
+ }
23
+
24
+ const previousMessageDate = previousMessage?.created_at.toDateString();
25
+ const messageDate = message?.created_at.toDateString();
26
+
27
+ if (previousMessageDate !== messageDate) {
28
+ return message?.created_at;
29
+ }
30
+
31
+ return undefined;
32
+ };
33
+
34
+ /**
35
+ * Hook to get whether a message should have a date separator above it
36
+ */
37
+ export const useMessageDateSeparator = ({
38
+ hideDateSeparators,
39
+ message,
40
+ messageListPreviousAndNextMessageStore,
41
+ }: {
42
+ hideDateSeparators?: boolean;
43
+ message?: LocalMessage;
44
+ messageListPreviousAndNextMessageStore: MessagePreviousAndNextMessageStore;
45
+ }) => {
46
+ const selector = useCallback(
47
+ (state: MessagePreviousAndNextMessageStoreType) => ({
48
+ previousMessage: message ? state.messageList[message.id]?.previousMessage : undefined,
49
+ }),
50
+ [message],
51
+ );
52
+ const { previousMessage } = useStateStore(messageListPreviousAndNextMessageStore.state, selector);
53
+
54
+ const dateSeparatorDate = useMemo(() => {
55
+ if (!message && !previousMessage) {
56
+ return undefined;
57
+ }
58
+ return getDateSeparatorValue({
59
+ hideDateSeparators,
60
+ message,
61
+ previousMessage,
62
+ });
63
+ }, [hideDateSeparators, message, previousMessage]);
64
+
65
+ return dateSeparatorDate;
66
+ };