stream-chat 9.6.1 → 9.8.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 (59) hide show
  1. package/dist/cjs/index.browser.cjs +532 -57
  2. package/dist/cjs/index.browser.cjs.map +4 -4
  3. package/dist/cjs/index.node.cjs +538 -57
  4. package/dist/cjs/index.node.cjs.map +4 -4
  5. package/dist/esm/index.js +532 -57
  6. package/dist/esm/index.js.map +4 -4
  7. package/dist/types/channel.d.ts +36 -4
  8. package/dist/types/client.d.ts +38 -0
  9. package/dist/types/messageComposer/messageComposer.d.ts +4 -1
  10. package/dist/types/messageComposer/middleware/messageComposer/commandInjection.d.ts +4 -0
  11. package/dist/types/messageComposer/middleware/messageComposer/index.d.ts +1 -0
  12. package/dist/types/messageComposer/middleware/textComposer/activeCommandGuard.d.ts +4 -0
  13. package/dist/types/messageComposer/middleware/textComposer/commandStringExtraction.d.ts +5 -0
  14. package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +5 -5
  15. package/dist/types/messageComposer/middleware/textComposer/index.d.ts +2 -0
  16. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +1 -2
  17. package/dist/types/messageComposer/middleware/textComposer/textMiddlewareUtils.d.ts +6 -0
  18. package/dist/types/messageComposer/middleware/textComposer/types.d.ts +3 -2
  19. package/dist/types/messageComposer/textComposer.d.ts +6 -3
  20. package/dist/types/offline-support/offline_support_api.d.ts +39 -0
  21. package/dist/types/offline-support/types.d.ts +36 -2
  22. package/dist/types/pagination/BasePaginator.d.ts +1 -1
  23. package/dist/types/pagination/ReminderPaginator.d.ts +6 -2
  24. package/dist/types/reminders/ReminderManager.d.ts +3 -3
  25. package/dist/types/search/BaseSearchSource.d.ts +37 -31
  26. package/dist/types/search/ChannelSearchSource.d.ts +1 -1
  27. package/dist/types/search/MessageSearchSource.d.ts +1 -1
  28. package/dist/types/search/UserSearchSource.d.ts +1 -1
  29. package/dist/types/search/index.d.ts +1 -0
  30. package/dist/types/search/types.d.ts +20 -0
  31. package/dist/types/types.d.ts +6 -2
  32. package/dist/types/utils.d.ts +11 -2
  33. package/package.json +1 -1
  34. package/src/channel.ts +85 -10
  35. package/src/client.ts +61 -3
  36. package/src/messageComposer/messageComposer.ts +120 -14
  37. package/src/messageComposer/middleware/messageComposer/commandInjection.ts +72 -0
  38. package/src/messageComposer/middleware/messageComposer/index.ts +1 -0
  39. package/src/messageComposer/middleware/textComposer/activeCommandGuard.ts +20 -0
  40. package/src/messageComposer/middleware/textComposer/commandStringExtraction.ts +56 -0
  41. package/src/messageComposer/middleware/textComposer/commands.ts +28 -11
  42. package/src/messageComposer/middleware/textComposer/index.ts +2 -0
  43. package/src/messageComposer/middleware/textComposer/mentions.ts +1 -2
  44. package/src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts +14 -0
  45. package/src/messageComposer/middleware/textComposer/types.ts +3 -2
  46. package/src/messageComposer/textComposer.ts +23 -3
  47. package/src/offline-support/offline_support_api.ts +79 -0
  48. package/src/offline-support/types.ts +41 -1
  49. package/src/pagination/BasePaginator.ts +1 -1
  50. package/src/pagination/ReminderPaginator.ts +20 -2
  51. package/src/reminders/ReminderManager.ts +16 -2
  52. package/src/search/BaseSearchSource.ts +123 -52
  53. package/src/search/ChannelSearchSource.ts +1 -1
  54. package/src/search/MessageSearchSource.ts +1 -1
  55. package/src/search/UserSearchSource.ts +1 -1
  56. package/src/search/index.ts +1 -0
  57. package/src/search/types.ts +20 -0
  58. package/src/types.ts +9 -2
  59. package/src/utils.ts +31 -2
