stream-chat 9.4.0 → 9.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/cjs/index.browser.cjs +663 -135
  2. package/dist/cjs/index.browser.cjs.map +4 -4
  3. package/dist/cjs/index.node.cjs +673 -136
  4. package/dist/cjs/index.node.cjs.map +4 -4
  5. package/dist/esm/index.js +663 -135
  6. package/dist/esm/index.js.map +4 -4
  7. package/dist/types/client.d.ts +53 -20
  8. package/dist/types/events.d.ts +4 -0
  9. package/dist/types/index.d.ts +3 -1
  10. package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +2 -2
  11. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +2 -2
  12. package/dist/types/messageComposer/middleware/textComposer/types.d.ts +1 -1
  13. package/dist/types/pagination/BasePaginator.d.ts +69 -0
  14. package/dist/types/pagination/ReminderPaginator.d.ts +12 -0
  15. package/dist/types/pagination/index.d.ts +2 -0
  16. package/dist/types/reminders/Reminder.d.ts +37 -0
  17. package/dist/types/reminders/ReminderManager.d.ts +65 -0
  18. package/dist/types/reminders/ReminderTimer.d.ts +17 -0
  19. package/dist/types/reminders/index.d.ts +3 -0
  20. package/dist/types/search/BaseSearchSource.d.ts +87 -0
  21. package/dist/types/search/ChannelSearchSource.d.ts +17 -0
  22. package/dist/types/search/MessageSearchSource.d.ts +23 -0
  23. package/dist/types/search/SearchController.d.ts +44 -0
  24. package/dist/types/search/UserSearchSource.d.ts +16 -0
  25. package/dist/types/search/index.d.ts +5 -0
  26. package/dist/types/types.d.ts +43 -0
  27. package/package.json +1 -1
  28. package/src/channel.ts +2 -1
  29. package/src/client.ts +109 -40
  30. package/src/events.ts +6 -0
  31. package/src/index.ts +3 -1
  32. package/src/messageComposer/middleware/pollComposer/state.ts +4 -5
  33. package/src/messageComposer/middleware/textComposer/commands.ts +2 -2
  34. package/src/messageComposer/middleware/textComposer/mentions.ts +2 -2
  35. package/src/messageComposer/middleware/textComposer/types.ts +1 -1
  36. package/src/messageComposer/pollComposer.ts +3 -2
  37. package/src/pagination/BasePaginator.ts +184 -0
  38. package/src/pagination/ReminderPaginator.ts +38 -0
  39. package/src/pagination/index.ts +2 -0
  40. package/src/reminders/Reminder.ts +89 -0
  41. package/src/reminders/ReminderManager.ts +284 -0
  42. package/src/reminders/ReminderTimer.ts +86 -0
  43. package/src/reminders/index.ts +3 -0
  44. package/src/search/BaseSearchSource.ts +227 -0
  45. package/src/search/ChannelSearchSource.ts +34 -0
  46. package/src/search/MessageSearchSource.ts +88 -0
  47. package/src/search/SearchController.ts +154 -0
  48. package/src/search/UserSearchSource.ts +35 -0
  49. package/src/search/index.ts +5 -0
  50. package/src/token_manager.ts +3 -1
  51. package/src/types.ts +88 -0
  52. package/dist/types/search_controller.d.ts +0 -174
  53. package/src/search_controller.ts +0 -523
@@ -73,6 +73,7 @@ export type AppSettingsAPIResponse = APIResponse & {
73
73
  updated_at?: string;
74
74
  uploads?: boolean;
75
75
  url_enrichment?: boolean;
76
+ user_message_reminders?: boolean;
76
77
  }>;
77
78
  reminders_interval: number;
78
79
  async_moderation_config?: AsyncModerationOptions;
