stream-chat 9.50.3 → 10.0.0-rc.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/dist/cjs/index.browser.js +19798 -14472
  2. package/dist/cjs/index.browser.js.map +4 -4
  3. package/dist/cjs/index.node.js +19817 -14482
  4. package/dist/cjs/index.node.js.map +4 -4
  5. package/dist/esm/index.mjs +19795 -14469
  6. package/dist/esm/index.mjs.map +4 -4
  7. package/dist/types/ChannelManager.d.ts +221 -0
  8. package/dist/types/EventHandlerPipeline.d.ts +104 -0
  9. package/dist/types/api-client.d.ts +26 -0
  10. package/dist/types/campaign.d.ts +0 -44
  11. package/dist/types/channel.d.ts +454 -479
  12. package/dist/types/channel_batch_updater.d.ts +0 -93
  13. package/dist/types/channel_state.d.ts +53 -201
  14. package/dist/types/client.d.ts +451 -1920
  15. package/dist/types/client_state.d.ts +4 -4
  16. package/dist/types/configuration/InstanceConfigurationService.d.ts +26 -0
  17. package/dist/types/configuration/index.d.ts +1 -0
  18. package/dist/types/configuration/types.d.ts +53 -0
  19. package/dist/types/connection.d.ts +49 -41
  20. package/dist/types/connection_fallback.d.ts +6 -4
  21. package/dist/types/constants.d.ts +0 -4
  22. package/dist/types/custom_types.d.ts +2 -0
  23. package/dist/types/entityStore/EntityStore.d.ts +111 -0
  24. package/dist/types/entityStore/StoreBackedItemIndex.d.ts +60 -0
  25. package/dist/types/entityStore/applyReactionLocally.d.ts +26 -0
  26. package/dist/types/entityStore/index.d.ts +2 -0
  27. package/dist/types/errors.d.ts +10 -2
  28. package/dist/types/gen/chat/ChannelApi.d.ts +45 -0
  29. package/dist/types/gen/chat/ChatApi.d.ts +334 -0
  30. package/dist/types/gen/model-decoders/decoders.d.ts +3 -0
  31. package/dist/types/gen/model-decoders/event-decoder-mapping.d.ts +6 -0
  32. package/dist/types/gen/models/index.d.ts +8836 -0
  33. package/dist/types/gen/moderation/ModerationApi.d.ts +50 -0
  34. package/dist/types/gen-imports.d.ts +3 -0
  35. package/dist/types/index.d.ts +8 -7
  36. package/dist/types/insights.d.ts +9 -8
  37. package/dist/types/logger.d.ts +9 -0
  38. package/dist/types/messageComposer/LocationComposer.d.ts +7 -4
  39. package/dist/types/messageComposer/attachmentIdentity.d.ts +2 -2
  40. package/dist/types/messageComposer/attachmentManager.d.ts +8 -4
  41. package/dist/types/messageComposer/configuration/types.d.ts +11 -11
  42. package/dist/types/messageComposer/linkPreviewsManager.d.ts +25 -13
  43. package/dist/types/messageComposer/messageComposer.d.ts +10 -10
  44. package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +3 -3
  45. package/dist/types/messageComposer/middleware/pollComposer/types.d.ts +4 -7
  46. package/dist/types/messageComposer/middleware/textComposer/commandUtils.d.ts +3 -3
  47. package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +7 -7
  48. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +218 -4
  49. package/dist/types/messageComposer/middleware/textComposer/types.d.ts +9 -4
  50. package/dist/types/messageComposer/pollComposer.d.ts +5 -4
  51. package/dist/types/messageComposer/textComposer.d.ts +8 -3
  52. package/dist/types/messageComposer/types.d.ts +6 -28
  53. package/dist/types/messageDelivery/MessageDeliveryReporter.d.ts +40 -25
  54. package/dist/types/messageDelivery/MessageReceiptsTracker.d.ts +53 -12
  55. package/dist/types/messageOperations/MessageOperationStatePolicy.d.ts +19 -0
  56. package/dist/types/messageOperations/MessageOperations.d.ts +17 -0
  57. package/dist/types/messageOperations/index.d.ts +3 -0
  58. package/dist/types/messageOperations/types.d.ts +49 -0
  59. package/dist/types/moderation.d.ts +21 -203
  60. package/dist/types/notifications/types.d.ts +8 -7
  61. package/dist/types/offline-support/offline_support_api.d.ts +111 -87
  62. package/dist/types/offline-support/offline_sync_manager.d.ts +0 -7
  63. package/dist/types/offline-support/types.d.ts +19 -23
  64. package/dist/types/offline-support/util.d.ts +4 -2
  65. package/dist/types/pagination/FilterBuilder.d.ts +1 -1
  66. package/dist/types/pagination/ItemIndex.d.ts +25 -0
  67. package/dist/types/pagination/cursorDerivation/createdAtAroundPaginationFlags.d.ts +9 -0
  68. package/dist/types/pagination/cursorDerivation/idAroundPaginationFlags.d.ts +6 -0
  69. package/dist/types/pagination/cursorDerivation/index.d.ts +1 -0
  70. package/dist/types/pagination/cursorDerivation/linearPaginationFlags.d.ts +6 -0
  71. package/dist/types/pagination/filterCompiler.d.ts +7 -0
  72. package/dist/types/pagination/index.d.ts +1 -3
  73. package/dist/types/pagination/paginators/BasePaginator.d.ts +848 -0
  74. package/dist/types/pagination/paginators/ChannelPaginator.d.ts +212 -0
  75. package/dist/types/pagination/paginators/MessageIntervalPaginator.d.ts +282 -0
  76. package/dist/types/pagination/paginators/MessagePaginator.d.ts +208 -0
  77. package/dist/types/pagination/paginators/PinnedMessagePaginator.d.ts +39 -0
  78. package/dist/types/pagination/paginators/ReminderPaginator.d.ts +19 -0
  79. package/dist/types/pagination/paginators/UserGroupPaginator.d.ts +24 -0
  80. package/dist/types/pagination/paginators/index.d.ts +7 -0
  81. package/dist/types/pagination/paginators/stateThrottling.d.ts +2 -0
  82. package/dist/types/pagination/sortCompiler.d.ts +49 -0
  83. package/dist/types/pagination/types.normalization.d.ts +11 -0
  84. package/dist/types/pagination/utility.normalization.d.ts +18 -0
  85. package/dist/types/pagination/utility.queryChannel.d.ts +26 -0
  86. package/dist/types/pagination/utility.search.d.ts +13 -0
  87. package/dist/types/permissions.d.ts +1 -1
  88. package/dist/types/poll.d.ts +29 -32
  89. package/dist/types/poll_manager.d.ts +2 -2
  90. package/dist/types/reminders/Reminder.d.ts +2 -5
  91. package/dist/types/reminders/ReminderManager.d.ts +2 -9
  92. package/dist/types/search/MessageSearchSource.d.ts +4 -4
  93. package/dist/types/search/UserSearchSource.d.ts +1 -1
  94. package/dist/types/search/types.d.ts +3 -3
  95. package/dist/types/segment.d.ts +0 -34
  96. package/dist/types/signing.d.ts +105 -65
  97. package/dist/types/store.d.ts +1 -0
  98. package/dist/types/thread.d.ts +70 -34
  99. package/dist/types/thread_manager.d.ts +3 -3
  100. package/dist/types/token_manager.d.ts +15 -10
  101. package/dist/types/types.d.ts +197 -3404
  102. package/dist/types/utils/FixedSizeQueueCache.d.ts +12 -6
  103. package/dist/types/utils/WithSubscriptions.d.ts +2 -1
  104. package/dist/types/utils/concurrency.d.ts +4 -4
  105. package/dist/types/utils/mergeWith/mergeWith.d.ts +3 -3
  106. package/dist/types/utils/mergeWith/mergeWithCore.d.ts +8 -3
  107. package/dist/types/utils/retryable.d.ts +34 -0
  108. package/dist/types/utils/throttling/throttle.d.ts +36 -0
  109. package/dist/types/utils.d.ts +100 -194
  110. package/package.json +7 -3
  111. package/src/ChannelManager.ts +724 -0
  112. package/src/CooldownTimer.ts +2 -2
  113. package/src/EventHandlerPipeline.ts +228 -0
  114. package/src/LiveLocationManager.ts +12 -11
  115. package/src/api-client.ts +275 -0
  116. package/src/campaign.ts +1 -77
  117. package/src/channel.ts +1344 -1282
  118. package/src/channel_batch_updater.ts +1 -211
  119. package/src/channel_state.ts +144 -1013
  120. package/src/client.ts +936 -3995
  121. package/src/client_state.ts +4 -4
  122. package/src/configuration/InstanceConfigurationService.ts +73 -0
  123. package/src/configuration/index.ts +1 -0
  124. package/src/configuration/types.ts +81 -0
  125. package/src/connection.ts +145 -94
  126. package/src/connection_fallback.ts +29 -27
  127. package/src/constants.ts +1 -5
  128. package/src/custom_types.ts +4 -1
  129. package/src/entityStore/EntityStore.ts +208 -0
  130. package/src/entityStore/StoreBackedItemIndex.ts +115 -0
  131. package/src/entityStore/applyReactionLocally.ts +113 -0
  132. package/src/entityStore/index.ts +2 -0
  133. package/src/errors.ts +14 -2
  134. package/src/gen/chat/ChannelApi.ts +275 -0
  135. package/src/gen/chat/ChatApi.ts +2554 -0
  136. package/src/gen/model-decoders/decoders.ts +2680 -0
  137. package/src/gen/model-decoders/event-decoder-mapping.ts +198 -0
  138. package/src/gen/models/index.ts +12673 -0
  139. package/src/gen/moderation/ModerationApi.ts +607 -0
  140. package/src/gen-imports.ts +3 -0
  141. package/src/index.ts +11 -12
  142. package/src/insights.ts +6 -5
  143. package/src/logger.ts +28 -0
  144. package/src/messageComposer/LocationComposer.ts +9 -5
  145. package/src/messageComposer/MessageComposerEffectHandlers.ts +1 -0
  146. package/src/messageComposer/attachmentIdentity.ts +16 -9
  147. package/src/messageComposer/attachmentManager.ts +13 -8
  148. package/src/messageComposer/configuration/types.ts +11 -11
  149. package/src/messageComposer/linkPreviewsManager.ts +9 -10
  150. package/src/messageComposer/messageComposer.ts +57 -53
  151. package/src/messageComposer/middleware/messageComposer/attachments.ts +1 -2
  152. package/src/messageComposer/middleware/messageComposer/cleanData.ts +1 -1
  153. package/src/messageComposer/middleware/messageComposer/compositionValidation.ts +2 -2
  154. package/src/messageComposer/middleware/messageComposer/messageComposerState.ts +2 -2
  155. package/src/messageComposer/middleware/messageComposer/sharedLocation.ts +4 -3
  156. package/src/messageComposer/middleware/messageComposer/textComposer.ts +2 -2
  157. package/src/messageComposer/middleware/messageComposer/types.ts +3 -4
  158. package/src/messageComposer/middleware/messageComposer/userDataInjection.ts +9 -5
  159. package/src/messageComposer/middleware/pollComposer/state.ts +0 -2
  160. package/src/messageComposer/middleware/pollComposer/types.ts +4 -7
  161. package/src/messageComposer/middleware/textComposer/TextComposerMiddlewareExecutor.ts +10 -1
  162. package/src/messageComposer/middleware/textComposer/commandEffects.ts +4 -4
  163. package/src/messageComposer/middleware/textComposer/commandUtils.ts +3 -6
  164. package/src/messageComposer/middleware/textComposer/commands.ts +3 -3
  165. package/src/messageComposer/middleware/textComposer/mentionUtils.ts +1 -1
  166. package/src/messageComposer/middleware/textComposer/mentions.ts +31 -15
  167. package/src/messageComposer/middleware/textComposer/types.ts +9 -4
  168. package/src/messageComposer/pollComposer.ts +6 -8
  169. package/src/messageComposer/textComposer.ts +27 -2
  170. package/src/messageComposer/types.ts +6 -28
  171. package/src/messageDelivery/MessageDeliveryReporter.ts +101 -45
  172. package/src/messageDelivery/MessageReceiptsTracker.ts +294 -21
  173. package/src/messageOperations/MessageOperationStatePolicy.ts +77 -0
  174. package/src/messageOperations/MessageOperations.ts +212 -0
  175. package/src/messageOperations/index.ts +10 -0
  176. package/src/messageOperations/types.ts +64 -0
  177. package/src/moderation.ts +38 -431
  178. package/src/notifications/types.ts +8 -7
  179. package/src/offline-support/offline_support_api.ts +200 -133
  180. package/src/offline-support/offline_sync_manager.ts +23 -44
  181. package/src/offline-support/types.ts +23 -26
  182. package/src/offline-support/util.ts +5 -4
  183. package/src/pagination/FilterBuilder.ts +4 -1
  184. package/src/pagination/ItemIndex.ts +25 -0
  185. package/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts +73 -0
  186. package/src/pagination/cursorDerivation/idAroundPaginationFlags.ts +53 -0
  187. package/src/pagination/cursorDerivation/index.ts +1 -0
  188. package/src/pagination/cursorDerivation/linearPaginationFlags.ts +86 -0
  189. package/src/pagination/filterCompiler.ts +196 -0
  190. package/src/pagination/index.ts +1 -3
  191. package/src/pagination/paginators/BasePaginator.ts +2859 -0
  192. package/src/pagination/paginators/ChannelPaginator.ts +729 -0
  193. package/src/pagination/paginators/MessageIntervalPaginator.ts +1116 -0
  194. package/src/pagination/paginators/MessagePaginator.ts +508 -0
  195. package/src/pagination/paginators/PinnedMessagePaginator.ts +108 -0
  196. package/src/pagination/paginators/ReminderPaginator.ts +107 -0
  197. package/src/pagination/paginators/UserGroupPaginator.ts +127 -0
  198. package/src/pagination/paginators/index.ts +7 -0
  199. package/src/pagination/paginators/stateThrottling.ts +31 -0
  200. package/src/pagination/sortCompiler.ts +193 -0
  201. package/src/pagination/types.normalization.ts +14 -0
  202. package/src/pagination/utility.normalization.ts +106 -0
  203. package/src/pagination/utility.queryChannel.ts +84 -0
  204. package/src/pagination/utility.search.ts +80 -0
  205. package/src/permissions.ts +7 -8
  206. package/src/poll.ts +101 -110
  207. package/src/poll_manager.ts +14 -8
  208. package/src/reminders/Reminder.ts +2 -5
  209. package/src/reminders/ReminderManager.ts +19 -21
  210. package/src/search/BaseSearchSource.ts +0 -3
  211. package/src/search/ChannelMemberSearchSource.ts +7 -1
  212. package/src/search/ChannelSearchSource.ts +10 -3
  213. package/src/search/MessageSearchSource.ts +29 -30
  214. package/src/search/UserSearchSource.ts +12 -8
  215. package/src/search/types.ts +3 -4
  216. package/src/segment.ts +1 -95
  217. package/src/signing.ts +107 -67
  218. package/src/store.ts +2 -1
  219. package/src/thread.ts +562 -238
  220. package/src/thread_manager.ts +37 -16
  221. package/src/token_manager.ts +21 -10
  222. package/src/types.ts +364 -4533
  223. package/src/uploadManager.ts +8 -0
  224. package/src/utils/FixedSizeQueueCache.ts +12 -6
  225. package/src/utils/WithSubscriptions.ts +2 -1
  226. package/src/utils/concurrency.ts +4 -4
  227. package/src/utils/mergeWith/mergeWith.ts +3 -3
  228. package/src/utils/mergeWith/mergeWithCore.ts +140 -164
  229. package/src/utils/mergeWith/mergeWithDiff.ts +3 -3
  230. package/src/utils/retryable.ts +117 -0
  231. package/src/utils/throttling/throttle.ts +130 -0
  232. package/src/utils.ts +208 -754
  233. package/dist/types/channel_manager.d.ts +0 -122
  234. package/dist/types/events.d.ts +0 -72
  235. package/dist/types/pagination/BasePaginator.d.ts +0 -69
  236. package/dist/types/pagination/ReminderPaginator.d.ts +0 -16
  237. package/dist/types/pagination/UserGroupPaginator.d.ts +0 -21
  238. package/src/channel_manager.ts +0 -830
  239. package/src/events.ts +0 -75
  240. package/src/pagination/BasePaginator.ts +0 -184
  241. package/src/pagination/ReminderPaginator.ts +0 -56
  242. package/src/pagination/UserGroupPaginator.ts +0 -93
