stream-chat 9.42.2 → 9.43.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 (41) hide show
  1. package/dist/cjs/index.browser.js +1451 -1169
  2. package/dist/cjs/index.browser.js.map +3 -3
  3. package/dist/cjs/index.node.js +1452 -1171
  4. package/dist/cjs/index.node.js.map +3 -3
  5. package/dist/esm/index.mjs +1451 -1169
  6. package/dist/esm/index.mjs.map +3 -3
  7. package/dist/types/messageComposer/CustomDataManager.d.ts +3 -0
  8. package/dist/types/messageComposer/LocationComposer.d.ts +3 -0
  9. package/dist/types/messageComposer/MessageComposerEffectHandlers.d.ts +17 -0
  10. package/dist/types/messageComposer/attachmentManager.d.ts +7 -0
  11. package/dist/types/messageComposer/linkPreviewsManager.d.ts +4 -1
  12. package/dist/types/messageComposer/messageComposer.d.ts +39 -1
  13. package/dist/types/messageComposer/middleware/textComposer/TextComposerMiddlewareExecutor.d.ts +2 -1
  14. package/dist/types/messageComposer/middleware/textComposer/commandEffects.d.ts +5 -0
  15. package/dist/types/messageComposer/middleware/textComposer/commandUtils.d.ts +7 -0
  16. package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +2 -0
  17. package/dist/types/messageComposer/middleware/textComposer/index.d.ts +1 -0
  18. package/dist/types/messageComposer/middleware/textComposer/textMiddlewareUtils.d.ts +0 -34
  19. package/dist/types/messageComposer/middleware/textComposer/types.d.ts +13 -0
  20. package/dist/types/messageComposer/pollComposer.d.ts +3 -0
  21. package/dist/types/messageComposer/textComposer.d.ts +5 -1
  22. package/dist/types/types.d.ts +1 -1
  23. package/package.json +1 -1
  24. package/src/messageComposer/CustomDataManager.ts +8 -0
  25. package/src/messageComposer/LocationComposer.ts +8 -0
  26. package/src/messageComposer/MessageComposerEffectHandlers.ts +87 -0
  27. package/src/messageComposer/attachmentManager.ts +55 -0
  28. package/src/messageComposer/linkPreviewsManager.ts +12 -3
  29. package/src/messageComposer/messageComposer.ts +107 -0
  30. package/src/messageComposer/middleware/messageComposer/compositionValidation.ts +58 -18
  31. package/src/messageComposer/middleware/textComposer/TextComposerMiddlewareExecutor.ts +7 -1
  32. package/src/messageComposer/middleware/textComposer/commandEffects.ts +51 -0
  33. package/src/messageComposer/middleware/textComposer/commandStringExtraction.ts +1 -4
  34. package/src/messageComposer/middleware/textComposer/commandUtils.ts +48 -0
  35. package/src/messageComposer/middleware/textComposer/commands.ts +15 -7
  36. package/src/messageComposer/middleware/textComposer/index.ts +1 -0
  37. package/src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts +3 -46
  38. package/src/messageComposer/middleware/textComposer/types.ts +20 -0
  39. package/src/messageComposer/pollComposer.ts +8 -0
  40. package/src/messageComposer/textComposer.ts +54 -6
  41. package/src/types.ts +1 -7