@@ -579,6 +580,7 @@ export type MessageResponseBase = MessageBase & {
579
580
  reaction_scores?: {
580
581
  [key: string]: number;
581
582
  } | null;
583
+ reminder?: ReminderResponseBase;
582
584
  reply_count?: number;
583
585
  shadowed?: boolean;
584
586
  status?: string;
@@ -855,6 +857,7 @@ export type CreateChannelOptions = {
855
857
  typing_events?: boolean;
856
858
  uploads?: boolean;
857
859
  url_enrichment?: boolean;
860
+ user_message_reminders?: boolean;
858
861
  };
859
862
  export type CreateCommandOptions = {
860
863
  description: string;
@@ -1258,6 +1261,7 @@ export type Event = CustomEventData & {
1258
1261
  offlineReactions?: ReactionResponse[];
1259
1262
  reaction?: ReactionResponse;
1260
1263
  received_at?: string | Date;
1264
+ reminder?: ReminderResponse;
1261
1265
  shadow?: boolean;
1262
1266
  team?: string;
1263
1267
  thread?: ThreadResponse;
@@ -1816,6 +1820,7 @@ export type ChannelConfigFields = {
1816
1820
  typing_events?: boolean;
1817
1821
  uploads?: boolean;
1818
1822
  url_enrichment?: boolean;
1823
+ user_message_reminders?: boolean;
1819
1824
  };
1820
1825
  export type ChannelConfigWithInfo = ChannelConfigFields & CreatedAtUpdatedAt & {
1821
1826
  commands?: CommandResponse[];
@@ -2909,6 +2914,44 @@ export type ThreadFilters = QueryFilters<{
2909
2914
  } & {
2910
2915
  last_message_at?: RequireOnlyOne<Pick<QueryFilter<ThreadResponse['last_message_at']>, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> | PrimitiveFilter<ThreadResponse['last_message_at']>;
2911
2916
  }>;
2917
+ export type ReminderResponseBase = {
2918
+ channel_cid: string;
2919
+ created_at: string;
2920
+ message_id: string;
2921
+ updated_at: string;
2922
+ user_id: string;
2923
+ remind_at?: string;
2924
+ };
2925
+ export type ReminderResponse = ReminderResponseBase & {
2926
+ user: UserResponse;
2927
+ message: MessageResponse;
2928
+ };
2929
+ export type ReminderAPIResponse = APIResponse & {
2930
+ reminder: ReminderResponse;
2931
+ };
2932
+ export type CreateReminderOptions = {
2933
+ messageId: string;
2934
+ remind_at?: string;
2935
+ user_id?: string;
2936
+ };
2937
+ export type UpdateReminderOptions = CreateReminderOptions;
2938
+ export type ReminderFilters = QueryFilters<{
2939
+ channel_cid?: RequireOnlyOne<Pick<QueryFilter<ReminderResponseBase['channel_cid']>, '$eq' | '$in'>> | PrimitiveFilter<ReminderResponseBase['channel_cid']>;
2940
+ created_at?: RequireOnlyOne<Pick<QueryFilter<ReminderResponseBase['created_at']>, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> | PrimitiveFilter<ReminderResponseBase['created_at']>;
2941
+ message_id?: RequireOnlyOne<Pick<QueryFilter<ReminderResponseBase['message_id']>, '$eq' | '$in'>> | PrimitiveFilter<ReminderResponseBase['message_id']>;
2942
+ remind_at?: RequireOnlyOne<Pick<QueryFilter<ReminderResponseBase['remind_at']>, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> | PrimitiveFilter<ReminderResponseBase['remind_at']>;
2943
+ user_id?: RequireOnlyOne<Pick<QueryFilter<ReminderResponseBase['user_id']>, '$eq' | '$in'>> | PrimitiveFilter<ReminderResponseBase['user_id']>;
2944
+ }>;
2945
+ export type ReminderSort = Sort<Pick<ReminderResponseBase, 'channel_cid' | 'created_at' | 'remind_at' | 'updated_at'>> | Array<Sort<Pick<ReminderResponseBase, 'channel_cid' | 'created_at' | 'remind_at' | 'updated_at'>>>;
2946
+ export type QueryRemindersOptions = Pager & {
2947
+ filter?: ReminderFilters;
2948
+ sort?: ReminderSort;
2949
+ };
2950
+ export type QueryRemindersResponse = {
2951
+ reminders: ReminderResponse[];
2952
+ prev?: string;
2953
+ next?: string;
2954
+ };
2912
2955
  export type HookType = 'webhook' | 'sqs' | 'sns';
2913
2956
  export type EventHook = {
2914
2957
  id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stream-chat",
3
- "version": "9.4.0",
3
+ "version": "9.5.1",
4
4
  "description": "JS SDK for the Stream Chat API",
5
5
  "homepage": "https://getstream.io/chat/",
6
6
  "author": {
package/src/channel.ts CHANGED
@@ -1325,7 +1325,7 @@ export class Channel {
1325
1325
  */
1326
1326
  countUnread(lastRead?: Date | null) {
1327
1327
  if (!lastRead) return this.state.unreadCount;
1328
-
1328
+ // todo: prevent finding the latest message set on each iteration
1329
1329
  let count = 0;
1330
1330
  for (let i = 0; i < this.state.latestMessages.length; i += 1) {
1331
1331
  const message = this.state.latestMessages[i];
@@ -1461,6 +1461,7 @@ export class Channel {
1461
1461
  };
1462
1462
 
1463
1463
  this.getClient().polls.hydratePollCache(state.messages, true);
1464
+ this.getClient().reminders.hydrateState(state.messages);
1464
1465
 
1465
1466
  if (state.draft) {
1466
1467
  this.messageComposer.initState({ composition: state.draft });
package/src/client.ts CHANGED
@@ -74,6 +74,7 @@ import type {
74
74
  CreatePollAPIResponse,
75
75
  CreatePollData,
76
76
  CreatePollOptionAPIResponse,
77
+ CreateReminderOptions,
77
78
  CustomPermissionOptions,
78
79
  DeactivateUsersOptions,
79
80
  DeleteChannelsResponse,
@@ -163,6 +164,8 @@ import type {
163
164
  QueryPollsResponse,
164
165
  QueryReactionsAPIResponse,
165
166
  QueryReactionsOptions,
167
+ QueryRemindersOptions,
168
+ QueryRemindersResponse,
166
169
  QuerySegmentsOptions,
167
170
  QuerySegmentTargetsFilter,
168
171
  QueryThreadsAPIResponse,
@@ -174,6 +177,7 @@ import type {
174
177
  ReactionSort,
175
178
  ReactivateUserOptions,
176
179
  ReactivateUsersOptions,
180
+ ReminderAPIResponse,
177
181
  ReviewFlagReportOptions,
178
182
  ReviewFlagReportResponse,
179
183
  SdkIdentifier,
@@ -206,6 +210,7 @@ import type {
206
210
  UpdateMessageOptions,
207
211
  UpdatePollAPIResponse,
208
212
  UpdatePollOptionAPIResponse,
213
+ UpdateReminderOptions,
209
214
  UpdateSegmentData,
210
215
  UpsertPushPreferencesResponse,
211
216
  UserCustomEvent,
@@ -228,6 +233,7 @@ import type {
228
233
  } from './channel_manager';
229
234
  import { ChannelManager } from './channel_manager';
230
235
  import { NotificationManager } from './notifications';
236
+ import { ReminderManager } from './reminders';
231
237
  import { StateStore } from './store';
232
238
  import type { MessageComposer } from './messageComposer';
233
239
  import type { AbstractOfflineDB } from './offline-support';
@@ -268,6 +274,7 @@ export class StreamChat {
268
274
  polls: PollManager;
269
275
  offlineDb?: AbstractOfflineDB;
270
276
  notifications: NotificationManager;
277
+ reminders: ReminderManager;
271
278
  anonymous: boolean;
272
279
  persistUserOnConnectionFailure?: boolean;
273
280
  axiosInstance: AxiosInstance;
@@ -443,9 +450,9 @@ export class StreamChat {
443
450
  *
444
451
  * e.g.,
445
452
  * const client = new StreamChat('api_key', {}, {
446
- * logger = (logLevel, message, extraData) => {
447
- * console.log(message);
448
- * }
453
+ * logger = (logLevel, message, extraData) => {
454
+ * console.log(message);
455
+ * }
449
456
  * })
450
457
  *
451
458
  * extraData contains tags array attached to log message. Tags can have one/many of following values:
@@ -459,34 +466,35 @@ export class StreamChat {
459
466
  *
460
467
  * It may also contains some extra data, some examples have been mentioned below:
461
468
  * 1. {
462
- * tags: ['api', 'api_request', 'client'],
463
- * url: string,
464
- * payload: object,
465
- * config: object
469
+ * tags: ['api', 'api_request', 'client'],
470
+ * url: string,
471
+ * payload: object,
472
+ * config: object
466
473
  * }
467
474
  * 2. {
468
- * tags: ['api', 'api_response', 'client'],
469
- * url: string,
470
- * response: object
475
+ * tags: ['api', 'api_response', 'client'],
476
+ * url: string,
477
+ * response: object
471
478
  * }
472
479
  * 3. {
473
- * tags: ['api', 'api_response', 'client'],
474
- * url: string,
475
- * error: object
480
+ * tags: ['api', 'api_response', 'client'],
481
+ * url: string,
482
+ * error: object
476
483
  * }
477
484
  * 4. {
478
- * tags: ['event', 'client'],
479
- * event: object
485
+ * tags: ['event', 'client'],
486
+ * event: object
480
487
  * }
481
488
  * 5. {
482
- * tags: ['channel'],
483
- * channel: object
489
+ * tags: ['channel'],
490
+ * channel: object
484
491
  * }
485
492
  */
486
493
  this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : () => null;
487
494
  this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
488
495
  this.threads = new ThreadManager({ client: this });
489
496
  this.polls = new PollManager({ client: this });
497
+ this.reminders = new ReminderManager({ client: this });
490
498
  }
491
499
 
492
500
  /**
@@ -559,6 +567,12 @@ export class StreamChat {
559
567
 
560
568
  _hasConnectionID = () => Boolean(this._getConnectionID());
561
569
 
570
+ public setMessageComposerSetupFunction = (
571
+ setupFunction: MessageComposerSetupState['setupFunction'],
572
+ ) => {
573
+ this._messageComposerSetupState.partialNext({ setupFunction });
574
+ };
575
+
562
576
  /**
563
577
  * connectUser - Set the current user and open a WebSocket connection
564
578
  *
@@ -878,15 +892,15 @@ export class StreamChat {
878
892
  * @param {string} userID User ID. If user has no devices, it will error
879
893
  * @param {TestPushDataInput} [data] Overrides for push templates/message used
880
894
  * IE: {
881
- messageID: 'id-of-message', // will error if message does not exist
882
- apnTemplate: '{}', // if app doesn't have apn configured it will error
883
- firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
884
- firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
885
- skipDevices: true, // skip config/device checks and sending to real devices
886
- pushProviderName: 'staging' // one of your configured push providers
887
- pushProviderType: 'apn' // one of supported provider types
888
- }
889
- */
895
+ messageID: 'id-of-message', // will error if message does not exist
896
+ apnTemplate: '{}', // if app doesn't have apn configured it will error
897
+ firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
898
+ firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
899
+ skipDevices: true, // skip config/device checks and sending to real devices
900
+ pushProviderName: 'staging' // one of your configured push providers
901
+ pushProviderType: 'apn' // one of supported provider types
902
+ }
903
+ */
890
904
  async testPushSettings(userID: string, data: TestPushDataInput = {}) {
891
905
  return await this.post<CheckPushResponse>(this.baseURL + '/check_push', {
892
906
  user_id: userID,
@@ -907,10 +921,10 @@ export class StreamChat {
907
921
  *
908
922
  * @param {TestSQSDataInput} [data] Overrides SQS settings for testing if needed
909
923
  * IE: {
910
- sqs_key: 'auth_key',
911
- sqs_secret: 'auth_secret',
912
- sqs_url: 'url_to_queue',
913
- }
924
+ sqs_key: 'auth_key',
925
+ sqs_secret: 'auth_secret',
926
+ sqs_url: 'url_to_queue',
927
+ }
914
928
  */
915
929
  async testSQSSettings(data: TestSQSDataInput = {}) {
916
930
  return await this.post<CheckSQSResponse>(this.baseURL + '/check_sqs', data);
@@ -921,10 +935,10 @@ export class StreamChat {
921
935
  *
922
936
  * @param {TestSNSDataInput} [data] Overrides SNS settings for testing if needed
923
937
  * IE: {
924
- sns_key: 'auth_key',
925
- sns_secret: 'auth_secret',
926
- sns_topic_arn: 'topic_to_publish_to',
927
- }
938
+ sns_key: 'auth_key',
939
+ sns_secret: 'auth_secret',
940
+ sns_topic_arn: 'topic_to_publish_to',
941
+ }
928
942
  */
929
943
  async testSNSSettings(data: TestSNSDataInput = {}) {
930
944
  return await this.post<CheckSNSResponse>(this.baseURL + '/check_sns', data);
@@ -1952,6 +1966,7 @@ export class StreamChat {
1952
1966
  }),
1953
1967
  };
1954
1968
  this.polls.hydratePollCache(channelState.messages, true);
1969
+ this.reminders.hydrateState(channelState.messages);
1955
1970
  }
1956
1971
 
1957
1972
  if (channelState.draft) {
@@ -2540,6 +2555,7 @@ export class StreamChat {
2540
2555
  ...options,
2541
2556
  });
2542
2557
  }
2558
+
2543
2559
  async blockUser(blockedUserID: string, user_id?: string) {
2544
2560
  return await this.post<BlockUserAPIResponse>(this.baseURL + '/users/block', {
2545
2561
  blocked_user_id: blockedUserID,
@@ -2552,12 +2568,14 @@ export class StreamChat {
2552
2568
  ...(user_id ? { user_id } : {}),
2553
2569
  });
2554
2570
  }
2571
+
2555
2572
  async unBlockUser(blockedUserID: string, userID?: string) {
2556
2573
  return await this.post<APIResponse>(this.baseURL + '/users/unblock', {
2557
2574
  blocked_user_id: blockedUserID,
2558
2575
  ...(userID ? { user_id: userID } : {}),
2559
2576
  });
2560
2577
  }
2578
+
2561
2579
  /** muteUser - mutes a user
2562
2580
  *
2563
2581
  * @param {string} targetID
@@ -2747,6 +2765,7 @@ export class StreamChat {
2747
2765
  ...options,
2748
2766
  });
2749
2767
  }
2768
+
2750
2769
  // alias for backwards compatibility
2751
2770
  _unblockMessage = this.unblockMessage;
2752
2771
 
@@ -2817,7 +2836,7 @@ export class StreamChat {
2817
2836
  );
2818
2837
  }
2819
2838
 
2820
- DBDeleteChannelType(channelType: string) {
2839
+ deleteChannelType(channelType: string) {
2821
2840
  return this.delete<APIResponse>(
2822
2841
  this.baseURL + `/channeltypes/${encodeURIComponent(channelType)}`,
2823
2842
  );
@@ -3714,6 +3733,7 @@ export class StreamChat {
3714
3733
  },
3715
3734
  );
3716
3735
  }
3736
+
3717
3737
  /**
3718
3738
  * removeSegmentTargets - Remove targets from a segment
3719
3739
  *
@@ -3830,6 +3850,7 @@ export class StreamChat {
3830
3850
  stop_at: options?.stopAt,
3831
3851
  });
3832
3852
  }
3853
+
3833
3854
  /**
3834
3855
  * queryCampaigns - Query Campaigns
3835
3856
  *
@@ -4496,9 +4517,57 @@ export class StreamChat {
4496
4517
  return await this.post<QueryDraftsResponse>(this.baseURL + '/drafts/query', payload);
4497
4518
  }
4498
4519
 
4499
- public setMessageComposerSetupFunction = (
4500
- setupFunction: MessageComposerSetupState['setupFunction'],
4501
- ) => {
4502
- this._messageComposerSetupState.partialNext({ setupFunction });
4503
- };
4520
+ /**
4521
+ * createReminder - Creates a reminder for a message
4522
+ *
4523
+ * @param {CreateReminderOptions} options The options for creating the reminder
4524
+ * @returns {Promise<ReminderAPIResponse>}
4525
+ */
4526
+ async createReminder({ messageId, ...options }: CreateReminderOptions) {
4527
+ return await this.post<ReminderAPIResponse>(
4528
+ `${this.baseURL}/messages/${messageId}/reminders`,
4529
+ options,
4530
+ );
4531
+ }
4532
+
4533
+ /**
4534
+ * updateReminder - Updates an existing reminder for a message
4535
+ *
4536
+ * @param {UpdateReminderOptions} options The options for updating the reminder
4537
+ * @returns {Promise<ReminderAPIResponse>}
4538
+ */
4539
+ async updateReminder({ messageId, ...options }: UpdateReminderOptions) {
4540
+ return await this.patch<ReminderAPIResponse>(
4541
+ `${this.baseURL}/messages/${messageId}/reminders`,
4542
+ options,
4543
+ );
4544
+ }
4545
+
4546
+ /**
4547
+ * deleteReminder - Deletes a reminder for a message
4548
+ *
4549
+ * @param {string} messageId The ID of the message whose reminder to delete
4550
+ * @param {string} [userId] Optional user ID, required for server-side operations
4551
+ * @returns {Promise<APIResponse>}
4552
+ */
4553
+ async deleteReminder(messageId: string, userId?: string): Promise<APIResponse> {
4554
+ return await this.delete<APIResponse>(
4555
+ `${this.baseURL}/messages/${messageId}/reminders`,
4556
+ userId ? { user_id: userId } : {},
4557
+ );
4558
+ }
4559
+
4560
+ /**
4561
+ * queryReminders - Queries reminders based on given filters
4562
+ *
4563
+ * @param {QueryRemindersOptions} options The options for querying reminders
4564
+ * @returns {Promise<QueryRemindersResponse>}
4565
+ */
4566
+ async queryReminders({ filter, sort, ...rest }: QueryRemindersOptions = {}) {
4567
+ return await this.post<QueryRemindersResponse>(`${this.baseURL}/reminders/query`, {
4568
+ filter_conditions: filter,
4569
+ sort: sort && normalizeQuerySort(sort),
4570
+ ...rest,
4571
+ });
4572
+ }
4504
4573
  }
package/src/events.ts CHANGED
@@ -63,4 +63,10 @@ export const EVENT_MAP = {
63
63
  'connection.recovered': true,
64
64
  'transport.changed': true,
65
65
  'capabilities.changed': true,
66
+
67
+ // Reminder events
68
+ 'reminder.created': true,
69
+ 'reminder.updated': true,
70
+ 'reminder.deleted': true,
71
+ 'notification.reminder_due': true,
66
72
  };
package/src/index.ts CHANGED
@@ -10,10 +10,12 @@ export * from './insights';
10
10
  export * from './messageComposer';
11
11
  export * from './middleware';
12
12
  export * from './moderation';
13
+ export * from './pagination';
13
14
  export * from './permissions';
14
15
  export * from './poll';
15
16
  export * from './poll_manager';
16
- export * from './search_controller';
17
+ export * from './reminders';
18
+ export * from './search';
17
19
  export * from './segment';
18
20
  export * from './signing';
19
21
  export * from './store';
@@ -41,16 +41,15 @@ export const pollStateChangeValidators: Partial<
41
41
  return { max_votes_allowed: 'Type a number from 2 to 10' };
42
42
  return { max_votes_allowed: undefined };
43
43
  },
44
- options: ({ value }) => {
44
+ options: ({ value: options }) => {
45
45
  const errors: Record<string, string> = {};
46
46
  const seenOptions = new Set<string>();
47
47
 
48
- value.forEach((option: { id: string; text: string }) => {
49
- const trimmedText = option.text.trim();
50
- if (seenOptions.has(trimmedText)) {
48
+ options.forEach((option: { id: string; text: string }) => {
49
+ if (seenOptions.has(option.text) && option.text.length) {
51
50
  errors[option.id] = 'Option already exists';
52
51
  } else {
53
- seenOptions.add(trimmedText);
52
+ seenOptions.add(option.text);
54
53
  }
55
54
  });
56
55
 
@@ -1,7 +1,7 @@
1
1
  import type { Channel } from '../../../channel';
2
2
  import type { Middleware } from '../../../middleware';
3
- import type { SearchSourceOptions } from '../../../search_controller';
4
- import { BaseSearchSource } from '../../../search_controller';
3
+ import type { SearchSourceOptions } from '../../../search';
4
+ import { BaseSearchSource } from '../../../search';
5
5
  import type { CommandResponse } from '../../../types';
6
6
  import { mergeWith } from '../../../utils/mergeWith';
7
7
  import type { CommandSuggestion, TextComposerMiddlewareOptions } from './types';
@@ -3,8 +3,8 @@ import {
3
3
  getTriggerCharWithToken,
4
4
  insertItemWithTrigger,
5
5
  } from './textMiddlewareUtils';
6
- import type { SearchSourceOptions } from '../../../search_controller';
7
- import { BaseSearchSource } from '../../../search_controller';
6
+ import type { SearchSourceOptions } from '../../../search';
7
+ import { BaseSearchSource } from '../../../search';
8
8
  import { mergeWith } from '../../../utils/mergeWith';
9
9
  import type { TextComposerMiddlewareOptions, UserSuggestion } from './types';
10
10
  import type { StreamChat } from '../../../client';
@@ -1,7 +1,7 @@
1
1
  import type { MessageComposer } from '../../messageComposer';
2
2
  import type { CommandResponse, UserResponse } from '../../../types';
3
3
  import type { TokenizationPayload } from './textMiddlewareUtils';
4
- import type { SearchSource } from '../../../search_controller';
4
+ import type { SearchSource } from '../../../search';
5
5
  import type { CustomTextComposerSuggestion } from '../../types.custom';
6
6
 
7
7
  export type TextComposerSuggestion<T = unknown> = T & {
@@ -83,7 +83,8 @@ export class PollComposer {
83
83
 
84
84
  get canCreatePoll() {
85
85
  const { data, errors } = this.state.getLatestValue();
86
- const hasAtLeastOneOption = data.options.filter((o) => !!o.text).length > 0;
86
+ const hasAtLeastOneNonEmptyOption =
87
+ data.options.filter((o) => !!o.text.trim()).length > 0;
87
88
  const hasName = !!data.name;
88
89
  const maxVotesAllowedNumber = parseInt(
89
90
  data.max_votes_allowed?.match(VALID_MAX_VOTES_VALUE_REGEX)?.[0] || '',
@@ -95,7 +96,7 @@ export class PollComposer {
95
96
  (2 <= maxVotesAllowedNumber || maxVotesAllowedNumber <= 10));
96
97
 
97
98
  return (
98
- hasAtLeastOneOption &&
99
+ hasAtLeastOneNonEmptyOption &&
99
100
  hasName &&
100
101
  validMaxVotesAllowed &&
101
102
  Object.values(errors).filter((errorText) => !!errorText).length === 0