stream-chat-react-native-core 6.5.1 → 6.6.0

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 (105) hide show
  1. package/README.md +1 -1
  2. package/lib/commonjs/components/ChannelList/ChannelList.js +55 -72
  3. package/lib/commonjs/components/ChannelList/ChannelList.js.map +1 -1
  4. package/lib/commonjs/components/ChannelList/ChannelListMessenger.js +4 -1
  5. package/lib/commonjs/components/ChannelList/ChannelListMessenger.js.map +1 -1
  6. package/lib/commonjs/components/ChannelList/hooks/useCreateChannelsContext.js +3 -1
  7. package/lib/commonjs/components/ChannelList/hooks/useCreateChannelsContext.js.map +1 -1
  8. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +73 -72
  9. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  10. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecorder.js +4 -1
  11. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecorder.js.map +1 -1
  12. package/lib/commonjs/contexts/channelsContext/ChannelsContext.js.map +1 -1
  13. package/lib/commonjs/i18n/en.json +1 -0
  14. package/lib/commonjs/i18n/es.json +1 -0
  15. package/lib/commonjs/i18n/fr.json +1 -0
  16. package/lib/commonjs/i18n/he.json +1 -0
  17. package/lib/commonjs/i18n/hi.json +1 -0
  18. package/lib/commonjs/i18n/it.json +1 -0
  19. package/lib/commonjs/i18n/ja.json +1 -0
  20. package/lib/commonjs/i18n/ko.json +1 -0
  21. package/lib/commonjs/i18n/nl.json +1 -0
  22. package/lib/commonjs/i18n/pt-br.json +1 -0
  23. package/lib/commonjs/i18n/ru.json +1 -0
  24. package/lib/commonjs/i18n/tr.json +1 -0
  25. package/lib/commonjs/version.json +1 -1
  26. package/lib/module/components/ChannelList/ChannelList.js +55 -72
  27. package/lib/module/components/ChannelList/ChannelList.js.map +1 -1
  28. package/lib/module/components/ChannelList/ChannelListMessenger.js +4 -1
  29. package/lib/module/components/ChannelList/ChannelListMessenger.js.map +1 -1
  30. package/lib/module/components/ChannelList/hooks/useCreateChannelsContext.js +3 -1
  31. package/lib/module/components/ChannelList/hooks/useCreateChannelsContext.js.map +1 -1
  32. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +73 -72
  33. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  34. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecorder.js +4 -1
  35. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecorder.js.map +1 -1
  36. package/lib/module/contexts/channelsContext/ChannelsContext.js.map +1 -1
  37. package/lib/module/i18n/en.json +1 -0
  38. package/lib/module/i18n/es.json +1 -0
  39. package/lib/module/i18n/fr.json +1 -0
  40. package/lib/module/i18n/he.json +1 -0
  41. package/lib/module/i18n/hi.json +1 -0
  42. package/lib/module/i18n/it.json +1 -0
  43. package/lib/module/i18n/ja.json +1 -0
  44. package/lib/module/i18n/ko.json +1 -0
  45. package/lib/module/i18n/nl.json +1 -0
  46. package/lib/module/i18n/pt-br.json +1 -0
  47. package/lib/module/i18n/ru.json +1 -0
  48. package/lib/module/i18n/tr.json +1 -0
  49. package/lib/module/version.json +1 -1
  50. package/lib/typescript/components/ChannelList/ChannelList.d.ts +11 -11
  51. package/lib/typescript/components/ChannelList/ChannelList.d.ts.map +1 -1
  52. package/lib/typescript/components/ChannelList/ChannelListMessenger.d.ts.map +1 -1
  53. package/lib/typescript/components/ChannelList/hooks/listeners/useChannelMemberUpdated.d.ts +2 -2
  54. package/lib/typescript/components/ChannelList/hooks/listeners/useChannelMemberUpdated.d.ts.map +1 -1
  55. package/lib/typescript/components/ChannelList/hooks/listeners/useChannelUpdated.d.ts +2 -2
  56. package/lib/typescript/components/ChannelList/hooks/listeners/useChannelUpdated.d.ts.map +1 -1
  57. package/lib/typescript/components/ChannelList/hooks/listeners/useUserPresence.d.ts +1 -1
  58. package/lib/typescript/components/ChannelList/hooks/listeners/useUserPresence.d.ts.map +1 -1
  59. package/lib/typescript/components/ChannelList/hooks/useCreateChannelsContext.d.ts +1 -1
  60. package/lib/typescript/components/ChannelList/hooks/useCreateChannelsContext.d.ts.map +1 -1
  61. package/lib/typescript/components/ChannelList/hooks/usePaginatedChannels.d.ts +6 -5
  62. package/lib/typescript/components/ChannelList/hooks/usePaginatedChannels.d.ts.map +1 -1
  63. package/lib/typescript/components/MessageInput/components/AudioRecorder/AudioRecorder.d.ts.map +1 -1
  64. package/lib/typescript/contexts/channelsContext/ChannelsContext.d.ts +4 -0
  65. package/lib/typescript/contexts/channelsContext/ChannelsContext.d.ts.map +1 -1
  66. package/lib/typescript/i18n/en.json +1 -0
  67. package/lib/typescript/i18n/es.json +1 -0
  68. package/lib/typescript/i18n/fr.json +1 -0
  69. package/lib/typescript/i18n/he.json +1 -0
  70. package/lib/typescript/i18n/hi.json +1 -0
  71. package/lib/typescript/i18n/it.json +1 -0
  72. package/lib/typescript/i18n/ja.json +1 -0
  73. package/lib/typescript/i18n/ko.json +1 -0
  74. package/lib/typescript/i18n/nl.json +1 -0
  75. package/lib/typescript/i18n/pt-br.json +1 -0
  76. package/lib/typescript/i18n/ru.json +1 -0
  77. package/lib/typescript/i18n/tr.json +1 -0
  78. package/lib/typescript/utils/i18n/Streami18n.d.ts +1 -0
  79. package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
  80. package/package.json +2 -2
  81. package/src/__tests__/offline-support/offline-feature.js +4 -3
  82. package/src/components/ChannelList/ChannelList.tsx +83 -81
  83. package/src/components/ChannelList/ChannelListMessenger.tsx +4 -1
  84. package/src/components/ChannelList/__tests__/ChannelList.test.js +2 -2
  85. package/src/components/ChannelList/__tests__/ChannelListMessenger.test.js +3 -1
  86. package/src/components/ChannelList/hooks/listeners/useChannelMemberUpdated.ts +2 -2
  87. package/src/components/ChannelList/hooks/listeners/useChannelUpdated.ts +2 -2
  88. package/src/components/ChannelList/hooks/listeners/useUserPresence.ts +1 -1
  89. package/src/components/ChannelList/hooks/useCreateChannelsContext.ts +3 -0
  90. package/src/components/ChannelList/hooks/usePaginatedChannels.ts +51 -41
  91. package/src/components/MessageInput/components/AudioRecorder/AudioRecorder.tsx +5 -1
  92. package/src/contexts/channelsContext/ChannelsContext.tsx +4 -0
  93. package/src/i18n/en.json +1 -0
  94. package/src/i18n/es.json +1 -0
  95. package/src/i18n/fr.json +1 -0
  96. package/src/i18n/he.json +1 -0
  97. package/src/i18n/hi.json +1 -0
  98. package/src/i18n/it.json +1 -0
  99. package/src/i18n/ja.json +1 -0
  100. package/src/i18n/ko.json +1 -0
  101. package/src/i18n/nl.json +1 -0
  102. package/src/i18n/pt-br.json +1 -0
  103. package/src/i18n/ru.json +1 -0
  104. package/src/i18n/tr.json +1 -0
  105. package/src/version.json +1 -1