@@ -131,6 +131,7 @@ __export(index_exports, {
131
131
  createActiveCommandGuardMiddleware: () => createActiveCommandGuardMiddleware,
132
132
  createAttachmentsCompositionMiddleware: () => createAttachmentsCompositionMiddleware,
133
133
  createBlockedAttachmentUploadNotificationMiddleware: () => createBlockedAttachmentUploadNotificationMiddleware,
134
+ createCommandEffectsMiddleware: () => createCommandEffectsMiddleware,
134
135
  createCommandInjectionMiddleware: () => createCommandInjectionMiddleware,
135
136
  createCommandStringExtractionMiddleware: () => createCommandStringExtractionMiddleware,
136
137
  createCommandsMiddleware: () => createCommandsMiddleware,
@@ -160,13 +161,11 @@ __export(index_exports, {
160
161
  defaultPollFieldChangeEventValidators: () => defaultPollFieldChangeEventValidators,
161
162
  encodeBase64: () => encodeBase64,
162
163
  ensureIsLocalAttachment: () => ensureIsLocalAttachment,
163
- escapeRegExp: () => escapeRegExp,
164
164
  extractPollData: () => extractPollData,
165
165
  extractPollEnrichedData: () => extractPollEnrichedData,
166
166
  formatMessage: () => formatMessage,
167
167
  generateFileName: () => generateFileName,
168
168
  getAttachmentTypeFromMimeType: () => getAttachmentTypeFromMimeType,
169
- getCompleteCommandInString: () => getCompleteCommandInString,
170
169
  getExtensionFromMimeType: () => getExtensionFromMimeType,
171
170
  getTokenizedSuggestionDisplayName: () => getTokenizedSuggestionDisplayName,
172
171
  getTriggerCharWithToken: () => getTriggerCharWithToken,
@@ -3255,9 +3254,52 @@ var _AttachmentManager = class _AttachmentManager {
3255
3254
  this.setCustomUploadFn = (doUploadRequest) => {
3256
3255
  this.composer.updateConfig({ attachments: { doUploadRequest } });
3257
3256
  };
3257
+ this.cancelAttachmentUploads = (attachments) => {
3258
+ for (const { localMetadata } of attachments) {
3259
+ this.client.uploadManager.deleteUploadRecord(localMetadata.id);
3260
+ }
3261
+ };
3262
+ this.normalizeSnapshotAttachment = (attachment) => {
3263
+ if (attachment.localMetadata.uploadState !== "uploading") return attachment;
3264
+ this.client.uploadManager.deleteUploadRecord(attachment.localMetadata.id);
3265
+ return {
3266
+ ...attachment,
3267
+ localMetadata: {
3268
+ ...attachment.localMetadata,
3269
+ uploadProgress: void 0,
3270
+ uploadState: "failed"
3271
+ }
3272
+ };
3273
+ };
3258
3274
  this.initState = ({ message } = {}) => {
3275
+ this.cancelAttachmentUploads(this.attachments);
3259
3276
  this.state.next(initState({ message }));
3260
3277
  };
3278
+ this.getSnapshot = () => {
3279
+ const state = this.state.getLatestValue();
3280
+ let hasUpdates = false;
3281
+ const attachments = state.attachments.map(this.normalizeSnapshotAttachment);
3282
+ for (let i = 0; i < attachments.length; i++) {
3283
+ if (attachments[i] !== state.attachments[i]) {
3284
+ hasUpdates = true;
3285
+ break;
3286
+ }
3287
+ }
3288
+ return hasUpdates ? { ...state, attachments } : state;
3289
+ };
3290
+ this.restoreSnapshot = (snapshot) => {
3291
+ this.cancelAttachmentUploads(this.attachments);
3292
+ this.state.next(snapshot);
3293
+ };
3294
+ this.setAttachments = (attachments) => {
3295
+ this.state.partialNext({ attachments });
3296
+ };
3297
+ this.clearAttachments = () => {
3298
+ if (!this.attachments.length) return;
3299
+ this.removeAttachments(
3300
+ this.attachments.map((attachment) => attachment.localMetadata.id)
3301
+ );
3302
+ };
3261
3303
  this.getAttachmentIndex = (localId) => {
3262
3304
  const attachmentsById = this.attachmentsById;
3263
3305
  return this.attachments.indexOf(attachmentsById[localId]);
@@ -3778,6 +3820,10 @@ var CustomDataManager = class {
3778
3820
  this.initState = ({ message } = {}) => {
3779
3821
  this.state.next(initState2({ composer: this.composer, message }));
3780
3822
  };
3823
+ this.getSnapshot = () => this.state.getLatestValue();
3824
+ this.restoreSnapshot = (snapshot) => {
3825
+ this.state.next(snapshot);
3826
+ };
3781
3827
  this.composer = composer;
3782
3828
  this.state = new StateStore(initState2({ composer, message }));
3783
3829
  }
@@ -3833,8 +3879,13 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
3833
3879
  constructor({ composer, message }) {
3834
3880
  this.shouldDiscardEnrichQueries = false;
3835
3881
  this.initState = ({ message } = {}) => {
3882
+ this.cancelURLEnrichment();
3836
3883
  this.state.next(initState3({ message: this.enabled ? message : void 0 }));
3837
3884
  };
3885
+ this.getSnapshot = () => this.state.getLatestValue();
3886
+ this.restoreSnapshot = (snapshot) => {
3887
+ this.state.next(snapshot);
3888
+ };
3838
3889
  this._findAndEnrichUrls = async (text) => {
3839
3890
  if (!this.enabled) return;
3840
3891
  const urls = this.config.findURLFn(text);
@@ -3887,8 +3938,8 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
3887
3938
  );
3888
3939
  };
3889
3940
  this.cancelURLEnrichment = () => {
3890
- this.findAndEnrichUrls.cancel();
3891
- this.findAndEnrichUrls.flush();
3941
+ this.findAndEnrichUrls.cancel?.();
3942
+ this.findAndEnrichUrls.flush?.();
3892
3943
  };
3893
3944
  /**
3894
3945
  * Clears all non-dismissed previews when the text composer is cleared.
@@ -4030,6 +4081,10 @@ var LocationComposer = class {
4030
4081
  this.initState = ({ message } = {}) => {
4031
4082
  this.state.next(initState4({ message }));
4032
4083
  };
4084
+ this.getSnapshot = () => this.state.getLatestValue();
4085
+ this.restoreSnapshot = (snapshot) => {
4086
+ this.state.next(snapshot);
4087
+ };
4033
4088
  this.setData = (data) => {
4034
4089
  if (!this.config.enabled) return;
4035
4090
  if (!data.latitude || !data.longitude) return;
@@ -4066,6 +4121,63 @@ var LocationComposer = class {
4066
4121
  }
4067
4122
  };
4068
4123
 
4124
+ // src/messageComposer/MessageComposerEffectHandlers.ts
4125
+ var applyCommandActivationEffect = (effect, composer) => {
4126
+ const snapshot = composer.getSnapshot();
4127
+ if (effect.stateToRestore) {
4128
+ snapshot.textComposer = {
4129
+ ...snapshot.textComposer,
4130
+ ...effect.stateToRestore,
4131
+ command: null
4132
+ };
4133
+ }
4134
+ composer.captureSnapshot(snapshot);
4135
+ composer.textComposer.state.next({
4136
+ command: effect.command,
4137
+ mentionedUsers: [],
4138
+ suggestions: void 0,
4139
+ selection: { start: 0, end: 0 },
4140
+ text: ""
4141
+ });
4142
+ composer.attachmentManager.initState();
4143
+ composer.linkPreviewsManager.initState();
4144
+ composer.locationComposer.initState();
4145
+ composer.pollComposer.initState();
4146
+ composer.customDataManager.initState();
4147
+ };
4148
+ var applyCommandClearEffect = (_, composer) => {
4149
+ const snapshot = composer.popSnapshot();
4150
+ if (!snapshot) return;
4151
+ composer.restoreSnapshot(snapshot);
4152
+ };
4153
+ var MessageComposerEffectHandlers = class {
4154
+ constructor(options) {
4155
+ this.options = options;
4156
+ this.handlers = /* @__PURE__ */ new Map();
4157
+ this.registerDefaultHandlers = () => {
4158
+ this.registerEffectHandler(
4159
+ "command.activate",
4160
+ applyCommandActivationEffect
4161
+ );
4162
+ this.registerEffectHandler(
4163
+ "command.clear",
4164
+ applyCommandClearEffect
4165
+ );
4166
+ };
4167
+ this.registerEffectHandler = (type, handler) => {
4168
+ this.handlers.set(type, handler);
4169
+ };
4170
+ this.applyEffects = (effects = []) => {
4171
+ effects.forEach((effect) => this.applyEffect(effect));
4172
+ };
4173
+ this.applyEffect = (effect) => {
4174
+ const handler = this.handlers.get(effect.type);
4175
+ handler?.(effect, this.options.composer);
4176
+ };
4177
+ this.registerDefaultHandlers();
4178
+ }
4179
+ };
4180
+
4069
4181
  // src/messageComposer/middleware/pollComposer/state.ts
4070
4182
  var VALID_MAX_VOTES_VALUE_REGEX = /^([2-9]|10)$/;
4071
4183
  var MAX_POLL_OPTIONS = 100;
@@ -4356,6 +4468,10 @@ var PollComposer = class {
4356
4468
  this.initState = () => {
4357
4469
  this.state.next(this.initialState);
4358
4470
  };
4471
+ this.getSnapshot = () => this.state.getLatestValue();
4472
+ this.restoreSnapshot = (snapshot) => {
4473
+ this.state.next(snapshot);
4474
+ };
4359
4475
  /**
4360
4476
  * Updates specified fields and generates relevant errors
4361
4477
  * @param data
@@ -4618,667 +4734,239 @@ var createDraftCustomDataCompositionMiddleware = (composer) => ({
4618
4734
  }
4619
4735
  });
4620
4736
 
4621
- // src/messageComposer/middleware/messageComposer/compositionValidation.ts
4622
- var createCompositionValidationMiddleware = (composer) => ({
4623
- id: "stream-io/message-composer-middleware/data-validation",
4624
- handlers: {
4625
- compose: async ({
4626
- state,
4627
- discard,
4628
- forward
4629
- }) => {
4630
- const { maxLengthOnSend } = composer.config.text ?? {};
4631
- const inputText = state.message.text ?? "";
4632
- const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
4633
- if (composer.compositionIsEmpty || hasExceededMaxLength) {
4634
- return await discard();
4635
- }
4636
- return await forward();
4637
- }
4737
+ // src/errors.ts
4738
+ var APIErrorCodes = {
4739
+ "-1": { name: "InternalSystemError", retryable: true },
4740
+ "2": { name: "AccessKeyError", retryable: false },
4741
+ "3": { name: "AuthenticationFailedError", retryable: true },
4742
+ "4": { name: "InputError", retryable: false },
4743
+ "6": { name: "DuplicateUsernameError", retryable: false },
4744
+ "9": { name: "RateLimitError", retryable: true },
4745
+ "16": { name: "DoesNotExistError", retryable: false },
4746
+ "17": { name: "NotAllowedError", retryable: false },
4747
+ "18": { name: "EventNotSupportedError", retryable: false },
4748
+ "19": { name: "ChannelFeatureNotSupportedError", retryable: false },
4749
+ "20": { name: "MessageTooLongError", retryable: false },
4750
+ "21": { name: "MultipleNestingLevelError", retryable: false },
4751
+ "22": { name: "PayloadTooBigError", retryable: false },
4752
+ "23": { name: "RequestTimeoutError", retryable: true },
4753
+ "24": { name: "MaxHeaderSizeExceededError", retryable: false },
4754
+ "40": { name: "AuthErrorTokenExpired", retryable: false },
4755
+ "41": { name: "AuthErrorTokenNotValidYet", retryable: false },
4756
+ "42": { name: "AuthErrorTokenUsedBeforeIssuedAt", retryable: false },
4757
+ "43": { name: "AuthErrorTokenSignatureInvalid", retryable: false },
4758
+ "44": { name: "CustomCommandEndpointMissingError", retryable: false },
4759
+ "45": { name: "CustomCommandEndpointCallError", retryable: true },
4760
+ "46": { name: "ConnectionIDNotFoundError", retryable: false },
4761
+ "60": { name: "CoolDownError", retryable: true },
4762
+ "69": { name: "ErrWrongRegion", retryable: false },
4763
+ "70": { name: "ErrQueryChannelPermissions", retryable: false },
4764
+ "71": { name: "ErrTooManyConnections", retryable: true },
4765
+ "99": { name: "AppSuspendedError", retryable: false }
4766
+ };
4767
+ function isAPIError(error) {
4768
+ return error.code !== void 0;
4769
+ }
4770
+ function isErrorRetryable(error) {
4771
+ if (!error.code) return false;
4772
+ const err = APIErrorCodes[`${error.code}`];
4773
+ if (!err) return false;
4774
+ return err.retryable;
4775
+ }
4776
+ function isConnectionIDError(error) {
4777
+ return error.code === 46;
4778
+ }
4779
+ function isWSFailure(err) {
4780
+ if (typeof err.isWSFailure === "boolean") {
4781
+ return err.isWSFailure;
4638
4782
  }
4639
- });
4640
- var createDraftCompositionValidationMiddleware = (composer) => ({
4641
- id: "stream-io/message-composer-middleware/draft-data-validation",
4642
- handlers: {
4643
- compose: async ({
4644
- state,
4645
- discard,
4646
- forward
4647
- }) => {
4648
- const hasData = !textIsEmpty(state.draft.text ?? "") || state.draft.attachments?.length || state.draft.poll_id || state.draft.quoted_message_id;
4649
- const shouldCreateDraft = composer.lastChangeOriginIsLocal && hasData;
4650
- if (!shouldCreateDraft) {
4651
- return await discard();
4652
- }
4653
- return await forward();
4654
- }
4783
+ try {
4784
+ return JSON.parse(err.message).isWSFailure;
4785
+ } catch (_) {
4786
+ return false;
4655
4787
  }
4656
- });
4788
+ }
4789
+ function isErrorResponse(res) {
4790
+ return !res.status || res.status < 200 || 300 <= res.status;
4791
+ }
4657
4792
 
4658
- // src/messageComposer/middleware/messageComposer/linkPreviews.ts
4659
- var createLinkPreviewsCompositionMiddleware = (composer) => ({
4660
- id: "stream-io/message-composer-middleware/link-previews",
4661
- handlers: {
4662
- compose: ({
4663
- state,
4664
- next,
4665
- forward
4666
- }) => {
4667
- const { linkPreviewsManager } = composer;
4668
- if (!linkPreviewsManager) return forward();
4669
- linkPreviewsManager.cancelURLEnrichment();
4670
- const someLinkPreviewsLoading = linkPreviewsManager.loadingPreviews.length > 0;
4671
- const someLinkPreviewsDismissed = linkPreviewsManager.dismissedPreviews.length > 0;
4672
- const linkPreviews = linkPreviewsManager.loadingPreviews.length > 0 ? [] : linkPreviewsManager.loadedPreviews.map(
4673
- (preview) => LinkPreviewsManager.getPreviewData(preview)
4674
- );
4675
- const attachments = (state.message.attachments ?? []).concat(
4676
- linkPreviews
4677
- );
4678
- if (!attachments.length) return forward();
4679
- const sendOptions = { ...state.sendOptions };
4680
- const skip_enrich_url = !someLinkPreviewsLoading && linkPreviews.length > 0 || someLinkPreviewsDismissed;
4681
- if (skip_enrich_url) {
4682
- sendOptions.skip_enrich_url = true;
4683
- }
4684
- return next({
4685
- ...state,
4686
- message: {
4687
- ...state.message,
4688
- attachments
4689
- },
4690
- localMessage: {
4691
- ...state.localMessage,
4692
- attachments
4693
- },
4694
- sendOptions
4695
- });
4696
- }
4793
+ // src/search/BaseSearchSource.ts
4794
+ var DEFAULT_SEARCH_SOURCE_OPTIONS = {
4795
+ debounceMs: 300,
4796
+ pageSize: 10
4797
+ };
4798
+ var BaseSearchSourceBase = class {
4799
+ constructor(options) {
4800
+ this.activate = () => {
4801
+ if (this.isActive) return;
4802
+ this.state.partialNext({ isActive: true });
4803
+ };
4804
+ this.deactivate = () => {
4805
+ if (!this.isActive) return;
4806
+ this.state.partialNext({ isActive: false });
4807
+ };
4808
+ this.canExecuteQuery = (newSearchString) => {
4809
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
4810
+ const searchString = newSearchString ?? this.searchQuery;
4811
+ return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && searchString);
4812
+ };
4813
+ const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4814
+ this.pageSize = pageSize;
4815
+ this.state = new StateStore(this.initialState);
4697
4816
  }
4698
- });
4699
- var createDraftLinkPreviewsCompositionMiddleware = (composer) => ({
4700
- id: "stream-io/message-composer-middleware/draft-link-previews",
4701
- handlers: {
4702
- compose: ({
4703
- state,
4704
- next,
4705
- forward
4706
- }) => {
4707
- const { linkPreviewsManager } = composer;
4708
- if (!linkPreviewsManager) return forward();
4709
- linkPreviewsManager.cancelURLEnrichment();
4710
- const linkPreviews = linkPreviewsManager.loadedPreviews.map(
4711
- (preview) => LinkPreviewsManager.getPreviewData(preview)
4712
- );
4713
- if (!linkPreviews.length) return forward();
4714
- return next({
4715
- ...state,
4716
- draft: {
4717
- ...state.draft,
4718
- attachments: (state.draft.attachments ?? []).concat(linkPreviews)
4719
- }
4720
- });
4721
- }
4817
+ get lastQueryError() {
4818
+ return this.state.getLatestValue().lastQueryError;
4722
4819
  }
4723
- });
4724
-
4725
- // src/messageComposer/middleware/messageComposer/textComposer.ts
4726
- var createTextComposerCompositionMiddleware = (composer) => ({
4727
- id: "stream-io/message-composer-middleware/text-composition",
4728
- handlers: {
4729
- compose: ({
4730
- state,
4731
- next,
4732
- forward
4733
- }) => {
4734
- if (!composer.textComposer) return forward();
4735
- const { mentionedUsers, text } = composer.textComposer;
4736
- const mentioned_users = Array.from(
4737
- new Set(
4738
- mentionedUsers.filter(
4739
- ({ id, name }) => text.includes(`@${id}`) || text.includes(`@${name}`)
4740
- )
4741
- )
4742
- );
4743
- if (!text && mentioned_users.length === 0) return forward();
4744
- return next({
4745
- ...state,
4746
- localMessage: {
4747
- ...state.localMessage,
4748
- mentioned_users,
4749
- text
4750
- },
4751
- message: {
4752
- ...state.message,
4753
- mentioned_users: mentioned_users.map((u) => u.id),
4754
- text
4755
- }
4756
- });
4757
- }
4820
+ get hasNext() {
4821
+ return this.state.getLatestValue().hasNext;
4758
4822
  }
4759
- });
4760
- var createDraftTextComposerCompositionMiddleware = (composer) => ({
4761
- id: "stream-io/message-composer-middleware/draft-text-composition",
4762
- handlers: {
4763
- compose: ({
4764
- state,
4765
- next,
4766
- forward
4767
- }) => {
4768
- if (!composer.textComposer) return forward();
4769
- const { maxLengthOnSend } = composer.config.text ?? {};
4770
- const { mentionedUsers, text: inputText } = composer.textComposer;
4771
- const mentioned_users = mentionedUsers.length ? Array.from(
4772
- new Set(
4773
- mentionedUsers.filter(
4774
- ({ id, name }) => inputText.includes(`@${id}`) || inputText.includes(`@${name}`)
4775
- )
4776
- )
4777
- ) : void 0;
4778
- const text = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend ? inputText.slice(0, maxLengthOnSend) : inputText;
4779
- return next({
4780
- ...state,
4781
- draft: {
4782
- ...state.draft,
4783
- mentioned_users: mentioned_users?.map((u) => u.id),
4784
- text
4785
- }
4786
- });
4823
+ get hasResults() {
4824
+ return Array.isArray(this.state.getLatestValue().items);
4825
+ }
4826
+ get isActive() {
4827
+ return this.state.getLatestValue().isActive;
4828
+ }
4829
+ get isLoading() {
4830
+ return this.state.getLatestValue().isLoading;
4831
+ }
4832
+ get initialState() {
4833
+ return {
4834
+ hasNext: true,
4835
+ isActive: false,
4836
+ isLoading: false,
4837
+ items: void 0,
4838
+ lastQueryError: void 0,
4839
+ next: void 0,
4840
+ offset: 0,
4841
+ searchQuery: ""
4842
+ };
4843
+ }
4844
+ get items() {
4845
+ return this.state.getLatestValue().items;
4846
+ }
4847
+ get next() {
4848
+ return this.state.getLatestValue().next;
4849
+ }
4850
+ get offset() {
4851
+ return this.state.getLatestValue().offset;
4852
+ }
4853
+ get searchQuery() {
4854
+ return this.state.getLatestValue().searchQuery;
4855
+ }
4856
+ getStateBeforeFirstQuery(newSearchString) {
4857
+ return {
4858
+ ...this.initialState,
4859
+ isActive: this.isActive,
4860
+ isLoading: true,
4861
+ searchQuery: newSearchString
4862
+ };
4863
+ }
4864
+ getStateAfterQuery(stateUpdate, isFirstPage) {
4865
+ const current = this.state.getLatestValue();
4866
+ return {
4867
+ ...current,
4868
+ lastQueryError: void 0,
4869
+ // reset lastQueryError that can be overridden by the stateUpdate
4870
+ ...stateUpdate,
4871
+ isLoading: false,
4872
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
4873
+ };
4874
+ }
4875
+ prepareStateForQuery(newSearchString) {
4876
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
4877
+ const searchString = newSearchString ?? this.searchQuery;
4878
+ if (hasNewSearchQuery) {
4879
+ this.state.next(this.getStateBeforeFirstQuery(newSearchString ?? ""));
4880
+ } else {
4881
+ this.state.partialNext({ isLoading: true });
4787
4882
  }
4883
+ return { searchString, hasNewSearchQuery };
4788
4884
  }
4789
- });
4790
-
4791
- // src/messageComposer/middleware/messageComposer/messageComposerState.ts
4792
- var createMessageComposerStateCompositionMiddleware = (composer) => ({
4793
- id: "stream-io/message-composer-middleware/own-state",
4794
- handlers: {
4795
- compose: ({
4796
- state,
4797
- next
4798
- }) => {
4799
- const payload = {};
4800
- if (composer.quotedMessage) {
4801
- payload.quoted_message_id = composer.quotedMessage.id;
4802
- }
4803
- if (composer.pollId) {
4804
- payload.poll_id = composer.pollId;
4805
- }
4806
- if (composer.showReplyInChannel) {
4807
- payload.show_in_channel = true;
4808
- }
4809
- return next({
4810
- ...state,
4811
- localMessage: {
4812
- ...state.localMessage,
4813
- ...payload,
4814
- quoted_message: composer.quotedMessage ?? void 0
4815
- },
4816
- message: {
4817
- ...state.message,
4818
- ...payload
4819
- }
4820
- });
4885
+ updatePaginationStateFromQuery(result) {
4886
+ const { items, next } = result;
4887
+ const stateUpdate = {};
4888
+ if (Object.prototype.hasOwnProperty.call(result, "next")) {
4889
+ stateUpdate.next = next;
4890
+ stateUpdate.hasNext = !!next;
4891
+ } else {
4892
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
4893
+ stateUpdate.hasNext = items.length === this.pageSize;
4821
4894
  }
4895
+ return stateUpdate;
4822
4896
  }
4823
- });
4824
- var createDraftMessageComposerStateCompositionMiddleware = (composer) => ({
4825
- id: "stream-io/message-composer-middleware/draft-own-state",
4826
- handlers: {
4827
- compose: ({
4828
- state,
4829
- next
4830
- }) => {
4831
- const payload = {};
4832
- if (composer.quotedMessage) {
4833
- payload.quoted_message_id = composer.quotedMessage.id;
4834
- }
4835
- if (composer.pollId) {
4836
- payload.poll_id = composer.pollId;
4837
- }
4838
- if (composer.showReplyInChannel) {
4839
- payload.show_in_channel = true;
4897
+ resetState() {
4898
+ this.state.next(this.initialState);
4899
+ }
4900
+ resetStateAndActivate() {
4901
+ this.resetState();
4902
+ this.activate();
4903
+ }
4904
+ };
4905
+ var BaseSearchSource = class extends BaseSearchSourceBase {
4906
+ constructor(options) {
4907
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4908
+ super(options);
4909
+ this.setDebounceOptions = ({ debounceMs }) => {
4910
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4911
+ };
4912
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4913
+ this.setDebounceOptions({ debounceMs });
4914
+ }
4915
+ async executeQuery(newSearchString) {
4916
+ if (!this.canExecuteQuery(newSearchString)) return;
4917
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4918
+ let stateUpdate = {};
4919
+ try {
4920
+ const results = await this.query(searchString);
4921
+ if (!results) return;
4922
+ const { items } = results;
4923
+ stateUpdate = this.updatePaginationStateFromQuery(results);
4924
+ stateUpdate.items = await this.filterQueryResults(items);
4925
+ } catch (e) {
4926
+ stateUpdate.lastQueryError = e;
4927
+ if (isAPIError(e) && !isErrorRetryable(e)) {
4928
+ stateUpdate.hasNext = false;
4840
4929
  }
4841
- return next({
4842
- ...state,
4843
- draft: {
4844
- ...state.draft,
4845
- ...payload
4846
- }
4847
- });
4930
+ } finally {
4931
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4848
4932
  }
4849
4933
  }
4850
- });
4851
-
4852
- // src/messageComposer/middleware/messageComposer/userDataInjection.ts
4853
- var createUserDataInjectionMiddleware = (composer) => ({
4854
- id: "stream-io/message-composer-middleware/user-data-injection",
4855
- handlers: {
4856
- compose: ({
4857
- state,
4858
- next,
4859
- forward
4860
- }) => {
4861
- if (!composer.client.user) {
4862
- return forward();
4934
+ cancelScheduledQuery() {
4935
+ this.searchDebounced.cancel();
4936
+ }
4937
+ };
4938
+ var BaseSearchSourceSync = class extends BaseSearchSourceBase {
4939
+ constructor(options) {
4940
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4941
+ super(options);
4942
+ this.setDebounceOptions = ({ debounceMs }) => {
4943
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4944
+ };
4945
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4946
+ this.setDebounceOptions({ debounceMs });
4947
+ }
4948
+ executeQuery(newSearchString) {
4949
+ if (!this.canExecuteQuery(newSearchString)) return;
4950
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4951
+ let stateUpdate = {};
4952
+ try {
4953
+ const results = this.query(searchString);
4954
+ if (!results) return;
4955
+ const { items } = results;
4956
+ stateUpdate = this.updatePaginationStateFromQuery(results);
4957
+ stateUpdate.items = this.filterQueryResults(items);
4958
+ } catch (e) {
4959
+ stateUpdate.lastQueryError = e;
4960
+ if (isAPIError(e) && !isErrorRetryable(e)) {
4961
+ stateUpdate.hasNext = false;
4863
4962
  }
4864
- const { channel_mutes, devices, mutes, ...messageUser } = composer.client.user;
4865
- return next({
4866
- ...state,
4867
- localMessage: {
4868
- ...state.localMessage,
4869
- user: messageUser,
4870
- user_id: messageUser.id
4871
- }
4872
- });
4963
+ } finally {
4964
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4873
4965
  }
4874
4966
  }
4875
- });
4876
-
4877
- // src/messageComposer/middleware/messageComposer/pollOnly.ts
4878
- var pollLocalMessageNullifiedFields = {
4879
- attachments: [],
4880
- mentioned_users: [],
4881
- parent_id: void 0,
4882
- quoted_message: void 0,
4883
- text: ""
4884
- };
4885
- var createPollOnlyCompositionMiddleware = (composer) => ({
4886
- id: "stream-io/message-composer-middleware/poll-only",
4887
- handlers: {
4888
- compose: ({
4889
- state,
4890
- complete,
4891
- forward
4892
- }) => {
4893
- const pollId = composer.pollId;
4894
- const isEditingMessage = !!composer.editedMessage;
4895
- const isComposingThreadReply = !!composer.threadId;
4896
- if (!pollId || isComposingThreadReply || isEditingMessage) return forward();
4897
- return complete({
4898
- ...state,
4899
- localMessage: {
4900
- ...state.localMessage,
4901
- ...pollLocalMessageNullifiedFields,
4902
- poll_id: pollId
4903
- },
4904
- message: {
4905
- id: state.localMessage.id,
4906
- poll_id: pollId
4907
- }
4908
- });
4909
- }
4910
- }
4911
- });
4912
-
4913
- // src/messageComposer/middleware/messageComposer/sharedLocation.ts
4914
- var createSharedLocationCompositionMiddleware = (composer) => ({
4915
- id: "stream-io/message-composer-middleware/shared-location",
4916
- handlers: {
4917
- compose: ({
4918
- state,
4919
- next,
4920
- forward
4921
- }) => {
4922
- const { locationComposer } = composer;
4923
- const location = locationComposer.validLocation;
4924
- if (!locationComposer || !location || !composer.client.user) return forward();
4925
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4926
- return next({
4927
- ...state,
4928
- localMessage: {
4929
- ...state.localMessage,
4930
- shared_location: {
4931
- ...location,
4932
- channel_cid: composer.channel.cid,
4933
- created_at: timestamp,
4934
- updated_at: timestamp,
4935
- user_id: composer.client.user.id
4936
- }
4937
- },
4938
- message: {
4939
- ...state.message,
4940
- shared_location: location
4941
- }
4942
- });
4943
- }
4944
- }
4945
- });
4946
-
4947
- // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
4948
- var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
4949
- constructor({ composer }) {
4950
- super();
4951
- this.use([
4952
- createUserDataInjectionMiddleware(composer),
4953
- createPollOnlyCompositionMiddleware(composer),
4954
- createTextComposerCompositionMiddleware(composer),
4955
- createAttachmentsCompositionMiddleware(composer),
4956
- createLinkPreviewsCompositionMiddleware(composer),
4957
- createSharedLocationCompositionMiddleware(composer),
4958
- createMessageComposerStateCompositionMiddleware(composer),
4959
- createCustomDataCompositionMiddleware(composer),
4960
- createCompositionValidationMiddleware(composer),
4961
- createCompositionDataCleanupMiddleware(composer)
4962
- ]);
4963
- }
4964
- };
4965
- var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
4966
- constructor({ composer }) {
4967
- super();
4968
- this.use([
4969
- createDraftTextComposerCompositionMiddleware(composer),
4970
- createDraftAttachmentsCompositionMiddleware(composer),
4971
- createDraftLinkPreviewsCompositionMiddleware(composer),
4972
- createDraftMessageComposerStateCompositionMiddleware(composer),
4973
- createDraftCustomDataCompositionMiddleware(composer),
4974
- createDraftCompositionValidationMiddleware(composer)
4975
- ]);
4976
- }
4977
- };
4978
-
4979
- // src/messageComposer/middleware/messageComposer/commandInjection.ts
4980
- var createCommandInjectionMiddleware = (composer) => ({
4981
- handlers: {
4982
- compose: ({
4983
- forward,
4984
- next,
4985
- state
4986
- }) => {
4987
- const command = composer.textComposer.command;
4988
- if (!command) {
4989
- return forward();
4990
- }
4991
- const { text } = state.localMessage;
4992
- const injection = `/${command?.name}`;
4993
- const enrichedText = `${injection} ${text}`;
4994
- return next({
4995
- ...state,
4996
- localMessage: {
4997
- ...state.localMessage,
4998
- text: enrichedText
4999
- },
5000
- message: {
5001
- ...state.message,
5002
- text: enrichedText
5003
- }
5004
- });
5005
- }
5006
- },
5007
- id: "stream-io/message-composer-middleware/command-string-injection"
5008
- });
5009
- var createDraftCommandInjectionMiddleware = (composer) => ({
5010
- handlers: {
5011
- compose: ({
5012
- forward,
5013
- next,
5014
- state
5015
- }) => {
5016
- const command = composer.textComposer.command;
5017
- if (!command) {
5018
- return forward();
5019
- }
5020
- const { text } = state.draft;
5021
- const injection = `/${command?.name}`;
5022
- const enrichedText = `${injection} ${text}`;
5023
- return next({
5024
- ...state,
5025
- draft: {
5026
- ...state.draft,
5027
- text: enrichedText
5028
- }
5029
- });
5030
- }
5031
- },
5032
- id: "stream-io/message-composer-middleware/draft-command-string-injection"
5033
- });
5034
-
5035
- // src/messageComposer/middleware/textComposer/activeCommandGuard.ts
5036
- var createActiveCommandGuardMiddleware = () => ({
5037
- handlers: {
5038
- onChange: ({ complete, forward, state }) => {
5039
- if (state.command) {
5040
- return complete(state);
5041
- }
5042
- return forward();
5043
- },
5044
- onSuggestionItemSelect: ({ forward }) => forward()
5045
- },
5046
- id: "stream-io/text-composer/active-command-guard"
5047
- });
5048
-
5049
- // src/errors.ts
5050
- var APIErrorCodes = {
5051
- "-1": { name: "InternalSystemError", retryable: true },
5052
- "2": { name: "AccessKeyError", retryable: false },
5053
- "3": { name: "AuthenticationFailedError", retryable: true },
5054
- "4": { name: "InputError", retryable: false },
5055
- "6": { name: "DuplicateUsernameError", retryable: false },
5056
- "9": { name: "RateLimitError", retryable: true },
5057
- "16": { name: "DoesNotExistError", retryable: false },
5058
- "17": { name: "NotAllowedError", retryable: false },
5059
- "18": { name: "EventNotSupportedError", retryable: false },
5060
- "19": { name: "ChannelFeatureNotSupportedError", retryable: false },
5061
- "20": { name: "MessageTooLongError", retryable: false },
5062
- "21": { name: "MultipleNestingLevelError", retryable: false },
5063
- "22": { name: "PayloadTooBigError", retryable: false },
5064
- "23": { name: "RequestTimeoutError", retryable: true },
5065
- "24": { name: "MaxHeaderSizeExceededError", retryable: false },
5066
- "40": { name: "AuthErrorTokenExpired", retryable: false },
5067
- "41": { name: "AuthErrorTokenNotValidYet", retryable: false },
5068
- "42": { name: "AuthErrorTokenUsedBeforeIssuedAt", retryable: false },
5069
- "43": { name: "AuthErrorTokenSignatureInvalid", retryable: false },
5070
- "44": { name: "CustomCommandEndpointMissingError", retryable: false },
5071
- "45": { name: "CustomCommandEndpointCallError", retryable: true },
5072
- "46": { name: "ConnectionIDNotFoundError", retryable: false },
5073
- "60": { name: "CoolDownError", retryable: true },
5074
- "69": { name: "ErrWrongRegion", retryable: false },
5075
- "70": { name: "ErrQueryChannelPermissions", retryable: false },
5076
- "71": { name: "ErrTooManyConnections", retryable: true },
5077
- "99": { name: "AppSuspendedError", retryable: false }
5078
- };
5079
- function isAPIError(error) {
5080
- return error.code !== void 0;
5081
- }
5082
- function isErrorRetryable(error) {
5083
- if (!error.code) return false;
5084
- const err = APIErrorCodes[`${error.code}`];
5085
- if (!err) return false;
5086
- return err.retryable;
5087
- }
5088
- function isConnectionIDError(error) {
5089
- return error.code === 46;
5090
- }
5091
- function isWSFailure(err) {
5092
- if (typeof err.isWSFailure === "boolean") {
5093
- return err.isWSFailure;
5094
- }
5095
- try {
5096
- return JSON.parse(err.message).isWSFailure;
5097
- } catch (_) {
5098
- return false;
5099
- }
5100
- }
5101
- function isErrorResponse(res) {
5102
- return !res.status || res.status < 200 || 300 <= res.status;
5103
- }
5104
-
5105
- // src/search/BaseSearchSource.ts
5106
- var DEFAULT_SEARCH_SOURCE_OPTIONS = {
5107
- debounceMs: 300,
5108
- pageSize: 10
5109
- };
5110
- var BaseSearchSourceBase = class {
5111
- constructor(options) {
5112
- this.activate = () => {
5113
- if (this.isActive) return;
5114
- this.state.partialNext({ isActive: true });
5115
- };
5116
- this.deactivate = () => {
5117
- if (!this.isActive) return;
5118
- this.state.partialNext({ isActive: false });
5119
- };
5120
- this.canExecuteQuery = (newSearchString) => {
5121
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5122
- const searchString = newSearchString ?? this.searchQuery;
5123
- return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && searchString);
5124
- };
5125
- const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5126
- this.pageSize = pageSize;
5127
- this.state = new StateStore(this.initialState);
5128
- }
5129
- get lastQueryError() {
5130
- return this.state.getLatestValue().lastQueryError;
5131
- }
5132
- get hasNext() {
5133
- return this.state.getLatestValue().hasNext;
5134
- }
5135
- get hasResults() {
5136
- return Array.isArray(this.state.getLatestValue().items);
5137
- }
5138
- get isActive() {
5139
- return this.state.getLatestValue().isActive;
5140
- }
5141
- get isLoading() {
5142
- return this.state.getLatestValue().isLoading;
5143
- }
5144
- get initialState() {
5145
- return {
5146
- hasNext: true,
5147
- isActive: false,
5148
- isLoading: false,
5149
- items: void 0,
5150
- lastQueryError: void 0,
5151
- next: void 0,
5152
- offset: 0,
5153
- searchQuery: ""
5154
- };
5155
- }
5156
- get items() {
5157
- return this.state.getLatestValue().items;
5158
- }
5159
- get next() {
5160
- return this.state.getLatestValue().next;
5161
- }
5162
- get offset() {
5163
- return this.state.getLatestValue().offset;
5164
- }
5165
- get searchQuery() {
5166
- return this.state.getLatestValue().searchQuery;
5167
- }
5168
- getStateBeforeFirstQuery(newSearchString) {
5169
- return {
5170
- ...this.initialState,
5171
- isActive: this.isActive,
5172
- isLoading: true,
5173
- searchQuery: newSearchString
5174
- };
5175
- }
5176
- getStateAfterQuery(stateUpdate, isFirstPage) {
5177
- const current = this.state.getLatestValue();
5178
- return {
5179
- ...current,
5180
- lastQueryError: void 0,
5181
- // reset lastQueryError that can be overridden by the stateUpdate
5182
- ...stateUpdate,
5183
- isLoading: false,
5184
- items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5185
- };
5186
- }
5187
- prepareStateForQuery(newSearchString) {
5188
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5189
- const searchString = newSearchString ?? this.searchQuery;
5190
- if (hasNewSearchQuery) {
5191
- this.state.next(this.getStateBeforeFirstQuery(newSearchString ?? ""));
5192
- } else {
5193
- this.state.partialNext({ isLoading: true });
5194
- }
5195
- return { searchString, hasNewSearchQuery };
5196
- }
5197
- updatePaginationStateFromQuery(result) {
5198
- const { items, next } = result;
5199
- const stateUpdate = {};
5200
- if (Object.prototype.hasOwnProperty.call(result, "next")) {
5201
- stateUpdate.next = next;
5202
- stateUpdate.hasNext = !!next;
5203
- } else {
5204
- stateUpdate.offset = (this.offset ?? 0) + items.length;
5205
- stateUpdate.hasNext = items.length === this.pageSize;
5206
- }
5207
- return stateUpdate;
5208
- }
5209
- resetState() {
5210
- this.state.next(this.initialState);
5211
- }
5212
- resetStateAndActivate() {
5213
- this.resetState();
5214
- this.activate();
5215
- }
5216
- };
5217
- var BaseSearchSource = class extends BaseSearchSourceBase {
5218
- constructor(options) {
5219
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5220
- super(options);
5221
- this.setDebounceOptions = ({ debounceMs }) => {
5222
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5223
- };
5224
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
5225
- this.setDebounceOptions({ debounceMs });
5226
- }
5227
- async executeQuery(newSearchString) {
5228
- if (!this.canExecuteQuery(newSearchString)) return;
5229
- const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
5230
- let stateUpdate = {};
5231
- try {
5232
- const results = await this.query(searchString);
5233
- if (!results) return;
5234
- const { items } = results;
5235
- stateUpdate = this.updatePaginationStateFromQuery(results);
5236
- stateUpdate.items = await this.filterQueryResults(items);
5237
- } catch (e) {
5238
- stateUpdate.lastQueryError = e;
5239
- if (isAPIError(e) && !isErrorRetryable(e)) {
5240
- stateUpdate.hasNext = false;
5241
- }
5242
- } finally {
5243
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
5244
- }
5245
- }
5246
- cancelScheduledQuery() {
5247
- this.searchDebounced.cancel();
5248
- }
5249
- };
5250
- var BaseSearchSourceSync = class extends BaseSearchSourceBase {
5251
- constructor(options) {
5252
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5253
- super(options);
5254
- this.setDebounceOptions = ({ debounceMs }) => {
5255
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5256
- };
5257
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
5258
- this.setDebounceOptions({ debounceMs });
5259
- }
5260
- executeQuery(newSearchString) {
5261
- if (!this.canExecuteQuery(newSearchString)) return;
5262
- const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
5263
- let stateUpdate = {};
5264
- try {
5265
- const results = this.query(searchString);
5266
- if (!results) return;
5267
- const { items } = results;
5268
- stateUpdate = this.updatePaginationStateFromQuery(results);
5269
- stateUpdate.items = this.filterQueryResults(items);
5270
- } catch (e) {
5271
- stateUpdate.lastQueryError = e;
5272
- if (isAPIError(e) && !isErrorRetryable(e)) {
5273
- stateUpdate.hasNext = false;
5274
- }
5275
- } finally {
5276
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
5277
- }
5278
- }
5279
- cancelScheduledQuery() {
5280
- this.searchDebounced.cancel();
5281
- }
4967
+ cancelScheduledQuery() {
4968
+ this.searchDebounced.cancel();
4969
+ }
5282
4970
  };
5283
4971
 
5284
4972
  // src/search/SearchController.ts
@@ -5374,586 +5062,1095 @@ var SearchController = class {
5374
5062
  get activeSources() {
5375
5063
  return this.state.getLatestValue().sources.filter((s) => s.isActive);
5376
5064
  }
5377
- get isActive() {
5378
- return this.state.getLatestValue().isActive;
5065
+ get isActive() {
5066
+ return this.state.getLatestValue().isActive;
5067
+ }
5068
+ get searchQuery() {
5069
+ return this.state.getLatestValue().searchQuery;
5070
+ }
5071
+ get searchSourceTypes() {
5072
+ return this.sources.map((s) => s.type);
5073
+ }
5074
+ };
5075
+
5076
+ // src/pagination/BasePaginator.ts
5077
+ var DEFAULT_PAGINATION_OPTIONS = {
5078
+ debounceMs: 300,
5079
+ pageSize: 10
5080
+ };
5081
+ var BasePaginator = class {
5082
+ constructor(options) {
5083
+ this._isCursorPagination = false;
5084
+ this.setDebounceOptions = ({ debounceMs }) => {
5085
+ this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5086
+ };
5087
+ this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
5088
+ this.next = () => this.executeQuery({ direction: "next" });
5089
+ this.prev = () => this.executeQuery({ direction: "prev" });
5090
+ this.nextDebounced = () => {
5091
+ this._executeQueryDebounced({ direction: "next" });
5092
+ };
5093
+ this.prevDebounced = () => {
5094
+ this._executeQueryDebounced({ direction: "prev" });
5095
+ };
5096
+ const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
5097
+ this.pageSize = pageSize;
5098
+ this.state = new StateStore(this.initialState);
5099
+ this.setDebounceOptions({ debounceMs });
5100
+ }
5101
+ get lastQueryError() {
5102
+ return this.state.getLatestValue().lastQueryError;
5103
+ }
5104
+ get hasNext() {
5105
+ return this.state.getLatestValue().hasNext;
5106
+ }
5107
+ get hasPrev() {
5108
+ return this.state.getLatestValue().hasPrev;
5109
+ }
5110
+ get hasResults() {
5111
+ return Array.isArray(this.state.getLatestValue().items);
5112
+ }
5113
+ get isLoading() {
5114
+ return this.state.getLatestValue().isLoading;
5115
+ }
5116
+ get initialState() {
5117
+ return {
5118
+ hasNext: true,
5119
+ hasPrev: true,
5120
+ //todo: check if optimistic value does not cause problems in UI
5121
+ isLoading: false,
5122
+ items: void 0,
5123
+ lastQueryError: void 0,
5124
+ cursor: void 0,
5125
+ offset: 0
5126
+ };
5127
+ }
5128
+ get items() {
5129
+ return this.state.getLatestValue().items;
5130
+ }
5131
+ get cursor() {
5132
+ return this.state.getLatestValue().cursor;
5133
+ }
5134
+ get offset() {
5135
+ return this.state.getLatestValue().offset;
5136
+ }
5137
+ getStateBeforeFirstQuery() {
5138
+ return {
5139
+ ...this.initialState,
5140
+ isLoading: true
5141
+ };
5142
+ }
5143
+ getStateAfterQuery(stateUpdate, isFirstPage) {
5144
+ const current = this.state.getLatestValue();
5145
+ return {
5146
+ ...current,
5147
+ lastQueryError: void 0,
5148
+ // reset lastQueryError that can be overridden by the stateUpdate
5149
+ ...stateUpdate,
5150
+ isLoading: false,
5151
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5152
+ };
5153
+ }
5154
+ async executeQuery({ direction }) {
5155
+ if (!this.canExecuteQuery(direction)) return;
5156
+ const isFirstPage = typeof this.items === "undefined";
5157
+ if (isFirstPage) {
5158
+ this.state.next(this.getStateBeforeFirstQuery());
5159
+ } else {
5160
+ this.state.partialNext({ isLoading: true });
5161
+ }
5162
+ const stateUpdate = {};
5163
+ try {
5164
+ const results = await this.query({ direction });
5165
+ if (!results) return;
5166
+ const { items, next, prev } = results;
5167
+ if (isFirstPage && (next || prev)) {
5168
+ this._isCursorPagination = true;
5169
+ }
5170
+ if (this._isCursorPagination) {
5171
+ stateUpdate.cursor = { next: next || null, prev: prev || null };
5172
+ stateUpdate.hasNext = !!next;
5173
+ stateUpdate.hasPrev = !!prev;
5174
+ } else {
5175
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
5176
+ stateUpdate.hasNext = items.length === this.pageSize;
5177
+ }
5178
+ stateUpdate.items = await this.filterQueryResults(items);
5179
+ } catch (e) {
5180
+ stateUpdate.lastQueryError = e;
5181
+ } finally {
5182
+ this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
5183
+ }
5184
+ }
5185
+ cancelScheduledQuery() {
5186
+ this._executeQueryDebounced.cancel();
5187
+ }
5188
+ resetState() {
5189
+ this.state.next(this.initialState);
5190
+ }
5191
+ };
5192
+
5193
+ // src/pagination/FilterBuilder.ts
5194
+ var FilterBuilder = class {
5195
+ constructor(params) {
5196
+ this.context = new StateStore(params?.initialContext ?? {});
5197
+ this.filterConfig = new StateStore(
5198
+ params?.initialFilterConfig ?? {}
5199
+ );
5200
+ }
5201
+ updateFilterConfig(config) {
5202
+ this.filterConfig.partialNext(config);
5203
+ }
5204
+ enableFilter(filterKey) {
5205
+ const config = this.filterConfig.getLatestValue();
5206
+ if (config[filterKey]) {
5207
+ this.filterConfig.partialNext({
5208
+ [filterKey]: {
5209
+ ...config[filterKey],
5210
+ enabled: true
5211
+ }
5212
+ });
5213
+ }
5214
+ }
5215
+ disableFilter(filterKey) {
5216
+ const config = this.filterConfig.getLatestValue();
5217
+ if (config[filterKey]) {
5218
+ this.filterConfig.partialNext({
5219
+ [filterKey]: {
5220
+ ...config[filterKey],
5221
+ enabled: false
5222
+ }
5223
+ });
5224
+ }
5225
+ }
5226
+ updateContext(newContext) {
5227
+ this.context.partialNext(newContext);
5228
+ }
5229
+ buildFilters(params) {
5230
+ const filters = {
5231
+ ...params?.baseFilters ?? {}
5232
+ };
5233
+ const filterConfig = this.filterConfig.getLatestValue();
5234
+ for (const key in filterConfig) {
5235
+ const configItem = filterConfig[key];
5236
+ if (!configItem?.enabled) continue;
5237
+ const generated = configItem.generate({
5238
+ ...this.context.getLatestValue(),
5239
+ ...params?.context ?? {}
5240
+ });
5241
+ if (generated) Object.assign(filters, generated);
5242
+ }
5243
+ return filters;
5244
+ }
5245
+ };
5246
+
5247
+ // src/pagination/ReminderPaginator.ts
5248
+ var ReminderPaginator = class extends BasePaginator {
5249
+ constructor(client, options) {
5250
+ super(options);
5251
+ this.query = async ({
5252
+ direction
5253
+ }) => {
5254
+ const cursor = this.cursor?.[direction];
5255
+ const {
5256
+ reminders: items,
5257
+ next,
5258
+ prev
5259
+ } = await this.client.queryReminders({
5260
+ filter: this.filters,
5261
+ sort: this.sort,
5262
+ limit: this.pageSize,
5263
+ [direction]: cursor
5264
+ });
5265
+ return { items, next, prev };
5266
+ };
5267
+ this.filterQueryResults = (items) => items;
5268
+ this.client = client;
5269
+ }
5270
+ get filters() {
5271
+ return this._filters;
5272
+ }
5273
+ get sort() {
5274
+ return this._sort;
5379
5275
  }
5380
- get searchQuery() {
5381
- return this.state.getLatestValue().searchQuery;
5276
+ set filters(filters) {
5277
+ this._filters = filters;
5278
+ this.resetState();
5382
5279
  }
5383
- get searchSourceTypes() {
5384
- return this.sources.map((s) => s.type);
5280
+ set sort(sort) {
5281
+ this._sort = sort;
5282
+ this.resetState();
5385
5283
  }
5386
5284
  };
5387
5285
 
5388
- // src/pagination/BasePaginator.ts
5389
- var DEFAULT_PAGINATION_OPTIONS = {
5390
- debounceMs: 300,
5391
- pageSize: 10
5392
- };
5393
- var BasePaginator = class {
5394
- constructor(options) {
5395
- this._isCursorPagination = false;
5396
- this.setDebounceOptions = ({ debounceMs }) => {
5397
- this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5398
- };
5399
- this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
5400
- this.next = () => this.executeQuery({ direction: "next" });
5401
- this.prev = () => this.executeQuery({ direction: "prev" });
5402
- this.nextDebounced = () => {
5403
- this._executeQueryDebounced({ direction: "next" });
5404
- };
5405
- this.prevDebounced = () => {
5406
- this._executeQueryDebounced({ direction: "prev" });
5407
- };
5408
- const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
5409
- this.pageSize = pageSize;
5410
- this.state = new StateStore(this.initialState);
5411
- this.setDebounceOptions({ debounceMs });
5286
+ // src/search/UserSearchSource.ts
5287
+ var UserSearchSource = class extends BaseSearchSource {
5288
+ constructor(client, options, filterBuilderOptions = {}) {
5289
+ super(options);
5290
+ this.type = "users";
5291
+ this.client = client;
5292
+ this.filterBuilder = new FilterBuilder({
5293
+ initialFilterConfig: {
5294
+ $or: {
5295
+ enabled: true,
5296
+ generate: ({ searchQuery }) => searchQuery ? {
5297
+ $or: [
5298
+ { id: { $autocomplete: searchQuery } },
5299
+ { name: { $autocomplete: searchQuery } }
5300
+ ]
5301
+ } : null
5302
+ }
5303
+ },
5304
+ ...filterBuilderOptions
5305
+ });
5412
5306
  }
5413
- get lastQueryError() {
5414
- return this.state.getLatestValue().lastQueryError;
5307
+ async query(searchQuery) {
5308
+ const filters = this.filterBuilder.buildFilters({
5309
+ baseFilters: this.filters,
5310
+ context: { searchQuery }
5311
+ });
5312
+ const sort = { id: 1, ...this.sort };
5313
+ const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5314
+ const { users } = await this.client.queryUsers(filters, sort, options);
5315
+ return { items: users };
5415
5316
  }
5416
- get hasNext() {
5417
- return this.state.getLatestValue().hasNext;
5317
+ filterQueryResults(items) {
5318
+ return items.filter((u) => u.id !== this.client.user?.id);
5418
5319
  }
5419
- get hasPrev() {
5420
- return this.state.getLatestValue().hasPrev;
5320
+ };
5321
+
5322
+ // src/search/ChannelSearchSource.ts
5323
+ var ChannelSearchSource = class extends BaseSearchSource {
5324
+ constructor(client, options, filterBuilderOptions = {}) {
5325
+ super(options);
5326
+ this.type = "channels";
5327
+ this.client = client;
5328
+ this.filterBuilder = new FilterBuilder({
5329
+ ...filterBuilderOptions,
5330
+ initialFilterConfig: {
5331
+ name: {
5332
+ enabled: true,
5333
+ generate: ({ searchQuery }) => searchQuery ? { name: { $autocomplete: searchQuery } } : null
5334
+ },
5335
+ ...filterBuilderOptions.initialFilterConfig
5336
+ }
5337
+ });
5421
5338
  }
5422
- get hasResults() {
5423
- return Array.isArray(this.state.getLatestValue().items);
5339
+ async query(searchQuery) {
5340
+ const filters = this.filterBuilder.buildFilters({
5341
+ baseFilters: {
5342
+ ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5343
+ ...this.filters
5344
+ },
5345
+ context: { searchQuery }
5346
+ });
5347
+ const sort = this.sort ?? {};
5348
+ const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5349
+ const items = await this.client.queryChannels(filters, sort, options);
5350
+ return { items };
5424
5351
  }
5425
- get isLoading() {
5426
- return this.state.getLatestValue().isLoading;
5352
+ filterQueryResults(items) {
5353
+ return items;
5427
5354
  }
5428
- get initialState() {
5429
- return {
5430
- hasNext: true,
5431
- hasPrev: true,
5432
- //todo: check if optimistic value does not cause problems in UI
5433
- isLoading: false,
5434
- items: void 0,
5435
- lastQueryError: void 0,
5436
- cursor: void 0,
5437
- offset: 0
5438
- };
5355
+ };
5356
+
5357
+ // src/search/MessageSearchSource.ts
5358
+ var MessageSearchSource = class extends BaseSearchSource {
5359
+ constructor(client, options, filterBuilderOptions) {
5360
+ super(options);
5361
+ this.type = "messages";
5362
+ this.client = client;
5363
+ this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5364
+ this.messageSearchFilterBuilder = new FilterBuilder({
5365
+ ...filterBuilderOptions?.messageSearch,
5366
+ initialFilterConfig: {
5367
+ text: {
5368
+ enabled: true,
5369
+ generate: ({ searchQuery }) => searchQuery ? { text: searchQuery } : null
5370
+ },
5371
+ ...filterBuilderOptions?.messageSearch?.initialFilterConfig
5372
+ }
5373
+ });
5374
+ this.channelQueryFilterBuilder = new FilterBuilder({
5375
+ ...filterBuilderOptions?.channelQuery,
5376
+ initialFilterConfig: {
5377
+ cid: {
5378
+ enabled: true,
5379
+ generate: ({ cids }) => cids ? { cid: { $in: cids } } : null
5380
+ },
5381
+ ...filterBuilderOptions?.channelQuery?.initialFilterConfig
5382
+ }
5383
+ });
5439
5384
  }
5440
- get items() {
5441
- return this.state.getLatestValue().items;
5385
+ async query(searchQuery) {
5386
+ if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
5387
+ const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5388
+ baseFilters: {
5389
+ ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5390
+ ...this.messageSearchChannelFilters
5391
+ },
5392
+ context: { searchQuery }
5393
+ });
5394
+ const messageFilters = this.messageSearchFilterBuilder.buildFilters({
5395
+ baseFilters: {
5396
+ type: "regular",
5397
+ ...this.messageSearchFilters
5398
+ },
5399
+ context: { searchQuery }
5400
+ });
5401
+ const sort = {
5402
+ created_at: -1,
5403
+ ...this.messageSearchSort
5404
+ };
5405
+ const options = {
5406
+ limit: this.pageSize,
5407
+ next: this.next,
5408
+ sort
5409
+ };
5410
+ const { next, results } = await this.client.search(
5411
+ channelFilters,
5412
+ messageFilters,
5413
+ options
5414
+ );
5415
+ const items = results.map(({ message }) => message);
5416
+ const cids = Array.from(
5417
+ items.reduce((acc, message) => {
5418
+ if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
5419
+ return acc;
5420
+ }, /* @__PURE__ */ new Set())
5421
+ );
5422
+ if (cids.length > 0) {
5423
+ const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
5424
+ baseFilters: this.channelQueryFilters,
5425
+ context: { cids }
5426
+ });
5427
+ await this.client.queryChannels(
5428
+ channelQueryFilters,
5429
+ {
5430
+ last_message_at: -1,
5431
+ ...this.channelQuerySort
5432
+ },
5433
+ this.channelQueryOptions
5434
+ );
5435
+ }
5436
+ return { items, next };
5442
5437
  }
