stream-chat-react-native-core 5.38.1 → 5.39.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 (242) 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/index.js +22 -0
  30. package/lib/commonjs/contexts/index.js.map +1 -1
  31. package/lib/commonjs/contexts/themeContext/utils/theme.js +19 -0
  32. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  33. package/lib/commonjs/contexts/threadContext/ThreadContext.js.map +1 -1
  34. package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js +28 -0
  35. package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js.map +1 -0
  36. package/lib/commonjs/contexts/threadsContext/ThreadsContext.js +33 -0
  37. package/lib/commonjs/contexts/threadsContext/ThreadsContext.js.map +1 -0
  38. package/lib/commonjs/hooks/index.js +11 -0
  39. package/lib/commonjs/hooks/index.js.map +1 -1
  40. package/lib/commonjs/hooks/useStateStore.js +23 -0
  41. package/lib/commonjs/hooks/useStateStore.js.map +1 -0
  42. package/lib/commonjs/i18n/en.json +7 -0
  43. package/lib/commonjs/i18n/es.json +7 -0
  44. package/lib/commonjs/i18n/fr.json +7 -0
  45. package/lib/commonjs/i18n/he.json +7 -0
  46. package/lib/commonjs/i18n/hi.json +7 -0
  47. package/lib/commonjs/i18n/it.json +7 -0
  48. package/lib/commonjs/i18n/ja.json +7 -0
  49. package/lib/commonjs/i18n/ko.json +7 -0
  50. package/lib/commonjs/i18n/nl.json +7 -0
  51. package/lib/commonjs/i18n/pt-br.json +7 -0
  52. package/lib/commonjs/i18n/ru.json +7 -0
  53. package/lib/commonjs/i18n/tr.json +7 -0
  54. package/lib/commonjs/icons/MessageBubble.js +19 -0
  55. package/lib/commonjs/icons/MessageBubble.js.map +1 -0
  56. package/lib/commonjs/icons/MessageBubbleEmpty.js +19 -0
  57. package/lib/commonjs/icons/MessageBubbleEmpty.js.map +1 -0
  58. package/lib/commonjs/icons/Reload.js +19 -0
  59. package/lib/commonjs/icons/Reload.js.map +1 -0
  60. package/lib/commonjs/icons/index.js +33 -0
  61. package/lib/commonjs/icons/index.js.map +1 -1
  62. package/lib/commonjs/store/mappers/mapDateTimeToStorable.js.map +1 -1
  63. package/lib/commonjs/version.json +1 -1
  64. package/lib/module/components/Channel/Channel.js +58 -25
  65. package/lib/module/components/Channel/Channel.js.map +1 -1
  66. package/lib/module/components/Channel/hooks/useCreateThreadContext.js +33 -21
  67. package/lib/module/components/Channel/hooks/useCreateThreadContext.js.map +1 -1
  68. package/lib/module/components/Chat/Chat.js +7 -0
  69. package/lib/module/components/Chat/Chat.js.map +1 -1
  70. package/lib/module/components/Indicators/EmptyStateIndicator.js +14 -0
  71. package/lib/module/components/Indicators/EmptyStateIndicator.js.map +1 -1
  72. package/lib/module/components/Indicators/LoadingIndicator.js +4 -0
  73. package/lib/module/components/Indicators/LoadingIndicator.js.map +1 -1
  74. package/lib/module/components/Message/hooks/useProcessReactions.js +13 -7
  75. package/lib/module/components/Message/hooks/useProcessReactions.js.map +1 -1
  76. package/lib/module/components/MessageList/InlineLoadingMoreRecentThreadIndicator.js +52 -0
  77. package/lib/module/components/MessageList/InlineLoadingMoreRecentThreadIndicator.js.map +1 -0
  78. package/lib/module/components/MessageList/MessageList.js +14 -6
  79. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  80. package/lib/module/components/Thread/Thread.js +12 -5
  81. package/lib/module/components/Thread/Thread.js.map +1 -1
  82. package/lib/module/components/Thread/components/ThreadFooterComponent.js +41 -18
  83. package/lib/module/components/Thread/components/ThreadFooterComponent.js.map +1 -1
  84. package/lib/module/components/ThreadList/ThreadList.js +132 -0
  85. package/lib/module/components/ThreadList/ThreadList.js.map +1 -0
  86. package/lib/module/components/ThreadList/ThreadListItem.js +246 -0
  87. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -0
  88. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js +66 -0
  89. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js.map +1 -0
  90. package/lib/module/components/index.js +11 -11
  91. package/lib/module/components/index.js.map +1 -1
  92. package/lib/module/contexts/index.js +22 -0
  93. package/lib/module/contexts/index.js.map +1 -1
  94. package/lib/module/contexts/themeContext/utils/theme.js +19 -0
  95. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  96. package/lib/module/contexts/threadContext/ThreadContext.js.map +1 -1
  97. package/lib/module/contexts/threadsContext/ThreadListItemContext.js +28 -0
  98. package/lib/module/contexts/threadsContext/ThreadListItemContext.js.map +1 -0
  99. package/lib/module/contexts/threadsContext/ThreadsContext.js +33 -0
  100. package/lib/module/contexts/threadsContext/ThreadsContext.js.map +1 -0
  101. package/lib/module/hooks/index.js +11 -0
  102. package/lib/module/hooks/index.js.map +1 -1
  103. package/lib/module/hooks/useStateStore.js +23 -0
  104. package/lib/module/hooks/useStateStore.js.map +1 -0
  105. package/lib/module/i18n/en.json +7 -0
  106. package/lib/module/i18n/es.json +7 -0
  107. package/lib/module/i18n/fr.json +7 -0
  108. package/lib/module/i18n/he.json +7 -0
  109. package/lib/module/i18n/hi.json +7 -0
  110. package/lib/module/i18n/it.json +7 -0
  111. package/lib/module/i18n/ja.json +7 -0
  112. package/lib/module/i18n/ko.json +7 -0
  113. package/lib/module/i18n/nl.json +7 -0
  114. package/lib/module/i18n/pt-br.json +7 -0
  115. package/lib/module/i18n/ru.json +7 -0
  116. package/lib/module/i18n/tr.json +7 -0
  117. package/lib/module/icons/MessageBubble.js +19 -0
  118. package/lib/module/icons/MessageBubble.js.map +1 -0
  119. package/lib/module/icons/MessageBubbleEmpty.js +19 -0
  120. package/lib/module/icons/MessageBubbleEmpty.js.map +1 -0
  121. package/lib/module/icons/Reload.js +19 -0
  122. package/lib/module/icons/Reload.js.map +1 -0
  123. package/lib/module/icons/index.js +33 -0
  124. package/lib/module/icons/index.js.map +1 -1
  125. package/lib/module/store/mappers/mapDateTimeToStorable.js.map +1 -1
  126. package/lib/module/version.json +1 -1
  127. package/lib/typescript/components/AttachmentPicker/AttachmentPicker.d.ts +1 -1
  128. package/lib/typescript/components/Channel/Channel.d.ts +3 -2
  129. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  130. package/lib/typescript/components/Channel/hooks/useCreateThreadContext.d.ts +34 -1
  131. package/lib/typescript/components/Channel/hooks/useCreateThreadContext.d.ts.map +1 -1
  132. package/lib/typescript/components/Chat/Chat.d.ts.map +1 -1
  133. package/lib/typescript/components/Indicators/EmptyStateIndicator.d.ts +1 -1
  134. package/lib/typescript/components/Indicators/EmptyStateIndicator.d.ts.map +1 -1
  135. package/lib/typescript/components/Indicators/LoadingIndicator.d.ts +1 -1
  136. package/lib/typescript/components/Indicators/LoadingIndicator.d.ts.map +1 -1
  137. package/lib/typescript/components/Message/hooks/useProcessReactions.d.ts.map +1 -1
  138. package/lib/typescript/components/MessageList/InlineLoadingMoreRecentThreadIndicator.d.ts +8 -0
  139. package/lib/typescript/components/MessageList/InlineLoadingMoreRecentThreadIndicator.d.ts.map +1 -0
  140. package/lib/typescript/components/MessageList/MessageList.d.ts +1 -1
  141. package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
  142. package/lib/typescript/components/Thread/Thread.d.ts +1 -1
  143. package/lib/typescript/components/Thread/Thread.d.ts.map +1 -1
  144. package/lib/typescript/components/Thread/components/ThreadFooterComponent.d.ts +1 -0
  145. package/lib/typescript/components/Thread/components/ThreadFooterComponent.d.ts.map +1 -1
  146. package/lib/typescript/components/ThreadList/ThreadList.d.ts +11 -0
  147. package/lib/typescript/components/ThreadList/ThreadList.d.ts.map +1 -0
  148. package/lib/typescript/components/ThreadList/ThreadListItem.d.ts +16 -0
  149. package/lib/typescript/components/ThreadList/ThreadListItem.d.ts.map +1 -0
  150. package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts +3 -0
  151. package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts.map +1 -0
  152. package/lib/typescript/components/index.d.ts +1 -1
  153. package/lib/typescript/components/index.d.ts.map +1 -1
  154. package/lib/typescript/contexts/attachmentPickerContext/AttachmentPickerContext.d.ts +1 -1
  155. package/lib/typescript/contexts/index.d.ts +2 -0
  156. package/lib/typescript/contexts/index.d.ts.map +1 -1
  157. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +1 -1
  158. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +1 -1
  159. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +19 -0
  160. package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
  161. package/lib/typescript/contexts/threadContext/ThreadContext.d.ts +11 -2
  162. package/lib/typescript/contexts/threadContext/ThreadContext.d.ts.map +1 -1
  163. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts +19 -0
  164. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts.map +1 -0
  165. package/lib/typescript/contexts/threadsContext/ThreadsContext.d.ts +25 -0
  166. package/lib/typescript/contexts/threadsContext/ThreadsContext.d.ts.map +1 -0
  167. package/lib/typescript/hooks/index.d.ts +1 -0
  168. package/lib/typescript/hooks/index.d.ts.map +1 -1
  169. package/lib/typescript/hooks/useStateStore.d.ts +4 -0
  170. package/lib/typescript/hooks/useStateStore.d.ts.map +1 -0
  171. package/lib/typescript/i18n/en.json +7 -0
  172. package/lib/typescript/i18n/es.json +7 -0
  173. package/lib/typescript/i18n/fr.json +7 -0
  174. package/lib/typescript/i18n/he.json +7 -0
  175. package/lib/typescript/i18n/hi.json +7 -0
  176. package/lib/typescript/i18n/it.json +7 -0
  177. package/lib/typescript/i18n/ja.json +7 -0
  178. package/lib/typescript/i18n/ko.json +7 -0
  179. package/lib/typescript/i18n/nl.json +7 -0
  180. package/lib/typescript/i18n/pt-br.json +7 -0
  181. package/lib/typescript/i18n/ru.json +7 -0
  182. package/lib/typescript/i18n/tr.json +7 -0
  183. package/lib/typescript/icons/MessageBubble.d.ts +4 -0
  184. package/lib/typescript/icons/MessageBubble.d.ts.map +1 -0
  185. package/lib/typescript/icons/MessageBubbleEmpty.d.ts +4 -0
  186. package/lib/typescript/icons/MessageBubbleEmpty.d.ts.map +1 -0
  187. package/lib/typescript/icons/Reload.d.ts +4 -0
  188. package/lib/typescript/icons/Reload.d.ts.map +1 -0
  189. package/lib/typescript/icons/index.d.ts +3 -0
  190. package/lib/typescript/icons/index.d.ts.map +1 -1
  191. package/lib/typescript/store/mappers/mapDateTimeToStorable.d.ts +1 -1
  192. package/lib/typescript/store/mappers/mapDateTimeToStorable.d.ts.map +1 -1
  193. package/lib/typescript/utils/i18n/Streami18n.d.ts +7 -0
  194. package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
  195. package/package.json +2 -2
  196. package/src/components/Channel/Channel.tsx +54 -20
  197. package/src/components/Channel/hooks/useCreateThreadContext.ts +36 -31
  198. package/src/components/Chat/Chat.tsx +10 -0
  199. package/src/components/Indicators/EmptyStateIndicator.tsx +9 -2
  200. package/src/components/Indicators/LoadingIndicator.tsx +3 -1
  201. package/src/components/Message/hooks/useProcessReactions.ts +25 -4
  202. package/src/components/MessageList/InlineLoadingMoreRecentThreadIndicator.tsx +64 -0
  203. package/src/components/MessageList/MessageList.tsx +23 -9
  204. package/src/components/Thread/Thread.tsx +18 -9
  205. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +6 -0
  206. package/src/components/Thread/components/ThreadFooterComponent.tsx +28 -2
  207. package/src/components/ThreadList/ThreadList.tsx +119 -0
  208. package/src/components/ThreadList/ThreadListItem.tsx +268 -0
  209. package/src/components/ThreadList/ThreadListUnreadBanner.tsx +52 -0
  210. package/src/components/index.ts +1 -1
  211. package/src/contexts/index.ts +2 -0
  212. package/src/contexts/themeContext/utils/theme.ts +36 -0
  213. package/src/contexts/threadContext/ThreadContext.tsx +9 -2
  214. package/src/contexts/threadsContext/ThreadListItemContext.tsx +41 -0
  215. package/src/contexts/threadsContext/ThreadsContext.tsx +62 -0
  216. package/src/hooks/index.ts +1 -0
  217. package/src/hooks/useStateStore.ts +31 -0
  218. package/src/i18n/en.json +7 -0
  219. package/src/i18n/es.json +7 -0
  220. package/src/i18n/fr.json +7 -0
  221. package/src/i18n/he.json +7 -0
  222. package/src/i18n/hi.json +7 -0
  223. package/src/i18n/it.json +7 -0
  224. package/src/i18n/ja.json +7 -0
  225. package/src/i18n/ko.json +7 -0
  226. package/src/i18n/nl.json +7 -0
  227. package/src/i18n/pt-br.json +7 -0
  228. package/src/i18n/ru.json +7 -0
  229. package/src/i18n/tr.json +7 -0
  230. package/src/icons/MessageBubble.tsx +12 -0
  231. package/src/icons/MessageBubbleEmpty.tsx +12 -0
  232. package/src/icons/Reload.tsx +12 -0
  233. package/src/icons/index.ts +3 -0
  234. package/src/store/mappers/mapDateTimeToStorable.ts +1 -1
  235. package/src/version.json +1 -1
  236. package/lib/commonjs/components/MessageList/InlineLoadingMoreThreadIndicator.js +0 -52
  237. package/lib/commonjs/components/MessageList/InlineLoadingMoreThreadIndicator.js.map +0 -1
  238. package/lib/module/components/MessageList/InlineLoadingMoreThreadIndicator.js +0 -52
  239. package/lib/module/components/MessageList/InlineLoadingMoreThreadIndicator.js.map +0 -1
  240. package/lib/typescript/components/MessageList/InlineLoadingMoreThreadIndicator.d.ts +0 -8
  241. package/lib/typescript/components/MessageList/InlineLoadingMoreThreadIndicator.d.ts.map +0 -1
  242. package/src/components/MessageList/InlineLoadingMoreThreadIndicator.tsx +0 -64