@@ -1,830 +0,0 @@
1
- import type { QueryChannelsResponseWithChannels, StreamChat } from './client';
2
- import type {
3
- ChannelFilters,
4
- ChannelOptions,
5
- ChannelSort,
6
- ChannelStateOptions,
7
- Event,
8
- QueryChannelsAPIResponse,
9
- } from './types';
10
- import type { ValueOrPatch } from './store';
11
- import { isPatch, StateStore } from './store';
12
- import type { Channel } from './channel';
13
- import {
14
- extractSortValue,
15
- findLastPinnedChannelIndex,
16
- getAndWatchChannel,
17
- isChannelArchived,
18
- isChannelPinned,
19
- promoteChannel,
20
- shouldConsiderArchivedChannels,
21
- shouldConsiderPinnedChannels,
22
- sleep,
23
- uniqBy,
24
- } from './utils';
25
- import { generateUUIDv4 } from './utils';
26
- import {
27
- DEFAULT_QUERY_CHANNELS_MS_BETWEEN_RETRIES,
28
- DEFAULT_QUERY_CHANNELS_RETRY_COUNT,
29
- } from './constants';
30
- import { WithSubscriptions } from './utils/WithSubscriptions';
31
-
32
- export type ChannelManagerPagination = {
33
- filters: ChannelFilters;
34
- hasNext: boolean;
35
- isLoading: boolean;
36
- isLoadingNext: boolean;
37
- options: ChannelOptions;
38
- responseFilters?: ChannelFilters;
39
- responseSort?: ChannelSort;
40
- sort: ChannelSort;
41
- };
42
-
43
- export type ChannelManagerState = {
44
- channels: Channel[];
45
- /**
46
- * This value will become true the first time queryChannels is successfully executed and
47
- * will remain false otherwise. It's used as a control property regarding whether the list
48
- * has been initialized yet (i.e a query has already been done at least once) or not. We do
49
- * this to prevent state.channels from being forced to be nullable.
50
- */
51
- initialized: boolean;
52
- pagination: ChannelManagerPagination;
53
- error: Error | undefined;
54
- };
55
-
56
- export type ChannelSetterParameterType = ValueOrPatch<ChannelManagerState['channels']>;
57
- export type ChannelSetterType = (arg: ChannelSetterParameterType) => void;
58
-
59
- export type GenericEventHandlerType<T extends unknown[]> = (
60
- ...args: T
61
- ) => void | (() => void) | ((...args: T) => Promise<void>) | Promise<void>;
62
- export type EventHandlerType = GenericEventHandlerType<[Event]>;
63
- export type EventHandlerOverrideType = GenericEventHandlerType<
64
- [ChannelSetterType, Event]
65
- >;
66
-
67
- export type ChannelManagerEventTypes =
68
- | 'notification.added_to_channel'
69
- | 'notification.message_new'
70
- | 'notification.removed_from_channel'
71
- | 'message.new'
72
- | 'member.updated'
73
- | 'channel.deleted'
74
- | 'channel.hidden'
75
- | 'channel.truncated'
76
- | 'channel.visible'
77
- | 'channel.updated';
78
-
79
- export type ChannelManagerEventHandlerNames =
80
- | 'channelDeletedHandler'
81
- | 'channelHiddenHandler'
82
- | 'channelTruncatedHandler'
83
- | 'channelUpdatedHandler'
84
- | 'channelVisibleHandler'
85
- | 'newMessageHandler'
86
- | 'memberUpdatedHandler'
87
- | 'notificationAddedToChannelHandler'
88
- | 'notificationNewMessageHandler'
89
- | 'notificationRemovedFromChannelHandler';
90
-
91
- export type ChannelManagerEventHandlerOverrides = Partial<
92
- Record<ChannelManagerEventHandlerNames, EventHandlerOverrideType>
93
- >;
94
-
95
- export type ExecuteChannelsQueryPayload = Pick<
96
- ChannelManagerPagination,
97
- 'filters' | 'sort' | 'options'
98
- > & { stateOptions: ChannelStateOptions };
99
-
100
- export const channelManagerEventToHandlerMapping: {
101
- [key in ChannelManagerEventTypes]: ChannelManagerEventHandlerNames;
102
- } = {
103
- 'channel.deleted': 'channelDeletedHandler',
104
- 'channel.hidden': 'channelHiddenHandler',
105
- 'channel.truncated': 'channelTruncatedHandler',
106
- 'channel.updated': 'channelUpdatedHandler',
107
- 'channel.visible': 'channelVisibleHandler',
108
- 'message.new': 'newMessageHandler',
109
- 'member.updated': 'memberUpdatedHandler',
110
- 'notification.added_to_channel': 'notificationAddedToChannelHandler',
111
- 'notification.message_new': 'notificationNewMessageHandler',
112
- 'notification.removed_from_channel': 'notificationRemovedFromChannelHandler',
113
- };
114
-
115
- export type ChannelManagerOptions = {
116
- /**
117
- * Aborts a channels query that is already in progress and runs the new one.
118
- */
119
- abortInFlightQuery?: boolean;
120
- /**
121
- * Allows channel promotion to be applied where applicable for channels that are
122
- * currently not part of the channel list within the state. A good example of
123
- * this would be a channel that is being watched and it receives a new message,
124
- * but is not part of the list initially.
125
- */
126
- allowNotLoadedChannelPromotionForEvent?: {
127
- 'channel.visible': boolean;
128
- 'message.new': boolean;
129
- 'notification.added_to_channel': boolean;
130
- 'notification.message_new': boolean;
131
- };
132
- /**
133
- * Allows us to lock the order of channels within the list. Any event that would
134
- * change the order of channels within the list will do nothing.
135
- */
136
- lockChannelOrder?: boolean;
137
- };
138
-
139
- export type QueryChannelsRequestOutput = Channel[] | QueryChannelsResponseWithChannels;
140
-
141
- export type QueryChannelsRequestType = (
142
- filters: ChannelFilters,
143
- sort?: ChannelSort,
144
- options?: ChannelOptions,
145
- stateOptions?: ChannelStateOptions,
146
- ) => Promise<QueryChannelsRequestOutput>;
147
-
148
- export const DEFAULT_CHANNEL_MANAGER_OPTIONS = {
149
- abortInFlightQuery: false,
150
- allowNotLoadedChannelPromotionForEvent: {
151
- 'channel.visible': true,
152
- 'message.new': true,
153
- 'notification.added_to_channel': true,
154
- 'notification.message_new': true,
155
- },
156
- lockChannelOrder: false,
157
- };
158
-
159
- export const DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS = {
160
- offset: 0,
161
- };
162
-
163
- const mapPredefinedFilterSortToChannelSort = (
164
- sort: NonNullable<QueryChannelsAPIResponse['predefined_filter']>['sort'],
165
- ): ChannelSort =>
166
- (sort ?? []).map(({ direction = 1, field }) => ({
167
- [field]: direction,
168
- })) as ChannelSort;
169
-
170
- const getResponsePaginationParams = ({
171
- queryChannelsResponse,
172
- sort,
173
- }: {
174
- queryChannelsResponse?: Pick<QueryChannelsAPIResponse, 'predefined_filter'>;
175
- sort: ChannelSort;
176
- }): Pick<ChannelManagerPagination, 'responseFilters' | 'responseSort'> => {
177
- const predefinedFilter = queryChannelsResponse?.predefined_filter;
178
-
179
- if (!predefinedFilter) {
180
- return {};
181
- }
182
-
183
- return {
184
- responseFilters: predefinedFilter.filter as ChannelFilters,
185
- responseSort:
186
- predefinedFilter.sort !== undefined
187
- ? mapPredefinedFilterSortToChannelSort(predefinedFilter.sort)
188
- : sort,
189
- };
190
- };
191
-
192
- const getResponseFiltersAndSort = (
193
- pagination: ChannelManagerPagination,
194
- ): Pick<ChannelManagerPagination, 'filters' | 'sort'> => ({
195
- filters: pagination.responseFilters ?? pagination.filters,
196
- sort: pagination.responseSort ?? pagination.sort,
197
- });
198
-
199
- const omitResponsePaginationParams = (pagination: ChannelManagerPagination) => {
200
- const paginationWithoutResponseParams = { ...pagination };
201
- delete paginationWithoutResponseParams.responseFilters;
202
- delete paginationWithoutResponseParams.responseSort;
203
-
204
- return paginationWithoutResponseParams;
205
- };
206
-
207
- const isQueryChannelsResponseWithChannels = (
208
- response: QueryChannelsRequestOutput,
209
- ): response is QueryChannelsResponseWithChannels => !Array.isArray(response);
210
-
211
- /**
212
- * A class that manages a list of channels and changes it based on configuration and WS events. The
213
- * list of channels is reactive as well as the pagination and it can be subscribed to for state updates.
214
- *
215
- * @internal
216
- */
217
- export class ChannelManager extends WithSubscriptions {
218
- public readonly state: StateStore<ChannelManagerState>;
219
- private client: StreamChat;
220
- private eventHandlers: Map<string, EventHandlerType> = new Map();
221
- private eventHandlerOverrides: Map<string, EventHandlerOverrideType> = new Map();
222
- private queryChannelsRequest: QueryChannelsRequestType;
223
- private options: ChannelManagerOptions = {};
224
- private stateOptions: ChannelStateOptions = {};
225
- private id: string;
226
-
227
- constructor({
228
- client,
229
- eventHandlerOverrides = {},
230
- options = {},
231
- queryChannelsOverride,
232
- }: {
233
- client: StreamChat;
234
- eventHandlerOverrides?: ChannelManagerEventHandlerOverrides;
235
- options?: ChannelManagerOptions;
236
- queryChannelsOverride?: QueryChannelsRequestType;
237
- }) {
238
- super();
239
-
240
- this.id = `channel-manager-${generateUUIDv4()}`;
241
- this.client = client;
242
- this.state = new StateStore<ChannelManagerState>({
243
- channels: [],
244
- pagination: {
245
- isLoading: false,
246
- isLoadingNext: false,
247
- hasNext: false,
248
- filters: {},
249
- sort: {},
250
- options: DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
251
- },
252
- initialized: false,
253
- error: undefined,
254
- });
255
- this.setEventHandlerOverrides(eventHandlerOverrides);
256
- this.setOptions(options);
257
- this.queryChannelsRequest =
258
- queryChannelsOverride ?? ((...params) => this.client.queryChannels(...params));
259
- this.eventHandlers = new Map(
260
- Object.entries<EventHandlerType>({
261
- channelDeletedHandler: this.channelDeletedHandler,
262
- channelHiddenHandler: this.channelHiddenHandler,
263
- channelVisibleHandler: this.channelVisibleHandler,
264
- memberUpdatedHandler: this.memberUpdatedHandler,
265
- newMessageHandler: this.newMessageHandler,
266
- notificationAddedToChannelHandler: this.notificationAddedToChannelHandler,
267
- notificationNewMessageHandler: this.notificationNewMessageHandler,
268
- notificationRemovedFromChannelHandler: this.notificationRemovedFromChannelHandler,
269
- }),
270
- );
271
- }
272
-
273
- public setChannels = (valueOrFactory: ChannelSetterParameterType) => {
274
- this.state.next((current) => {
275
- const { channels: currentChannels } = current;
276
- const newChannels = isPatch(valueOrFactory)
277
- ? valueOrFactory(currentChannels)
278
- : valueOrFactory;
279
-
280
- // If the references between the two values are the same, just return the
281
- // current state; otherwise trigger a state change.
282
- if (currentChannels === newChannels) {
283
- return current;
284
- }
285
-
286
- return { ...current, channels: newChannels };
287
- });
288
- const {
289
- channels,
290
- pagination: { filters, options, sort },
291
- } = this.state.getLatestValue();
292
- this.client.offlineDb?.executeQuerySafely(
293
- (db) =>
294
- db.upsertCidsForQuery({
295
- cids: channels.map((channel) => channel.cid),
296
- filters,
297
- options,
298
- sort,
299
- }),
300
- { method: 'upsertCidsForQuery' },
301
- );
302
- };
303
-
304
- public setEventHandlerOverrides = (
305
- eventHandlerOverrides: ChannelManagerEventHandlerOverrides = {},
306
- ) => {
307
- const truthyEventHandlerOverrides = Object.entries(eventHandlerOverrides).reduce<
308
- Partial<ChannelManagerEventHandlerOverrides>
309
- >((acc, [key, value]) => {
310
- if (value) {
311
- acc[key as keyof ChannelManagerEventHandlerOverrides] = value;
312
- }
313
- return acc;
314
- }, {});
315
- this.eventHandlerOverrides = new Map(
316
- Object.entries<EventHandlerOverrideType>(truthyEventHandlerOverrides),
317
- );
318
- };
319
-
320
- public setQueryChannelsRequest = (queryChannelsRequest: QueryChannelsRequestType) => {
321
- this.queryChannelsRequest = queryChannelsRequest;
322
- };
323
-
324
- public setOptions = (options: ChannelManagerOptions = {}) => {
325
- this.options = { ...DEFAULT_CHANNEL_MANAGER_OPTIONS, ...options };
326
- };
327
-
328
- private executeChannelsQuery = async (
329
- payload: ExecuteChannelsQueryPayload,
330
- retryCount = 0,
331
- ): Promise<void> => {
332
- const { filters, sort, options, stateOptions } = payload;
333
- const { offset, limit } = {
334
- ...DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
335
- ...options,
336
- };
337
- try {
338
- const queryChannelsResponse = await this.queryChannelsRequest(
339
- filters,
340
- sort,
341
- options,
342
- { ...stateOptions, withResponse: true },
343
- );
344
- const channels = isQueryChannelsResponseWithChannels(queryChannelsResponse)
345
- ? queryChannelsResponse.channels
346
- : queryChannelsResponse;
347
- const newOffset = offset + (channels?.length ?? 0);
348
- const newOptions = { ...options, offset: newOffset };
349
- const { pagination } = this.state.getLatestValue();
350
- const responsePaginationParams = getResponsePaginationParams({
351
- queryChannelsResponse: isQueryChannelsResponseWithChannels(queryChannelsResponse)
352
- ? queryChannelsResponse
353
- : undefined,
354
- sort,
355
- });
356
- const paginationWithoutResponseParams = omitResponsePaginationParams(pagination);
357
-
358
- this.state.partialNext({
359
- channels,
360
- pagination: {
361
- // Drop response derived filter/sort from the previous query before applying
362
- // the current response. Non predefined queries do not return this metadata,
363
- // so keeping the old values would make later WS mutations use stale
364
- // predefined filter semantics. Also the predefined_filter might change, producing
365
- // a different combination as well so we always need to first clean up.
366
- ...paginationWithoutResponseParams,
367
- hasNext: (channels?.length ?? 0) >= (limit ?? 1),
368
- isLoading: false,
369
- options: newOptions,
370
- ...responsePaginationParams,
371
- },
372
- initialized: true,
373
- error: undefined,
374
- });
375
- this.client.offlineDb?.executeQuerySafely(
376
- (db) =>
377
- db.upsertCidsForQuery({
378
- cids: channels.map((channel) => channel.cid),
379
- filters: pagination.filters,
380
- options,
381
- sort: pagination.sort,
382
- }),
383
- { method: 'upsertCidsForQuery' },
384
- );
385
- } catch (err) {
386
- if (retryCount >= DEFAULT_QUERY_CHANNELS_RETRY_COUNT) {
387
- console.warn(err);
388
-
389
- const wrappedError = new Error(
390
- `Maximum number of retries reached in queryChannels. Last error message is: ${err}`,
391
- );
392
-
393
- const state = this.state.getLatestValue();
394
- // If the offline support is enabled, and there are channels in the DB, we should not error out.
395
- const isOfflineSupportEnabledWithChannels =
396
- this.client.offlineDb && state.channels.length > 0;
397
-
398
- this.state.partialNext({
399
- error: isOfflineSupportEnabledWithChannels ? undefined : wrappedError,
400
- pagination: {
401
- ...state.pagination,
402
- isLoading: false,
403
- isLoadingNext: false,
404
- },
405
- });
406
- return;
407
- }
408
-
409
- await sleep(DEFAULT_QUERY_CHANNELS_MS_BETWEEN_RETRIES);
410
-
411
- return this.executeChannelsQuery(payload, retryCount + 1);
412
- }
413
- };
414
-
415
- public queryChannels = async (
416
- filters: ChannelFilters,
417
- sort: ChannelSort = [],
418
- options: ChannelOptions = {},
419
- stateOptions: ChannelStateOptions = {},
420
- ) => {
421
- const {
422
- pagination: { isLoading, filters: filtersFromState },
423
- initialized,
424
- } = this.state.getLatestValue();
425
-
426
- if (
427
- isLoading &&
428
- !this.options.abortInFlightQuery &&
429
- // TODO: Figure a proper way to either deeply compare these or
430
- // create hashes from each.
431
- JSON.stringify(filtersFromState) === JSON.stringify(filters)
432
- ) {
433
- return;
434
- }
435
-
436
- const executeChannelsQueryPayload = { filters, sort, options, stateOptions };
437
-
438
- try {
439
- this.stateOptions = stateOptions;
440
- this.state.next((currentState) => ({
441
- ...currentState,
442
- pagination: {
443
- ...omitResponsePaginationParams(currentState.pagination),
444
- isLoading: true,
445
- isLoadingNext: false,
446
- filters,
447
- sort,
448
- options,
449
- },
450
- error: undefined,
451
- }));
452
-
453
- if (this.client.offlineDb?.getChannelsForQuery && this.client.user?.id) {
454
- if (!initialized) {
455
- const channelsFromDB = await this.client.offlineDb.getChannelsForQuery({
456
- userId: this.client.user.id,
457
- filters,
458
- options,
459
- sort,
460
- });
461
-
462
- if (channelsFromDB) {
463
- const offlineChannels = this.client.hydrateActiveChannels(channelsFromDB, {
464
- offlineMode: true,
465
- skipInitialization: [], // passing empty array will clear out the existing messages from channel state, this removes the possibility of duplicate messages
466
- });
467
-
468
- this.state.partialNext({ channels: offlineChannels });
469
- }
470
- }
471
-
472
- if (!this.client.offlineDb.syncManager.syncStatus) {
473
- this.client.offlineDb.syncManager.scheduleSyncStatusChangeCallback(
474
- this.id,
475
- async () => {
476
- await this.executeChannelsQuery(executeChannelsQueryPayload);
477
- },
478
- );
479
- return;
480
- }
481
- }
482
- await this.executeChannelsQuery(executeChannelsQueryPayload);
483
- } catch (error) {
484
- this.client.logger('error', (error as Error).message);
485
- this.state.next((currentState) => ({
486
- ...currentState,
487
- pagination: { ...currentState.pagination, isLoading: false },
488
- }));
489
- throw error;
490
- }
491
- };
492
-
493
- public loadNext = async () => {
494
- const { pagination, initialized } = this.state.getLatestValue();
495
- const { filters, sort, options, isLoadingNext, hasNext } = pagination;
496
-
497
- if (!initialized || isLoadingNext || !hasNext) {
498
- return;
499
- }
500
-
501
- try {
502
- const { offset, limit } = {
503
- ...DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
504
- ...options,
505
- };
506
- this.state.partialNext({
507
- pagination: { ...pagination, isLoading: false, isLoadingNext: true },
508
- });
509
- const queryChannelsResponse = await this.queryChannelsRequest(
510
- filters,
511
- sort,
512
- options,
513
- this.stateOptions,
514
- );
515
- const nextChannels = isQueryChannelsResponseWithChannels(queryChannelsResponse)
516
- ? queryChannelsResponse.channels
517
- : queryChannelsResponse;
518
- const { channels } = this.state.getLatestValue();
519
- const newOffset = offset + (nextChannels?.length ?? 0);
520
- const newOptions = { ...options, offset: newOffset };
521
-
522
- this.state.partialNext({
523
- channels: uniqBy<Channel>([...(channels || []), ...nextChannels], 'cid'),
524
- pagination: {
525
- ...pagination,
526
- hasNext: (nextChannels?.length ?? 0) >= (limit ?? 1),
527
- isLoading: false,
528
- isLoadingNext: false,
529
- options: newOptions,
530
- },
531
- });
532
- } catch (error) {
533
- this.client.logger('error', (error as Error).message);
534
- this.state.next((currentState) => ({
535
- ...currentState,
536
- pagination: {
537
- ...currentState.pagination,
538
- isLoadingNext: false,
539
- isLoading: false,
540
- },
541
- }));
542
- throw error;
543
- }
544
- };
545
-
546
- private notificationAddedToChannelHandler = async (event: Event) => {
547
- const { id, type, members } = event?.channel ?? {};
548
-
549
- if (
550
- !type ||
551
- !this.options.allowNotLoadedChannelPromotionForEvent?.[
552
- 'notification.added_to_channel'
553
- ]
554
- ) {
555
- return;
556
- }
557
-
558
- const channel = await getAndWatchChannel({
559
- client: this.client,
560
- id,
561
- members: members?.reduce<string[]>((acc, { user, user_id }) => {
562
- const userId = user_id || user?.id;
563
- if (userId) {
564
- acc.push(userId);
565
- }
566
- return acc;
567
- }, []),
568
- type,
569
- });
570
-
571
- const { pagination, channels } = this.state.getLatestValue();
572
- if (!channels) {
573
- return;
574
- }
575
-
576
- const { sort } = getResponseFiltersAndSort(pagination);
577
-
578
- this.setChannels(
579
- promoteChannel({
580
- channels,
581
- channelToMove: channel,
582
- sort,
583
- }),
584
- );
585
- };
586
-
587
- private channelDeletedHandler = (event: Event) => {
588
- const { channels } = this.state.getLatestValue();
589
- if (!channels) {
590
- return;
591
- }
592
-
593
- const newChannels = [...channels];
594
- const channelIndex = newChannels.findIndex(
595
- (channel) => channel.cid === (event.cid || event.channel?.cid),
596
- );
597
-
598
- if (channelIndex < 0) {
599
- return;
600
- }
601
-
602
- newChannels.splice(channelIndex, 1);
603
- this.setChannels(newChannels);
604
- };
605
-
606
- private channelHiddenHandler = this.channelDeletedHandler;
607
-
608
- private newMessageHandler = (event: Event) => {
609
- const { pagination, channels } = this.state.getLatestValue();
610
- if (!channels) {
611
- return;
612
- }
613
- const { filters, sort } = getResponseFiltersAndSort(pagination);
614
-
615
- const channelType = event.channel_type;
616
- const channelId = event.channel_id;
617
-
618
- if (!channelType || !channelId) {
619
- return;
620
- }
621
-
622
- const targetChannel = this.client.channel(channelType, channelId);
623
- const targetChannelIndex = channels.indexOf(targetChannel);
624
- const targetChannelExistsWithinList = targetChannelIndex >= 0;
625
-
626
- const isTargetChannelPinned = isChannelPinned(targetChannel);
627
- const isTargetChannelArchived = isChannelArchived(targetChannel);
628
-
629
- const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
630
- const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
631
-
632
- if (
633
- // filter is defined, target channel is archived and filter option is set to false
634
- (considerArchivedChannels && isTargetChannelArchived && !filters.archived) ||
635
- // filter is defined, target channel isn't archived and filter option is set to true
636
- (considerArchivedChannels && !isTargetChannelArchived && filters.archived) ||
637
- // sort option is defined, target channel is pinned
638
- (considerPinnedChannels && isTargetChannelPinned) ||
639
- // list order is locked
640
- this.options.lockChannelOrder ||
641
- // target channel is not within the loaded list and loading from cache is disallowed
642
- (!targetChannelExistsWithinList &&
643
- !this.options.allowNotLoadedChannelPromotionForEvent?.['message.new'])
644
- ) {
645
- return;
646
- }
647
-
648
- this.setChannels(
649
- promoteChannel({
650
- channels,
651
- channelToMove: targetChannel,
652
- channelToMoveIndexWithinChannels: targetChannelIndex,
653
- sort,
654
- }),
655
- );
656
- };
657
-
658
- private notificationNewMessageHandler = async (event: Event) => {
659
- const { id, type } = event?.channel ?? {};
660
-
661
- if (!id || !type) {
662
- return;
663
- }
664
-
665
- const channel = await getAndWatchChannel({
666
- client: this.client,
667
- id,
668
- type,
669
- });
670
-
671
- const { channels, pagination } = this.state.getLatestValue();
672
- const { filters, sort } = getResponseFiltersAndSort(pagination);
673
-
674
- const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
675
- const isTargetChannelArchived = isChannelArchived(channel);
676
-
677
- if (
678
- !channels ||
679
- (considerArchivedChannels && isTargetChannelArchived && !filters.archived) ||
680
- (considerArchivedChannels && !isTargetChannelArchived && filters.archived) ||
681
- !this.options.allowNotLoadedChannelPromotionForEvent?.['notification.message_new']
682
- ) {
683
- return;
684
- }
685
-
686
- this.setChannels(
687
- promoteChannel({
688
- channels,
689
- channelToMove: channel,
690
- sort,
691
- }),
692
- );
693
- };
694
-
695
- private channelVisibleHandler = async (event: Event) => {
696
- const { channel_type: channelType, channel_id: channelId } = event;
697
-
698
- if (!channelType || !channelId) {
699
- return;
700
- }
701
-
702
- const channel = await getAndWatchChannel({
703
- client: this.client,
704
- id: event.channel_id,
705
- type: event.channel_type,
706
- });
707
-
708
- const { channels, pagination } = this.state.getLatestValue();
709
- const { filters, sort } = getResponseFiltersAndSort(pagination);
710
-
711
- const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
712
- const isTargetChannelArchived = isChannelArchived(channel);
713
-
714
- if (
715
- !channels ||
716
- (considerArchivedChannels && isTargetChannelArchived && !filters.archived) ||
717
- (considerArchivedChannels && !isTargetChannelArchived && filters.archived) ||
718
- !this.options.allowNotLoadedChannelPromotionForEvent?.['channel.visible']
719
- ) {
720
- return;
721
- }
722
-
723
- this.setChannels(
724
- promoteChannel({
725
- channels,
726
- channelToMove: channel,
727
- sort,
728
- }),
729
- );
730
- };
731
-
732
- private notificationRemovedFromChannelHandler = this.channelDeletedHandler;
733
-
734
- private memberUpdatedHandler = (event: Event) => {
735
- const { pagination, channels } = this.state.getLatestValue();
736
- const { filters, sort } = getResponseFiltersAndSort(pagination);
737
- if (
738
- !event.member?.user ||
739
- event.member.user.id !== this.client.userID ||
740
- !event.channel_type ||
741
- !event.channel_id
742
- ) {
743
- return;
744
- }
745
- const channelType = event.channel_type;
746
- const channelId = event.channel_id;
747
-
748
- const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
749
- const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
750
- const pinnedAtSort = extractSortValue({ atIndex: 0, sort, targetKey: 'pinned_at' });
751
-
752
- if (
753
- !channels ||
754
- (!considerPinnedChannels && !considerArchivedChannels) ||
755
- this.options.lockChannelOrder
756
- ) {
757
- return;
758
- }
759
-
760
- const targetChannel = this.client.channel(channelType, channelId);
761
- // assumes that channel instances are not changing
762
- const targetChannelIndex = channels.indexOf(targetChannel);
763
- const targetChannelExistsWithinList = targetChannelIndex >= 0;
764
-
765
- const isTargetChannelPinned = isChannelPinned(targetChannel);
766
- const isTargetChannelArchived = isChannelArchived(targetChannel);
767
-
768
- const newChannels = [...channels];
769
-
770
- if (targetChannelExistsWithinList) {
771
- newChannels.splice(targetChannelIndex, 1);
772
- }
773
-
774
- // handle archiving (remove channel)
775
- if (
776
- // When archived filter true, and channel is unarchived
777
- (considerArchivedChannels && !isTargetChannelArchived && filters?.archived) ||
778
- // When archived filter false, and channel is archived
779
- (considerArchivedChannels && isTargetChannelArchived && !filters?.archived)
780
- ) {
781
- this.setChannels(newChannels);
782
- return;
783
- }
784
-
785
- // handle pinning
786
- let lastPinnedChannelIndex: number | null = null;
787
-
788
- if (pinnedAtSort === 1 || (pinnedAtSort === -1 && !isTargetChannelPinned)) {
789
- lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels });
790
- }
791
- const newTargetChannelIndex =
792
- typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0;
793
-
794
- // skip state update if the position of the channel does not change
795
- if (channels[newTargetChannelIndex] === targetChannel) {
796
- return;
797
- }
798
-
799
- newChannels.splice(newTargetChannelIndex, 0, targetChannel);
800
- this.setChannels(newChannels);
801
- };
802
-
803
- private subscriptionOrOverride = (event: Event) => {
804
- const handlerName =
805
- channelManagerEventToHandlerMapping[event.type as ChannelManagerEventTypes];
806
- const defaultEventHandler = this.eventHandlers.get(handlerName);
807
- const eventHandlerOverride = this.eventHandlerOverrides.get(handlerName);
808
- if (eventHandlerOverride && typeof eventHandlerOverride === 'function') {
809
- eventHandlerOverride(this.setChannels, event);
810
- return;
811
- }
812
-
813
- if (defaultEventHandler && typeof defaultEventHandler === 'function') {
814
- defaultEventHandler(event);
815
- }
816
- };
817
-
818
- public registerSubscriptions = () => {
819
- if (this.hasSubscriptions) {
820
- // Already listening for events and changes
821
- return;
822
- }
823
-
824
- for (const eventType of Object.keys(channelManagerEventToHandlerMapping)) {
825
- this.addUnsubscribeFunction(
826
- this.client.on(eventType, this.subscriptionOrOverride).unsubscribe,
827
- );
828
- }
829
- };
830
- }