stream-chat-react-native-core 5.38.1 → 5.39.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 (235) hide show
  1. package/lib/commonjs/components/Channel/Channel.js +58 -25
  2. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  3. package/lib/commonjs/components/Channel/hooks/useCreateThreadContext.js +33 -21
  4. package/lib/commonjs/components/Channel/hooks/useCreateThreadContext.js.map +1 -1
  5. package/lib/commonjs/components/Chat/Chat.js +7 -0
  6. package/lib/commonjs/components/Chat/Chat.js.map +1 -1
  7. package/lib/commonjs/components/Indicators/EmptyStateIndicator.js +14 -0
  8. package/lib/commonjs/components/Indicators/EmptyStateIndicator.js.map +1 -1
  9. package/lib/commonjs/components/Indicators/LoadingIndicator.js +4 -0
  10. package/lib/commonjs/components/Indicators/LoadingIndicator.js.map +1 -1
  11. package/lib/commonjs/components/Message/hooks/useProcessReactions.js +13 -7
  12. package/lib/commonjs/components/Message/hooks/useProcessReactions.js.map +1 -1
  13. package/lib/commonjs/components/MessageList/InlineLoadingMoreRecentThreadIndicator.js +52 -0
  14. package/lib/commonjs/components/MessageList/InlineLoadingMoreRecentThreadIndicator.js.map +1 -0
  15. package/lib/commonjs/components/MessageList/MessageList.js +14 -6
  16. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  17. package/lib/commonjs/components/Thread/Thread.js +12 -5
  18. package/lib/commonjs/components/Thread/Thread.js.map +1 -1
  19. package/lib/commonjs/components/Thread/components/ThreadFooterComponent.js +41 -18
  20. package/lib/commonjs/components/Thread/components/ThreadFooterComponent.js.map +1 -1
  21. package/lib/commonjs/components/ThreadList/ThreadList.js +132 -0
  22. package/lib/commonjs/components/ThreadList/ThreadList.js.map +1 -0
  23. package/lib/commonjs/components/ThreadList/ThreadListItem.js +246 -0
  24. package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -0
  25. package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js +66 -0
  26. package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js.map +1 -0
  27. package/lib/commonjs/components/index.js +11 -11
  28. package/lib/commonjs/components/index.js.map +1 -1
  29. package/lib/commonjs/contexts/themeContext/utils/theme.js +19 -0
  30. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  31. package/lib/commonjs/contexts/threadContext/ThreadContext.js.map +1 -1
  32. package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js +28 -0
  33. package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js.map +1 -0
  34. package/lib/commonjs/contexts/threadsContext/ThreadsContext.js +33 -0
  35. package/lib/commonjs/contexts/threadsContext/ThreadsContext.js.map +1 -0
  36. package/lib/commonjs/hooks/index.js +11 -0
  37. package/lib/commonjs/hooks/index.js.map +1 -1
  38. package/lib/commonjs/hooks/useStateStore.js +23 -0
  39. package/lib/commonjs/hooks/useStateStore.js.map +1 -0
  40. package/lib/commonjs/i18n/en.json +7 -0
  41. package/lib/commonjs/i18n/es.json +7 -0
  42. package/lib/commonjs/i18n/fr.json +7 -0
  43. package/lib/commonjs/i18n/he.json +7 -0
  44. package/lib/commonjs/i18n/hi.json +7 -0
  45. package/lib/commonjs/i18n/it.json +7 -0
  46. package/lib/commonjs/i18n/ja.json +7 -0
  47. package/lib/commonjs/i18n/ko.json +7 -0
  48. package/lib/commonjs/i18n/nl.json +7 -0
  49. package/lib/commonjs/i18n/pt-br.json +7 -0
  50. package/lib/commonjs/i18n/ru.json +7 -0
  51. package/lib/commonjs/i18n/tr.json +7 -0
  52. package/lib/commonjs/icons/MessageBubble.js +19 -0
  53. package/lib/commonjs/icons/MessageBubble.js.map +1 -0
  54. package/lib/commonjs/icons/MessageBubbleEmpty.js +19 -0
  55. package/lib/commonjs/icons/MessageBubbleEmpty.js.map +1 -0
  56. package/lib/commonjs/icons/Reload.js +19 -0
  57. package/lib/commonjs/icons/Reload.js.map +1 -0
  58. package/lib/commonjs/icons/index.js +33 -0
  59. package/lib/commonjs/icons/index.js.map +1 -1
  60. package/lib/commonjs/store/mappers/mapDateTimeToStorable.js.map +1 -1
  61. package/lib/commonjs/version.json +1 -1
  62. package/lib/module/components/Channel/Channel.js +58 -25
  63. package/lib/module/components/Channel/Channel.js.map +1 -1
  64. package/lib/module/components/Channel/hooks/useCreateThreadContext.js +33 -21
  65. package/lib/module/components/Channel/hooks/useCreateThreadContext.js.map +1 -1
  66. package/lib/module/components/Chat/Chat.js +7 -0
  67. package/lib/module/components/Chat/Chat.js.map +1 -1
  68. package/lib/module/components/Indicators/EmptyStateIndicator.js +14 -0
  69. package/lib/module/components/Indicators/EmptyStateIndicator.js.map +1 -1
  70. package/lib/module/components/Indicators/LoadingIndicator.js +4 -0
  71. package/lib/module/components/Indicators/LoadingIndicator.js.map +1 -1
  72. package/lib/module/components/Message/hooks/useProcessReactions.js +13 -7
  73. package/lib/module/components/Message/hooks/useProcessReactions.js.map +1 -1
  74. package/lib/module/components/MessageList/InlineLoadingMoreRecentThreadIndicator.js +52 -0
  75. package/lib/module/components/MessageList/InlineLoadingMoreRecentThreadIndicator.js.map +1 -0
  76. package/lib/module/components/MessageList/MessageList.js +14 -6
  77. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  78. package/lib/module/components/Thread/Thread.js +12 -5
  79. package/lib/module/components/Thread/Thread.js.map +1 -1
  80. package/lib/module/components/Thread/components/ThreadFooterComponent.js +41 -18
  81. package/lib/module/components/Thread/components/ThreadFooterComponent.js.map +1 -1
  82. package/lib/module/components/ThreadList/ThreadList.js +132 -0
  83. package/lib/module/components/ThreadList/ThreadList.js.map +1 -0
  84. package/lib/module/components/ThreadList/ThreadListItem.js +246 -0
  85. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -0
  86. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js +66 -0
  87. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js.map +1 -0
  88. package/lib/module/components/index.js +11 -11
  89. package/lib/module/components/index.js.map +1 -1
  90. package/lib/module/contexts/themeContext/utils/theme.js +19 -0
  91. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  92. package/lib/module/contexts/threadContext/ThreadContext.js.map +1 -1
  93. package/lib/module/contexts/threadsContext/ThreadListItemContext.js +28 -0
  94. package/lib/module/contexts/threadsContext/ThreadListItemContext.js.map +1 -0
  95. package/lib/module/contexts/threadsContext/ThreadsContext.js +33 -0
  96. package/lib/module/contexts/threadsContext/ThreadsContext.js.map +1 -0
  97. package/lib/module/hooks/index.js +11 -0
  98. package/lib/module/hooks/index.js.map +1 -1
  99. package/lib/module/hooks/useStateStore.js +23 -0
  100. package/lib/module/hooks/useStateStore.js.map +1 -0
  101. package/lib/module/i18n/en.json +7 -0
  102. package/lib/module/i18n/es.json +7 -0
  103. package/lib/module/i18n/fr.json +7 -0
  104. package/lib/module/i18n/he.json +7 -0
  105. package/lib/module/i18n/hi.json +7 -0
  106. package/lib/module/i18n/it.json +7 -0
  107. package/lib/module/i18n/ja.json +7 -0
  108. package/lib/module/i18n/ko.json +7 -0
  109. package/lib/module/i18n/nl.json +7 -0
  110. package/lib/module/i18n/pt-br.json +7 -0
  111. package/lib/module/i18n/ru.json +7 -0
  112. package/lib/module/i18n/tr.json +7 -0
  113. package/lib/module/icons/MessageBubble.js +19 -0
  114. package/lib/module/icons/MessageBubble.js.map +1 -0
  115. package/lib/module/icons/MessageBubbleEmpty.js +19 -0
  116. package/lib/module/icons/MessageBubbleEmpty.js.map +1 -0
  117. package/lib/module/icons/Reload.js +19 -0
  118. package/lib/module/icons/Reload.js.map +1 -0
  119. package/lib/module/icons/index.js +33 -0
  120. package/lib/module/icons/index.js.map +1 -1
  121. package/lib/module/store/mappers/mapDateTimeToStorable.js.map +1 -1
  122. package/lib/module/version.json +1 -1
  123. package/lib/typescript/components/AttachmentPicker/AttachmentPicker.d.ts +1 -1
  124. package/lib/typescript/components/Channel/Channel.d.ts +3 -2
  125. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  126. package/lib/typescript/components/Channel/hooks/useCreateThreadContext.d.ts +34 -1
  127. package/lib/typescript/components/Channel/hooks/useCreateThreadContext.d.ts.map +1 -1
  128. package/lib/typescript/components/Chat/Chat.d.ts.map +1 -1
  129. package/lib/typescript/components/Indicators/EmptyStateIndicator.d.ts +1 -1
  130. package/lib/typescript/components/Indicators/EmptyStateIndicator.d.ts.map +1 -1
  131. package/lib/typescript/components/Indicators/LoadingIndicator.d.ts +1 -1
  132. package/lib/typescript/components/Indicators/LoadingIndicator.d.ts.map +1 -1
  133. package/lib/typescript/components/Message/hooks/useProcessReactions.d.ts.map +1 -1
  134. package/lib/typescript/components/MessageList/InlineLoadingMoreRecentThreadIndicator.d.ts +8 -0
  135. package/lib/typescript/components/MessageList/InlineLoadingMoreRecentThreadIndicator.d.ts.map +1 -0
  136. package/lib/typescript/components/MessageList/MessageList.d.ts +1 -1
  137. package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
  138. package/lib/typescript/components/Thread/Thread.d.ts +1 -1
  139. package/lib/typescript/components/Thread/Thread.d.ts.map +1 -1
  140. package/lib/typescript/components/Thread/components/ThreadFooterComponent.d.ts +1 -0
  141. package/lib/typescript/components/Thread/components/ThreadFooterComponent.d.ts.map +1 -1
  142. package/lib/typescript/components/ThreadList/ThreadList.d.ts +11 -0
  143. package/lib/typescript/components/ThreadList/ThreadList.d.ts.map +1 -0
  144. package/lib/typescript/components/ThreadList/ThreadListItem.d.ts +16 -0
  145. package/lib/typescript/components/ThreadList/ThreadListItem.d.ts.map +1 -0
  146. package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts +3 -0
  147. package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts.map +1 -0
  148. package/lib/typescript/components/index.d.ts +1 -1
  149. package/lib/typescript/components/index.d.ts.map +1 -1
  150. package/lib/typescript/contexts/attachmentPickerContext/AttachmentPickerContext.d.ts +1 -1
  151. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +1 -1
  152. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +1 -1
  153. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +19 -0
  154. package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
  155. package/lib/typescript/contexts/threadContext/ThreadContext.d.ts +11 -2
  156. package/lib/typescript/contexts/threadContext/ThreadContext.d.ts.map +1 -1
  157. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts +19 -0
  158. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts.map +1 -0
  159. package/lib/typescript/contexts/threadsContext/ThreadsContext.d.ts +25 -0
  160. package/lib/typescript/contexts/threadsContext/ThreadsContext.d.ts.map +1 -0
  161. package/lib/typescript/hooks/index.d.ts +1 -0
  162. package/lib/typescript/hooks/index.d.ts.map +1 -1
  163. package/lib/typescript/hooks/useStateStore.d.ts +4 -0
  164. package/lib/typescript/hooks/useStateStore.d.ts.map +1 -0
  165. package/lib/typescript/i18n/en.json +7 -0
  166. package/lib/typescript/i18n/es.json +7 -0
  167. package/lib/typescript/i18n/fr.json +7 -0
  168. package/lib/typescript/i18n/he.json +7 -0
  169. package/lib/typescript/i18n/hi.json +7 -0
  170. package/lib/typescript/i18n/it.json +7 -0
  171. package/lib/typescript/i18n/ja.json +7 -0
  172. package/lib/typescript/i18n/ko.json +7 -0
  173. package/lib/typescript/i18n/nl.json +7 -0
  174. package/lib/typescript/i18n/pt-br.json +7 -0
  175. package/lib/typescript/i18n/ru.json +7 -0
  176. package/lib/typescript/i18n/tr.json +7 -0
  177. package/lib/typescript/icons/MessageBubble.d.ts +4 -0
  178. package/lib/typescript/icons/MessageBubble.d.ts.map +1 -0
  179. package/lib/typescript/icons/MessageBubbleEmpty.d.ts +4 -0
  180. package/lib/typescript/icons/MessageBubbleEmpty.d.ts.map +1 -0
  181. package/lib/typescript/icons/Reload.d.ts +4 -0
  182. package/lib/typescript/icons/Reload.d.ts.map +1 -0
  183. package/lib/typescript/icons/index.d.ts +3 -0
  184. package/lib/typescript/icons/index.d.ts.map +1 -1
  185. package/lib/typescript/store/mappers/mapDateTimeToStorable.d.ts +1 -1
  186. package/lib/typescript/store/mappers/mapDateTimeToStorable.d.ts.map +1 -1
  187. package/lib/typescript/utils/i18n/Streami18n.d.ts +7 -0
  188. package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
  189. package/package.json +2 -2
  190. package/src/components/Channel/Channel.tsx +54 -20
  191. package/src/components/Channel/hooks/useCreateThreadContext.ts +36 -31
  192. package/src/components/Chat/Chat.tsx +10 -0
  193. package/src/components/Indicators/EmptyStateIndicator.tsx +9 -2
  194. package/src/components/Indicators/LoadingIndicator.tsx +3 -1
  195. package/src/components/Message/hooks/useProcessReactions.ts +25 -4
  196. package/src/components/MessageList/InlineLoadingMoreRecentThreadIndicator.tsx +64 -0
  197. package/src/components/MessageList/MessageList.tsx +23 -9
  198. package/src/components/Thread/Thread.tsx +18 -9
  199. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +6 -0
  200. package/src/components/Thread/components/ThreadFooterComponent.tsx +28 -2
  201. package/src/components/ThreadList/ThreadList.tsx +118 -0
  202. package/src/components/ThreadList/ThreadListItem.tsx +268 -0
  203. package/src/components/ThreadList/ThreadListUnreadBanner.tsx +52 -0
  204. package/src/components/index.ts +1 -1
  205. package/src/contexts/themeContext/utils/theme.ts +36 -0
  206. package/src/contexts/threadContext/ThreadContext.tsx +9 -2
  207. package/src/contexts/threadsContext/ThreadListItemContext.tsx +41 -0
  208. package/src/contexts/threadsContext/ThreadsContext.tsx +62 -0
  209. package/src/hooks/index.ts +1 -0
  210. package/src/hooks/useStateStore.ts +31 -0
  211. package/src/i18n/en.json +7 -0
  212. package/src/i18n/es.json +7 -0
  213. package/src/i18n/fr.json +7 -0
  214. package/src/i18n/he.json +7 -0
  215. package/src/i18n/hi.json +7 -0
  216. package/src/i18n/it.json +7 -0
  217. package/src/i18n/ja.json +7 -0
  218. package/src/i18n/ko.json +7 -0
  219. package/src/i18n/nl.json +7 -0
  220. package/src/i18n/pt-br.json +7 -0
  221. package/src/i18n/ru.json +7 -0
  222. package/src/i18n/tr.json +7 -0
  223. package/src/icons/MessageBubble.tsx +12 -0
  224. package/src/icons/MessageBubbleEmpty.tsx +12 -0
  225. package/src/icons/Reload.tsx +12 -0
  226. package/src/icons/index.ts +3 -0
  227. package/src/store/mappers/mapDateTimeToStorable.ts +1 -1
  228. package/src/version.json +1 -1
  229. package/lib/commonjs/components/MessageList/InlineLoadingMoreThreadIndicator.js +0 -52
  230. package/lib/commonjs/components/MessageList/InlineLoadingMoreThreadIndicator.js.map +0 -1
  231. package/lib/module/components/MessageList/InlineLoadingMoreThreadIndicator.js +0 -52
  232. package/lib/module/components/MessageList/InlineLoadingMoreThreadIndicator.js.map +0 -1
  233. package/lib/typescript/components/MessageList/InlineLoadingMoreThreadIndicator.d.ts +0 -8
  234. package/lib/typescript/components/MessageList/InlineLoadingMoreThreadIndicator.d.ts.map +0 -1
  235. package/src/components/MessageList/InlineLoadingMoreThreadIndicator.tsx +0 -64