@@ -1,24 +1,15 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
2
 
3
3
  import type { FlatList } from 'react-native-gesture-handler';
4
4
 
5
- import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
5
+ import { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
6
6
 
7
7
  import { ChannelListFooterLoadingIndicator } from './ChannelListFooterLoadingIndicator';
8
8
  import { ChannelListHeaderErrorIndicator } from './ChannelListHeaderErrorIndicator';
9
9
  import { ChannelListHeaderNetworkDownIndicator } from './ChannelListHeaderNetworkDownIndicator';
10
10
  import { ChannelListLoadingIndicator } from './ChannelListLoadingIndicator';
11
11
  import { ChannelListMessenger, ChannelListMessengerProps } from './ChannelListMessenger';
12
- import { useAddedToChannelNotification } from './hooks/listeners/useAddedToChannelNotification';
13
- import { useChannelDeleted } from './hooks/listeners/useChannelDeleted';
14
- import { useChannelHidden } from './hooks/listeners/useChannelHidden';
15
- import { useChannelMemberUpdated } from './hooks/listeners/useChannelMemberUpdated';
16
- import { useChannelTruncated } from './hooks/listeners/useChannelTruncated';
17
12
  import { useChannelUpdated } from './hooks/listeners/useChannelUpdated';
18
- import { useChannelVisible } from './hooks/listeners/useChannelVisible';
19
- import { useNewMessage } from './hooks/listeners/useNewMessage';
20
- import { useNewMessageNotification } from './hooks/listeners/useNewMessageNotification';
21
- import { useRemovedFromChannelNotification } from './hooks/listeners/useRemovedFromChannelNotification';
22
13
  import { useUserPresence } from './hooks/listeners/useUserPresence';
23
14
  import { useCreateChannelsContext } from './hooks/useCreateChannelsContext';
24
15
  import { usePaginatedChannels } from './hooks/usePaginatedChannels';
@@ -96,7 +87,7 @@ export type ChannelListProps<
96
87
  * @overrideType Function
97
88
  * */
98
89
  onAddedToChannel?: (
99
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
90
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
100
91
  event: Event<StreamChatGenerics>,
101
92
  options?: ChannelListEventListenerOptions<StreamChatGenerics>,
102
93
  ) => void;
@@ -109,7 +100,7 @@ export type ChannelListProps<
109
100
  * @overrideType Function
110
101
  * */
111
102
  onChannelDeleted?: (
112
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
103
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
113
104
  event: Event<StreamChatGenerics>,
114
105
  ) => void;
115
106
  /**
@@ -121,7 +112,7 @@ export type ChannelListProps<
121
112
  * @overrideType Function
122
113
  * */
123
114
  onChannelHidden?: (
124
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
115
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
125
116
  event: Event<StreamChatGenerics>,
126
117
  ) => void;
127
118
  /**
@@ -135,7 +126,7 @@ export type ChannelListProps<
135
126
  */
136
127
  onChannelMemberUpdated?: (
137
128
  lockChannelOrder: boolean,
138
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
129
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
139
130
  event: Event<StreamChatGenerics>,
140
131
  options?: ChannelListEventListenerOptions<StreamChatGenerics>,
141
132
  ) => void;
@@ -148,7 +139,7 @@ export type ChannelListProps<
148
139
  * @overrideType Function
149
140
  * */
150
141
  onChannelTruncated?: (
151
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
142
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
152
143
  event: Event<StreamChatGenerics>,
153
144
  ) => void;
154
145
  /**
@@ -160,7 +151,7 @@ export type ChannelListProps<
160
151
  * @overrideType Function
161
152
  * */
162
153
  onChannelUpdated?: (
163
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
154
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
164
155
  event: Event<StreamChatGenerics>,
165
156
  ) => void;
166
157
  /**
@@ -172,7 +163,7 @@ export type ChannelListProps<
172
163
  * @overrideType Function
173
164
  * */
174
165
  onChannelVisible?: (
175
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
166
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
176
167
  event: Event<StreamChatGenerics>,
177
168
  ) => void;
178
169
  /**
@@ -189,7 +180,7 @@ export type ChannelListProps<
189
180
  * */
190
181
  onNewMessage?: (
191
182
  lockChannelOrder: boolean,
192
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
183
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
193
184
  event: Event<StreamChatGenerics>,
194
185
  options?: ChannelListEventListenerOptions<StreamChatGenerics>,
195
186
  ) => void;
@@ -203,7 +194,7 @@ export type ChannelListProps<
203
194
  * @overrideType Function
204
195
  * */
205
196
  onNewMessageNotification?: (
206
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
197
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
207
198
  event: Event<StreamChatGenerics>,
208
199
  options?: ChannelListEventListenerOptions<StreamChatGenerics>,
209
200
  ) => void;
@@ -217,7 +208,7 @@ export type ChannelListProps<
217
208
  * @overrideType Function
218
209
  * */
219
210
  onRemovedFromChannel?: (
220
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
211
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
221
212
  event: Event<StreamChatGenerics>,
222
213
  ) => void;
223
214
  /**
@@ -290,8 +281,68 @@ export const ChannelList = <
290
281
  } = props;
291
282
 
292
283
  const [forceUpdate, setForceUpdate] = useState(0);
293
- const { enableOfflineSupport } = useChatContext<StreamChatGenerics>();
284
+ const { client, enableOfflineSupport } = useChatContext<StreamChatGenerics>();
285
+ const channelManager = useMemo(() => client.createChannelManager({}), [client]);
286
+
287
+ /**
288
+ * This hook sets the event handler overrides in the channelManager internally
289
+ * whenever they change. We do this to avoid recreating the channelManager instance
290
+ * every time these change, as we want to keep it as static as possible.
291
+ * This protects us from something like defining the overrides as inline functions
292
+ * causing the manager instance to be recreated over and over again.
293
+ */
294
+ useEffect(() => {
295
+ channelManager.setEventHandlerOverrides({
296
+ channelDeletedHandler: onChannelDeleted,
297
+ channelHiddenHandler: onChannelHidden,
298
+ channelTruncatedHandler: onChannelTruncated,
299
+ channelVisibleHandler: onChannelVisible,
300
+ memberUpdatedHandler: onChannelMemberUpdated
301
+ ? (setChannels, event) =>
302
+ onChannelMemberUpdated(lockChannelOrder, setChannels, event, { filters, sort })
303
+ : undefined,
304
+ newMessageHandler: onNewMessage
305
+ ? (setChannels, event) =>
306
+ onNewMessage(lockChannelOrder, setChannels, event, { filters, sort })
307
+ : undefined,
308
+ notificationAddedToChannelHandler: onAddedToChannel
309
+ ? (setChannels, event) => onAddedToChannel(setChannels, event, { filters, sort })
310
+ : undefined,
311
+ notificationNewMessageHandler: onNewMessageNotification
312
+ ? (setChannels, event) => onNewMessageNotification(setChannels, event, { filters, sort })
313
+ : undefined,
314
+ notificationRemovedFromChannelHandler: onRemovedFromChannel,
315
+ });
316
+ }, [
317
+ channelManager,
318
+ filters,
319
+ lockChannelOrder,
320
+ onAddedToChannel,
321
+ onChannelDeleted,
322
+ onChannelHidden,
323
+ onChannelMemberUpdated,
324
+ onChannelTruncated,
325
+ onChannelVisible,
326
+ onNewMessage,
327
+ onNewMessageNotification,
328
+ onRemovedFromChannel,
329
+ sort,
330
+ ]);
331
+
332
+ useEffect(() => {
333
+ channelManager.setOptions({ abortInFlightQuery: true, lockChannelOrder });
334
+ }, [channelManager, lockChannelOrder]);
335
+
336
+ useEffect(() => {
337
+ channelManager.registerSubscriptions();
338
+
339
+ return () => {
340
+ channelManager.unregisterSubscriptions();
341
+ };
342
+ }, [channelManager]);
343
+
294
344
  const {
345
+ channelListInitialized,
295
346
  channels,
296
347
  error,
297
348
  hasNextPage,
@@ -301,9 +352,9 @@ export const ChannelList = <
301
352
  refreshing,
302
353
  refreshList,
303
354
  reloadList,
304
- setChannels,
305
355
  staticChannelsActive,
306
356
  } = usePaginatedChannels<StreamChatGenerics>({
357
+ channelManager,
307
358
  enableOfflineSupport,
308
359
  filters,
309
360
  options,
@@ -311,75 +362,25 @@ export const ChannelList = <
311
362
  sort,
312
363
  });
313
364
 
314
- // Setup event listeners
315
- useAddedToChannelNotification({
316
- onAddedToChannel,
317
- options: { filters, sort },
318
- setChannels,
319
- });
320
-
321
- useChannelDeleted({
322
- onChannelDeleted,
323
- setChannels,
324
- });
325
-
326
- useChannelHidden({
327
- onChannelHidden,
328
- setChannels,
329
- });
330
-
331
- useChannelMemberUpdated({
332
- lockChannelOrder,
333
- onChannelMemberUpdated,
334
- options: { filters, sort },
335
- setChannels,
336
- });
337
-
338
- useChannelTruncated({
339
- onChannelTruncated,
340
- refreshList,
341
- setChannels,
342
- setForceUpdate,
343
- });
344
-
345
365
  useChannelUpdated({
346
366
  onChannelUpdated,
347
- setChannels,
348
- });
349
-
350
- useChannelVisible({
351
- onChannelVisible,
352
- options: { sort },
353
- setChannels,
354
- });
355
-
356
- useNewMessage({
357
- lockChannelOrder,
358
- onNewMessage,
359
- options: { filters, sort },
360
- setChannels,
361
- });
362
-
363
- useNewMessageNotification({
364
- onNewMessageNotification,
365
- options: { filters, sort },
366
- setChannels,
367
- });
368
-
369
- useRemovedFromChannelNotification({
370
- onRemovedFromChannel,
371
- setChannels,
367
+ setChannels: channelManager.setChannels,
372
368
  });
373
369
 
374
370
  useUserPresence({
375
- setChannels,
371
+ setChannels: channelManager.setChannels,
376
372
  setForceUpdate,
377
373
  });
378
374
 
379
375
  const channelIdsStr = channels?.reduce((acc, channel) => `${acc}${channel.cid}`, '');
380
376
 
381
377
  useEffect(() => {
382
- if (channels === null || staticChannelsActive || !enableOfflineSupport) {
378
+ if (
379
+ channels == null ||
380
+ !channelListInitialized ||
381
+ staticChannelsActive ||
382
+ !enableOfflineSupport
383
+ ) {
383
384
  return;
384
385
  }
385
386
 
@@ -393,6 +394,7 @@ export const ChannelList = <
393
394
 
394
395
  const channelsContext = useCreateChannelsContext({
395
396
  additionalFlatListProps,
397
+ channelListInitialized,
396
398
  channels: channelRenderFilterFn ? channelRenderFilterFn(channels ?? []) : channels,
397
399
  EmptyStateIndicator,
398
400
  error,
@@ -86,6 +86,7 @@ const ChannelListMessengerWithContext = <
86
86
  const onEndReachedCalledDuringCurrentScrollRef = useRef<boolean>(false);
87
87
  const {
88
88
  additionalFlatListProps,
89
+ channelListInitialized,
89
90
  channels,
90
91
  EmptyStateIndicator,
91
92
  error,
@@ -140,7 +141,7 @@ const ChannelListMessengerWithContext = <
140
141
  });
141
142
  }
142
143
 
143
- if (error && !refreshing && !loadingChannels && channels === null) {
144
+ if (error && !refreshing && !loadingChannels && (channels === null || !channelListInitialized)) {
144
145
  return (
145
146
  <LoadingErrorIndicator
146
147
  error={error}
@@ -211,6 +212,7 @@ export const ChannelListMessenger = <
211
212
  ) => {
212
213
  const {
213
214
  additionalFlatListProps,
215
+ channelListInitialized,
214
216
  channels,
215
217
  EmptyStateIndicator,
216
218
  error,
@@ -234,6 +236,7 @@ export const ChannelListMessenger = <
234
236
  <ChannelListMessengerWithContext
235
237
  {...{
236
238
  additionalFlatListProps,
239
+ channelListInitialized,
237
240
  channels,
238
241
  EmptyStateIndicator,
239
242
  error,
@@ -287,12 +287,12 @@ describe('ChannelList', () => {
287
287
  it('should not alter order if `lockChannelOrder` prop is true', async () => {
288
288
  render(
289
289
  <Chat client={chatClient}>
290
- <ChannelList {...props} lockChannelOrder={true} />
290
+ <ChannelList lockChannelOrder={true} Preview={props.Preview} />
291
291
  </Chat>,
292
292
  );
293
293
 
294
294
  await waitFor(() => {
295
- expect(screen.getByTestId('channel-list')).toBeTruthy();
295
+ expect(screen.getByTestId('channel-list-messenger')).toBeTruthy();
296
296
  });
297
297
 
298
298
  const newMessage = sendNewMessageOnChannel3();
@@ -80,7 +80,9 @@ describe('ChannelListMessenger', () => {
80
80
  });
81
81
 
82
82
  it('renders the `LoadingErrorIndicator` when `error` prop is true', async () => {
83
- const { getByTestId } = render(<Component channels={mockChannels} error={true} />);
83
+ const { getByTestId } = render(
84
+ <Component channels={mockChannels} error={true} loadingChannels={false} />,
85
+ );
84
86
  await waitFor(() => {
85
87
  expect(getByTestId('loading-error')).toBeTruthy();
86
88
  });
@@ -20,10 +20,10 @@ import {
20
20
  type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
21
21
  {
22
22
  lockChannelOrder: boolean;
23
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>;
23
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>;
24
24
  onChannelMemberUpdated?: (
25
25
  lockChannelOrder: boolean,
26
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
26
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
27
27
  event: Event<StreamChatGenerics>,
28
28
  options?: ChannelListEventListenerOptions<StreamChatGenerics>,
29
29
  ) => void;
@@ -8,9 +8,9 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types';
8
8
 
9
9
  type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
10
10
  {
11
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>;
11
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>;
12
12
  onChannelUpdated?: (
13
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
13
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>,
14
14
  event: Event<StreamChatGenerics>,
15
15
  ) => void;
16
16
  };
@@ -8,7 +8,7 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types';
8
8
 
9
9
  type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
10
10
  {
11
- setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>;
11
+ setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[]>>;
12
12
  setForceUpdate: React.Dispatch<React.SetStateAction<number>>;
13
13
  };
14
14
 
@@ -7,6 +7,7 @@ export const useCreateChannelsContext = <
7
7
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
8
8
  >({
9
9
  additionalFlatListProps,
10
+ channelListInitialized,
10
11
  channels,
11
12
  EmptyStateIndicator,
12
13
  error,
@@ -51,6 +52,7 @@ export const useCreateChannelsContext = <
51
52
  const channelsContext: ChannelsContextValue<StreamChatGenerics> = useMemo(
52
53
  () => ({
53
54
  additionalFlatListProps,
55
+ channelListInitialized,
54
56
  channels,
55
57
  EmptyStateIndicator,
56
58
  error,
@@ -90,6 +92,7 @@ export const useCreateChannelsContext = <
90
92
  hasNextPage,
91
93
  loadingChannels,
92
94
  loadingNextPage,
95
+ channelListInitialized,
93
96
  refreshing,
94
97
  ],
95
98
  );
@@ -1,9 +1,16 @@
1
1
  import { useEffect, useMemo, useRef, useState } from 'react';
2
2
 
3
- import type { Channel, ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat';
3
+ import {
4
+ ChannelFilters,
5
+ ChannelManager,
6
+ ChannelManagerState,
7
+ ChannelOptions,
8
+ ChannelSort,
9
+ } from 'stream-chat';
4
10
 
5
11
  import { useActiveChannelsRefContext } from '../../../contexts/activeChannelsRefContext/ActiveChannelsRefContext';
6
12
  import { useChatContext } from '../../../contexts/chatContext/ChatContext';
13
+ import { useStateStore } from '../../../hooks';
7
14
  import { useIsMountedRef } from '../../../hooks/useIsMountedRef';
8
15
 
9
16
  import { getChannelsForFilterSort } from '../../../store/apis/getChannelsForFilterSort';
@@ -19,6 +26,7 @@ const waitSeconds = (seconds: number) =>
19
26
 
20
27
  type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
21
28
  {
29
+ channelManager: ChannelManager<StreamChatGenerics>;
22
30
  enableOfflineSupport: boolean;
23
31
  filters: ChannelFilters<StreamChatGenerics>;
24
32
  options: ChannelOptions;
@@ -37,23 +45,34 @@ type QueryType = 'queryLocalDB' | 'reload' | 'refresh' | 'loadChannels';
37
45
 
38
46
  export type QueryChannels = (queryType?: QueryType, retryCount?: number) => Promise<void>;
39
47
 
48
+ const selector = <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(
49
+ nextValue: ChannelManagerState<StreamChatGenerics>,
50
+ ) =>
51
+ ({
52
+ channelListInitialized: nextValue.initialized,
53
+ channels: nextValue.channels,
54
+ pagination: nextValue.pagination,
55
+ } as const);
56
+
40
57
  export const usePaginatedChannels = <
41
58
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
42
59
  >({
60
+ channelManager,
43
61
  enableOfflineSupport,
44
62
  filters = {},
45
63
  options = DEFAULT_OPTIONS,
46
64
  setForceUpdate,
47
65
  sort = {},
48
66
  }: Parameters<StreamChatGenerics>) => {
49
- const [channels, setChannels] = useState<Channel<StreamChatGenerics>[] | null>(null);
50
67
  const [error, setError] = useState<Error | undefined>(undefined);
51
68
  const [staticChannelsActive, setStaticChannelsActive] = useState<boolean>(false);
52
- const [activeQueryType, setActiveQueryType] = useState<QueryType | null>('queryLocalDB');
53
- const [hasNextPage, setHasNextPage] = useState<boolean>(false);
69
+ const activeQueryType = useRef<QueryType | null>('queryLocalDB');
54
70
  const activeChannels = useActiveChannelsRefContext();
55
71
  const isMountedRef = useIsMountedRef();
56
72
  const { client } = useChatContext<StreamChatGenerics>();
73
+ const { channelListInitialized, channels, pagination } =
74
+ useStateStore(channelManager?.state, selector) ?? {};
75
+ const hasNextPage = pagination?.hasNext;
57
76
 
58
77
  const filtersRef = useRef<typeof filters | null>(null);
59
78
  const sortRef = useRef<typeof sort | null>(null);
@@ -91,43 +110,32 @@ export const usePaginatedChannels = <
91
110
  setError(undefined);
92
111
  activeRequestId.current++;
93
112
  const currentRequestId = activeRequestId.current;
94
- setActiveQueryType(queryType);
113
+ activeQueryType.current = queryType;
95
114
 
96
115
  const newOptions = {
97
116
  limit: options?.limit ?? MAX_QUERY_CHANNELS_LIMIT,
98
- offset:
99
- queryType === 'loadChannels' && !staticChannelsActive && channels ? channels.length : 0,
117
+ offset: 0,
100
118
  ...options,
101
119
  };
102
120
 
103
121
  try {
122
+ if (isQueryStale() || !isMountedRef.current) {
123
+ return;
124
+ }
104
125
  /**
105
126
  * We skipInitialization here for handling race condition between ChannelList, Channel (and Thread)
106
127
  * when they all (may) update the channel state at the same time (when connection state recovers)
107
128
  * TODO: if we move the channel state to a single context and share it between ChannelList, Channel and Thread we can remove this
108
129
  */
109
- const channelQueryResponse = await client.queryChannels(filters, sort, newOptions, {
110
- skipInitialization: enableOfflineSupport ? undefined : activeChannels.current,
111
- });
112
- if (isQueryStale() || !isMountedRef.current) {
113
- return;
130
+ if (queryType === 'loadChannels') {
131
+ await channelManager.loadNext();
132
+ } else {
133
+ await channelManager.queryChannels(filters, sort, newOptions, {
134
+ skipInitialization: enableOfflineSupport ? undefined : activeChannels.current,
135
+ });
114
136
  }
115
137
 
116
- const newChannels =
117
- queryType === 'loadChannels' && !staticChannelsActive && channels
118
- ? [...channels, ...channelQueryResponse]
119
- : channelQueryResponse.map((c) => {
120
- const existingChannel = client.activeChannels[c.cid];
121
- if (existingChannel) {
122
- return existingChannel;
123
- }
124
-
125
- return c;
126
- });
127
-
128
- setChannels(newChannels);
129
138
  setStaticChannelsActive(false);
130
- setHasNextPage(channelQueryResponse.length >= newOptions.limit);
131
139
  isQueryingRef.current = false;
132
140
  } catch (err: unknown) {
133
141
  isQueryingRef.current = false;
@@ -140,7 +148,7 @@ export const usePaginatedChannels = <
140
148
  // querying.current check is needed in order to make sure the next query call doesnt flick an error
141
149
  // state and then succeed (reconnect case)
142
150
  if (retryCount === MAX_NUMBER_OF_RETRIES && !isQueryingRef.current) {
143
- setActiveQueryType(null);
151
+ activeQueryType.current = null;
144
152
  console.warn(err);
145
153
 
146
154
  setError(
@@ -154,7 +162,7 @@ export const usePaginatedChannels = <
154
162
  return queryChannels(queryType, retryCount + 1);
155
163
  }
156
164
 
157
- setActiveQueryType(null);
165
+ activeQueryType.current = null;
158
166
  };
159
167
 
160
168
  const refreshList = async () => {
@@ -207,7 +215,7 @@ export const usePaginatedChannels = <
207
215
  skipInitialization: [], // passing empty array will clear out the existing messages from channel state, this removes the possibility of duplicate messages
208
216
  });
209
217
 
210
- setChannels(offlineChannels);
218
+ channelManager.setChannels(offlineChannels);
211
219
  setStaticChannelsActive(true);
212
220
  }
213
221
  } catch (e) {
@@ -215,7 +223,7 @@ export const usePaginatedChannels = <
215
223
  return false;
216
224
  }
217
225
 
218
- setActiveQueryType(null);
226
+ activeQueryType.current = null;
219
227
 
220
228
  return true;
221
229
  };
@@ -257,26 +265,28 @@ export const usePaginatedChannels = <
257
265
 
258
266
  return () => listener?.unsubscribe?.();
259
267
  // eslint-disable-next-line react-hooks/exhaustive-deps
260
- }, [filterStr, sortStr]);
268
+ }, [filterStr, sortStr, channelManager]);
261
269
 
262
270
  return {
271
+ channelListInitialized,
263
272
  channels,
264
273
  error,
265
274
  hasNextPage,
266
275
  loadingChannels:
267
- activeQueryType === 'queryLocalDB'
276
+ activeQueryType.current === 'queryLocalDB'
268
277
  ? true
269
- : (activeQueryType === 'reload' || activeQueryType === null) && channels === null,
270
- loadingNextPage: activeQueryType === 'loadChannels',
271
- loadNextPage: queryChannels,
272
- refreshing: activeQueryType === 'refresh',
278
+ : // Although channels.length === 0 should come as a given when we have !channelListInitialized,
279
+ // due to the way offline storage works currently we have to do this additional
280
+ // check to make sure channels were not populated before the reactive list becomes
281
+ // ready. I do not like providing a way to set the ready state, as it should be managed
282
+ // in the LLC entirely. Once we move offline support to the LLC, we can remove this check
283
+ // too as it'll be redundant.
284
+ pagination?.isLoading || (!channelListInitialized && channels.length === 0),
285
+ loadingNextPage: pagination?.isLoadingNext,
286
+ loadNextPage: channelManager.loadNext,
287
+ refreshing: activeQueryType.current === 'refresh',
273
288
  refreshList,
274
289
  reloadList,
275
- // Although channels can be null, there is no practical case where channels will be null
276
- // when setChannels is used. setChannels is only recommended to be used for overriding
277
- // event handler. Thus instead of adding if check for channels === null, its better to
278
- // simply reassign types here.
279
- setChannels,
280
290
  staticChannelsActive,
281
291
  };
282
292
  };
@@ -10,6 +10,7 @@ import {
10
10
  useMessageInputContext,
11
11
  } from '../../../../contexts/messageInputContext/MessageInputContext';
12
12
  import { useTheme } from '../../../../contexts/themeContext/ThemeContext';
13
+ import { useTranslationContext } from '../../../../contexts/translationContext/TranslationContext';
13
14
  import { ArrowLeft, CircleStop, Delete, Mic, SendCheck } from '../../../../icons';
14
15
 
15
16
  import { AudioRecordingReturnType } from '../../../../native';
@@ -142,6 +143,7 @@ const AudioRecorderWithContext = <
142
143
  stopVoiceRecording,
143
144
  uploadVoiceRecording,
144
145
  } = props;
146
+ const { t } = useTranslationContext();
145
147
 
146
148
  const {
147
149
  theme: {
@@ -189,7 +191,9 @@ const AudioRecorderWithContext = <
189
191
  <Animated.View
190
192
  style={[styles.slideToCancelContainer, slideToCancelStyle, slideToCancelContainer]}
191
193
  >
192
- <Text style={[styles.slideToCancel, { color: grey_dark }]}>Slide to Cancel</Text>
194
+ <Text style={[styles.slideToCancel, { color: grey_dark }]}>
195
+ {t<string>('Slide to Cancel')}
196
+ </Text>
193
197
  <ArrowLeft fill={grey_dark} size={24} {...arrowLeftIcon} />
194
198
  </Animated.View>
195
199
  </>
@@ -42,6 +42,10 @@ export type ChannelsContextValue<
42
42
  * **Note:** Don't use `additionalFlatListProps` to access the FlatList ref, use `setFlatListRef`
43
43
  */
44
44
  additionalFlatListProps: Partial<FlatListProps<Channel<StreamChatGenerics>>>;
45
+ /**
46
+ * A control prop used to determine whether the first query of the channel list has succeeded.
47
+ */
48
+ channelListInitialized: boolean;
45
49
  /**
46
50
  * Channels can be either an array of channels or a promise which resolves to an array of channels
47
51
  */
package/src/i18n/en.json CHANGED
@@ -94,6 +94,7 @@
94
94
  "Send a message": "Send a message",
95
95
  "Sending links is not allowed in this conversation": "Sending links is not allowed in this conversation",
96
96
  "Show All": "Show All",
97
+ "Slide to Cancel": "Slide to Cancel",
97
98
  "Slow mode ON": "Slow mode ON",
98
99
  "Suggest an option": "Suggest an option",
99
100
  "The message has been reported to a moderator.": "The message has been reported to a moderator.",
package/src/i18n/es.json CHANGED
@@ -96,6 +96,7 @@
96
96
  "Send a message": "Enviar un mensaje",
97
97
  "Sending links is not allowed in this conversation": "No está permitido enviar enlaces en esta conversación",
98
98
  "Show All": "Mostrar todo",
99
+ "Slide to Cancel": "Desliza para cancelar",
99
100
  "Slow mode ON": "Modo lento ACTIVADO",
100
101
  "Suggest an option": "Sugerir una opción",
101
102
  "The message has been reported to a moderator.": "El mensaje ha sido reportado a un moderador.",
package/src/i18n/fr.json CHANGED
@@ -96,6 +96,7 @@
96
96
  "Send a message": "Envoyer un message",
97
97
  "Sending links is not allowed in this conversation": "Sending links is not allowed in this conversation",
98
98
  "Show All": "Afficher tout",
99
+ "Slide to Cancel": "Glisser pour annuler",
99
100
  "Slow mode ON": "Mode lent activé",
100
101
  "Suggest an option": "Suggérer une option",
101
102
  "The message has been reported to a moderator.": "Le message a été signalé à un modérateur.",