@@ -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,119 @@
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
+ | 'ThreadListItem'
31
+ | 'ThreadListEmptyPlaceholder'
32
+ | 'ThreadListLoadingIndicator'
33
+ | 'ThreadListUnreadBanner'
34
+ > & { ThreadList?: React.ComponentType };
35
+
36
+ export const DefaultThreadListEmptyPlaceholder = () => <EmptyStateIndicator listType='threads' />;
37
+
38
+ export const DefaultThreadListLoadingIndicator = () => <LoadingIndicator listType='threads' />;
39
+ export const DefaultThreadListLoadingNextIndicator = () => <LoadingIndicator />;
40
+
41
+ const DefaultThreadListItem = (props: { item: Thread }) => <ThreadListItem thread={props.item} />;
42
+
43
+ const ThreadListComponent = () => {
44
+ const {
45
+ additionalFlatListProps,
46
+ isLoading,
47
+ isLoadingNext,
48
+ loadMore,
49
+ ThreadListEmptyPlaceholder = DefaultThreadListEmptyPlaceholder,
50
+ ThreadListLoadingIndicator = DefaultThreadListLoadingIndicator,
51
+ ThreadListLoadingMoreIndicator = DefaultThreadListLoadingNextIndicator,
52
+ ThreadListUnreadBanner = DefaultThreadListBanner,
53
+ threads,
54
+ } = useThreadsContext();
55
+
56
+ if (isLoading) {
57
+ return (
58
+ <View style={{ flex: 1 }}>
59
+ <ThreadListLoadingIndicator />
60
+ </View>
61
+ );
62
+ }
63
+
64
+ return (
65
+ <>
66
+ <ThreadListUnreadBanner />
67
+ <FlatList
68
+ contentContainerStyle={{ flexGrow: 1 }}
69
+ data={threads}
70
+ keyExtractor={(props) => props.id}
71
+ ListEmptyComponent={ThreadListEmptyPlaceholder}
72
+ ListFooterComponent={isLoadingNext ? ThreadListLoadingMoreIndicator : null}
73
+ onEndReached={loadMore}
74
+ renderItem={DefaultThreadListItem}
75
+ testID='thread-flatlist'
76
+ {...additionalFlatListProps}
77
+ />
78
+ </>
79
+ );
80
+ };
81
+
82
+ export const ThreadList = (props: ThreadListProps) => {
83
+ const { isFocused = true, ThreadList = ThreadListComponent } = props;
84
+ const { client } = useChatContext();
85
+
86
+ useEffect(() => {
87
+ if (!client) return;
88
+ if (isFocused) {
89
+ client.threads.activate();
90
+ } else {
91
+ client.threads.deactivate();
92
+ }
93
+ }, [client, isFocused]);
94
+
95
+ useEffect(() => {
96
+ if (!client) return;
97
+
98
+ const listener = client.on('connection.recovered', () => {
99
+ client.threads.reload({ force: true });
100
+ });
101
+
102
+ client.threads.reload({ force: true });
103
+
104
+ return () => {
105
+ client.threads.deactivate();
106
+ listener.unsubscribe();
107
+ };
108
+ }, [client]);
109
+
110
+ const [threads, isLoading, isLoadingNext] = useStateStore(client.threads.state, selector);
111
+
112
+ return (
113
+ <ThreadsProvider
114
+ value={{ isLoading, isLoadingNext, loadMore: client.threads.loadNextPage, threads, ...props }}
115
+ >
116
+ <ThreadList />
117
+ </ThreadsProvider>
118
+ );
119
+ };
@@ -0,0 +1,268 @@
1
+ import React, { useCallback, useMemo } from 'react';
2
+ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3
+
4
+ import { Thread, ThreadState } from 'stream-chat';
5
+
6
+ import {
7
+ TranslationContextValue,
8
+ useChatContext,
9
+ useTheme,
10
+ useTranslationContext,
11
+ } from '../../contexts';
12
+ import {
13
+ ThreadListItemProvider,
14
+ useThreadListItemContext,
15
+ } from '../../contexts/threadsContext/ThreadListItemContext';
16
+ import { useThreadsContext } from '../../contexts/threadsContext/ThreadsContext';
17
+ import { useStateStore } from '../../hooks';
18
+ import { MessageBubble } from '../../icons';
19
+ import { getDateString } from '../../utils/i18n/getDateString';
20
+ import { Avatar } from '../Avatar/Avatar';
21
+ import { useChannelPreviewDisplayName } from '../ChannelPreview/hooks/useChannelPreviewDisplayName';
22
+ import { MessageType } from '../MessageList/hooks/useMessageList';
23
+
24
+ export type ThreadListItemProps = {
25
+ thread: Thread;
26
+ timestampTranslationKey?: string;
27
+ };
28
+
29
+ const styles = StyleSheet.create({
30
+ boldText: { fontSize: 14, fontWeight: '500' },
31
+ contentRow: {
32
+ flexDirection: 'row',
33
+ marginTop: 6,
34
+ },
35
+ contentTextWrapper: {
36
+ flex: 1,
37
+ marginLeft: 8,
38
+ },
39
+ dateText: { alignSelf: 'flex-end' },
40
+ headerRow: {
41
+ flexDirection: 'row',
42
+ },
43
+ infoRow: {
44
+ alignItems: 'center',
45
+ flexDirection: 'row',
46
+ },
47
+ lastReplyText: { flex: 1, fontSize: 14, marginTop: 4 },
48
+ parentMessageText: { flex: 1, fontSize: 12 },
49
+ touchableWrapper: {
50
+ flex: 1,
51
+ paddingHorizontal: 8,
52
+ paddingVertical: 14,
53
+ },
54
+ unreadBubbleWrapper: {
55
+ alignItems: 'center',
56
+ alignSelf: 'flex-end',
57
+ borderRadius: 50,
58
+ height: 22,
59
+ justifyContent: 'center',
60
+ width: 22,
61
+ },
62
+ });
63
+
64
+ export const attachmentTypeIconMap = {
65
+ audio: '🔈',
66
+ file: '📄',
67
+ image: '📷',
68
+ video: '🎥',
69
+ voiceRecording: '🎙️',
70
+ } as const;
71
+
72
+ const getTitleFromMessage = ({
73
+ currentUserId,
74
+ message,
75
+ t,
76
+ }: {
77
+ t: TranslationContextValue['t'];
78
+ currentUserId?: string;
79
+ message?: MessageType | undefined;
80
+ }) => {
81
+ const attachment = message?.attachments?.at(0);
82
+
83
+ const attachmentIcon = attachment
84
+ ? `${
85
+ attachmentTypeIconMap[(attachment.type as keyof typeof attachmentTypeIconMap) ?? 'file'] ??
86
+ attachmentTypeIconMap.file
87
+ } `
88
+ : '';
89
+
90
+ const messageBelongsToCurrentUserPrefix =
91
+ message?.user?.id === currentUserId ? `${t('You')}: ` : '';
92
+
93
+ if (message?.deleted_at && message.parent_id)
94
+ return `${messageBelongsToCurrentUserPrefix}${t('This reply was deleted')}.`;
95
+
96
+ if (message?.deleted_at && !message.parent_id)
97
+ return `${messageBelongsToCurrentUserPrefix}${t('The source message was deleted')}.`;
98
+
99
+ if (attachment?.type === 'voiceRecording')
100
+ return `${attachmentIcon}${messageBelongsToCurrentUserPrefix}${t('Voice message')}.`;
101
+
102
+ return `${attachmentIcon}${messageBelongsToCurrentUserPrefix}${
103
+ message?.text || attachment?.fallback || 'N/A'
104
+ }`;
105
+ };
106
+
107
+ export const ThreadListItemComponent = () => {
108
+ const {
109
+ channel,
110
+ dateString,
111
+ deletedAtDateString,
112
+ lastReply,
113
+ ownUnreadMessageCount,
114
+ parentMessage,
115
+ thread,
116
+ } = useThreadListItemContext();
117
+ const displayName = useChannelPreviewDisplayName(channel);
118
+ const { onThreadSelect } = useThreadsContext();
119
+ const { client } = useChatContext();
120
+ const { t } = useTranslationContext();
121
+ const {
122
+ theme: {
123
+ colors: { accent_red, text_low_emphasis, white },
124
+ threadListItem,
125
+ },
126
+ } = useTheme();
127
+
128
+ return (
129
+ <TouchableOpacity
130
+ onPress={() => {
131
+ if (onThreadSelect) {
132
+ onThreadSelect({ thread: parentMessage as MessageType, threadInstance: thread }, channel);
133
+ }
134
+ }}
135
+ style={[styles.touchableWrapper, threadListItem.touchableWrapper]}
136
+ testID='thread-list-item'
137
+ >
138
+ <View style={[styles.headerRow, threadListItem.headerRow]}>
139
+ <MessageBubble />
140
+ <Text style={[styles.boldText, { color: text_low_emphasis }, threadListItem.boldText]}>
141
+ {displayName || 'N/A'}
142
+ </Text>
143
+ </View>
144
+ <View style={[styles.infoRow, threadListItem.infoRow]}>
145
+ <Text
146
+ numberOfLines={1}
147
+ style={[
148
+ styles.parentMessageText,
149
+ { color: text_low_emphasis },
150
+ threadListItem.parentMessageText,
151
+ ]}
152
+ >
153
+ {t<string>('replied to')}: {getTitleFromMessage({ message: parentMessage, t })}
154
+ </Text>
155
+ {ownUnreadMessageCount > 0 && !deletedAtDateString ? (
156
+ <View
157
+ style={[
158
+ styles.unreadBubbleWrapper,
159
+ { backgroundColor: accent_red },
160
+ threadListItem.unreadBubbleWrapper,
161
+ ]}
162
+ >
163
+ <Text style={[{ color: white }, threadListItem.unreadBubbleText]}>
164
+ {ownUnreadMessageCount}
165
+ </Text>
166
+ </View>
167
+ ) : null}
168
+ </View>
169
+ <View style={[styles.contentRow, threadListItem.contentRow]}>
170
+ <Avatar
171
+ image={lastReply?.user?.image as string}
172
+ online={lastReply?.user?.online}
173
+ size={40}
174
+ />
175
+ <View style={[styles.contentTextWrapper, threadListItem.contentTextWrapper]}>
176
+ <Text style={[styles.boldText, { color: text_low_emphasis }, threadListItem.boldText]}>
177
+ {lastReply?.user?.name}
178
+ </Text>
179
+ <View style={[styles.headerRow, threadListItem.headerRow]}>
180
+ <Text
181
+ numberOfLines={1}
182
+ style={[
183
+ styles.lastReplyText,
184
+ { color: text_low_emphasis },
185
+ threadListItem.lastReplyText,
186
+ ]}
187
+ >
188
+ {deletedAtDateString
189
+ ? 'This thread was deleted.'
190
+ : getTitleFromMessage({
191
+ currentUserId: client.userID,
192
+ message: lastReply,
193
+ t,
194
+ })}
195
+ </Text>
196
+ <Text style={[styles.dateText, { color: text_low_emphasis }, threadListItem.dateText]}>
197
+ {deletedAtDateString ?? dateString}
198
+ </Text>
199
+ </View>
200
+ </View>
201
+ </View>
202
+ </TouchableOpacity>
203
+ );
204
+ };
205
+
206
+ export const ThreadListItem = (props: ThreadListItemProps) => {
207
+ const { client } = useChatContext();
208
+ const { t, tDateTimeParser } = useTranslationContext();
209
+ const { thread, timestampTranslationKey = 'timestamp/ThreadListItem' } = props;
210
+ const { ThreadListItem = ThreadListItemComponent } = useThreadsContext();
211
+
212
+ const selector = useCallback(
213
+ (nextValue: ThreadState) =>
214
+ [
215
+ nextValue.replies.at(-1),
216
+ (client.userID && nextValue.read[client.userID]?.unreadMessageCount) || 0,
217
+ nextValue.parentMessage,
218
+ nextValue.channel,
219
+ nextValue.deletedAt,
220
+ ] as const,
221
+ [client],
222
+ );
223
+
224
+ const [lastReply, ownUnreadMessageCount, parentMessage, channel, deletedAt] = useStateStore(
225
+ thread.state,
226
+ selector,
227
+ );
228
+
229
+ const timestamp = lastReply?.created_at;
230
+
231
+ // TODO: Please rethink this, we have the same line of code in about 5 places in the SDK.
232
+ const dateString = useMemo(
233
+ () =>
234
+ getDateString({
235
+ date: timestamp,
236
+ t,
237
+ tDateTimeParser,
238
+ timestampTranslationKey,
239
+ }),
240
+ [timestamp, t, tDateTimeParser, timestampTranslationKey],
241
+ );
242
+ const deletedAtDateString = useMemo(
243
+ () =>
244
+ getDateString({
245
+ date: deletedAt as Date | undefined,
246
+ t,
247
+ tDateTimeParser,
248
+ timestampTranslationKey,
249
+ }),
250
+ [deletedAt, t, tDateTimeParser, timestampTranslationKey],
251
+ );
252
+
253
+ return (
254
+ <ThreadListItemProvider
255
+ value={{
256
+ channel,
257
+ dateString,
258
+ deletedAtDateString,
259
+ lastReply,
260
+ ownUnreadMessageCount,
261
+ parentMessage,
262
+ thread,
263
+ }}
264
+ >
265
+ <ThreadListItem />
266
+ </ThreadListItemProvider>
267
+ );
268
+ };
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { StyleSheet, Text, TouchableOpacity } from 'react-native';
3
+
4
+ import { ThreadManagerState } from 'stream-chat';
5
+
6
+ import { useChatContext, useTheme } from '../../contexts';
7
+ import { useStateStore } from '../../hooks';
8
+ import { Reload } from '../../icons';
9
+
10
+ const styles = StyleSheet.create({
11
+ text: { alignSelf: 'flex-start', flex: 1, fontSize: 16 },
12
+ touchableWrapper: {
13
+ borderRadius: 16,
14
+ flexDirection: 'row',
15
+ marginHorizontal: 8,
16
+ marginVertical: 6,
17
+ paddingHorizontal: 16,
18
+ paddingVertical: 14,
19
+ },
20
+ });
21
+
22
+ const selector = (nextValue: ThreadManagerState) => [nextValue.unseenThreadIds] as const;
23
+
24
+ export const ThreadListUnreadBanner = () => {
25
+ const { client } = useChatContext();
26
+ const {
27
+ theme: {
28
+ colors: { text_high_emphasis, white },
29
+ threadListUnreadBanner,
30
+ },
31
+ } = useTheme();
32
+ const [unseenThreadIds] = useStateStore(client.threads.state, selector);
33
+ if (!unseenThreadIds.length) {
34
+ return null;
35
+ }
36
+
37
+ return (
38
+ <TouchableOpacity
39
+ onPress={() => client.threads.reload()}
40
+ style={[
41
+ styles.touchableWrapper,
42
+ { backgroundColor: text_high_emphasis },
43
+ threadListUnreadBanner.touchableWrapper,
44
+ ]}
45
+ >
46
+ <Text style={[styles.text, { color: white }, threadListUnreadBanner.text]}>
47
+ {unseenThreadIds.length} unread threads
48
+ </Text>
49
+ <Reload pathFill={white} />
50
+ </TouchableOpacity>
51
+ );
52
+ };
@@ -139,7 +139,6 @@ export * from './MessageList/hooks/useTypingString';
139
139
  export * from './MessageList/InlineDateSeparator';