@@ -1,8 +1,16 @@
1
- import { useMemo } from 'react';
1
+ import { ThreadState } from 'stream-chat';
2
2
 
3
3
  import type { ThreadContextValue } from '../../../contexts/threadContext/ThreadContext';
4
+ import { useStateStore } from '../../../hooks';
4
5
  import type { DefaultStreamChatGenerics } from '../../../types/types';
5
- import { reduceMessagesToString } from '../../../utils/utils';
6
+
7
+ const selector = (nextValue: ThreadState) =>
8
+ [
9
+ nextValue.replies,
10
+ nextValue.pagination.isLoadingPrev,
11
+ nextValue.pagination.isLoadingNext,
12
+ nextValue.parentMessage,
13
+ ] as const;
6
14
 
7
15
  export const useCreateThreadContext = <
8
16
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
@@ -15,38 +23,35 @@ export const useCreateThreadContext = <
15
23
  setThreadLoadingMore,
16
24
  thread,
17
25
  threadHasMore,
26
+ threadInstance,
18
27
  threadLoadingMore,
19
28
  threadMessages,
20
29
  }: ThreadContextValue<StreamChatGenerics>) => {
21
- const threadId = thread?.id;
22
- const threadReplyCount = thread?.reply_count;
23
- const threadLatestReactions = thread?.latest_reactions;
24
- const threadMessagesStr = reduceMessagesToString(threadMessages);
30
+ const [latestReplies, isLoadingPrev, isLoadingNext] =
31
+ useStateStore(threadInstance?.state, selector) ?? [];
25
32
 
26
- const threadContext: ThreadContextValue<StreamChatGenerics> = useMemo(
27
- () => ({
28
- allowThreadMessagesInChannel,
29
- closeThread,
30
- loadMoreThread,
31
- openThread,
32
- reloadThread,
33
- setThreadLoadingMore,
34
- thread,
35
- threadHasMore,
36
- threadLoadingMore,
37
- threadMessages,
38
- }),
39
- // eslint-disable-next-line react-hooks/exhaustive-deps
40
- [
41
- allowThreadMessagesInChannel,
42
- threadHasMore,
43
- threadId,
44
- threadLoadingMore,
45
- threadMessagesStr,
46
- threadReplyCount,
47
- threadLatestReactions,
48
- ],
49
- );
33
+ const contextAdapter = threadInstance
34
+ ? {
35
+ loadMoreRecentThread: threadInstance.loadNextPage,
36
+ loadMoreThread: threadInstance.loadPrevPage,
37
+ threadInstance,
38
+ threadLoadingMore: isLoadingPrev,
39
+ threadLoadingMoreRecent: isLoadingNext,
40
+ threadMessages: latestReplies ?? [],
41
+ }
42
+ : {};
50
43
 
51
- return threadContext;
44
+ return {
45
+ allowThreadMessagesInChannel,
46
+ closeThread,
47
+ loadMoreThread,
48
+ openThread,
49
+ reloadThread,
50
+ setThreadLoadingMore,
51
+ thread,
52
+ threadHasMore,
53
+ threadLoadingMore,
54
+ threadMessages,
55
+ ...contextAdapter,
56
+ };
52
57
  };