@@ -1,19 +1,21 @@
1
1
  import { StateStore } from '../store';
2
2
  import { debounce, type DebouncedFunc } from '../utils';
3
+ import type {
4
+ QueryReturnValue,
5
+ SearchSourceOptions,
6
+ SearchSourceState,
7
+ SearchSourceType,
8
+ } from './types';
3
9
 
4
- export type SearchSourceType = 'channels' | 'users' | 'messages' | (string & {});
5
- export type QueryReturnValue<T> = { items: T[]; next?: string | null };
6
10
  export type DebounceOptions = {
7
11
  debounceMs: number;
8
12
  };
9
13
  type DebouncedExecQueryFunction = DebouncedFunc<(searchString?: string) => Promise<void>>;
10
14
 
11
15
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- export interface SearchSource<T = any> {
16
+ interface ISearchSource<T = any> {
13
17
  activate(): void;
14
18
 
15
- cancelScheduledQuery(): void;
16
-
17
19
  canExecuteQuery(newSearchString?: string): boolean;
18
20
 
19
21
  deactivate(): void;
@@ -30,48 +32,40 @@ export interface SearchSource<T = any> {
30
32
 
31
33
  resetState(): void;
32
34
 
33
- search(text?: string): Promise<void> | undefined;
34
-
35
35
  readonly searchQuery: string;
36
36
 
37
- setDebounceOptions(options: DebounceOptions): void;
38
-
39
37
  readonly state: StateStore<SearchSourceState<T>>;
40
38
  readonly type: SearchSourceType;
41
39
  }
42
40
 
43
41
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- export type SearchSourceState<T = any> = {
45
- hasNext: boolean;
46
- isActive: boolean;
47
- isLoading: boolean;
48
- items: T[] | undefined;
49
- searchQuery: string;
50
- lastQueryError?: Error;
51
- next?: string | null;
52
- offset?: number;
53
- };
54
- export type SearchSourceOptions = {
55
- /** The number of milliseconds to debounce the search query. The default interval is 300ms. */
56
- debounceMs?: number;
57
- pageSize?: number;
58
- };
42
+ export interface SearchSource<T = any> extends ISearchSource<T> {
43
+ cancelScheduledQuery(): void;
44
+ setDebounceOptions(options: DebounceOptions): void;
45
+ search(text?: string): Promise<void> | undefined;
46
+ }
47
+
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ export interface SearchSourceSync<T = any> extends ISearchSource<T> {
50
+ cancelScheduledQuery(): void;
51
+ setDebounceOptions(options: DebounceOptions): void;
52
+ search(text?: string): void;
53
+ }
54
+
59
55
  const DEFAULT_SEARCH_SOURCE_OPTIONS: Required<SearchSourceOptions> = {
60
56
  debounceMs: 300,
61
57
  pageSize: 10,
62
58
  } as const;
63
59
 
64
- export abstract class BaseSearchSource<T> implements SearchSource<T> {
60
+ abstract class BaseSearchSourceBase<T> implements ISearchSource<T> {
65
61
  state: StateStore<SearchSourceState<T>>;
66
62
  protected pageSize: number;
67
63
  abstract readonly type: SearchSourceType;
68
- protected searchDebounced!: DebouncedExecQueryFunction;
69
64
 
70
65
  protected constructor(options?: SearchSourceOptions) {
71
- const { debounceMs, pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
66
+ const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
72
67
  this.pageSize = pageSize;
73
68
  this.state = new StateStore<SearchSourceState<T>>(this.initialState);
74
- this.setDebounceOptions({ debounceMs });
75
69
  }
76
70
 
77
71
  get lastQueryError() {
@@ -123,14 +117,6 @@ export abstract class BaseSearchSource<T> implements SearchSource<T> {
123
117
  return this.state.getLatestValue().searchQuery;
124
118
  }
125
119
 
126
- protected abstract query(searchQuery: string): Promise<QueryReturnValue<T>>;
127
-
128
- protected abstract filterQueryResults(items: T[]): T[] | Promise<T[]>;
129
-
130
- setDebounceOptions = ({ debounceMs }: DebounceOptions) => {
131
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
132
- };
133
-
134
120
  activate = () => {
135
121
  if (this.isActive) return;
136
122
  this.state.partialNext({ isActive: true });
@@ -177,8 +163,7 @@ export abstract class BaseSearchSource<T> implements SearchSource<T> {
177
163
  };
178
164
  }
179
165
 
180
- async executeQuery(newSearchString?: string) {
181
- if (!this.canExecuteQuery(newSearchString)) return;
166
+ protected prepareStateForQuery(newSearchString?: string) {
182
167
  const hasNewSearchQuery = typeof newSearchString !== 'undefined';
183
168
  const searchString = newSearchString ?? this.searchQuery;
184
169
 
@@ -188,20 +173,67 @@ export abstract class BaseSearchSource<T> implements SearchSource<T> {
188
173
  this.state.partialNext({ isLoading: true });
189
174
  }
190
175
 
176
+ return { searchString, hasNewSearchQuery };
177
+ }
178
+
179
+ protected updatePaginationStateFromQuery(result: QueryReturnValue<T>) {
180
+ const { items, next } = result;
181
+
191
182
  const stateUpdate: Partial<SearchSourceState<T>> = {};
183
+ if (next || next === null) {
184
+ stateUpdate.next = next;
185
+ stateUpdate.hasNext = !!next;
186
+ } else {
187
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
188
+ stateUpdate.hasNext = items.length === this.pageSize;
189
+ }
190
+
191
+ return stateUpdate;
192
+ }
193
+
194
+ resetState() {
195
+ this.state.next(this.initialState);
196
+ }
197
+
198
+ resetStateAndActivate() {
199
+ this.resetState();
200
+ this.activate();
201
+ }
202
+ }
203
+
204
+ export abstract class BaseSearchSource<T>
205
+ extends BaseSearchSourceBase<T>
206
+ implements SearchSource<T>
207
+ {
208
+ protected searchDebounced!: DebouncedExecQueryFunction;
209
+
210
+ constructor(options?: SearchSourceOptions) {
211
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
212
+ super(options);
213
+ this.setDebounceOptions({ debounceMs });
214
+ }
215
+
216
+ protected abstract query(searchQuery: string): Promise<QueryReturnValue<T>>;
217
+
218
+ protected abstract filterQueryResults(items: T[]): T[] | Promise<T[]>;
219
+
220
+ setDebounceOptions = ({ debounceMs }: DebounceOptions) => {
221
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
222
+ };
223
+
224
+ async executeQuery(newSearchString?: string) {
225
+ if (!this.canExecuteQuery(newSearchString)) return;
226
+
227
+ const { hasNewSearchQuery, searchString } =
228
+ this.prepareStateForQuery(newSearchString);
229
+
230
+ let stateUpdate: Partial<SearchSourceState<T>> = {};
192
231
  try {
193
232
  const results = await this.query(searchString);
194
233
  if (!results) return;
195
- const { items, next } = results;
196
-
197
- if (next || next === null) {
198
- stateUpdate.next = next;
199
- stateUpdate.hasNext = !!next;
200
- } else {
201
- stateUpdate.offset = (this.offset ?? 0) + items.length;
202
- stateUpdate.hasNext = items.length === this.pageSize;
203
- }
204
234
 
235
+ const { items } = results;
236
+ stateUpdate = this.updatePaginationStateFromQuery(results);
205
237
  stateUpdate.items = await this.filterQueryResults(items);
206
238
  } catch (e) {
207
239
  stateUpdate.lastQueryError = e as Error;
@@ -215,13 +247,52 @@ export abstract class BaseSearchSource<T> implements SearchSource<T> {
215
247
  cancelScheduledQuery() {
216
248
  this.searchDebounced.cancel();
217
249
  }
250
+ }
218
251
 
219
- resetState() {
220
- this.state.next(this.initialState);
252
+ export abstract class BaseSearchSourceSync<T>
253
+ extends BaseSearchSourceBase<T>
254
+ implements SearchSourceSync<T>
255
+ {
256
+ protected searchDebounced!: DebouncedExecQueryFunction;
257
+
258
+ constructor(options?: SearchSourceOptions) {
259
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
260
+ super(options);
261
+ this.setDebounceOptions({ debounceMs });
221
262
  }
222
263
 
223
- resetStateAndActivate() {
224
- this.resetState();
225
- this.activate();
264
+ protected abstract query(searchQuery: string): QueryReturnValue<T>;
265
+
266
+ protected abstract filterQueryResults(items: T[]): T[];
267
+
268
+ setDebounceOptions = ({ debounceMs }: DebounceOptions) => {
269
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
270
+ };
271
+
272
+ executeQuery(newSearchString?: string) {
273
+ if (!this.canExecuteQuery(newSearchString)) return;
274
+
275
+ const { hasNewSearchQuery, searchString } =
276
+ this.prepareStateForQuery(newSearchString);
277
+
278
+ let stateUpdate: Partial<SearchSourceState<T>> = {};
279
+ try {
280
+ const results = this.query(searchString);
281
+ if (!results) return;
282
+
283
+ const { items } = results;
284
+ stateUpdate = this.updatePaginationStateFromQuery(results);
285
+ stateUpdate.items = this.filterQueryResults(items);
286
+ } catch (e) {
287
+ stateUpdate.lastQueryError = e as Error;
288
+ } finally {
289
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
290
+ }
291
+ }
292
+
293
+ search = (searchQuery?: string) => this.searchDebounced(searchQuery);
294
+
295
+ cancelScheduledQuery() {
296
+ this.searchDebounced.cancel();
226
297
  }
227
298
  }
@@ -1,8 +1,8 @@
1
1
  import { BaseSearchSource } from './BaseSearchSource';
2
- import type { SearchSourceOptions } from './BaseSearchSource';
3
2
  import type { Channel } from '../channel';
4
3
  import type { StreamChat } from '../client';
5
4
  import type { ChannelFilters, ChannelOptions, ChannelSort } from '../types';
5
+ import type { SearchSourceOptions } from './types';
6
6
 
7
7
  export class ChannelSearchSource extends BaseSearchSource<Channel> {
8
8
  readonly type = 'channels';
@@ -1,5 +1,4 @@
1
1
  import { BaseSearchSource } from './BaseSearchSource';
2
- import type { SearchSourceOptions } from './BaseSearchSource';
3
2
  import type {
4
3
  ChannelFilters,
5
4
  ChannelOptions,
@@ -10,6 +9,7 @@ import type {
10
9
  SearchOptions,
11
10
  } from '../types';
12
11
  import type { StreamChat } from '../client';
12
+ import type { SearchSourceOptions } from './types';
13
13
 
14
14
  export class MessageSearchSource extends BaseSearchSource<MessageResponse> {
15
15
  readonly type = 'messages';
@@ -1,7 +1,7 @@
1
1
  import { BaseSearchSource } from './BaseSearchSource';
2
- import type { SearchSourceOptions } from './BaseSearchSource';
3
2
  import type { StreamChat } from '../client';
4
3
  import type { UserFilters, UserOptions, UserResponse, UserSort } from '../types';
4
+ import type { SearchSourceOptions } from './types';
5
5
 
6
6
  export class UserSearchSource extends BaseSearchSource<UserResponse> {
7
7
  readonly type = 'users';
@@ -3,3 +3,4 @@ export * from './SearchController';
3
3
  export { UserSearchSource } from './UserSearchSource';
4
4
  export { ChannelSearchSource } from './ChannelSearchSource';
5
5
  export { MessageSearchSource } from './MessageSearchSource';
6
+ export * from './types';
@@ -0,0 +1,20 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export type SearchSourceState<T = any> = {
3
+ hasNext: boolean;
4
+ isActive: boolean;
5
+ isLoading: boolean;
6
+ items: T[] | undefined;
7
+ searchQuery: string;
8
+ lastQueryError?: Error;
9
+ next?: string | null;
10
+ offset?: number;
11
+ };
12
+
13
+ export type SearchSourceOptions = {
14
+ /** The number of milliseconds to debounce the search query. The default interval is 300ms. */
15
+ debounceMs?: number;
16
+ pageSize?: number;
17
+ };
18
+
19
+ export type SearchSourceType = 'channels' | 'users' | 'messages' | (string & {});
20
+ export type QueryReturnValue<T> = { items: T[]; next?: string | null };
package/src/types.ts CHANGED
@@ -4036,7 +4036,7 @@ export type ReminderAPIResponse = APIResponse & {
4036
4036
 
4037
4037
  export type CreateReminderOptions = {
4038
4038
  messageId: string;
4039
- remind_at?: string;
4039
+ remind_at?: string | null;
4040
4040
  user_id?: string;
4041
4041
  };
4042
4042
 
@@ -4099,7 +4099,7 @@ export type QueryRemindersResponse = {
4099
4099
  next?: string;
4100
4100
  };
4101
4101
 
4102
- export type HookType = 'webhook' | 'sqs' | 'sns';
4102
+ export type HookType = 'webhook' | 'sqs' | 'sns' | 'pending_message';
4103
4103
 
4104
4104
  export type EventHook = {
4105
4105
  id?: string;
@@ -4119,6 +4119,13 @@ export type EventHook = {
4119
4119
  sns_key?: string;
4120
4120
  sns_secret?: string;
4121
4121
  sns_role_arn?: string;
4122
+
4123
+ // pending message config
4124
+ timeout_ms?: number;
4125
+ callback?: {
4126
+ mode: 'CALLBACK_MODE_NONE' | 'CALLBACK_MODE_REST' | 'CALLBACK_MODE_TWIRP';
4127
+ };
4128
+
4122
4129
  created_at?: string;
4123
4130
  updated_at?: string;
4124
4131
  };
package/src/utils.ts CHANGED
@@ -297,9 +297,9 @@ export const axiosParamsSerializer: AxiosRequestConfig['paramsSerializer'] = (pa
297
297
 
298
298
  /**
299
299
  * Takes the message object, parses the dates, sets `__html`
300
- * and sets the status to `received` if missing; returns a new message object.
300
+ * and sets the status to `received` if missing; returns a new LocalMessage object.
301
301
  *
302
- * @param {MessageResponse} message `MessageResponse` object
302
+ * @param {LocalMessage} message `LocalMessage` object
303
303
  */
304
304
  export function formatMessage(
305
305
  message: MessageResponse | MessageResponseBase | LocalMessage,
@@ -330,6 +330,35 @@ export function formatMessage(
330
330
  } as LocalMessage;
331
331
  }
332
332
 
333
+ /**
334
+ * @private
335
+ *
336
+ * Takes a LocalMessage, parses the dates back to strings,
337
+ * and converts the message back to a MessageResponse.
338
+ *
339
+ * @param {MessageResponse} message `MessageResponse` object
340
+ */
341
+ export function unformatMessage(message: LocalMessage): MessageResponse {
342
+ const toMessageResponseBase = (
343
+ msg: LocalMessage | null | undefined,
344
+ ): MessageResponseBase | null => {
345
+ if (!msg) return null;
346
+ const newDateString = new Date().toISOString();
347
+ return {
348
+ ...msg,
349
+ created_at: message.created_at ? message.created_at.toISOString() : newDateString,
350
+ deleted_at: message.deleted_at ? message.deleted_at.toISOString() : undefined,
351
+ pinned_at: message.pinned_at ? message.pinned_at.toISOString() : undefined,
352
+ updated_at: message.updated_at ? message.updated_at.toISOString() : newDateString,
353
+ };
354
+ };
355
+
356
+ return {
357
+ ...toMessageResponseBase(message),
358
+ quoted_message: toMessageResponseBase((message as LocalMessage).quoted_message),
359
+ } as MessageResponse;
360
+ }
361
+
333
362
  export const localMessageToNewMessagePayload = (localMessage: LocalMessage): Message => {
334
363
  /* eslint-disable @typescript-eslint/no-unused-vars */
335
364
  const {