140
140
  export * from './MessageList/InlineLoadingMoreIndicator';
141
141
  export * from './MessageList/InlineLoadingMoreRecentIndicator';
142
- export * from './MessageList/InlineLoadingMoreThreadIndicator';
143
142
  export * from './MessageList/InlineUnreadIndicator';
144
143
  export * from './MessageList/MessageList';
145
144
  export * from './MessageList/MessageSystem';
@@ -169,3 +168,4 @@ export * from './Spinner/Spinner';
169
168
 
170
169
  export * from './Thread/Thread';
171
170
  export * from './Thread/components/ThreadFooterComponent';
171
+ export * from './ThreadList/ThreadList';
@@ -19,6 +19,8 @@ export * from './suggestionsContext/SuggestionsContext';
19
19
  export * from './themeContext/ThemeContext';
20
20
  export * from './themeContext/utils/theme';
21
21
  export * from './threadContext/ThreadContext';
22
+ export * from './threadsContext/ThreadsContext';
23
+ export * from './threadsContext/ThreadListItemContext';
22
24
  export * from './translationContext/TranslationContext';
23
25
  export * from './typingContext/TypingContext';
24
26
  export * from './utils/getDisplayName';
@@ -27,6 +27,8 @@ export const Colors = {
27
27
  static_black: '#000000',
28
28
  static_white: '#ffffff',
29
29
  targetedMessageBackground: '#FBF4DD', // dark mode = #302D22
30
+ text_high_emphasis: '#080707',
31
+ text_low_emphasis: '#7E828B',
30
32
  transparent: 'transparent',
31
33
  white: '#FFFFFF',
32
34
  white_smoke: '#F2F2F2',
@@ -637,6 +639,23 @@ export type Theme = {
637
639
  threadHeight?: number;
638
640
  };
639
641
  };