@@ -215,6 +215,16 @@ const ChatWithContext = <
215
215
  // eslint-disable-next-line react-hooks/exhaustive-deps
216
216
  }, [userID, enableOfflineSupport]);
217
217
 
218
+ useEffect(() => {
219
+ if (!client) return;
220
+
221
+ client.threads.registerSubscriptions();
222
+
223
+ return () => {
224
+ client.threads.unregisterSubscriptions();
225
+ };
226
+ }, [client]);
227
+
218
228
  // In case something went wrong, make sure to also unsubscribe the listener
219
229
  // on unmount if it exists to prevent a memory leak.
220
230
  useEffect(() => () => DBSyncManager.connectionChangedListener?.unsubscribe(), []);
@@ -4,10 +4,10 @@ import { StyleSheet, Text, View } from 'react-native';
4
4
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
5
5
  import { useTranslationContext } from '../../contexts/translationContext/TranslationContext';
6
6
  import { useViewport } from '../../hooks/useViewport';
7
- import { ChatIcon, MessageIcon } from '../../icons';
7
+ import { ChatIcon, MessageBubbleEmpty, MessageIcon } from '../../icons';
8
8
 
9
9
  export type EmptyStateProps = {
10
- listType?: 'channel' | 'message' | 'default';
10
+ listType?: 'channel' | 'message' | 'threads' | 'default';
11
11
  };