5443
- get cursor() {
5444
- return this.state.getLatestValue().cursor;
5438
+ filterQueryResults(items) {
5439
+ return items;
5445
5440
  }
5446
- get offset() {
5447
- return this.state.getLatestValue().offset;
5441
+ };
5442
+
5443
+ // src/messageComposer/middleware/textComposer/commandUtils.ts
5444
+ function escapeCommandRegExp(text) {
5445
+ return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, "\\$&");
5446
+ }
5447
+ var getRawCommandName = (text) => text?.match(/^\/(\S+)(?:\s.*)?$/)?.[1];
5448
+ var getCompleteCommandInString = (text) => {
5449
+ const match = text.match(/^\/(\S+)\s+.*/);
5450
+ const commandName = match && match[1];
5451
+ return commandName;
5452
+ };
5453
+ var stripCommandFromText = (text, commandName) => text.replace(new RegExp(`^${escapeCommandRegExp(`/${commandName}`)}\\s*`), "");
5454
+ var notifyCommandDisabled = (composer, command) => {
5455
+ const disabledReason = composer.getCommandDisabledReason(command);
5456
+ if (!disabledReason) return;
5457
+ composer.client.notifications.addWarning({
5458
+ message: disabledReason === "editing" ? "Command not available while editing" : "Command not available while replying",
5459
+ origin: {
5460
+ emitter: "MessageComposer",
5461
+ context: { command, composer }
5462
+ },
5463
+ options: {
5464
+ type: "validation:command:disabled",
5465
+ metadata: {
5466
+ command: command.name,
5467
+ reason: disabledReason
5468
+ }
5469
+ }
5470
+ });
5471
+ return true;
5472
+ };
5473
+
5474
+ // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
5475
+ var getTriggerCharWithToken = ({
5476
+ trigger,
5477
+ text,
5478
+ isCommand = false,
5479
+ acceptTrailingSpaces = true
5480
+ }) => {
5481
+ const escapedTrigger = escapeCommandRegExp(trigger);
5482
+ const triggerNorWhitespace = `[^\\s${escapedTrigger}]*`;
5483
+ const match = text.match(
5484
+ new RegExp(
5485
+ isCommand ? `^[${escapedTrigger}]${triggerNorWhitespace}$` : acceptTrailingSpaces ? `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}\\s?${triggerNorWhitespace}$` : `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}$`,
5486
+ "g"
5487
+ )
5488
+ );
5489
+ return match && match[match.length - 1].trim();
5490
+ };
5491
+ var insertItemWithTrigger = ({
5492
+ insertText,
5493
+ selection,
5494
+ text,
5495
+ trigger
5496
+ }) => {
5497
+ const beforeCursor = text.slice(0, selection.end);
5498
+ const afterCursor = text.slice(selection.end);
5499
+ const lastIndex = beforeCursor.lastIndexOf(trigger);
5500
+ const newText = beforeCursor.slice(0, lastIndex) + insertText + afterCursor;
5501
+ return {
5502
+ text: newText,
5503
+ selection: {
5504
+ start: lastIndex + insertText.length,
5505
+ end: lastIndex + insertText.length
5506
+ }
5507
+ };
5508
+ };
5509
+ var replaceWordWithEntity = async ({
5510
+ caretPosition,
5511
+ getEntityString,
5512
+ text
5513
+ }) => {
5514
+ const lastWordRegex = /([^\s]+)(\s*)$/;
5515
+ const match = lastWordRegex.exec(text.slice(0, caretPosition));
5516
+ if (!match) return text;
5517
+ const lastWord = match[1];
5518
+ if (!lastWord) return text;
5519
+ const spaces = match[2];
5520
+ const newWord = await getEntityString(lastWord);
5521
+ if (newWord == null) return text;
5522
+ const textBeforeWord = text.slice(0, caretPosition - match[0].length);
5523
+ const textAfterCaret = text.slice(caretPosition, -1);
5524
+ return textBeforeWord + newWord + spaces + textAfterCaret;
5525
+ };
5526
+ var getTokenizedSuggestionDisplayName = ({
5527
+ displayName,
5528
+ searchToken
5529
+ }) => ({
5530
+ tokenizedDisplayName: {
5531
+ token: searchToken,
5532
+ parts: searchToken ? displayName.split(new RegExp(`(${escapeCommandRegExp(searchToken)})`, "gi")).filter(Boolean) : [displayName]
5448
5533
  }
5449
- getStateBeforeFirstQuery() {
5450
- return {
5451
- ...this.initialState,
5452
- isLoading: true
5534
+ });
5535
+
5536
+ // src/messageComposer/middleware/textComposer/commands.ts
5537
+ var CommandSearchSource = class extends BaseSearchSourceSync {
5538
+ constructor(channel, options) {
5539
+ super(options);
5540
+ this.type = "commands";
5541
+ this.canExecuteQuery = (newSearchString) => {
5542
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
5543
+ return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
5453
5544
  };
5545
+ this.channel = channel;
5454
5546
  }
5455
- getStateAfterQuery(stateUpdate, isFirstPage) {
5456
- const current = this.state.getLatestValue();
5547
+ getStateBeforeFirstQuery(newSearchString) {
5548
+ const newState = super.getStateBeforeFirstQuery(newSearchString);
5549
+ const { items } = this.state.getLatestValue();
5457
5550
  return {
5458
- ...current,
5459
- lastQueryError: void 0,
5460
- // reset lastQueryError that can be overridden by the stateUpdate
5461
- ...stateUpdate,
5462
- isLoading: false,
5463
- items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5551
+ ...newState,
5552
+ items
5553
+ // preserve items to avoid flickering
5464
5554
  };
5465
5555
  }
5466
- async executeQuery({ direction }) {
5467
- if (!this.canExecuteQuery(direction)) return;
5468
- const isFirstPage = typeof this.items === "undefined";
5469
- if (isFirstPage) {
5470
- this.state.next(this.getStateBeforeFirstQuery());
5471
- } else {
5472
- this.state.partialNext({ isLoading: true });
5473
- }
5474
- const stateUpdate = {};
5475
- try {
5476
- const results = await this.query({ direction });
5477
- if (!results) return;
5478
- const { items, next, prev } = results;
5479
- if (isFirstPage && (next || prev)) {
5480
- this._isCursorPagination = true;
5556
+ query(searchQuery) {
5557
+ const channelConfig = this.channel.getConfig();
5558
+ const commands = channelConfig?.commands || [];
5559
+ const selectedCommands = commands.filter(
5560
+ (command) => !!(command.name && command.name.toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1)
5561
+ );
5562
+ selectedCommands.sort((a, b) => {
5563
+ let nameA = a.name?.toLowerCase();
5564
+ let nameB = b.name?.toLowerCase();
5565
+ if (nameA?.indexOf(searchQuery) === 0) {
5566
+ nameA = `0${nameA}`;
5481
5567
  }
5482
- if (this._isCursorPagination) {
5483
- stateUpdate.cursor = { next: next || null, prev: prev || null };
5484
- stateUpdate.hasNext = !!next;
5485
- stateUpdate.hasPrev = !!prev;
5486
- } else {
5487
- stateUpdate.offset = (this.offset ?? 0) + items.length;
5488
- stateUpdate.hasNext = items.length === this.pageSize;
5568
+ if (nameB?.indexOf(searchQuery) === 0) {
5569
+ nameB = `0${nameB}`;
5489
5570
  }
5490
- stateUpdate.items = await this.filterQueryResults(items);
5491
- } catch (e) {
5492
- stateUpdate.lastQueryError = e;
5493
- } finally {
5494
- this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
5495
- }
5571
+ if (nameA != null && nameB != null) {
5572
+ if (nameA < nameB) {
5573
+ return -1;
5574
+ }
5575
+ if (nameA > nameB) {
5576
+ return 1;
5577
+ }
5578
+ }
5579
+ return 0;
5580
+ });
5581
+ return {
5582
+ items: selectedCommands.map((command) => ({
5583
+ ...command,
5584
+ id: command.name
5585
+ })),
5586
+ next: null
5587
+ };
5496
5588
  }
5497
- cancelScheduledQuery() {
5498
- this._executeQueryDebounced.cancel();
5589
+ filterQueryResults(items) {
5590
+ return items;
5499
5591
  }
5500
- resetState() {
5501
- this.state.next(this.initialState);
5592
+ };
5593
+ var DEFAULT_OPTIONS = { minChars: 1, trigger: "/" };
5594
+ var createCommandsMiddleware = (channel, options) => {
5595
+ const finalOptions = mergeWith(DEFAULT_OPTIONS, options ?? {});
5596
+ let searchSource = new CommandSearchSource(channel);
5597
+ if (options?.searchSource) {
5598
+ searchSource = options.searchSource;
5599
+ searchSource.resetState();
5502
5600
  }
5601
+ searchSource.activate();
5602
+ return {
5603
+ id: "stream-io/text-composer/commands-middleware",
5604
+ handlers: {
5605
+ onChange: ({ state, next, complete, forward }) => {
5606
+ if (!state.selection) return forward();
5607
+ const finalText = state.text.slice(0, state.selection.end);
5608
+ const commandName = getCompleteCommandInString(finalText);
5609
+ if (commandName) {
5610
+ const command = searchSource?.query(commandName).items[0];
5611
+ const composer = options?.composer;
5612
+ if (command && !composer?.isCommandDisabled(command)) {
5613
+ return next({
5614
+ ...state,
5615
+ command,
5616
+ suggestions: void 0
5617
+ });
5618
+ }
5619
+ }
5620
+ const triggerWithToken = getTriggerCharWithToken({
5621
+ trigger: finalOptions.trigger,
5622
+ text: finalText,
5623
+ acceptTrailingSpaces: false,
5624
+ isCommand: true
5625
+ });
5626
+ const newSearchTriggerred = triggerWithToken && triggerWithToken.length === finalOptions.minChars;
5627
+ if (newSearchTriggerred) {
5628
+ searchSource.resetStateAndActivate();
5629
+ }
5630
+ const triggerWasRemoved = !triggerWithToken || triggerWithToken.length < finalOptions.minChars;
5631
+ if (triggerWasRemoved) {
5632
+ const hasStaleSuggestions = state.suggestions?.trigger === finalOptions.trigger;
5633
+ const newState = { ...state };
5634
+ if (hasStaleSuggestions) {
5635
+ delete newState.suggestions;
5636
+ }
5637
+ return next(newState);
5638
+ }
5639
+ return complete({
5640
+ ...state,
5641
+ command: null,
5642
+ suggestions: {
5643
+ query: triggerWithToken.slice(1),
5644
+ searchSource,
5645
+ trigger: finalOptions.trigger
5646
+ }
5647
+ });
5648
+ },
5649
+ onSuggestionItemSelect: ({ state, next, forward }) => {
5650
+ const { selectedSuggestion } = state.change ?? {};
5651
+ if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger)
5652
+ return forward();
5653
+ const composer = options?.composer;
5654
+ if (composer && notifyCommandDisabled(composer, selectedSuggestion)) {
5655
+ return forward();
5656
+ }
5657
+ searchSource.resetStateAndActivate();
5658
+ return next({
5659
+ ...state,
5660
+ ...insertItemWithTrigger({
5661
+ insertText: `/${selectedSuggestion.name} `,
5662
+ selection: state.selection,
5663
+ text: state.text,
5664
+ trigger: finalOptions.trigger
5665
+ }),
5666
+ command: selectedSuggestion,
5667
+ suggestions: void 0
5668
+ });
5669
+ }
5670
+ }
5671
+ };
5503
5672
  };
5504
5673
 
5505
- // src/pagination/FilterBuilder.ts
5506
- var FilterBuilder = class {
5507
- constructor(params) {
5508
- this.context = new StateStore(params?.initialContext ?? {});
5509
- this.filterConfig = new StateStore(
5510
- params?.initialFilterConfig ?? {}
5511
- );
5512
- }
5513
- updateFilterConfig(config) {
5514
- this.filterConfig.partialNext(config);
5674
+ // src/messageComposer/middleware/messageComposer/compositionValidation.ts
5675
+ var getCommandByName = (searchSource, commandName) => {
5676
+ if (!commandName) return;
5677
+ const normalizedCommandName = commandName.toLowerCase();
5678
+ return searchSource.query(normalizedCommandName).items.find((command) => command.name?.toLowerCase() === normalizedCommandName);
5679
+ };
5680
+ var getDisabledRawCommand = (composer, searchSource, text) => {
5681
+ const rawCommand = getCommandByName(searchSource, getRawCommandName(text));
5682
+ if (rawCommand && composer.isCommandDisabled(rawCommand)) {
5683
+ return rawCommand;
5515
5684
  }
5516
- enableFilter(filterKey) {
5517
- const config = this.filterConfig.getLatestValue();
5518
- if (config[filterKey]) {
5519
- this.filterConfig.partialNext({
5520
- [filterKey]: {
5521
- ...config[filterKey],
5522
- enabled: true
5685
+ };
5686
+ var createCompositionValidationMiddleware = (composer) => {
5687
+ const commandSearchSource = new CommandSearchSource(composer.channel);
5688
+ return {
5689
+ id: "stream-io/message-composer-middleware/data-validation",
5690
+ handlers: {
5691
+ compose: async ({
5692
+ state,
5693
+ discard,
5694
+ forward
5695
+ }) => {
5696
+ const { maxLengthOnSend } = composer.config.text ?? {};
5697
+ const inputText = state.message.text ?? "";
5698
+ const disabledRawCommand = getDisabledRawCommand(
5699
+ composer,
5700
+ commandSearchSource,
5701
+ inputText
5702
+ );
5703
+ if (disabledRawCommand) {
5704
+ notifyCommandDisabled(composer, disabledRawCommand);
5705
+ return await discard();
5706
+ }
5707
+ const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
5708
+ if (composer.compositionIsEmpty || hasExceededMaxLength) {
5709
+ return await discard();
5523
5710
  }
5711
+ return await forward();
5712
+ }
5713
+ }
5714
+ };
5715
+ };
5716
+ var createDraftCompositionValidationMiddleware = (composer) => ({
5717
+ id: "stream-io/message-composer-middleware/draft-data-validation",
5718
+ handlers: {
5719
+ compose: async ({
5720
+ state,
5721
+ discard,
5722
+ forward
5723
+ }) => {
5724
+ const hasData = !textIsEmpty(state.draft.text ?? "") || state.draft.attachments?.length || state.draft.poll_id || state.draft.quoted_message_id;
5725
+ const shouldCreateDraft = composer.lastChangeOriginIsLocal && hasData;
5726
+ if (!shouldCreateDraft) {
5727
+ return await discard();
5728
+ }
5729
+ return await forward();
5730
+ }
5731
+ }
5732
+ });
5733
+
5734
+ // src/messageComposer/middleware/messageComposer/linkPreviews.ts
5735
+ var createLinkPreviewsCompositionMiddleware = (composer) => ({
5736
+ id: "stream-io/message-composer-middleware/link-previews",
5737
+ handlers: {
5738
+ compose: ({
5739
+ state,
5740
+ next,
5741
+ forward
5742
+ }) => {
5743
+ const { linkPreviewsManager } = composer;
5744
+ if (!linkPreviewsManager) return forward();
5745
+ linkPreviewsManager.cancelURLEnrichment();
5746
+ const someLinkPreviewsLoading = linkPreviewsManager.loadingPreviews.length > 0;
5747
+ const someLinkPreviewsDismissed = linkPreviewsManager.dismissedPreviews.length > 0;
5748
+ const linkPreviews = linkPreviewsManager.loadingPreviews.length > 0 ? [] : linkPreviewsManager.loadedPreviews.map(
5749
+ (preview) => LinkPreviewsManager.getPreviewData(preview)
5750
+ );
5751
+ const attachments = (state.message.attachments ?? []).concat(
5752
+ linkPreviews
5753
+ );
5754
+ if (!attachments.length) return forward();
5755
+ const sendOptions = { ...state.sendOptions };
5756
+ const skip_enrich_url = !someLinkPreviewsLoading && linkPreviews.length > 0 || someLinkPreviewsDismissed;
5757
+ if (skip_enrich_url) {
5758
+ sendOptions.skip_enrich_url = true;
5759
+ }
5760
+ return next({
5761
+ ...state,
5762
+ message: {
5763
+ ...state.message,
5764
+ attachments
5765
+ },
5766
+ localMessage: {
5767
+ ...state.localMessage,
5768
+ attachments
5769
+ },
5770
+ sendOptions
5524
5771
  });
5525
5772
  }
5526
5773
  }
5527
- disableFilter(filterKey) {
5528
- const config = this.filterConfig.getLatestValue();
5529
- if (config[filterKey]) {
5530
- this.filterConfig.partialNext({
5531
- [filterKey]: {
5532
- ...config[filterKey],
5533
- enabled: false
5774
+ });
5775
+ var createDraftLinkPreviewsCompositionMiddleware = (composer) => ({
5776
+ id: "stream-io/message-composer-middleware/draft-link-previews",
5777
+ handlers: {
5778
+ compose: ({
5779
+ state,
5780
+ next,
5781
+ forward
5782
+ }) => {
5783
+ const { linkPreviewsManager } = composer;
5784
+ if (!linkPreviewsManager) return forward();
5785
+ linkPreviewsManager.cancelURLEnrichment();
5786
+ const linkPreviews = linkPreviewsManager.loadedPreviews.map(
5787
+ (preview) => LinkPreviewsManager.getPreviewData(preview)
5788
+ );
5789
+ if (!linkPreviews.length) return forward();
5790
+ return next({
5791
+ ...state,
5792
+ draft: {
5793
+ ...state.draft,
5794
+ attachments: (state.draft.attachments ?? []).concat(linkPreviews)
5534
5795
  }
5535
5796
  });
5536
5797
  }
5537
5798
  }
5538
- updateContext(newContext) {
5539
- this.context.partialNext(newContext);
5540
- }
5541
- buildFilters(params) {
5542
- const filters = {
5543
- ...params?.baseFilters ?? {}
5544
- };
5545
- const filterConfig = this.filterConfig.getLatestValue();
5546
- for (const key in filterConfig) {
5547
- const configItem = filterConfig[key];
5548
- if (!configItem?.enabled) continue;
5549
- const generated = configItem.generate({
5550
- ...this.context.getLatestValue(),
5551
- ...params?.context ?? {}
5552
- });
5553
- if (generated) Object.assign(filters, generated);
5554
- }
5555
- return filters;
5556
- }
5557
- };
5799
+ });
5558
5800
 
5559
- // src/pagination/ReminderPaginator.ts
5560
- var ReminderPaginator = class extends BasePaginator {
5561
- constructor(client, options) {
5562
- super(options);
5563
- this.query = async ({
5564
- direction
5801
+ // src/messageComposer/middleware/messageComposer/textComposer.ts
5802
+ var createTextComposerCompositionMiddleware = (composer) => ({
5803
+ id: "stream-io/message-composer-middleware/text-composition",
5804
+ handlers: {
5805
+ compose: ({
5806
+ state,
5807
+ next,
5808
+ forward
5565
5809
  }) => {
5566
- const cursor = this.cursor?.[direction];
5567
- const {
5568
- reminders: items,
5569
- next,
5570
- prev
5571
- } = await this.client.queryReminders({
5572
- filter: this.filters,
5573
- sort: this.sort,
5574
- limit: this.pageSize,
5575
- [direction]: cursor
5810
+ if (!composer.textComposer) return forward();
5811
+ const { mentionedUsers, text } = composer.textComposer;
5812
+ const mentioned_users = Array.from(
5813
+ new Set(
5814
+ mentionedUsers.filter(
5815
+ ({ id, name }) => text.includes(`@${id}`) || text.includes(`@${name}`)
5816
+ )
5817
+ )
5818
+ );
5819
+ if (!text && mentioned_users.length === 0) return forward();
5820
+ return next({
5821
+ ...state,
5822
+ localMessage: {
5823
+ ...state.localMessage,
5824
+ mentioned_users,
5825
+ text
5826
+ },
5827
+ message: {
5828
+ ...state.message,
5829
+ mentioned_users: mentioned_users.map((u) => u.id),
5830
+ text
5831
+ }
5576
5832
  });
5577
- return { items, next, prev };
5578
- };
5579
- this.filterQueryResults = (items) => items;
5580
- this.client = client;
5581
- }
5582
- get filters() {
5583
- return this._filters;
5584
- }
5585
- get sort() {
5586
- return this._sort;
5587
- }
5588
- set filters(filters) {
5589
- this._filters = filters;
5590
- this.resetState();
5591
- }
5592
- set sort(sort) {
5593
- this._sort = sort;
5594
- this.resetState();
5833
+ }
5595
5834
  }
5596
- };
5597
-
5598
- // src/search/UserSearchSource.ts
5599
- var UserSearchSource = class extends BaseSearchSource {
5600
- constructor(client, options, filterBuilderOptions = {}) {
5601
- super(options);
5602
- this.type = "users";
5603
- this.client = client;
5604
- this.filterBuilder = new FilterBuilder({
5605
- initialFilterConfig: {
5606
- $or: {
5607
- enabled: true,
5608
- generate: ({ searchQuery }) => searchQuery ? {
5609
- $or: [
5610
- { id: { $autocomplete: searchQuery } },
5611
- { name: { $autocomplete: searchQuery } }
5612
- ]
5613
- } : null
5835
+ });
5836
+ var createDraftTextComposerCompositionMiddleware = (composer) => ({
5837
+ id: "stream-io/message-composer-middleware/draft-text-composition",
5838
+ handlers: {
5839
+ compose: ({
5840
+ state,
5841
+ next,
5842
+ forward
5843
+ }) => {
5844
+ if (!composer.textComposer) return forward();
5845
+ const { maxLengthOnSend } = composer.config.text ?? {};
5846
+ const { mentionedUsers, text: inputText } = composer.textComposer;
5847
+ const mentioned_users = mentionedUsers.length ? Array.from(
5848
+ new Set(
5849
+ mentionedUsers.filter(
5850
+ ({ id, name }) => inputText.includes(`@${id}`) || inputText.includes(`@${name}`)
5851
+ )
5852
+ )
5853
+ ) : void 0;
5854
+ const text = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend ? inputText.slice(0, maxLengthOnSend) : inputText;
5855
+ return next({
5856
+ ...state,
5857
+ draft: {
5858
+ ...state.draft,
5859
+ mentioned_users: mentioned_users?.map((u) => u.id),
5860
+ text
5614
5861
  }
5615
- },
5616
- ...filterBuilderOptions
5617
- });
5618
- }
5619
- async query(searchQuery) {
5620
- const filters = this.filterBuilder.buildFilters({
5621
- baseFilters: this.filters,
5622
- context: { searchQuery }
5623
- });
5624
- const sort = { id: 1, ...this.sort };
5625
- const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5626
- const { users } = await this.client.queryUsers(filters, sort, options);
5627
- return { items: users };
5628
- }
5629
- filterQueryResults(items) {
5630
- return items.filter((u) => u.id !== this.client.user?.id);
5631
- }
5632
- };
5633
-
5634
- // src/search/ChannelSearchSource.ts
5635
- var ChannelSearchSource = class extends BaseSearchSource {
5636
- constructor(client, options, filterBuilderOptions = {}) {
5637
- super(options);
5638
- this.type = "channels";
5639
- this.client = client;
5640
- this.filterBuilder = new FilterBuilder({
5641
- ...filterBuilderOptions,
5642
- initialFilterConfig: {
5643
- name: {
5644
- enabled: true,
5645
- generate: ({ searchQuery }) => searchQuery ? { name: { $autocomplete: searchQuery } } : null
5646
- },
5647
- ...filterBuilderOptions.initialFilterConfig
5648
- }
5649
- });
5650
- }
5651
- async query(searchQuery) {
5652
- const filters = this.filterBuilder.buildFilters({
5653
- baseFilters: {
5654
- ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5655
- ...this.filters
5656
- },
5657
- context: { searchQuery }
5658
- });
5659
- const sort = this.sort ?? {};
5660
- const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5661
- const items = await this.client.queryChannels(filters, sort, options);
5662
- return { items };
5663
- }
5664
- filterQueryResults(items) {
5665
- return items;
5862
+ });
5863
+ }
5666
5864
  }
5667
- };
5865
+ });
5668
5866
 
5669
- // src/search/MessageSearchSource.ts
5670
- var MessageSearchSource = class extends BaseSearchSource {
5671
- constructor(client, options, filterBuilderOptions) {
5672
- super(options);
5673
- this.type = "messages";
5674
- this.client = client;
5675
- this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5676
- this.messageSearchFilterBuilder = new FilterBuilder({
5677
- ...filterBuilderOptions?.messageSearch,
5678
- initialFilterConfig: {
5679
- text: {
5680
- enabled: true,
5681
- generate: ({ searchQuery }) => searchQuery ? { text: searchQuery } : null
5682
- },
5683
- ...filterBuilderOptions?.messageSearch?.initialFilterConfig
5867
+ // src/messageComposer/middleware/messageComposer/messageComposerState.ts
5868
+ var createMessageComposerStateCompositionMiddleware = (composer) => ({
5869
+ id: "stream-io/message-composer-middleware/own-state",
5870
+ handlers: {
5871
+ compose: ({
5872
+ state,
5873
+ next
5874
+ }) => {
5875
+ const payload = {};
5876
+ if (composer.quotedMessage) {
5877
+ payload.quoted_message_id = composer.quotedMessage.id;
5684
5878
  }
5685
- });
5686
- this.channelQueryFilterBuilder = new FilterBuilder({
5687
- ...filterBuilderOptions?.channelQuery,
5688
- initialFilterConfig: {
5689
- cid: {
5690
- enabled: true,
5691
- generate: ({ cids }) => cids ? { cid: { $in: cids } } : null
5692
- },
5693
- ...filterBuilderOptions?.channelQuery?.initialFilterConfig
5879
+ if (composer.pollId) {
5880
+ payload.poll_id = composer.pollId;
5694
5881
  }
5695
- });
5882
+ if (composer.showReplyInChannel) {
5883
+ payload.show_in_channel = true;
5884
+ }
5885
+ return next({
5886
+ ...state,
5887
+ localMessage: {
5888
+ ...state.localMessage,
5889
+ ...payload,
5890
+ quoted_message: composer.quotedMessage ?? void 0
5891
+ },
5892
+ message: {
5893
+ ...state.message,
5894
+ ...payload
5895
+ }
5896
+ });
5897
+ }
5696
5898
  }
5697
- async query(searchQuery) {
5698
- if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
5699
- const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5700
- baseFilters: {
5701
- ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5702
- ...this.messageSearchChannelFilters
5703
- },
5704
- context: { searchQuery }
5705
- });
5706
- const messageFilters = this.messageSearchFilterBuilder.buildFilters({
5707
- baseFilters: {
5708
- type: "regular",
5709
- ...this.messageSearchFilters
5710
- },
5711
- context: { searchQuery }
5712
- });
5713
- const sort = {
5714
- created_at: -1,
5715
- ...this.messageSearchSort
5716
- };
5717
- const options = {
5718
- limit: this.pageSize,
5719
- next: this.next,
5720
- sort
5721
- };
5722
- const { next, results } = await this.client.search(
5723
- channelFilters,
5724
- messageFilters,
5725
- options
5726
- );
5727
- const items = results.map(({ message }) => message);
5728
- const cids = Array.from(
5729
- items.reduce((acc, message) => {
5730
- if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
5731
- return acc;
5732
- }, /* @__PURE__ */ new Set())
5733
- );
5734
- if (cids.length > 0) {
5735
- const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
5736
- baseFilters: this.channelQueryFilters,
5737
- context: { cids }
5899
+ });
5900
+ var createDraftMessageComposerStateCompositionMiddleware = (composer) => ({
5901
+ id: "stream-io/message-composer-middleware/draft-own-state",
5902
+ handlers: {
5903
+ compose: ({
5904
+ state,
5905
+ next
5906
+ }) => {
5907
+ const payload = {};
5908
+ if (composer.quotedMessage) {
5909
+ payload.quoted_message_id = composer.quotedMessage.id;
5910
+ }
5911
+ if (composer.pollId) {
5912
+ payload.poll_id = composer.pollId;
5913
+ }
5914
+ if (composer.showReplyInChannel) {
5915
+ payload.show_in_channel = true;
5916
+ }
5917
+ return next({
5918
+ ...state,
5919
+ draft: {
5920
+ ...state.draft,
5921
+ ...payload
5922
+ }
5738
5923
  });
5739
- await this.client.queryChannels(
5740
- channelQueryFilters,
5741
- {
5742
- last_message_at: -1,
5743
- ...this.channelQuerySort
5744
- },
5745
- this.channelQueryOptions
5746
- );
5747
5924
  }
5748
- return { items, next };
5749
5925
  }
5750
- filterQueryResults(items) {
5751
- return items;
5926
+ });
5927
+
5928
+ // src/messageComposer/middleware/messageComposer/userDataInjection.ts
5929
+ var createUserDataInjectionMiddleware = (composer) => ({
5930
+ id: "stream-io/message-composer-middleware/user-data-injection",
5931
+ handlers: {
5932
+ compose: ({
5933
+ state,
5934
+ next,
5935
+ forward
5936
+ }) => {
5937
+ if (!composer.client.user) {
5938
+ return forward();
5939
+ }
5940
+ const { channel_mutes, devices, mutes, ...messageUser } = composer.client.user;
5941
+ return next({
5942
+ ...state,
5943
+ localMessage: {
5944
+ ...state.localMessage,
5945
+ user: messageUser,
5946
+ user_id: messageUser.id
5947
+ }
5948
+ });
5949
+ }
5752
5950
  }
5753
- };
5951
+ });
5754
5952
 
5755
- // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
5756
- var getTriggerCharWithToken = ({
5757
- trigger,
5758
- text,
5759
- isCommand = false,
5760
- acceptTrailingSpaces = true
5761
- }) => {
5762
- const escapedTrigger = escapeRegExp(trigger);
5763
- const triggerNorWhitespace = `[^\\s${escapedTrigger}]*`;
5764
- const match = text.match(
5765
- new RegExp(
5766
- isCommand ? `^[${escapedTrigger}]${triggerNorWhitespace}$` : acceptTrailingSpaces ? `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}\\s?${triggerNorWhitespace}$` : `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}$`,
5767
- "g"
5768
- )
5769
- );
5770
- return match && match[match.length - 1].trim();
5771
- };
5772
- var getCompleteCommandInString = (text) => {
5773
- const match = text.match(/^\/(\S+)\s+.*/);
5774
- const commandName = match && match[1];
5775
- return commandName;
5953
+ // src/messageComposer/middleware/messageComposer/pollOnly.ts
5954
+ var pollLocalMessageNullifiedFields = {
5955
+ attachments: [],
5956
+ mentioned_users: [],
5957
+ parent_id: void 0,
5958
+ quoted_message: void 0,
5959
+ text: ""
5776
5960
  };
5777
- var insertItemWithTrigger = ({
5778
- insertText,
5779
- selection,
5780
- text,
5781
- trigger
5782
- }) => {
5783
- const beforeCursor = text.slice(0, selection.end);
5784
- const afterCursor = text.slice(selection.end);
5785
- const lastIndex = beforeCursor.lastIndexOf(trigger);
5786
- const newText = beforeCursor.slice(0, lastIndex) + insertText + afterCursor;
5787
- return {
5788
- text: newText,
5789
- selection: {
5790
- start: lastIndex + insertText.length,
5791
- end: lastIndex + insertText.length
5961
+ var createPollOnlyCompositionMiddleware = (composer) => ({
5962
+ id: "stream-io/message-composer-middleware/poll-only",
5963
+ handlers: {
5964
+ compose: ({
5965
+ state,
5966
+ complete,
5967
+ forward
5968
+ }) => {
5969
+ const pollId = composer.pollId;
5970
+ const isEditingMessage = !!composer.editedMessage;
5971
+ const isComposingThreadReply = !!composer.threadId;
5972
+ if (!pollId || isComposingThreadReply || isEditingMessage) return forward();
5973
+ return complete({
5974
+ ...state,
5975
+ localMessage: {
5976
+ ...state.localMessage,
5977
+ ...pollLocalMessageNullifiedFields,
5978
+ poll_id: pollId
5979
+ },
5980
+ message: {
5981
+ id: state.localMessage.id,
5982
+ poll_id: pollId
5983
+ }
5984
+ });
5985
+ }
5986
+ }
5987
+ });
5988
+
5989
+ // src/messageComposer/middleware/messageComposer/sharedLocation.ts
5990
+ var createSharedLocationCompositionMiddleware = (composer) => ({
5991
+ id: "stream-io/message-composer-middleware/shared-location",
5992
+ handlers: {
5993
+ compose: ({
5994
+ state,
5995
+ next,
5996
+ forward
5997
+ }) => {
5998
+ const { locationComposer } = composer;
5999
+ const location = locationComposer.validLocation;
6000
+ if (!locationComposer || !location || !composer.client.user) return forward();
6001
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6002
+ return next({
6003
+ ...state,
6004
+ localMessage: {
6005
+ ...state.localMessage,
6006
+ shared_location: {
6007
+ ...location,
6008
+ channel_cid: composer.channel.cid,
6009
+ created_at: timestamp,
6010
+ updated_at: timestamp,
6011
+ user_id: composer.client.user.id
6012
+ }
6013
+ },
6014
+ message: {
6015
+ ...state.message,
6016
+ shared_location: location
6017
+ }
6018
+ });
5792
6019
  }
5793
- };
5794
- };
5795
- var replaceWordWithEntity = async ({
5796
- caretPosition,
5797
- getEntityString,
5798
- text
5799
- }) => {
5800
- const lastWordRegex = /([^\s]+)(\s*)$/;
5801
- const match = lastWordRegex.exec(text.slice(0, caretPosition));
5802
- if (!match) return text;
5803
- const lastWord = match[1];
5804
- if (!lastWord) return text;
5805
- const spaces = match[2];
5806
- const newWord = await getEntityString(lastWord);
5807
- if (newWord == null) return text;
5808
- const textBeforeWord = text.slice(0, caretPosition - match[0].length);
5809
- const textAfterCaret = text.slice(caretPosition, -1);
5810
- return textBeforeWord + newWord + spaces + textAfterCaret;
5811
- };
5812
- function escapeRegExp(text) {
5813
- return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, "\\$&");
5814
- }
5815
- var getTokenizedSuggestionDisplayName = ({
5816
- displayName,
5817
- searchToken
5818
- }) => ({
5819
- tokenizedDisplayName: {
5820
- token: searchToken,
5821
- parts: searchToken ? displayName.split(new RegExp(`(${escapeRegExp(searchToken)})`, "gi")).filter(Boolean) : [displayName]
5822
6020
  }
5823
6021
  });
5824
6022
 
5825
- // src/messageComposer/middleware/textComposer/commands.ts
5826
- var CommandSearchSource = class extends BaseSearchSourceSync {
5827
- constructor(channel, options) {
5828
- super(options);
5829
- this.type = "commands";
5830
- this.canExecuteQuery = (newSearchString) => {
5831
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5832
- return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
5833
- };
5834
- this.channel = channel;
6023
+ // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
6024
+ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6025
+ constructor({ composer }) {
6026
+ super();
6027
+ this.use([
6028
+ createUserDataInjectionMiddleware(composer),
6029
+ createPollOnlyCompositionMiddleware(composer),
6030
+ createTextComposerCompositionMiddleware(composer),
6031
+ createAttachmentsCompositionMiddleware(composer),
6032
+ createLinkPreviewsCompositionMiddleware(composer),
6033
+ createSharedLocationCompositionMiddleware(composer),
6034
+ createMessageComposerStateCompositionMiddleware(composer),
6035
+ createCustomDataCompositionMiddleware(composer),
6036
+ createCompositionValidationMiddleware(composer),
6037
+ createCompositionDataCleanupMiddleware(composer)
6038
+ ]);
5835
6039
  }
5836
- getStateBeforeFirstQuery(newSearchString) {
5837
- const newState = super.getStateBeforeFirstQuery(newSearchString);
5838
- const { items } = this.state.getLatestValue();
5839
- return {
5840
- ...newState,
5841
- items
5842
- // preserve items to avoid flickering
5843
- };
6040
+ };
6041
+ var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6042
+ constructor({ composer }) {
6043
+ super();
6044
+ this.use([
6045
+ createDraftTextComposerCompositionMiddleware(composer),
6046
+ createDraftAttachmentsCompositionMiddleware(composer),
6047
+ createDraftLinkPreviewsCompositionMiddleware(composer),
6048
+ createDraftMessageComposerStateCompositionMiddleware(composer),
6049
+ createDraftCustomDataCompositionMiddleware(composer),
6050
+ createDraftCompositionValidationMiddleware(composer)
6051
+ ]);
5844
6052
  }
5845
- query(searchQuery) {
5846
- const channelConfig = this.channel.getConfig();
5847
- const commands = channelConfig?.commands || [];
5848
- const selectedCommands = commands.filter(
5849
- (command) => !!(command.name && command.name.toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1)
5850
- );
5851
- selectedCommands.sort((a, b) => {
5852
- let nameA = a.name?.toLowerCase();
5853
- let nameB = b.name?.toLowerCase();
5854
- if (nameA?.indexOf(searchQuery) === 0) {
5855
- nameA = `0${nameA}`;
5856
- }
5857
- if (nameB?.indexOf(searchQuery) === 0) {
5858
- nameB = `0${nameB}`;
6053
+ };
6054
+
6055
+ // src/messageComposer/middleware/messageComposer/commandInjection.ts
6056
+ var createCommandInjectionMiddleware = (composer) => ({
6057
+ handlers: {
6058
+ compose: ({
6059
+ forward,
6060
+ next,
6061
+ state
6062
+ }) => {
6063
+ const command = composer.textComposer.command;
6064
+ if (!command) {
6065
+ return forward();
5859
6066
  }
5860
- if (nameA != null && nameB != null) {
5861
- if (nameA < nameB) {
5862
- return -1;
6067
+ const { text } = state.localMessage;
6068
+ const injection = `/${command?.name}`;
6069
+ const enrichedText = `${injection} ${text}`;
6070
+ return next({
6071
+ ...state,
6072
+ localMessage: {
6073
+ ...state.localMessage,
6074
+ text: enrichedText
6075
+ },
6076
+ message: {
6077
+ ...state.message,
6078
+ text: enrichedText
5863
6079
  }
5864
- if (nameA > nameB) {
5865
- return 1;
6080
+ });
6081
+ }
6082
+ },
6083
+ id: "stream-io/message-composer-middleware/command-string-injection"
6084
+ });
6085
+ var createDraftCommandInjectionMiddleware = (composer) => ({
6086
+ handlers: {
6087
+ compose: ({
6088
+ forward,
6089
+ next,
6090
+ state
6091
+ }) => {
6092
+ const command = composer.textComposer.command;
6093
+ if (!command) {
6094
+ return forward();
6095
+ }
6096
+ const { text } = state.draft;
6097
+ const injection = `/${command?.name}`;
6098
+ const enrichedText = `${injection} ${text}`;
6099
+ return next({
6100
+ ...state,
6101
+ draft: {
6102
+ ...state.draft,
6103
+ text: enrichedText
5866
6104
  }
6105
+ });
6106
+ }
6107
+ },
6108
+ id: "stream-io/message-composer-middleware/draft-command-string-injection"
6109
+ });
6110
+
6111
+ // src/messageComposer/middleware/textComposer/activeCommandGuard.ts
6112
+ var createActiveCommandGuardMiddleware = () => ({
6113
+ handlers: {
6114
+ onChange: ({ complete, forward, state }) => {
6115
+ if (state.command) {
6116
+ return complete(state);
5867
6117
  }
5868
- return 0;
5869
- });
5870
- return {
5871
- items: selectedCommands.map((c) => ({ ...c, id: c.name })),
5872
- next: null
5873
- };
5874
- }
5875
- filterQueryResults(items) {
5876
- return items;
5877
- }
6118
+ return forward();
6119
+ },
6120
+ onSuggestionItemSelect: ({ forward }) => forward()
6121
+ },
6122
+ id: "stream-io/text-composer/active-command-guard"
6123
+ });
6124
+
6125
+ // src/messageComposer/middleware/textComposer/commandEffects.ts
6126
+ var emptyCommandStateToRestore = {
6127
+ selection: { start: 0, end: 0 },
6128
+ text: ""
5878
6129
  };
5879
- var DEFAULT_OPTIONS = { minChars: 1, trigger: "/" };
5880
- var createCommandsMiddleware = (channel, options) => {
5881
- const finalOptions = mergeWith(DEFAULT_OPTIONS, options ?? {});
5882
- let searchSource = new CommandSearchSource(channel);
5883
- if (options?.searchSource) {
5884
- searchSource = options.searchSource;
5885
- searchSource.resetState();
5886
- }
5887
- searchSource.activate();
5888
- return {
5889
- id: "stream-io/text-composer/commands-middleware",
5890
- handlers: {
5891
- onChange: ({ state, next, complete, forward }) => {
5892
- if (!state.selection) return forward();
5893
- const finalText = state.text.slice(0, state.selection.end);
5894
- const commandName = getCompleteCommandInString(finalText);
5895
- if (commandName) {
5896
- const command = searchSource?.query(commandName).items[0];
5897
- if (command) {
5898
- return next({
5899
- ...state,
5900
- command,
5901
- suggestions: void 0
5902
- });
5903
- }
5904
- }
5905
- const triggerWithToken = getTriggerCharWithToken({
5906
- trigger: finalOptions.trigger,
5907
- text: finalText,
5908
- acceptTrailingSpaces: false,
5909
- isCommand: true
5910
- });
5911
- const newSearchTriggerred = triggerWithToken && triggerWithToken.length === finalOptions.minChars;
5912
- if (newSearchTriggerred) {
5913
- searchSource.resetStateAndActivate();
5914
- }
5915
- const triggerWasRemoved = !triggerWithToken || triggerWithToken.length < finalOptions.minChars;
5916
- if (triggerWasRemoved) {
5917
- const hasStaleSuggestions = state.suggestions?.trigger === finalOptions.trigger;
5918
- const newState = { ...state };
5919
- if (hasStaleSuggestions) {
5920
- delete newState.suggestions;
5921
- }
5922
- return next(newState);
5923
- }
5924
- return complete({
5925
- ...state,
5926
- command: null,
5927
- suggestions: {
5928
- query: triggerWithToken.slice(1),
5929
- searchSource,
5930
- trigger: finalOptions.trigger
5931
- }
5932
- });
5933
- },
5934
- onSuggestionItemSelect: ({ state, next, forward }) => {
5935
- const { selectedSuggestion } = state.change ?? {};
5936
- if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger)
5937
- return forward();
5938
- searchSource.resetStateAndActivate();
5939
- return next({
5940
- ...state,
5941
- ...insertItemWithTrigger({
5942
- insertText: `/${selectedSuggestion.name} `,
5943
- selection: state.selection,
5944
- text: state.text,
5945
- trigger: finalOptions.trigger
5946
- }),
5947
- command: selectedSuggestion,
5948
- suggestions: void 0
5949
- });
6130
+ var createCommandActivationEffect = (command) => ({
6131
+ command,
6132
+ stateToRestore: emptyCommandStateToRestore,
6133
+ type: "command.activate"
6134
+ });
6135
+ var isCommandResponse = (suggestion) => typeof suggestion?.name === "string";
6136
+ var createCommandEffectsMiddleware = () => ({
6137
+ handlers: {
6138
+ onChange: ({ forward }) => forward(),
6139
+ onSuggestionItemSelect: ({ state, next, forward }) => {
6140
+ const { selectedSuggestion } = state.change ?? {};
6141
+ if (!isCommandResponse(selectedSuggestion) || !state.command || state.command.name !== selectedSuggestion.name) {
6142
+ return forward();
5950
6143
  }
6144
+ return next({
6145
+ ...state,
6146
+ effects: [...state.effects ?? [], createCommandActivationEffect(state.command)]
6147
+ });
5951
6148
  }
5952
- };
5953
- };
6149
+ },
6150
+ id: "stream-io/text-composer/command-effects-middleware"
6151
+ });
5954
6152
 
5955
6153
  // src/messageComposer/middleware/textComposer/commandStringExtraction.ts
5956
- var stripCommandFromText = (text, commandName) => text.replace(new RegExp(`^${escapeRegExp(`/${commandName}`)}\\s*`), "");
5957
6154
  var createCommandStringExtractionMiddleware = () => ({
5958
6155
  handlers: {
5959
6156
  onChange: ({ complete, forward, state }) => {
@@ -6293,7 +6490,10 @@ var TextComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6293
6490
  this.use([
6294
6491
  createTextComposerPreValidationMiddleware(composer),
6295
6492
  createMentionsMiddleware(composer.channel),
6296
- createCommandsMiddleware(composer.channel)
6493
+ createCommandsMiddleware(composer.channel, {
6494
+ composer
6495
+ }),
6496
+ createCommandEffectsMiddleware()
6297
6497
  ]);
6298
6498
  }
6299
6499
  async execute({
@@ -6342,6 +6542,10 @@ var TextComposer = class {
6342
6542
  this.initState = ({ message } = {}) => {
6343
6543
  this.state.next(initState5({ composer: this.composer, message }));
6344
6544
  };
6545
+ this.getSnapshot = (state = this.state.getLatestValue()) => state;
6546
+ this.restoreSnapshot = (snapshot) => {
6547
+ this.state.next(snapshot);
6548
+ };
6345
6549
  this.upsertMentionedUser = (user) => {
6346
6550
  const mentionedUsers = [...this.mentionedUsers];
6347
6551
  const existingUserIndex = mentionedUsers.findIndex((u) => u.id === user.id);
@@ -6362,8 +6566,27 @@ var TextComposer = class {
6362
6566
  this.state.partialNext({ mentionedUsers });
6363
6567
  };
6364
6568
  this.setCommand = (command) => {
6365
- if (command?.name === this.command?.name) return;
6366
- this.state.partialNext({ command });
6569
+ if (!command) {
6570
+ this.clearCommand();
6571
+ return;
6572
+ }
6573
+ if (command.name === this.command?.name) return;
6574
+ if (this.composer.isCommandDisabled(command)) return;
6575
+ const stateToRestore = {
6576
+ command: null
6577
+ };
6578
+ this.commitState({
6579
+ ...this.state.getLatestValue(),
6580
+ command,
6581
+ effects: [
6582
+ {
6583
+ command,
6584
+ stateToRestore,
6585
+ type: "command.activate"
6586
+ }
6587
+ ],
6588
+ suggestions: void 0
6589
+ });
6367
6590
  };
6368
6591
  this.setText = (text) => {
6369
6592
  if (!this.enabled || text === this.text) return;
@@ -6430,6 +6653,12 @@ var TextComposer = class {
6430
6653
  this.state.partialNext({ suggestions: void 0 });
6431
6654
  };
6432
6655
  // --- END STATE API ---
6656
+ this.commitState = (state) => {
6657
+ const { change, effects, ...nextState } = state;
6658
+ void change;
6659
+ this.state.next(nextState);
6660
+ this.composer.applyEffects(effects);
6661
+ };
6433
6662
  // --- START TEXT PROCESSING ----
6434
6663
  this.handleChange = async ({
6435
6664
  text,
@@ -6445,7 +6674,7 @@ var TextComposer = class {
6445
6674
  }
6446
6675
  });
6447
6676
  if (output.status === "discard") return;
6448
- this.state.next(output.state);
6677
+ this.commitState(output.state);
6449
6678
  if (this.config.publishTypingEvents && text) {
6450
6679
  logChatPromiseExecution(
6451
6680
  this.channel.keystroke(this.composer.threadId ?? void 0),
@@ -6466,7 +6695,7 @@ var TextComposer = class {
6466
6695
  }
6467
6696
  });
6468
6697
  if (output?.status === "discard") return;
6469
- this.state.next(output.state);
6698
+ this.commitState(output.state);
6470
6699
  };
6471
6700
  this.composer = composer;
6472
6701
  this.state = new StateStore(initState5({ composer, message }));
@@ -6536,7 +6765,12 @@ var TextComposer = class {
6536
6765
  this.state.partialNext({ mentionedUsers: users });
6537
6766
  }
6538
6767
  clearCommand() {
6539
- this.state.partialNext({ command: null });
6768
+ if (!this.command) return;
6769
+ this.commitState({
6770
+ ...this.state.getLatestValue(),
6771
+ command: null,
6772
+ effects: [{ type: "command.clear" }]
6773
+ });
6540
6774
  }
6541
6775
  // --- END TEXT PROCESSING ----
6542
6776
  };
@@ -7027,15 +7261,28 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7027
7261
  client
7028
7262
  }) {
7029
7263
  super();
7264
+ this.snapshots = [];
7030
7265
  this.setEditedMessage = (editedMessage) => {
7031
7266
  this.state.partialNext({ editedMessage: editedMessage ?? null });
7267
+ if (editedMessage) {
7268
+ this.textComposer.clearCommand();
7269
+ }
7270
+ };
7271
+ this.getCommandDisabledReason = (command) => {
7272
+ if (this.editedMessage) return "editing";
7273
+ if (this.quotedMessage && (command.set === "moderation_set" || command.name === "moderation_set")) {
7274
+ return "quoted_message";
7275
+ }
7276
+ return void 0;
7032
7277
  };
7278
+ this.isCommandDisabled = (command) => !!this.getCommandDisabledReason(command);
7033
7279
  this.refreshId = () => {
7034
7280
  this.state.partialNext({ id: _MessageComposer.generateId() });
7035
7281
  };
7036
7282
  this.initState = ({
7037
7283
  composition
7038
7284
  } = {}) => {
7285
+ this.clearSnapshots();
7039
7286
  this.editingAuditState.partialNext(this.initEditingAuditState(composition));
7040
7287
  const message = typeof composition === "undefined" ? composition : compositionIsDraftResponse(composition) ? composition.message : formatMessage(composition);
7041
7288
  this.attachmentManager.initState({ message });
@@ -7065,6 +7312,36 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7065
7312
  }
7066
7313
  };
7067
7314
  this.initEditingAuditState = (composition) => initEditingAuditState(composition);
7315
+ this.clearSnapshots = () => {
7316
+ this.snapshots = [];
7317
+ };
7318
+ this.getSnapshot = () => ({
7319
+ attachmentManager: this.attachmentManager.getSnapshot(),
7320
+ customDataManager: this.customDataManager.getSnapshot(),
7321
+ linkPreviewsManager: this.linkPreviewsManager.getSnapshot(),
7322
+ locationComposer: this.locationComposer.getSnapshot(),
7323
+ pollComposer: this.pollComposer.getSnapshot(),
7324
+ textComposer: this.textComposer.getSnapshot()
7325
+ });
7326
+ this.restoreSnapshot = (snapshot) => {
7327
+ this.attachmentManager.restoreSnapshot(snapshot.attachmentManager);
7328
+ this.linkPreviewsManager.restoreSnapshot(snapshot.linkPreviewsManager);
7329
+ this.locationComposer.restoreSnapshot(snapshot.locationComposer);
7330
+ this.pollComposer.restoreSnapshot(snapshot.pollComposer);
7331
+ this.customDataManager.restoreSnapshot(snapshot.customDataManager);
7332
+ this.textComposer.restoreSnapshot(snapshot.textComposer);
7333
+ };
7334
+ this.captureSnapshot = (snapshot = this.getSnapshot()) => {
7335
+ if (this.snapshots.length) return;
7336
+ this.snapshots.push(snapshot);
7337
+ };
7338
+ this.popSnapshot = () => this.snapshots.pop();
7339
+ this.registerEffectHandler = (type, handler) => {
7340
+ this.effectHandlers.registerEffectHandler(type, handler);
7341
+ };
7342
+ this.applyEffects = (effects = []) => {
7343
+ this.effectHandlers.applyEffects(effects);
7344
+ };
7068
7345
  this.registerDraftEventSubscriptions = () => {
7069
7346
  const unsubscribeDraftUpdated = this.subscribeDraftUpdated();
7070
7347
  const unsubscribeDraftDeleted = this.subscribeDraftDeleted();
@@ -7244,6 +7521,10 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7244
7521
  };
7245
7522
  this.setQuotedMessage = (quotedMessage) => {
7246
7523
  this.state.partialNext({ quotedMessage });
7524
+ const activeCommand = this.textComposer.command;
7525
+ if (quotedMessage && activeCommand && this.isCommandDisabled(activeCommand)) {
7526
+ this.textComposer.clearCommand();
7527
+ }
7247
7528
  };
7248
7529
  this.toggleShowReplyInChannel = () => {
7249
7530
  this.state.partialNext({ showReplyInChannel: !this.showReplyInChannel });
@@ -7478,6 +7759,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7478
7759
  this.draftCompositionMiddlewareExecutor = new MessageDraftComposerMiddlewareExecutor({
7479
7760
  composer: this
7480
7761
  });
7762
+ this.effectHandlers = new MessageComposerEffectHandlers({ composer: this });
7481
7763
  }
7482
7764
  static evaluateContextType(compositionContext) {
7483
7765
  if (compositionContext instanceof Channel) {
@@ -15361,7 +15643,7 @@ var StreamChat = class _StreamChat {
15361
15643
  if (this.userAgent) {
15362
15644
  return this.userAgent;
15363
15645
  }
15364
- const version = "9.42.2";
15646
+ const version = "9.43.0";
15365
15647
  const clientBundle = "node-cjs";
15366
15648
  let userAgentString = "";
15367
15649
  if (this.sdkIdentifier) {
@@ -18065,6 +18347,7 @@ var FixedSizeQueueCache = class {
18065
18347
  createActiveCommandGuardMiddleware,
18066
18348
  createAttachmentsCompositionMiddleware,
18067
18349
  createBlockedAttachmentUploadNotificationMiddleware,
18350
+ createCommandEffectsMiddleware,
18068
18351
  createCommandInjectionMiddleware,
18069
18352
  createCommandStringExtractionMiddleware,
18070
18353
  createCommandsMiddleware,
@@ -18094,13 +18377,11 @@ var FixedSizeQueueCache = class {
18094
18377
  defaultPollFieldChangeEventValidators,
18095
18378
  encodeBase64,
18096
18379
  ensureIsLocalAttachment,
18097
- escapeRegExp,
18098
18380
  extractPollData,
18099
18381
  extractPollEnrichedData,
18100
18382
  formatMessage,
18101
18383
  generateFileName,
18102
18384
  getAttachmentTypeFromMimeType,
18103
- getCompleteCommandInString,
18104
18385
  getExtensionFromMimeType,
18105
18386
  getTokenizedSuggestionDisplayName,
18106
18387
  getTriggerCharWithToken,