642
+ threadListItem: {
643
+ boldText: TextStyle;
644
+ contentRow: ViewStyle;
645
+ contentTextWrapper: ViewStyle;
646
+ dateText: TextStyle;
647
+ headerRow: ViewStyle;
648
+ infoRow: ViewStyle;
649
+ lastReplyText: TextStyle;
650
+ parentMessageText: TextStyle;
651
+ touchableWrapper: ViewStyle;
652
+ unreadBubbleText: TextStyle;
653
+ unreadBubbleWrapper: ViewStyle;
654
+ };
655
+ threadListUnreadBanner: {
656
+ text: TextStyle;
657
+ touchableWrapper: ViewStyle;
658
+ };
640
659
  typingIndicator: {
641
660
  container: ViewStyle;
642
661
  text: TextStyle & {
@@ -1241,6 +1260,23 @@ export const defaultTheme: Theme = {
1241
1260
  text: {},
1242
1261
  },
1243
1262
  },
1263
+ threadListItem: {
1264
+ boldText: {},
1265
+ contentRow: {},
1266
+ contentTextWrapper: {},
1267
+ dateText: {},
1268
+ headerRow: {},
1269
+ infoRow: {},
1270
+ lastReplyText: {},
1271
+ parentMessageText: {},
1272
+ touchableWrapper: {},
1273
+ unreadBubbleText: {},
1274
+ unreadBubbleWrapper: {},
1275
+ },
1276
+ threadListUnreadBanner: {
1277
+ text: {},
1278
+ touchableWrapper: {},
1279
+ },
1244
1280
  typingIndicator: {
1245
1281
  container: {},
1246
1282
  text: {
@@ -1,6 +1,6 @@
1
1
  import React, { PropsWithChildren, useContext } from 'react';
2
2
 
3
- import type { ChannelState } from 'stream-chat';
3
+ import { ChannelState, Thread } from 'stream-chat';
4
4
 
5
5
  import type { MessageType } from '../../components/MessageList/hooks/useMessageList';
6
6
  import type { DefaultStreamChatGenerics, UnknownType } from '../../types/types';
@@ -9,6 +9,10 @@ import { DEFAULT_BASE_CONTEXT_VALUE } from '../utils/defaultBaseContextValue';
9
9
  import { getDisplayName } from '../utils/getDisplayName';
10
10
  import { isTestEnvironment } from '../utils/isTestEnvironment';
11
11
 
12
+ export type ThreadType<
13
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
14
+ > = { thread: MessageType<StreamChatGenerics>; threadInstance: Thread };
15
+
12
16
  export type ThreadContextValue<
13
17
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
14
18
  > = {
@@ -20,12 +24,15 @@ export type ThreadContextValue<
20
24
  setThreadLoadingMore: React.Dispatch<React.SetStateAction<boolean>>;
21
25
  thread: MessageType<StreamChatGenerics> | null;
22
26
  threadHasMore: boolean;
23
- threadLoadingMore: boolean;
24
27
  threadMessages: ChannelState<StreamChatGenerics>['threads'][string];
28
+ loadMoreRecentThread?: (opts: { limit?: number }) => Promise<void>;
25
29
  /**
26
30
  * Boolean to enable/disable parent message press
27
31
  */
28
32
  parentMessagePreventPress?: boolean;
33
+ threadInstance?: Thread | null;
34
+ threadLoadingMore?: boolean;
35
+ threadLoadingMoreRecent?: boolean;
29
36
  };
30
37
 
31
38
  export const ThreadContext = React.createContext(DEFAULT_BASE_CONTEXT_VALUE as ThreadContextValue);