12
12
 
13
13
  export const EmptyStateIndicator = ({ listType }: EmptyStateProps) => {
@@ -55,6 +55,13 @@ export const EmptyStateIndicator = ({ listType }: EmptyStateProps) => {
55
55
  </Text>
56
56
  </View>
57
57
  );
58
+ case 'threads':
59
+ return (
60
+ <View style={[styles.container]}>
61
+ <MessageBubbleEmpty height={width} pathFill={'#B4BBBA'} width={width} />
62
+ <Text style={{ color: '#7E828B' }}>{t<string>('No threads here yet')}...</Text>
63
+ </View>
64
+ );
58
65
  default:
59
66
  return <Text style={[{ color: black }, messageContainer]}>No items exist</Text>;
60
67
  }
@@ -26,7 +26,7 @@ const LoadingIndicatorWrapper = ({ text }: LoadingIndicatorWrapperProps) => {
26
26
  };
27
27
 
28
28
  export type LoadingProps = {
29
- listType?: 'channel' | 'message' | 'default';
29
+ listType?: 'channel' | 'message' | 'threads' | 'default';
30
30
  loadingText?: string;
31
31
  };
32
32
 
@@ -47,6 +47,8 @@ export const LoadingIndicator = (props: LoadingProps) => {
47
47
  return <LoadingIndicatorWrapper text={t('Loading channels...')} />;
48
48
  case 'message':
49
49
  return <LoadingIndicatorWrapper text={t('Loading messages...')} />;
50
+ case 'threads':
51
+ return <LoadingIndicatorWrapper text={t('Loading threads...')} />;
50
52
  default:
51
53
  return <LoadingIndicatorWrapper text={t('Loading...')} />;
52
54
  }
@@ -2,6 +2,7 @@ import { ComponentType, useMemo } from 'react';
2
2
 
3
3
  import { ReactionResponse } from 'stream-chat';
4
4
 
5
+ import { useChatContext } from '../../../contexts';
5
6
  import {
6
7
  MessagesContextValue,
7
8
  useMessagesContext,
@@ -46,7 +47,15 @@ const isOwnReaction = <
46
47
  >(
47
48
  reactionType: string,
48
49
  ownReactions?: ReactionResponse<StreamChatGenerics>[] | null,
49
- ) => (ownReactions ? ownReactions.some((reaction) => reaction.type === reactionType) : false);
50
+ latestReactions?: ReactionResponse<StreamChatGenerics>[] | null,
51
+ userID?: string,
52
+ ) =>
53
+ (ownReactions ? ownReactions.some((reaction) => reaction.type === reactionType) : false) ||
54
+ (latestReactions
55
+ ? latestReactions.some(
56
+ (reaction) => reaction?.user?.id === userID && reaction.type === reactionType,
57
+ )
58
+ : false);
50
59
 
51
60
  const isSupportedReaction = (reactionType: string, supportedReactions: ReactionData[]) =>
52
61
  supportedReactions
@@ -76,6 +85,7 @@ export const useProcessReactions = <
76
85
  props: UseProcessReactionsParams<StreamChatGenerics>,
77
86
  ) => {
78
87
  const { supportedReactions: contextSupportedReactions } = useMessagesContext();
88
+ const { client } = useChatContext<StreamChatGenerics>();
79
89
 
80
90
  const {
81
91
  latest_reactions,
@@ -100,7 +110,12 @@ export const useProcessReactions = <
100
110
  Icon: getEmojiByReactionType(reactionType, supportedReactions),
101
111
  lastReactionAt: last_reaction_at ? new Date(last_reaction_at) : null,
102
112
  latestReactedUserNames,
103
- own: isOwnReaction<StreamChatGenerics>(reactionType, own_reactions),
113
+ own: isOwnReaction<StreamChatGenerics>(
114
+ reactionType,
115
+ own_reactions,
116
+ latest_reactions,
117
+ client.userID,
118
+ ),
104
119
  type: reactionType,
105
120
  unlistedReactedUserCount: count - latestReactedUserNames.length,
106
121
  };
@@ -112,6 +127,12 @@ export const useProcessReactions = <
112
127
  hasReactions: unsortedReactions.length > 0,
113
128
  totalReactionCount: unsortedReactions.reduce((total, { count }) => total + count, 0),
114
129
  };
115
- // eslint-disable-next-line react-hooks/exhaustive-deps
116
- }, [reaction_groups, own_reactions?.length, latest_reactions?.length, sortReactions]);
130
+ }, [
131
+ client.userID,
132
+ reaction_groups,
133
+ own_reactions,
134
+ latest_reactions,
135
+ supportedReactions,
136
+ sortReactions,
137
+ ]);
117
138
  };
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import { ActivityIndicator, StyleSheet, View } from 'react-native';
3
+
4
+ import { useThreadContext } from '../../contexts';
5
+ import { useTheme } from '../../contexts/themeContext/ThemeContext';
6
+
7
+ import type { DefaultStreamChatGenerics } from '../../types/types';
8
+
9
+ const styles = StyleSheet.create({
10
+ activityIndicatorContainer: {
11
+ padding: 10,
12
+ width: '100%',
13
+ },
14
+ });
15
+
16
+ export type InlineLoadingMoreRecentThreadIndicatorPropsWithContext = {
17
+ loadingMoreRecent?: boolean;
18
+ };
19
+
20
+ export const InlineLoadingMoreRecentIndicatorWithContext = ({
21
+ loadingMoreRecent,
22
+ }: InlineLoadingMoreRecentThreadIndicatorPropsWithContext) => {
23
+ const { theme } = useTheme();
24
+
25
+ const {
26
+ colors: { accent_blue },
27
+ } = theme;
28
+
29
+ if (!loadingMoreRecent) {
30
+ return null;
31
+ }
32
+
33
+ return (
34
+ <View style={styles.activityIndicatorContainer}>
35
+ <ActivityIndicator color={accent_blue} size='small' />
36
+ </View>
37
+ );
38
+ };
39
+
40
+ const areEqual = (
41
+ prevProps: InlineLoadingMoreRecentThreadIndicatorPropsWithContext,
42
+ nextProps: InlineLoadingMoreRecentThreadIndicatorPropsWithContext,
43
+ ) => {
44
+ const { loadingMoreRecent: prevLoadingMoreRecent } = prevProps;
45
+ const { loadingMoreRecent: nextLoadingMoreRecent } = nextProps;
46
+
47
+ const loadingMoreRecentEqual = prevLoadingMoreRecent === nextLoadingMoreRecent;
48
+ if (!loadingMoreRecentEqual) return false;
49
+
50
+ return true;
51
+ };
52
+
53
+ const MemoizedInlineLoadingMoreRecentIndicator = React.memo(
54
+ InlineLoadingMoreRecentIndicatorWithContext,
55
+ areEqual,
56
+ ) as typeof InlineLoadingMoreRecentIndicatorWithContext;
57
+
58
+ export const InlineLoadingMoreRecentThreadIndicator = <
59
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
60
+ >() => {
61
+ const { threadLoadingMoreRecent } = useThreadContext<StreamChatGenerics>();
62
+
63
+ return <MemoizedInlineLoadingMoreRecentIndicator loadingMoreRecent={threadLoadingMoreRecent} />;
64
+ };
@@ -20,7 +20,7 @@ import { useShouldScrollToRecentOnNewOwnMessage } from './hooks/useShouldScrollT
20
20
 
21
21
  import { InlineLoadingMoreIndicator } from './InlineLoadingMoreIndicator';
22
22
  import { InlineLoadingMoreRecentIndicator } from './InlineLoadingMoreRecentIndicator';
23
- import { InlineLoadingMoreThreadIndicator } from './InlineLoadingMoreThreadIndicator';
23
+ import { InlineLoadingMoreRecentThreadIndicator } from './InlineLoadingMoreRecentThreadIndicator';
24
24
  import { getLastReceivedMessage } from './utils/getLastReceivedMessage';
25
25
 
26
26
  import {
@@ -147,7 +147,10 @@ type MessageListPropsWithContext<
147
147
  | 'TypingIndicator'
148
148
  | 'TypingIndicatorContainer'
149
149
  > &
150
- Pick<ThreadContextValue<StreamChatGenerics>, 'loadMoreThread' | 'thread'> & {
150
+ Pick<
151
+ ThreadContextValue<StreamChatGenerics>,
152
+ 'loadMoreRecentThread' | 'loadMoreThread' | 'thread' | 'threadInstance'
153
+ > & {
151
154
  /**
152
155
  * Besides existing (default) UX behavior of underlying FlatList of MessageList component, if you want
153
156
  * to attach some additional props to underlying FlatList, you can add it to following prop.
@@ -221,9 +224,9 @@ const MessageListWithContext = <
221
224
  >(
222
225
  props: MessageListPropsWithContext<StreamChatGenerics>,
223
226
  ) => {
224
- const LoadingMoreIndicator = props.threadList
225
- ? InlineLoadingMoreThreadIndicator
226
- : InlineLoadingMoreIndicator;
227
+ const LoadingMoreRecentIndicator = props.threadList
228
+ ? InlineLoadingMoreRecentThreadIndicator
229
+ : InlineLoadingMoreRecentIndicator;
227
230
  const {
228
231
  additionalFlatListProps,
229
232
  channel,
@@ -234,9 +237,9 @@ const MessageListWithContext = <
234
237
  disableTypingIndicator,
235
238
  EmptyStateIndicator,
236
239
  FlatList,
237
- FooterComponent = LoadingMoreIndicator,
240
+ FooterComponent = InlineLoadingMoreIndicator,
238
241
  hasNoMoreRecentMessagesToLoad,
239
- HeaderComponent = InlineLoadingMoreRecentIndicator,
242
+ HeaderComponent = LoadingMoreRecentIndicator,
240
243
  hideStickyDateHeader,
241
244
  initialScrollToFirstUnreadMessage,
242
245
  InlineDateSeparator,
@@ -249,6 +252,7 @@ const MessageListWithContext = <
249
252
  LoadingIndicator,
250
253
  loadMore,
251
254
  loadMoreRecent,
255
+ loadMoreRecentThread,
252
256
  loadMoreThread,
253
257
  markRead,
254
258
  Message,
@@ -269,6 +273,7 @@ const MessageListWithContext = <
269
273
  StickyHeader,
270
274
  targetedMessage,
271
275
  thread,
276
+ threadInstance,
272
277
  threadList = false,
273
278
  TypingIndicator,
274
279
  TypingIndicatorContainer,
@@ -744,7 +749,13 @@ const MessageListWithContext = <
744
749
  if (onEndReachedInPromise.current) {
745
750
  await onEndReachedInPromise.current;
746
751
  }
747
- onStartReachedInPromise.current = loadMoreRecent(limit).then(callback).catch(onError);
752
+ onStartReachedInPromise.current = (
753
+ threadList && !!threadInstance && loadMoreRecentThread
754
+ ? loadMoreRecentThread({ limit })
755
+ : loadMoreRecent(limit)
756
+ )
757
+ .then(callback)
758
+ .catch(onError);
748
759
  };
749
760
 
750
761
  /**
@@ -1253,7 +1264,8 @@ export const MessageList = <
1253
1264
  const { hasNoMoreRecentMessagesToLoad, loadMore, loadMoreRecent } =
1254
1265
  usePaginatedMessageListContext<StreamChatGenerics>();
1255
1266
  const { overlay } = useOverlayContext();
1256
- const { loadMoreThread, thread } = useThreadContext<StreamChatGenerics>();
1267
+ const { loadMoreRecentThread, loadMoreThread, thread, threadInstance } =
1268
+ useThreadContext<StreamChatGenerics>();
1257
1269
 
1258
1270
  return (
1259
1271
  <MessageListWithContext
@@ -1280,6 +1292,7 @@ export const MessageList = <
1280
1292
  LoadingIndicator,
1281
1293
  loadMore,
1282
1294
  loadMoreRecent,
1295
+ loadMoreRecentThread,
1283
1296
  loadMoreThread,
1284
1297
  markRead,
1285
1298
  Message,
@@ -1297,6 +1310,7 @@ export const MessageList = <
1297
1310
  StickyHeader,
1298
1311
  targetedMessage,
1299
1312
  thread,
1313
+ threadInstance,
1300
1314
  threadList,
1301
1315
  TypingIndicator,
1302
1316
  TypingIndicatorContainer,
@@ -23,7 +23,12 @@ type ThreadPropsWithContext<
23
23
  Pick<MessagesContextValue<StreamChatGenerics>, 'MessageList'> &
24
24
  Pick<
25
25
  ThreadContextValue<StreamChatGenerics>,
26
- 'closeThread' | 'loadMoreThread' | 'parentMessagePreventPress' | 'reloadThread' | 'thread'
26
+ | 'closeThread'
27
+ | 'loadMoreThread'
28
+ | 'parentMessagePreventPress'
29
+ | 'reloadThread'
30
+ | 'thread'
31
+ | 'threadInstance'
27
32
  > & {
28
33
  /**
29
34
  * Additional props for underlying MessageInput component.
@@ -70,9 +75,13 @@ const ThreadWithContext = <
70
75
  onThreadDismount,
71
76
  parentMessagePreventPress = true,
72
77
  thread,
78
+ threadInstance,
73
79
  } = props;
74
80
 
75
81
  useEffect(() => {
82
+ if (threadInstance?.activate) {
83
+ threadInstance.activate();
84
+ }
76
85
  const loadMoreThreadAsync = async () => {
77
86
  await loadMoreThread();
78
87
  };
@@ -80,21 +89,20 @@ const ThreadWithContext = <
80
89
  if (thread?.id && thread.reply_count) {
81
90
  loadMoreThreadAsync();
82
91
  }
83
- // eslint-disable-next-line react-hooks/exhaustive-deps
84
- }, []);
85
92
 
86
- useEffect(
87
- () => () => {
93
+ return () => {
94
+ if (threadInstance?.deactivate) {
95
+ threadInstance.deactivate();
96
+ }
88
97
  if (closeThreadOnDismount) {
89
98
  closeThread();
90
99
  }
91
100
  if (onThreadDismount) {
92
101
  onThreadDismount();
93
102
  }
94
- },
103
+ };
95
104
  // eslint-disable-next-line react-hooks/exhaustive-deps
96
- [],
97
- );
105
+ }, []);
98
106
 
99
107
  if (!thread) return null;
100
108
 
@@ -137,7 +145,7 @@ export const Thread = <
137
145
  const { client } = useChatContext<StreamChatGenerics>();
138
146
  const { threadList } = useChannelContext<StreamChatGenerics>();
139
147
  const { MessageList } = useMessagesContext<StreamChatGenerics>();
140
- const { closeThread, loadMoreThread, reloadThread, thread } =
148
+ const { closeThread, loadMoreThread, reloadThread, thread, threadInstance } =
141
149
  useThreadContext<StreamChatGenerics>();
142
150
 
143
151
  if (thread?.id && !threadList) {
@@ -155,6 +163,7 @@ export const Thread = <
155
163
  MessageList,
156
164
  reloadThread,
157
165
  thread,
166
+ threadInstance,
158
167
  }}
159
168
  {...props}
160
169
  />
@@ -54,6 +54,7 @@ exports[`Thread should match thread snapshot 1`] = `
54
54
  "attachments": [],
55
55
  "created_at": 2020-05-05T14:50:00.000Z,
56
56
  "dateSeparator": undefined,
57
+ "deleted_at": null,
57
58
  "groupStyles": [
58
59
  "single",
59
60
  ],
@@ -62,6 +63,7 @@ exports[`Thread should match thread snapshot 1`] = `
62
63
  "message_text_updated_at": "2020-05-05T14:50:00.000Z",
63
64
  "parent_id": "b4612a73-fa2b-5787-bf71-1adc8f291a04",
64
65
  "pinned_at": null,
66
+ "reaction_groups": null,
65
67
  "readBy": false,
66
68
  "status": "received",
67
69
  "text": "Message6",
@@ -83,6 +85,7 @@ exports[`Thread should match thread snapshot 1`] = `
83
85
  "attachments": [],
84
86
  "created_at": 2020-05-05T14:50:00.000Z,
85
87
  "dateSeparator": undefined,
88
+ "deleted_at": null,
86
89
  "groupStyles": [
87
90
  "single",
88
91
  ],
@@ -91,6 +94,7 @@ exports[`Thread should match thread snapshot 1`] = `
91
94
  "message_text_updated_at": "2020-05-05T14:50:00.000Z",
92
95
  "parent_id": "b4612a73-fa2b-5787-bf71-1adc8f291a04",
93
96
  "pinned_at": null,
97
+ "reaction_groups": null,
94
98
  "readBy": false,
95
99
  "status": "received",
96
100
  "text": "Message5",
@@ -112,6 +116,7 @@ exports[`Thread should match thread snapshot 1`] = `
112
116
  "attachments": [],
113
117
  "created_at": 2020-05-05T14:50:00.000Z,
114
118
  "dateSeparator": 2020-05-05T14:50:00.000Z,
119
+ "deleted_at": null,
115
120
  "groupStyles": [
116
121
  "single",
117
122
  ],
@@ -120,6 +125,7 @@ exports[`Thread should match thread snapshot 1`] = `
120
125
  "message_text_updated_at": "2020-05-05T14:50:00.000Z",
121
126
  "parent_id": "b4612a73-fa2b-5787-bf71-1adc8f291a04",
122
127
  "pinned_at": null,
128
+ "reaction_groups": null,
123
129
  "readBy": false,
124
130
  "status": "received",
125
131
  "text": "Message4",
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, Text, View } from 'react-native';
2
+ import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
3
3
  import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg';
4
4
 
5
5
  import {
@@ -17,6 +17,10 @@ import type { DefaultStreamChatGenerics } from '../../../types/types';
17
17
 
18
18
  const styles = StyleSheet.create({
19
19
  absolute: { position: 'absolute' },
20
+ activityIndicatorContainer: {
21
+ padding: 10,
22
+ width: '100%',
23
+ },
20
24
  messagePadding: {
21
25
  paddingHorizontal: 8,
22
26
  },
@@ -40,6 +44,25 @@ type ThreadFooterComponentPropsWithContext<
40
44
  > = Pick<MessagesContextValue<StreamChatGenerics>, 'Message'> &
41
45
  Pick<ThreadContextValue<StreamChatGenerics>, 'parentMessagePreventPress' | 'thread'>;
42
46
 
47
+ export const InlineLoadingMoreThreadIndicator = () => {
48
+ const { threadLoadingMore } = useThreadContext();
49
+ const {
50
+ theme: {
51
+ colors: { accent_blue },
52
+ },
53
+ } = useTheme();
54
+
55
+ if (!threadLoadingMore) {
56
+ return null;
57
+ }
58
+
59
+ return (
60
+ <View style={styles.activityIndicatorContainer}>
61
+ <ActivityIndicator color={accent_blue} size='small' />
62
+ </View>
63
+ );
64
+ };
65
+
43
66
  const ThreadFooterComponentWithContext = <
44
67
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
45
68
  >(
@@ -111,6 +134,7 @@ const ThreadFooterComponentWithContext = <
111
134
  })}
112
135
  </Text>
113
136
  </View>
137
+ <InlineLoadingMoreThreadIndicator />
114
138
  </View>
115
139
  );
116
140
  };
@@ -163,7 +187,8 @@ export const ThreadFooterComponent = <
163
187
  props: ThreadFooterComponentProps<StreamChatGenerics>,
164
188
  ) => {
165
189
  const { Message } = useMessagesContext<StreamChatGenerics>();
166
- const { parentMessagePreventPress, thread } = useThreadContext<StreamChatGenerics>();
190
+ const { parentMessagePreventPress, thread, threadLoadingMore } =
191
+ useThreadContext<StreamChatGenerics>();
167
192
 
168
193
  return (
169
194
  <MemoizedThreadFooter
@@ -171,6 +196,7 @@ export const ThreadFooterComponent = <
171
196
  Message,
172
197
  parentMessagePreventPress,
173
198
  thread,
199
+ threadLoadingMore,
174
200
  }}
175
201
  {...props}
176
202
  />
@@ -0,0 +1,118 @@
1
+ import React, { useEffect } from 'react';
2
+ import { FlatList, View } from 'react-native';
3
+
4
+ import { Thread, ThreadManagerState } from 'stream-chat';
5
+
6
+ import { ThreadListItem } from './ThreadListItem';
7
+ import { ThreadListUnreadBanner as DefaultThreadListBanner } from './ThreadListUnreadBanner';
8
+
9
+ import { useChatContext } from '../../contexts';
10
+ import {
11
+ ThreadsContextValue,
12
+ ThreadsProvider,
13
+ useThreadsContext,
14
+ } from '../../contexts/threadsContext/ThreadsContext';
15
+ import { useStateStore } from '../../hooks';
16
+ import type { DefaultStreamChatGenerics } from '../../types/types';
17
+ import { EmptyStateIndicator } from '../Indicators/EmptyStateIndicator';
18
+ import { LoadingIndicator } from '../Indicators/LoadingIndicator';
19
+
20
+ const selector = (nextValue: ThreadManagerState) =>
21
+ [nextValue.threads, nextValue.pagination.isLoading, nextValue.pagination.isLoadingNext] as const;
22
+
23
+ export type ThreadListProps<
24
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
25
+ > = Pick<
26
+ ThreadsContextValue<StreamChatGenerics>,
27
+ | 'additionalFlatListProps'
28
+ | 'isFocused'
29
+ | 'onThreadSelect'
30
+ | 'ThreadListEmptyPlaceholder'
31
+ | 'ThreadListLoadingIndicator'
32
+ | 'ThreadListUnreadBanner'
33
+ > & { ThreadList?: React.ComponentType };
34
+
35
+ export const DefaultThreadListEmptyPlaceholder = () => <EmptyStateIndicator listType='threads' />;
36
+
37
+ export const DefaultThreadListLoadingIndicator = () => <LoadingIndicator listType='threads' />;
38
+ export const DefaultThreadListLoadingNextIndicator = () => <LoadingIndicator />;
39
+
40
+ const DefaultThreadListItem = (props: { item: Thread }) => <ThreadListItem thread={props.item} />;
41
+
42
+ const ThreadListComponent = () => {
43
+ const {
44
+ additionalFlatListProps,
45
+ isLoading,
46
+ isLoadingNext,
47
+ loadMore,
48
+ ThreadListEmptyPlaceholder = DefaultThreadListEmptyPlaceholder,
49
+ ThreadListLoadingIndicator = DefaultThreadListLoadingIndicator,
50
+ ThreadListLoadingMoreIndicator = DefaultThreadListLoadingNextIndicator,
51
+ ThreadListUnreadBanner = DefaultThreadListBanner,
52
+ threads,
53
+ } = useThreadsContext();
54
+
55
+ if (isLoading) {
56
+ return (
57
+ <View style={{ flex: 1 }}>
58
+ <ThreadListLoadingIndicator />
59
+ </View>
60
+ );
61
+ }
62
+
63
+ return (
64
+ <>
65
+ <ThreadListUnreadBanner />
66
+ <FlatList
67
+ contentContainerStyle={{ flexGrow: 1 }}
68
+ data={threads}
69
+ keyExtractor={(props) => props.id}
70
+ ListEmptyComponent={ThreadListEmptyPlaceholder}
71
+ ListFooterComponent={isLoadingNext ? ThreadListLoadingMoreIndicator : null}
72
+ onEndReached={loadMore}
73
+ renderItem={DefaultThreadListItem}
74
+ testID='thread-flatlist'
75
+ {...additionalFlatListProps}
76
+ />
77
+ </>
78
+ );
79
+ };
80
+
81
+ export const ThreadList = (props: ThreadListProps) => {
82
+ const { isFocused = true, ThreadList = ThreadListComponent } = props;
83
+ const { client } = useChatContext();
84
+
85
+ useEffect(() => {
86
+ if (!client) return;
87
+ if (isFocused) {
88
+ client.threads.activate();
89
+ } else {
90
+ client.threads.deactivate();
91
+ }
92
+ }, [client, isFocused]);
93
+
94
+ useEffect(() => {
95
+ if (!client) return;
96
+
97
+ const listener = client.on('connection.recovered', () => {
98
+ client.threads.reload({ force: true });
99
+ });
100
+
101
+ client.threads.reload({ force: true });
102
+
103
+ return () => {
104
+ client.threads.deactivate();
105
+ listener.unsubscribe();
106
+ };
107
+ }, [client]);
108
+
109
+ const [threads, isLoading, isLoadingNext] = useStateStore(client.threads.state, selector);
110
+
111
+ return (
112
+ <ThreadsProvider
113
+ value={{ isLoading, isLoadingNext, loadMore: client.threads.loadNextPage, threads, ...props }}
114
+ >
115
+ <ThreadList />
116
+ </ThreadsProvider>
117
+ );
118
+ };