stream-chat 9.42.3 → 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 (39) 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/package.json +1 -1
  23. package/src/messageComposer/CustomDataManager.ts +8 -0
  24. package/src/messageComposer/LocationComposer.ts +8 -0
  25. package/src/messageComposer/MessageComposerEffectHandlers.ts +87 -0
  26. package/src/messageComposer/attachmentManager.ts +55 -0
  27. package/src/messageComposer/linkPreviewsManager.ts +12 -3
  28. package/src/messageComposer/messageComposer.ts +107 -0
  29. package/src/messageComposer/middleware/messageComposer/compositionValidation.ts +58 -18
  30. package/src/messageComposer/middleware/textComposer/TextComposerMiddlewareExecutor.ts +7 -1
  31. package/src/messageComposer/middleware/textComposer/commandEffects.ts +51 -0
  32. package/src/messageComposer/middleware/textComposer/commandStringExtraction.ts +1 -4
  33. package/src/messageComposer/middleware/textComposer/commandUtils.ts +48 -0
  34. package/src/messageComposer/middleware/textComposer/commands.ts +15 -7
  35. package/src/messageComposer/middleware/textComposer/index.ts +1 -0
  36. package/src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts +3 -46
  37. package/src/messageComposer/middleware/textComposer/types.ts +20 -0
  38. package/src/messageComposer/pollComposer.ts +8 -0
  39. package/src/messageComposer/textComposer.ts +54 -6
@@ -152,6 +152,7 @@ __export(index_exports, {
152
152
  createActiveCommandGuardMiddleware: () => createActiveCommandGuardMiddleware,
153
153
  createAttachmentsCompositionMiddleware: () => createAttachmentsCompositionMiddleware,
154
154
  createBlockedAttachmentUploadNotificationMiddleware: () => createBlockedAttachmentUploadNotificationMiddleware,
155
+ createCommandEffectsMiddleware: () => createCommandEffectsMiddleware,
155
156
  createCommandInjectionMiddleware: () => createCommandInjectionMiddleware,
156
157
  createCommandStringExtractionMiddleware: () => createCommandStringExtractionMiddleware,
157
158
  createCommandsMiddleware: () => createCommandsMiddleware,
@@ -181,13 +182,11 @@ __export(index_exports, {
181
182
  defaultPollFieldChangeEventValidators: () => defaultPollFieldChangeEventValidators,
182
183
  encodeBase64: () => encodeBase64,
183
184
  ensureIsLocalAttachment: () => ensureIsLocalAttachment,
184
- escapeRegExp: () => escapeRegExp,
185
185
  extractPollData: () => extractPollData,
186
186
  extractPollEnrichedData: () => extractPollEnrichedData,
187
187
  formatMessage: () => formatMessage,
188
188
  generateFileName: () => generateFileName,
189
189
  getAttachmentTypeFromMimeType: () => getAttachmentTypeFromMimeType,
190
- getCompleteCommandInString: () => getCompleteCommandInString,
191
190
  getExtensionFromMimeType: () => getExtensionFromMimeType,
192
191
  getTokenizedSuggestionDisplayName: () => getTokenizedSuggestionDisplayName,
193
192
  getTriggerCharWithToken: () => getTriggerCharWithToken,
@@ -3276,9 +3275,52 @@ var _AttachmentManager = class _AttachmentManager {
3276
3275
  this.setCustomUploadFn = (doUploadRequest) => {
3277
3276
  this.composer.updateConfig({ attachments: { doUploadRequest } });
3278
3277
  };
3278
+ this.cancelAttachmentUploads = (attachments) => {
3279
+ for (const { localMetadata } of attachments) {
3280
+ this.client.uploadManager.deleteUploadRecord(localMetadata.id);
3281
+ }
3282
+ };
3283
+ this.normalizeSnapshotAttachment = (attachment) => {
3284
+ if (attachment.localMetadata.uploadState !== "uploading") return attachment;
3285
+ this.client.uploadManager.deleteUploadRecord(attachment.localMetadata.id);
3286
+ return {
3287
+ ...attachment,
3288
+ localMetadata: {
3289
+ ...attachment.localMetadata,
3290
+ uploadProgress: void 0,
3291
+ uploadState: "failed"
3292
+ }
3293
+ };
3294
+ };
3279
3295
  this.initState = ({ message } = {}) => {
3296
+ this.cancelAttachmentUploads(this.attachments);
3280
3297
  this.state.next(initState({ message }));
3281
3298
  };
3299
+ this.getSnapshot = () => {
3300
+ const state = this.state.getLatestValue();
3301
+ let hasUpdates = false;
3302
+ const attachments = state.attachments.map(this.normalizeSnapshotAttachment);
3303
+ for (let i = 0; i < attachments.length; i++) {
3304
+ if (attachments[i] !== state.attachments[i]) {
3305
+ hasUpdates = true;
3306
+ break;
3307
+ }
3308
+ }
3309
+ return hasUpdates ? { ...state, attachments } : state;
3310
+ };
3311
+ this.restoreSnapshot = (snapshot) => {
3312
+ this.cancelAttachmentUploads(this.attachments);
3313
+ this.state.next(snapshot);
3314
+ };
3315
+ this.setAttachments = (attachments) => {
3316
+ this.state.partialNext({ attachments });
3317
+ };
3318
+ this.clearAttachments = () => {
3319
+ if (!this.attachments.length) return;
3320
+ this.removeAttachments(
3321
+ this.attachments.map((attachment) => attachment.localMetadata.id)
3322
+ );
3323
+ };
3282
3324
  this.getAttachmentIndex = (localId) => {
3283
3325
  const attachmentsById = this.attachmentsById;
3284
3326
  return this.attachments.indexOf(attachmentsById[localId]);
@@ -3799,6 +3841,10 @@ var CustomDataManager = class {
3799
3841
  this.initState = ({ message } = {}) => {
3800
3842
  this.state.next(initState2({ composer: this.composer, message }));
3801
3843
  };
3844
+ this.getSnapshot = () => this.state.getLatestValue();
3845
+ this.restoreSnapshot = (snapshot) => {
3846
+ this.state.next(snapshot);
3847
+ };
3802
3848
  this.composer = composer;
3803
3849
  this.state = new StateStore(initState2({ composer, message }));
3804
3850
  }
@@ -3854,8 +3900,13 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
3854
3900
  constructor({ composer, message }) {
3855
3901
  this.shouldDiscardEnrichQueries = false;
3856
3902
  this.initState = ({ message } = {}) => {
3903
+ this.cancelURLEnrichment();
3857
3904
  this.state.next(initState3({ message: this.enabled ? message : void 0 }));
3858
3905
  };
3906
+ this.getSnapshot = () => this.state.getLatestValue();
3907
+ this.restoreSnapshot = (snapshot) => {
3908
+ this.state.next(snapshot);
3909
+ };
3859
3910
  this._findAndEnrichUrls = async (text) => {
3860
3911
  if (!this.enabled) return;
3861
3912
  const urls = this.config.findURLFn(text);
@@ -3908,8 +3959,8 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
3908
3959
  );
3909
3960
  };
3910
3961
  this.cancelURLEnrichment = () => {
3911
- this.findAndEnrichUrls.cancel();
3912
- this.findAndEnrichUrls.flush();
3962
+ this.findAndEnrichUrls.cancel?.();
3963
+ this.findAndEnrichUrls.flush?.();
3913
3964
  };
3914
3965
  /**
3915
3966
  * Clears all non-dismissed previews when the text composer is cleared.
@@ -4051,6 +4102,10 @@ var LocationComposer = class {
4051
4102
  this.initState = ({ message } = {}) => {
4052
4103
  this.state.next(initState4({ message }));
4053
4104
  };
4105
+ this.getSnapshot = () => this.state.getLatestValue();
4106
+ this.restoreSnapshot = (snapshot) => {
4107
+ this.state.next(snapshot);
4108
+ };
4054
4109
  this.setData = (data) => {
4055
4110
  if (!this.config.enabled) return;
4056
4111
  if (!data.latitude || !data.longitude) return;
@@ -4087,6 +4142,63 @@ var LocationComposer = class {
4087
4142
  }
4088
4143
  };
4089
4144
 
4145
+ // src/messageComposer/MessageComposerEffectHandlers.ts
4146
+ var applyCommandActivationEffect = (effect, composer) => {
4147
+ const snapshot = composer.getSnapshot();
4148
+ if (effect.stateToRestore) {
4149
+ snapshot.textComposer = {
4150
+ ...snapshot.textComposer,
4151
+ ...effect.stateToRestore,
4152
+ command: null
4153
+ };
4154
+ }
4155
+ composer.captureSnapshot(snapshot);
4156
+ composer.textComposer.state.next({
4157
+ command: effect.command,
4158
+ mentionedUsers: [],
4159
+ suggestions: void 0,
4160
+ selection: { start: 0, end: 0 },
4161
+ text: ""
4162
+ });
4163
+ composer.attachmentManager.initState();
4164
+ composer.linkPreviewsManager.initState();
4165
+ composer.locationComposer.initState();
4166
+ composer.pollComposer.initState();
4167
+ composer.customDataManager.initState();
4168
+ };
4169
+ var applyCommandClearEffect = (_, composer) => {
4170
+ const snapshot = composer.popSnapshot();
4171
+ if (!snapshot) return;
4172
+ composer.restoreSnapshot(snapshot);
4173
+ };
4174
+ var MessageComposerEffectHandlers = class {
4175
+ constructor(options) {
4176
+ this.options = options;
4177
+ this.handlers = /* @__PURE__ */ new Map();
4178
+ this.registerDefaultHandlers = () => {
4179
+ this.registerEffectHandler(
4180
+ "command.activate",
4181
+ applyCommandActivationEffect
4182
+ );
4183
+ this.registerEffectHandler(
4184
+ "command.clear",
4185
+ applyCommandClearEffect
4186
+ );
4187
+ };
4188
+ this.registerEffectHandler = (type, handler) => {
4189
+ this.handlers.set(type, handler);
4190
+ };
4191
+ this.applyEffects = (effects = []) => {
4192
+ effects.forEach((effect) => this.applyEffect(effect));
4193
+ };
4194
+ this.applyEffect = (effect) => {
4195
+ const handler = this.handlers.get(effect.type);
4196
+ handler?.(effect, this.options.composer);
4197
+ };
4198
+ this.registerDefaultHandlers();
4199
+ }
4200
+ };
4201
+
4090
4202
  // src/messageComposer/middleware/pollComposer/state.ts
4091
4203
  var VALID_MAX_VOTES_VALUE_REGEX = /^([2-9]|10)$/;
4092
4204
  var MAX_POLL_OPTIONS = 100;
@@ -4377,6 +4489,10 @@ var PollComposer = class {
4377
4489
  this.initState = () => {
4378
4490
  this.state.next(this.initialState);
4379
4491
  };
4492
+ this.getSnapshot = () => this.state.getLatestValue();
4493
+ this.restoreSnapshot = (snapshot) => {
4494
+ this.state.next(snapshot);
4495
+ };
4380
4496
  /**
4381
4497
  * Updates specified fields and generates relevant errors
4382
4498
  * @param data
@@ -4639,667 +4755,239 @@ var createDraftCustomDataCompositionMiddleware = (composer) => ({
4639
4755
  }
4640
4756
  });
4641
4757
 
4642
- // src/messageComposer/middleware/messageComposer/compositionValidation.ts
4643
- var createCompositionValidationMiddleware = (composer) => ({
4644
- id: "stream-io/message-composer-middleware/data-validation",
4645
- handlers: {
4646
- compose: async ({
4647
- state,
4648
- discard,
4649
- forward
4650
- }) => {
4651
- const { maxLengthOnSend } = composer.config.text ?? {};
4652
- const inputText = state.message.text ?? "";
4653
- const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
4654
- if (composer.compositionIsEmpty || hasExceededMaxLength) {
4655
- return await discard();
4656
- }
4657
- return await forward();
4658
- }
4758
+ // src/errors.ts
4759
+ var APIErrorCodes = {
4760
+ "-1": { name: "InternalSystemError", retryable: true },
4761
+ "2": { name: "AccessKeyError", retryable: false },
4762
+ "3": { name: "AuthenticationFailedError", retryable: true },
4763
+ "4": { name: "InputError", retryable: false },
4764
+ "6": { name: "DuplicateUsernameError", retryable: false },
4765
+ "9": { name: "RateLimitError", retryable: true },
4766
+ "16": { name: "DoesNotExistError", retryable: false },
4767
+ "17": { name: "NotAllowedError", retryable: false },
4768
+ "18": { name: "EventNotSupportedError", retryable: false },
4769
+ "19": { name: "ChannelFeatureNotSupportedError", retryable: false },
4770
+ "20": { name: "MessageTooLongError", retryable: false },
4771
+ "21": { name: "MultipleNestingLevelError", retryable: false },
4772
+ "22": { name: "PayloadTooBigError", retryable: false },
4773
+ "23": { name: "RequestTimeoutError", retryable: true },
4774
+ "24": { name: "MaxHeaderSizeExceededError", retryable: false },
4775
+ "40": { name: "AuthErrorTokenExpired", retryable: false },
4776
+ "41": { name: "AuthErrorTokenNotValidYet", retryable: false },
4777
+ "42": { name: "AuthErrorTokenUsedBeforeIssuedAt", retryable: false },
4778
+ "43": { name: "AuthErrorTokenSignatureInvalid", retryable: false },
4779
+ "44": { name: "CustomCommandEndpointMissingError", retryable: false },
4780
+ "45": { name: "CustomCommandEndpointCallError", retryable: true },
4781
+ "46": { name: "ConnectionIDNotFoundError", retryable: false },
4782
+ "60": { name: "CoolDownError", retryable: true },
4783
+ "69": { name: "ErrWrongRegion", retryable: false },
4784
+ "70": { name: "ErrQueryChannelPermissions", retryable: false },
4785
+ "71": { name: "ErrTooManyConnections", retryable: true },
4786
+ "99": { name: "AppSuspendedError", retryable: false }
4787
+ };
4788
+ function isAPIError(error) {
4789
+ return error.code !== void 0;
4790
+ }
4791
+ function isErrorRetryable(error) {
4792
+ if (!error.code) return false;
4793
+ const err = APIErrorCodes[`${error.code}`];
4794
+ if (!err) return false;
4795
+ return err.retryable;
4796
+ }
4797
+ function isConnectionIDError(error) {
4798
+ return error.code === 46;
4799
+ }
4800
+ function isWSFailure(err) {
4801
+ if (typeof err.isWSFailure === "boolean") {
4802
+ return err.isWSFailure;
4659
4803
  }
4660
- });
4661
- var createDraftCompositionValidationMiddleware = (composer) => ({
4662
- id: "stream-io/message-composer-middleware/draft-data-validation",
4663
- handlers: {
4664
- compose: async ({
4665
- state,
4666
- discard,
4667
- forward
4668
- }) => {
4669
- const hasData = !textIsEmpty(state.draft.text ?? "") || state.draft.attachments?.length || state.draft.poll_id || state.draft.quoted_message_id;
4670
- const shouldCreateDraft = composer.lastChangeOriginIsLocal && hasData;
4671
- if (!shouldCreateDraft) {
4672
- return await discard();
4673
- }
4674
- return await forward();
4675
- }
4804
+ try {
4805
+ return JSON.parse(err.message).isWSFailure;
4806
+ } catch (_) {
4807
+ return false;
4676
4808
  }
4677
- });
4809
+ }
4810
+ function isErrorResponse(res) {
4811
+ return !res.status || res.status < 200 || 300 <= res.status;
4812
+ }
4678
4813
 
4679
- // src/messageComposer/middleware/messageComposer/linkPreviews.ts
4680
- var createLinkPreviewsCompositionMiddleware = (composer) => ({
4681
- id: "stream-io/message-composer-middleware/link-previews",
4682
- handlers: {
4683
- compose: ({
4684
- state,
4685
- next,
4686
- forward
4687
- }) => {
4688
- const { linkPreviewsManager } = composer;
4689
- if (!linkPreviewsManager) return forward();
4690
- linkPreviewsManager.cancelURLEnrichment();
4691
- const someLinkPreviewsLoading = linkPreviewsManager.loadingPreviews.length > 0;
4692
- const someLinkPreviewsDismissed = linkPreviewsManager.dismissedPreviews.length > 0;
4693
- const linkPreviews = linkPreviewsManager.loadingPreviews.length > 0 ? [] : linkPreviewsManager.loadedPreviews.map(
4694
- (preview) => LinkPreviewsManager.getPreviewData(preview)
4695
- );
4696
- const attachments = (state.message.attachments ?? []).concat(
4697
- linkPreviews
4698
- );
4699
- if (!attachments.length) return forward();
4700
- const sendOptions = { ...state.sendOptions };
4701
- const skip_enrich_url = !someLinkPreviewsLoading && linkPreviews.length > 0 || someLinkPreviewsDismissed;
4702
- if (skip_enrich_url) {
4703
- sendOptions.skip_enrich_url = true;
4704
- }
4705
- return next({
4706
- ...state,
4707
- message: {
4708
- ...state.message,
4709
- attachments
4710
- },
4711
- localMessage: {
4712
- ...state.localMessage,
4713
- attachments
4714
- },
4715
- sendOptions
4716
- });
4717
- }
4814
+ // src/search/BaseSearchSource.ts
4815
+ var DEFAULT_SEARCH_SOURCE_OPTIONS = {
4816
+ debounceMs: 300,
4817
+ pageSize: 10
4818
+ };
4819
+ var BaseSearchSourceBase = class {
4820
+ constructor(options) {
4821
+ this.activate = () => {
4822
+ if (this.isActive) return;
4823
+ this.state.partialNext({ isActive: true });
4824
+ };
4825
+ this.deactivate = () => {
4826
+ if (!this.isActive) return;
4827
+ this.state.partialNext({ isActive: false });
4828
+ };
4829
+ this.canExecuteQuery = (newSearchString) => {
4830
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
4831
+ const searchString = newSearchString ?? this.searchQuery;
4832
+ return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && searchString);
4833
+ };
4834
+ const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4835
+ this.pageSize = pageSize;
4836
+ this.state = new StateStore(this.initialState);
4718
4837
  }
4719
- });
4720
- var createDraftLinkPreviewsCompositionMiddleware = (composer) => ({
4721
- id: "stream-io/message-composer-middleware/draft-link-previews",
4722
- handlers: {
4723
- compose: ({
4724
- state,
4725
- next,
4726
- forward
4727
- }) => {
4728
- const { linkPreviewsManager } = composer;
4729
- if (!linkPreviewsManager) return forward();
4730
- linkPreviewsManager.cancelURLEnrichment();
4731
- const linkPreviews = linkPreviewsManager.loadedPreviews.map(
4732
- (preview) => LinkPreviewsManager.getPreviewData(preview)
4733
- );
4734
- if (!linkPreviews.length) return forward();
4735
- return next({
4736
- ...state,
4737
- draft: {
4738
- ...state.draft,
4739
- attachments: (state.draft.attachments ?? []).concat(linkPreviews)
4740
- }
4741
- });
4742
- }
4838
+ get lastQueryError() {
4839
+ return this.state.getLatestValue().lastQueryError;
4743
4840
  }
4744
- });
4745
-
4746
- // src/messageComposer/middleware/messageComposer/textComposer.ts
4747
- var createTextComposerCompositionMiddleware = (composer) => ({
4748
- id: "stream-io/message-composer-middleware/text-composition",
4749
- handlers: {
4750
- compose: ({
4751
- state,
4752
- next,
4753
- forward
4754
- }) => {
4755
- if (!composer.textComposer) return forward();
4756
- const { mentionedUsers, text } = composer.textComposer;
4757
- const mentioned_users = Array.from(
4758
- new Set(
4759
- mentionedUsers.filter(
4760
- ({ id, name }) => text.includes(`@${id}`) || text.includes(`@${name}`)
4761
- )
4762
- )
4763
- );
4764
- if (!text && mentioned_users.length === 0) return forward();
4765
- return next({
4766
- ...state,
4767
- localMessage: {
4768
- ...state.localMessage,
4769
- mentioned_users,
4770
- text
4771
- },
4772
- message: {
4773
- ...state.message,
4774
- mentioned_users: mentioned_users.map((u) => u.id),
4775
- text
4776
- }
4777
- });
4778
- }
4841
+ get hasNext() {
4842
+ return this.state.getLatestValue().hasNext;
4779
4843
  }
4780
- });
4781
- var createDraftTextComposerCompositionMiddleware = (composer) => ({
4782
- id: "stream-io/message-composer-middleware/draft-text-composition",
4783
- handlers: {
4784
- compose: ({
4785
- state,
4786
- next,
4787
- forward
4788
- }) => {
4789
- if (!composer.textComposer) return forward();
4790
- const { maxLengthOnSend } = composer.config.text ?? {};
4791
- const { mentionedUsers, text: inputText } = composer.textComposer;
4792
- const mentioned_users = mentionedUsers.length ? Array.from(
4793
- new Set(
4794
- mentionedUsers.filter(
4795
- ({ id, name }) => inputText.includes(`@${id}`) || inputText.includes(`@${name}`)
4796
- )
4797
- )
4798
- ) : void 0;
4799
- const text = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend ? inputText.slice(0, maxLengthOnSend) : inputText;
4800
- return next({
4801
- ...state,
4802
- draft: {
4803
- ...state.draft,
4804
- mentioned_users: mentioned_users?.map((u) => u.id),
4805
- text
4806
- }
4807
- });
4844
+ get hasResults() {
4845
+ return Array.isArray(this.state.getLatestValue().items);
4846
+ }
4847
+ get isActive() {
4848
+ return this.state.getLatestValue().isActive;
4849
+ }
4850
+ get isLoading() {
4851
+ return this.state.getLatestValue().isLoading;
4852
+ }
4853
+ get initialState() {
4854
+ return {
4855
+ hasNext: true,
4856
+ isActive: false,
4857
+ isLoading: false,
4858
+ items: void 0,
4859
+ lastQueryError: void 0,
4860
+ next: void 0,
4861
+ offset: 0,
4862
+ searchQuery: ""
4863
+ };
4864
+ }
4865
+ get items() {
4866
+ return this.state.getLatestValue().items;
4867
+ }
4868
+ get next() {
4869
+ return this.state.getLatestValue().next;
4870
+ }
4871
+ get offset() {
4872
+ return this.state.getLatestValue().offset;
4873
+ }
4874
+ get searchQuery() {
4875
+ return this.state.getLatestValue().searchQuery;
4876
+ }
4877
+ getStateBeforeFirstQuery(newSearchString) {
4878
+ return {
4879
+ ...this.initialState,
4880
+ isActive: this.isActive,
4881
+ isLoading: true,
4882
+ searchQuery: newSearchString
4883
+ };
4884
+ }
4885
+ getStateAfterQuery(stateUpdate, isFirstPage) {
4886
+ const current = this.state.getLatestValue();
4887
+ return {
4888
+ ...current,
4889
+ lastQueryError: void 0,
4890
+ // reset lastQueryError that can be overridden by the stateUpdate
4891
+ ...stateUpdate,
4892
+ isLoading: false,
4893
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
4894
+ };
4895
+ }
4896
+ prepareStateForQuery(newSearchString) {
4897
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
4898
+ const searchString = newSearchString ?? this.searchQuery;
4899
+ if (hasNewSearchQuery) {
4900
+ this.state.next(this.getStateBeforeFirstQuery(newSearchString ?? ""));
4901
+ } else {
4902
+ this.state.partialNext({ isLoading: true });
4808
4903
  }
4904
+ return { searchString, hasNewSearchQuery };
4809
4905
  }
4810
- });
4811
-
4812
- // src/messageComposer/middleware/messageComposer/messageComposerState.ts
4813
- var createMessageComposerStateCompositionMiddleware = (composer) => ({
4814
- id: "stream-io/message-composer-middleware/own-state",
4815
- handlers: {
4816
- compose: ({
4817
- state,
4818
- next
4819
- }) => {
4820
- const payload = {};
4821
- if (composer.quotedMessage) {
4822
- payload.quoted_message_id = composer.quotedMessage.id;
4823
- }
4824
- if (composer.pollId) {
4825
- payload.poll_id = composer.pollId;
4826
- }
4827
- if (composer.showReplyInChannel) {
4828
- payload.show_in_channel = true;
4829
- }
4830
- return next({
4831
- ...state,
4832
- localMessage: {
4833
- ...state.localMessage,
4834
- ...payload,
4835
- quoted_message: composer.quotedMessage ?? void 0
4836
- },
4837
- message: {
4838
- ...state.message,
4839
- ...payload
4840
- }
4841
- });
4906
+ updatePaginationStateFromQuery(result) {
4907
+ const { items, next } = result;
4908
+ const stateUpdate = {};
4909
+ if (Object.prototype.hasOwnProperty.call(result, "next")) {
4910
+ stateUpdate.next = next;
4911
+ stateUpdate.hasNext = !!next;
4912
+ } else {
4913
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
4914
+ stateUpdate.hasNext = items.length === this.pageSize;
4842
4915
  }
4916
+ return stateUpdate;
4843
4917
  }
4844
- });
4845
- var createDraftMessageComposerStateCompositionMiddleware = (composer) => ({
4846
- id: "stream-io/message-composer-middleware/draft-own-state",
4847
- handlers: {
4848
- compose: ({
4849
- state,
4850
- next
4851
- }) => {
4852
- const payload = {};
4853
- if (composer.quotedMessage) {
4854
- payload.quoted_message_id = composer.quotedMessage.id;
4855
- }
4856
- if (composer.pollId) {
4857
- payload.poll_id = composer.pollId;
4858
- }
4859
- if (composer.showReplyInChannel) {
4860
- payload.show_in_channel = true;
4918
+ resetState() {
4919
+ this.state.next(this.initialState);
4920
+ }
4921
+ resetStateAndActivate() {
4922
+ this.resetState();
4923
+ this.activate();
4924
+ }
4925
+ };
4926
+ var BaseSearchSource = class extends BaseSearchSourceBase {
4927
+ constructor(options) {
4928
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4929
+ super(options);
4930
+ this.setDebounceOptions = ({ debounceMs }) => {
4931
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4932
+ };
4933
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4934
+ this.setDebounceOptions({ debounceMs });
4935
+ }
4936
+ async executeQuery(newSearchString) {
4937
+ if (!this.canExecuteQuery(newSearchString)) return;
4938
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4939
+ let stateUpdate = {};
4940
+ try {
4941
+ const results = await this.query(searchString);
4942
+ if (!results) return;
4943
+ const { items } = results;
4944
+ stateUpdate = this.updatePaginationStateFromQuery(results);
4945
+ stateUpdate.items = await this.filterQueryResults(items);
4946
+ } catch (e) {
4947
+ stateUpdate.lastQueryError = e;
4948
+ if (isAPIError(e) && !isErrorRetryable(e)) {
4949
+ stateUpdate.hasNext = false;
4861
4950
  }
4862
- return next({
4863
- ...state,
4864
- draft: {
4865
- ...state.draft,
4866
- ...payload
4867
- }
4868
- });
4951
+ } finally {
4952
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4869
4953
  }
4870
4954
  }
4871
- });
4872
-
4873
- // src/messageComposer/middleware/messageComposer/userDataInjection.ts
4874
- var createUserDataInjectionMiddleware = (composer) => ({
4875
- id: "stream-io/message-composer-middleware/user-data-injection",
4876
- handlers: {
4877
- compose: ({
4878
- state,
4879
- next,
4880
- forward
4881
- }) => {
4882
- if (!composer.client.user) {
4883
- return forward();
4955
+ cancelScheduledQuery() {
4956
+ this.searchDebounced.cancel();
4957
+ }
4958
+ };
4959
+ var BaseSearchSourceSync = class extends BaseSearchSourceBase {
4960
+ constructor(options) {
4961
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4962
+ super(options);
4963
+ this.setDebounceOptions = ({ debounceMs }) => {
4964
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4965
+ };
4966
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4967
+ this.setDebounceOptions({ debounceMs });
4968
+ }
4969
+ executeQuery(newSearchString) {
4970
+ if (!this.canExecuteQuery(newSearchString)) return;
4971
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4972
+ let stateUpdate = {};
4973
+ try {
4974
+ const results = this.query(searchString);
4975
+ if (!results) return;
4976
+ const { items } = results;
4977
+ stateUpdate = this.updatePaginationStateFromQuery(results);
4978
+ stateUpdate.items = this.filterQueryResults(items);
4979
+ } catch (e) {
4980
+ stateUpdate.lastQueryError = e;
4981
+ if (isAPIError(e) && !isErrorRetryable(e)) {
4982
+ stateUpdate.hasNext = false;
4884
4983
  }
4885
- const { channel_mutes, devices, mutes, ...messageUser } = composer.client.user;
4886
- return next({
4887
- ...state,
4888
- localMessage: {
4889
- ...state.localMessage,
4890
- user: messageUser,
4891
- user_id: messageUser.id
4892
- }
4893
- });
4984
+ } finally {
4985
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4894
4986
  }
4895
4987
  }
4896
- });
4897
-
4898
- // src/messageComposer/middleware/messageComposer/pollOnly.ts
4899
- var pollLocalMessageNullifiedFields = {
4900
- attachments: [],
4901
- mentioned_users: [],
4902
- parent_id: void 0,
4903
- quoted_message: void 0,
4904
- text: ""
4905
- };
4906
- var createPollOnlyCompositionMiddleware = (composer) => ({
4907
- id: "stream-io/message-composer-middleware/poll-only",
4908
- handlers: {
4909
- compose: ({
4910
- state,
4911
- complete,
4912
- forward
4913
- }) => {
4914
- const pollId = composer.pollId;
4915
- const isEditingMessage = !!composer.editedMessage;
4916
- const isComposingThreadReply = !!composer.threadId;
4917
- if (!pollId || isComposingThreadReply || isEditingMessage) return forward();
4918
- return complete({
4919
- ...state,
4920
- localMessage: {
4921
- ...state.localMessage,
4922
- ...pollLocalMessageNullifiedFields,
4923
- poll_id: pollId
4924
- },
4925
- message: {
4926
- id: state.localMessage.id,
4927
- poll_id: pollId
4928
- }
4929
- });
4930
- }
4931
- }
4932
- });
4933
-
4934
- // src/messageComposer/middleware/messageComposer/sharedLocation.ts
4935
- var createSharedLocationCompositionMiddleware = (composer) => ({
4936
- id: "stream-io/message-composer-middleware/shared-location",
4937
- handlers: {
4938
- compose: ({
4939
- state,
4940
- next,
4941
- forward
4942
- }) => {
4943
- const { locationComposer } = composer;
4944
- const location = locationComposer.validLocation;
4945
- if (!locationComposer || !location || !composer.client.user) return forward();
4946
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4947
- return next({
4948
- ...state,
4949
- localMessage: {
4950
- ...state.localMessage,
4951
- shared_location: {
4952
- ...location,
4953
- channel_cid: composer.channel.cid,
4954
- created_at: timestamp,
4955
- updated_at: timestamp,
4956
- user_id: composer.client.user.id
4957
- }
4958
- },
4959
- message: {
4960
- ...state.message,
4961
- shared_location: location
4962
- }
4963
- });
4964
- }
4965
- }
4966
- });
4967
-
4968
- // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
4969
- var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
4970
- constructor({ composer }) {
4971
- super();
4972
- this.use([
4973
- createUserDataInjectionMiddleware(composer),
4974
- createPollOnlyCompositionMiddleware(composer),
4975
- createTextComposerCompositionMiddleware(composer),
4976
- createAttachmentsCompositionMiddleware(composer),
4977
- createLinkPreviewsCompositionMiddleware(composer),
4978
- createSharedLocationCompositionMiddleware(composer),
4979
- createMessageComposerStateCompositionMiddleware(composer),
4980
- createCustomDataCompositionMiddleware(composer),
4981
- createCompositionValidationMiddleware(composer),
4982
- createCompositionDataCleanupMiddleware(composer)
4983
- ]);
4984
- }
4985
- };
4986
- var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
4987
- constructor({ composer }) {
4988
- super();
4989
- this.use([
4990
- createDraftTextComposerCompositionMiddleware(composer),
4991
- createDraftAttachmentsCompositionMiddleware(composer),
4992
- createDraftLinkPreviewsCompositionMiddleware(composer),
4993
- createDraftMessageComposerStateCompositionMiddleware(composer),
4994
- createDraftCustomDataCompositionMiddleware(composer),
4995
- createDraftCompositionValidationMiddleware(composer)
4996
- ]);
4997
- }
4998
- };
4999
-
5000
- // src/messageComposer/middleware/messageComposer/commandInjection.ts
5001
- var createCommandInjectionMiddleware = (composer) => ({
5002
- handlers: {
5003
- compose: ({
5004
- forward,
5005
- next,
5006
- state
5007
- }) => {
5008
- const command = composer.textComposer.command;
5009
- if (!command) {
5010
- return forward();
5011
- }
5012
- const { text } = state.localMessage;
5013
- const injection = `/${command?.name}`;
5014
- const enrichedText = `${injection} ${text}`;
5015
- return next({
5016
- ...state,
5017
- localMessage: {
5018
- ...state.localMessage,
5019
- text: enrichedText
5020
- },
5021
- message: {
5022
- ...state.message,
5023
- text: enrichedText
5024
- }
5025
- });
5026
- }
5027
- },
5028
- id: "stream-io/message-composer-middleware/command-string-injection"
5029
- });
5030
- var createDraftCommandInjectionMiddleware = (composer) => ({
5031
- handlers: {
5032
- compose: ({
5033
- forward,
5034
- next,
5035
- state
5036
- }) => {
5037
- const command = composer.textComposer.command;
5038
- if (!command) {
5039
- return forward();
5040
- }
5041
- const { text } = state.draft;
5042
- const injection = `/${command?.name}`;
5043
- const enrichedText = `${injection} ${text}`;
5044
- return next({
5045
- ...state,
5046
- draft: {
5047
- ...state.draft,
5048
- text: enrichedText
5049
- }
5050
- });
5051
- }
5052
- },
5053
- id: "stream-io/message-composer-middleware/draft-command-string-injection"
5054
- });
5055
-
5056
- // src/messageComposer/middleware/textComposer/activeCommandGuard.ts
5057
- var createActiveCommandGuardMiddleware = () => ({
5058
- handlers: {
5059
- onChange: ({ complete, forward, state }) => {
5060
- if (state.command) {
5061
- return complete(state);
5062
- }
5063
- return forward();
5064
- },
5065
- onSuggestionItemSelect: ({ forward }) => forward()
5066
- },
5067
- id: "stream-io/text-composer/active-command-guard"
5068
- });
5069
-
5070
- // src/errors.ts
5071
- var APIErrorCodes = {
5072
- "-1": { name: "InternalSystemError", retryable: true },
5073
- "2": { name: "AccessKeyError", retryable: false },
5074
- "3": { name: "AuthenticationFailedError", retryable: true },
5075
- "4": { name: "InputError", retryable: false },
5076
- "6": { name: "DuplicateUsernameError", retryable: false },
5077
- "9": { name: "RateLimitError", retryable: true },
5078
- "16": { name: "DoesNotExistError", retryable: false },
5079
- "17": { name: "NotAllowedError", retryable: false },
5080
- "18": { name: "EventNotSupportedError", retryable: false },
5081
- "19": { name: "ChannelFeatureNotSupportedError", retryable: false },
5082
- "20": { name: "MessageTooLongError", retryable: false },
5083
- "21": { name: "MultipleNestingLevelError", retryable: false },
5084
- "22": { name: "PayloadTooBigError", retryable: false },
5085
- "23": { name: "RequestTimeoutError", retryable: true },
5086
- "24": { name: "MaxHeaderSizeExceededError", retryable: false },
5087
- "40": { name: "AuthErrorTokenExpired", retryable: false },
5088
- "41": { name: "AuthErrorTokenNotValidYet", retryable: false },
5089
- "42": { name: "AuthErrorTokenUsedBeforeIssuedAt", retryable: false },
5090
- "43": { name: "AuthErrorTokenSignatureInvalid", retryable: false },
5091
- "44": { name: "CustomCommandEndpointMissingError", retryable: false },
5092
- "45": { name: "CustomCommandEndpointCallError", retryable: true },
5093
- "46": { name: "ConnectionIDNotFoundError", retryable: false },
5094
- "60": { name: "CoolDownError", retryable: true },
5095
- "69": { name: "ErrWrongRegion", retryable: false },
5096
- "70": { name: "ErrQueryChannelPermissions", retryable: false },
5097
- "71": { name: "ErrTooManyConnections", retryable: true },
5098
- "99": { name: "AppSuspendedError", retryable: false }
5099
- };
5100
- function isAPIError(error) {
5101
- return error.code !== void 0;
5102
- }
5103
- function isErrorRetryable(error) {
5104
- if (!error.code) return false;
5105
- const err = APIErrorCodes[`${error.code}`];
5106
- if (!err) return false;
5107
- return err.retryable;
5108
- }
5109
- function isConnectionIDError(error) {
5110
- return error.code === 46;
5111
- }
5112
- function isWSFailure(err) {
5113
- if (typeof err.isWSFailure === "boolean") {
5114
- return err.isWSFailure;
5115
- }
5116
- try {
5117
- return JSON.parse(err.message).isWSFailure;
5118
- } catch (_) {
5119
- return false;
5120
- }
5121
- }
5122
- function isErrorResponse(res) {
5123
- return !res.status || res.status < 200 || 300 <= res.status;
5124
- }
5125
-
5126
- // src/search/BaseSearchSource.ts
5127
- var DEFAULT_SEARCH_SOURCE_OPTIONS = {
5128
- debounceMs: 300,
5129
- pageSize: 10
5130
- };
5131
- var BaseSearchSourceBase = class {
5132
- constructor(options) {
5133
- this.activate = () => {
5134
- if (this.isActive) return;
5135
- this.state.partialNext({ isActive: true });
5136
- };
5137
- this.deactivate = () => {
5138
- if (!this.isActive) return;
5139
- this.state.partialNext({ isActive: false });
5140
- };
5141
- this.canExecuteQuery = (newSearchString) => {
5142
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5143
- const searchString = newSearchString ?? this.searchQuery;
5144
- return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && searchString);
5145
- };
5146
- const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5147
- this.pageSize = pageSize;
5148
- this.state = new StateStore(this.initialState);
5149
- }
5150
- get lastQueryError() {
5151
- return this.state.getLatestValue().lastQueryError;
5152
- }
5153
- get hasNext() {
5154
- return this.state.getLatestValue().hasNext;
5155
- }
5156
- get hasResults() {
5157
- return Array.isArray(this.state.getLatestValue().items);
5158
- }
5159
- get isActive() {
5160
- return this.state.getLatestValue().isActive;
5161
- }
5162
- get isLoading() {
5163
- return this.state.getLatestValue().isLoading;
5164
- }
5165
- get initialState() {
5166
- return {
5167
- hasNext: true,
5168
- isActive: false,
5169
- isLoading: false,
5170
- items: void 0,
5171
- lastQueryError: void 0,
5172
- next: void 0,
5173
- offset: 0,
5174
- searchQuery: ""
5175
- };
5176
- }
5177
- get items() {
5178
- return this.state.getLatestValue().items;
5179
- }
5180
- get next() {
5181
- return this.state.getLatestValue().next;
5182
- }
5183
- get offset() {
5184
- return this.state.getLatestValue().offset;
5185
- }
5186
- get searchQuery() {
5187
- return this.state.getLatestValue().searchQuery;
5188
- }
5189
- getStateBeforeFirstQuery(newSearchString) {
5190
- return {
5191
- ...this.initialState,
5192
- isActive: this.isActive,
5193
- isLoading: true,
5194
- searchQuery: newSearchString
5195
- };
5196
- }
5197
- getStateAfterQuery(stateUpdate, isFirstPage) {
5198
- const current = this.state.getLatestValue();
5199
- return {
5200
- ...current,
5201
- lastQueryError: void 0,
5202
- // reset lastQueryError that can be overridden by the stateUpdate
5203
- ...stateUpdate,
5204
- isLoading: false,
5205
- items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5206
- };
5207
- }
5208
- prepareStateForQuery(newSearchString) {
5209
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5210
- const searchString = newSearchString ?? this.searchQuery;
5211
- if (hasNewSearchQuery) {
5212
- this.state.next(this.getStateBeforeFirstQuery(newSearchString ?? ""));
5213
- } else {
5214
- this.state.partialNext({ isLoading: true });
5215
- }
5216
- return { searchString, hasNewSearchQuery };
5217
- }
5218
- updatePaginationStateFromQuery(result) {
5219
- const { items, next } = result;
5220
- const stateUpdate = {};
5221
- if (Object.prototype.hasOwnProperty.call(result, "next")) {
5222
- stateUpdate.next = next;
5223
- stateUpdate.hasNext = !!next;
5224
- } else {
5225
- stateUpdate.offset = (this.offset ?? 0) + items.length;
5226
- stateUpdate.hasNext = items.length === this.pageSize;
5227
- }
5228
- return stateUpdate;
5229
- }
5230
- resetState() {
5231
- this.state.next(this.initialState);
5232
- }
5233
- resetStateAndActivate() {
5234
- this.resetState();
5235
- this.activate();
5236
- }
5237
- };
5238
- var BaseSearchSource = class extends BaseSearchSourceBase {
5239
- constructor(options) {
5240
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5241
- super(options);
5242
- this.setDebounceOptions = ({ debounceMs }) => {
5243
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5244
- };
5245
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
5246
- this.setDebounceOptions({ debounceMs });
5247
- }
5248
- async executeQuery(newSearchString) {
5249
- if (!this.canExecuteQuery(newSearchString)) return;
5250
- const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
5251
- let stateUpdate = {};
5252
- try {
5253
- const results = await this.query(searchString);
5254
- if (!results) return;
5255
- const { items } = results;
5256
- stateUpdate = this.updatePaginationStateFromQuery(results);
5257
- stateUpdate.items = await this.filterQueryResults(items);
5258
- } catch (e) {
5259
- stateUpdate.lastQueryError = e;
5260
- if (isAPIError(e) && !isErrorRetryable(e)) {
5261
- stateUpdate.hasNext = false;
5262
- }
5263
- } finally {
5264
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
5265
- }
5266
- }
5267
- cancelScheduledQuery() {
5268
- this.searchDebounced.cancel();
5269
- }
5270
- };
5271
- var BaseSearchSourceSync = class extends BaseSearchSourceBase {
5272
- constructor(options) {
5273
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5274
- super(options);
5275
- this.setDebounceOptions = ({ debounceMs }) => {
5276
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5277
- };
5278
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
5279
- this.setDebounceOptions({ debounceMs });
5280
- }
5281
- executeQuery(newSearchString) {
5282
- if (!this.canExecuteQuery(newSearchString)) return;
5283
- const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
5284
- let stateUpdate = {};
5285
- try {
5286
- const results = this.query(searchString);
5287
- if (!results) return;
5288
- const { items } = results;
5289
- stateUpdate = this.updatePaginationStateFromQuery(results);
5290
- stateUpdate.items = this.filterQueryResults(items);
5291
- } catch (e) {
5292
- stateUpdate.lastQueryError = e;
5293
- if (isAPIError(e) && !isErrorRetryable(e)) {
5294
- stateUpdate.hasNext = false;
5295
- }
5296
- } finally {
5297
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
5298
- }
5299
- }
5300
- cancelScheduledQuery() {
5301
- this.searchDebounced.cancel();
5302
- }
4988
+ cancelScheduledQuery() {
4989
+ this.searchDebounced.cancel();
4990
+ }
5303
4991
  };
5304
4992
 
5305
4993
  // src/search/SearchController.ts
@@ -5395,586 +5083,1095 @@ var SearchController = class {
5395
5083
  get activeSources() {
5396
5084
  return this.state.getLatestValue().sources.filter((s) => s.isActive);
5397
5085
  }
5398
- get isActive() {
5399
- return this.state.getLatestValue().isActive;
5086
+ get isActive() {
5087
+ return this.state.getLatestValue().isActive;
5088
+ }
5089
+ get searchQuery() {
5090
+ return this.state.getLatestValue().searchQuery;
5091
+ }
5092
+ get searchSourceTypes() {
5093
+ return this.sources.map((s) => s.type);
5094
+ }
5095
+ };
5096
+
5097
+ // src/pagination/BasePaginator.ts
5098
+ var DEFAULT_PAGINATION_OPTIONS = {
5099
+ debounceMs: 300,
5100
+ pageSize: 10
5101
+ };
5102
+ var BasePaginator = class {
5103
+ constructor(options) {
5104
+ this._isCursorPagination = false;
5105
+ this.setDebounceOptions = ({ debounceMs }) => {
5106
+ this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5107
+ };
5108
+ this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
5109
+ this.next = () => this.executeQuery({ direction: "next" });
5110
+ this.prev = () => this.executeQuery({ direction: "prev" });
5111
+ this.nextDebounced = () => {
5112
+ this._executeQueryDebounced({ direction: "next" });
5113
+ };
5114
+ this.prevDebounced = () => {
5115
+ this._executeQueryDebounced({ direction: "prev" });
5116
+ };
5117
+ const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
5118
+ this.pageSize = pageSize;
5119
+ this.state = new StateStore(this.initialState);
5120
+ this.setDebounceOptions({ debounceMs });
5121
+ }
5122
+ get lastQueryError() {
5123
+ return this.state.getLatestValue().lastQueryError;
5124
+ }
5125
+ get hasNext() {
5126
+ return this.state.getLatestValue().hasNext;
5127
+ }
5128
+ get hasPrev() {
5129
+ return this.state.getLatestValue().hasPrev;
5130
+ }
5131
+ get hasResults() {
5132
+ return Array.isArray(this.state.getLatestValue().items);
5133
+ }
5134
+ get isLoading() {
5135
+ return this.state.getLatestValue().isLoading;
5136
+ }
5137
+ get initialState() {
5138
+ return {
5139
+ hasNext: true,
5140
+ hasPrev: true,
5141
+ //todo: check if optimistic value does not cause problems in UI
5142
+ isLoading: false,
5143
+ items: void 0,
5144
+ lastQueryError: void 0,
5145
+ cursor: void 0,
5146
+ offset: 0
5147
+ };
5148
+ }
5149
+ get items() {
5150
+ return this.state.getLatestValue().items;
5151
+ }
5152
+ get cursor() {
5153
+ return this.state.getLatestValue().cursor;
5154
+ }
5155
+ get offset() {
5156
+ return this.state.getLatestValue().offset;
5157
+ }
5158
+ getStateBeforeFirstQuery() {
5159
+ return {
5160
+ ...this.initialState,
5161
+ isLoading: true
5162
+ };
5163
+ }
5164
+ getStateAfterQuery(stateUpdate, isFirstPage) {
5165
+ const current = this.state.getLatestValue();
5166
+ return {
5167
+ ...current,
5168
+ lastQueryError: void 0,
5169
+ // reset lastQueryError that can be overridden by the stateUpdate
5170
+ ...stateUpdate,
5171
+ isLoading: false,
5172
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5173
+ };
5174
+ }
5175
+ async executeQuery({ direction }) {
5176
+ if (!this.canExecuteQuery(direction)) return;
5177
+ const isFirstPage = typeof this.items === "undefined";
5178
+ if (isFirstPage) {
5179
+ this.state.next(this.getStateBeforeFirstQuery());
5180
+ } else {
5181
+ this.state.partialNext({ isLoading: true });
5182
+ }
5183
+ const stateUpdate = {};
5184
+ try {
5185
+ const results = await this.query({ direction });
5186
+ if (!results) return;
5187
+ const { items, next, prev } = results;
5188
+ if (isFirstPage && (next || prev)) {
5189
+ this._isCursorPagination = true;
5190
+ }
5191
+ if (this._isCursorPagination) {
5192
+ stateUpdate.cursor = { next: next || null, prev: prev || null };
5193
+ stateUpdate.hasNext = !!next;
5194
+ stateUpdate.hasPrev = !!prev;
5195
+ } else {
5196
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
5197
+ stateUpdate.hasNext = items.length === this.pageSize;
5198
+ }
5199
+ stateUpdate.items = await this.filterQueryResults(items);
5200
+ } catch (e) {
5201
+ stateUpdate.lastQueryError = e;
5202
+ } finally {
5203
+ this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
5204
+ }
5205
+ }
5206
+ cancelScheduledQuery() {
5207
+ this._executeQueryDebounced.cancel();
5208
+ }
5209
+ resetState() {
5210
+ this.state.next(this.initialState);
5211
+ }
5212
+ };
5213
+
5214
+ // src/pagination/FilterBuilder.ts
5215
+ var FilterBuilder = class {
5216
+ constructor(params) {
5217
+ this.context = new StateStore(params?.initialContext ?? {});
5218
+ this.filterConfig = new StateStore(
5219
+ params?.initialFilterConfig ?? {}
5220
+ );
5221
+ }
5222
+ updateFilterConfig(config) {
5223
+ this.filterConfig.partialNext(config);
5224
+ }
5225
+ enableFilter(filterKey) {
5226
+ const config = this.filterConfig.getLatestValue();
5227
+ if (config[filterKey]) {
5228
+ this.filterConfig.partialNext({
5229
+ [filterKey]: {
5230
+ ...config[filterKey],
5231
+ enabled: true
5232
+ }
5233
+ });
5234
+ }
5235
+ }
5236
+ disableFilter(filterKey) {
5237
+ const config = this.filterConfig.getLatestValue();
5238
+ if (config[filterKey]) {
5239
+ this.filterConfig.partialNext({
5240
+ [filterKey]: {
5241
+ ...config[filterKey],
5242
+ enabled: false
5243
+ }
5244
+ });
5245
+ }
5246
+ }
5247
+ updateContext(newContext) {
5248
+ this.context.partialNext(newContext);
5249
+ }
5250
+ buildFilters(params) {
5251
+ const filters = {
5252
+ ...params?.baseFilters ?? {}
5253
+ };
5254
+ const filterConfig = this.filterConfig.getLatestValue();
5255
+ for (const key in filterConfig) {
5256
+ const configItem = filterConfig[key];
5257
+ if (!configItem?.enabled) continue;
5258
+ const generated = configItem.generate({
5259
+ ...this.context.getLatestValue(),
5260
+ ...params?.context ?? {}
5261
+ });
5262
+ if (generated) Object.assign(filters, generated);
5263
+ }
5264
+ return filters;
5265
+ }
5266
+ };
5267
+
5268
+ // src/pagination/ReminderPaginator.ts
5269
+ var ReminderPaginator = class extends BasePaginator {
5270
+ constructor(client, options) {
5271
+ super(options);
5272
+ this.query = async ({
5273
+ direction
5274
+ }) => {
5275
+ const cursor = this.cursor?.[direction];
5276
+ const {
5277
+ reminders: items,
5278
+ next,
5279
+ prev
5280
+ } = await this.client.queryReminders({
5281
+ filter: this.filters,
5282
+ sort: this.sort,
5283
+ limit: this.pageSize,
5284
+ [direction]: cursor
5285
+ });
5286
+ return { items, next, prev };
5287
+ };
5288
+ this.filterQueryResults = (items) => items;
5289
+ this.client = client;
5290
+ }
5291
+ get filters() {
5292
+ return this._filters;
5293
+ }
5294
+ get sort() {
5295
+ return this._sort;
5400
5296
  }
5401
- get searchQuery() {
5402
- return this.state.getLatestValue().searchQuery;
5297
+ set filters(filters) {
5298
+ this._filters = filters;
5299
+ this.resetState();
5403
5300
  }
5404
- get searchSourceTypes() {
5405
- return this.sources.map((s) => s.type);
5301
+ set sort(sort) {
5302
+ this._sort = sort;
5303
+ this.resetState();
5406
5304
  }
5407
5305
  };
5408
5306
 
5409
- // src/pagination/BasePaginator.ts
5410
- var DEFAULT_PAGINATION_OPTIONS = {
5411
- debounceMs: 300,
5412
- pageSize: 10
5413
- };
5414
- var BasePaginator = class {
5415
- constructor(options) {
5416
- this._isCursorPagination = false;
5417
- this.setDebounceOptions = ({ debounceMs }) => {
5418
- this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5419
- };
5420
- this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
5421
- this.next = () => this.executeQuery({ direction: "next" });
5422
- this.prev = () => this.executeQuery({ direction: "prev" });
5423
- this.nextDebounced = () => {
5424
- this._executeQueryDebounced({ direction: "next" });
5425
- };
5426
- this.prevDebounced = () => {
5427
- this._executeQueryDebounced({ direction: "prev" });
5428
- };
5429
- const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
5430
- this.pageSize = pageSize;
5431
- this.state = new StateStore(this.initialState);
5432
- this.setDebounceOptions({ debounceMs });
5307
+ // src/search/UserSearchSource.ts
5308
+ var UserSearchSource = class extends BaseSearchSource {
5309
+ constructor(client, options, filterBuilderOptions = {}) {
5310
+ super(options);
5311
+ this.type = "users";
5312
+ this.client = client;
5313
+ this.filterBuilder = new FilterBuilder({
5314
+ initialFilterConfig: {
5315
+ $or: {
5316
+ enabled: true,
5317
+ generate: ({ searchQuery }) => searchQuery ? {
5318
+ $or: [
5319
+ { id: { $autocomplete: searchQuery } },
5320
+ { name: { $autocomplete: searchQuery } }
5321
+ ]
5322
+ } : null
5323
+ }
5324
+ },
5325
+ ...filterBuilderOptions
5326
+ });
5433
5327
  }
5434
- get lastQueryError() {
5435
- return this.state.getLatestValue().lastQueryError;
5328
+ async query(searchQuery) {
5329
+ const filters = this.filterBuilder.buildFilters({
5330
+ baseFilters: this.filters,
5331
+ context: { searchQuery }
5332
+ });
5333
+ const sort = { id: 1, ...this.sort };
5334
+ const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5335
+ const { users } = await this.client.queryUsers(filters, sort, options);
5336
+ return { items: users };
5436
5337
  }
5437
- get hasNext() {
5438
- return this.state.getLatestValue().hasNext;
5338
+ filterQueryResults(items) {
5339
+ return items.filter((u) => u.id !== this.client.user?.id);
5439
5340
  }
5440
- get hasPrev() {
5441
- return this.state.getLatestValue().hasPrev;
5341
+ };
5342
+
5343
+ // src/search/ChannelSearchSource.ts
5344
+ var ChannelSearchSource = class extends BaseSearchSource {
5345
+ constructor(client, options, filterBuilderOptions = {}) {
5346
+ super(options);
5347
+ this.type = "channels";
5348
+ this.client = client;
5349
+ this.filterBuilder = new FilterBuilder({
5350
+ ...filterBuilderOptions,
5351
+ initialFilterConfig: {
5352
+ name: {
5353
+ enabled: true,
5354
+ generate: ({ searchQuery }) => searchQuery ? { name: { $autocomplete: searchQuery } } : null
5355
+ },
5356
+ ...filterBuilderOptions.initialFilterConfig
5357
+ }
5358
+ });
5442
5359
  }
5443
- get hasResults() {
5444
- return Array.isArray(this.state.getLatestValue().items);
5360
+ async query(searchQuery) {
5361
+ const filters = this.filterBuilder.buildFilters({
5362
+ baseFilters: {
5363
+ ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5364
+ ...this.filters
5365
+ },
5366
+ context: { searchQuery }
5367
+ });
5368
+ const sort = this.sort ?? {};
5369
+ const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5370
+ const items = await this.client.queryChannels(filters, sort, options);
5371
+ return { items };
5445
5372
  }
5446
- get isLoading() {
5447
- return this.state.getLatestValue().isLoading;
5373
+ filterQueryResults(items) {
5374
+ return items;
5448
5375
  }
5449
- get initialState() {
5450
- return {
5451
- hasNext: true,
5452
- hasPrev: true,
5453
- //todo: check if optimistic value does not cause problems in UI
5454
- isLoading: false,
5455
- items: void 0,
5456
- lastQueryError: void 0,
5457
- cursor: void 0,
5458
- offset: 0
5459
- };
5376
+ };
5377
+
5378
+ // src/search/MessageSearchSource.ts
5379
+ var MessageSearchSource = class extends BaseSearchSource {
5380
+ constructor(client, options, filterBuilderOptions) {
5381
+ super(options);
5382
+ this.type = "messages";
5383
+ this.client = client;
5384
+ this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5385
+ this.messageSearchFilterBuilder = new FilterBuilder({
5386
+ ...filterBuilderOptions?.messageSearch,
5387
+ initialFilterConfig: {
5388
+ text: {
5389
+ enabled: true,
5390
+ generate: ({ searchQuery }) => searchQuery ? { text: searchQuery } : null
5391
+ },
5392
+ ...filterBuilderOptions?.messageSearch?.initialFilterConfig
5393
+ }
5394
+ });
5395
+ this.channelQueryFilterBuilder = new FilterBuilder({
5396
+ ...filterBuilderOptions?.channelQuery,
5397
+ initialFilterConfig: {
5398
+ cid: {
5399
+ enabled: true,
5400
+ generate: ({ cids }) => cids ? { cid: { $in: cids } } : null
5401
+ },
5402
+ ...filterBuilderOptions?.channelQuery?.initialFilterConfig
5403
+ }
5404
+ });
5460
5405
  }
5461
- get items() {
5462
- return this.state.getLatestValue().items;
5406
+ async query(searchQuery) {
5407
+ if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
5408
+ const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5409
+ baseFilters: {
5410
+ ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5411
+ ...this.messageSearchChannelFilters
5412
+ },
5413
+ context: { searchQuery }
5414
+ });
5415
+ const messageFilters = this.messageSearchFilterBuilder.buildFilters({
5416
+ baseFilters: {
5417
+ type: "regular",
5418
+ ...this.messageSearchFilters
5419
+ },
5420
+ context: { searchQuery }
5421
+ });
5422
+ const sort = {
5423
+ created_at: -1,
5424
+ ...this.messageSearchSort
5425
+ };
5426
+ const options = {
5427
+ limit: this.pageSize,
5428
+ next: this.next,
5429
+ sort
5430
+ };
5431
+ const { next, results } = await this.client.search(
5432
+ channelFilters,
5433
+ messageFilters,
5434
+ options
5435
+ );
5436
+ const items = results.map(({ message }) => message);
5437
+ const cids = Array.from(
5438
+ items.reduce((acc, message) => {
5439
+ if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
5440
+ return acc;
5441
+ }, /* @__PURE__ */ new Set())
5442
+ );
5443
+ if (cids.length > 0) {
5444
+ const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
5445
+ baseFilters: this.channelQueryFilters,
5446
+ context: { cids }
5447
+ });
5448
+ await this.client.queryChannels(
5449
+ channelQueryFilters,
5450
+ {
5451
+ last_message_at: -1,
5452
+ ...this.channelQuerySort
5453
+ },
5454
+ this.channelQueryOptions
5455
+ );
5456
+ }
5457
+ return { items, next };
5463
5458
  }
5464
- get cursor() {
5465
- return this.state.getLatestValue().cursor;
5459
+ filterQueryResults(items) {
5460
+ return items;
5466
5461
  }
5467
- get offset() {
5468
- return this.state.getLatestValue().offset;
5462
+ };
5463
+
5464
+ // src/messageComposer/middleware/textComposer/commandUtils.ts
5465
+ function escapeCommandRegExp(text) {
5466
+ return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, "\\$&");
5467
+ }
5468
+ var getRawCommandName = (text) => text?.match(/^\/(\S+)(?:\s.*)?$/)?.[1];
5469
+ var getCompleteCommandInString = (text) => {
5470
+ const match = text.match(/^\/(\S+)\s+.*/);
5471
+ const commandName = match && match[1];
5472
+ return commandName;
5473
+ };
5474
+ var stripCommandFromText = (text, commandName) => text.replace(new RegExp(`^${escapeCommandRegExp(`/${commandName}`)}\\s*`), "");
5475
+ var notifyCommandDisabled = (composer, command) => {
5476
+ const disabledReason = composer.getCommandDisabledReason(command);
5477
+ if (!disabledReason) return;
5478
+ composer.client.notifications.addWarning({
5479
+ message: disabledReason === "editing" ? "Command not available while editing" : "Command not available while replying",
5480
+ origin: {
5481
+ emitter: "MessageComposer",
5482
+ context: { command, composer }
5483
+ },
5484
+ options: {
5485
+ type: "validation:command:disabled",
5486
+ metadata: {
5487
+ command: command.name,
5488
+ reason: disabledReason
5489
+ }
5490
+ }
5491
+ });
5492
+ return true;
5493
+ };
5494
+
5495
+ // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
5496
+ var getTriggerCharWithToken = ({
5497
+ trigger,
5498
+ text,
5499
+ isCommand = false,
5500
+ acceptTrailingSpaces = true
5501
+ }) => {
5502
+ const escapedTrigger = escapeCommandRegExp(trigger);
5503
+ const triggerNorWhitespace = `[^\\s${escapedTrigger}]*`;
5504
+ const match = text.match(
5505
+ new RegExp(
5506
+ isCommand ? `^[${escapedTrigger}]${triggerNorWhitespace}$` : acceptTrailingSpaces ? `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}\\s?${triggerNorWhitespace}$` : `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}$`,
5507
+ "g"
5508
+ )
5509
+ );
5510
+ return match && match[match.length - 1].trim();
5511
+ };
5512
+ var insertItemWithTrigger = ({
5513
+ insertText,
5514
+ selection,
5515
+ text,
5516
+ trigger
5517
+ }) => {
5518
+ const beforeCursor = text.slice(0, selection.end);
5519
+ const afterCursor = text.slice(selection.end);
5520
+ const lastIndex = beforeCursor.lastIndexOf(trigger);
5521
+ const newText = beforeCursor.slice(0, lastIndex) + insertText + afterCursor;
5522
+ return {
5523
+ text: newText,
5524
+ selection: {
5525
+ start: lastIndex + insertText.length,
5526
+ end: lastIndex + insertText.length
5527
+ }
5528
+ };
5529
+ };
5530
+ var replaceWordWithEntity = async ({
5531
+ caretPosition,
5532
+ getEntityString,
5533
+ text
5534
+ }) => {
5535
+ const lastWordRegex = /([^\s]+)(\s*)$/;
5536
+ const match = lastWordRegex.exec(text.slice(0, caretPosition));
5537
+ if (!match) return text;
5538
+ const lastWord = match[1];
5539
+ if (!lastWord) return text;
5540
+ const spaces = match[2];
5541
+ const newWord = await getEntityString(lastWord);
5542
+ if (newWord == null) return text;
5543
+ const textBeforeWord = text.slice(0, caretPosition - match[0].length);
5544
+ const textAfterCaret = text.slice(caretPosition, -1);
5545
+ return textBeforeWord + newWord + spaces + textAfterCaret;
5546
+ };
5547
+ var getTokenizedSuggestionDisplayName = ({
5548
+ displayName,
5549
+ searchToken
5550
+ }) => ({
5551
+ tokenizedDisplayName: {
5552
+ token: searchToken,
5553
+ parts: searchToken ? displayName.split(new RegExp(`(${escapeCommandRegExp(searchToken)})`, "gi")).filter(Boolean) : [displayName]
5469
5554
  }
5470
- getStateBeforeFirstQuery() {
5471
- return {
5472
- ...this.initialState,
5473
- isLoading: true
5555
+ });
5556
+
5557
+ // src/messageComposer/middleware/textComposer/commands.ts
5558
+ var CommandSearchSource = class extends BaseSearchSourceSync {
5559
+ constructor(channel, options) {
5560
+ super(options);
5561
+ this.type = "commands";
5562
+ this.canExecuteQuery = (newSearchString) => {
5563
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
5564
+ return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
5474
5565
  };
5566
+ this.channel = channel;
5475
5567
  }
5476
- getStateAfterQuery(stateUpdate, isFirstPage) {
5477
- const current = this.state.getLatestValue();
5568
+ getStateBeforeFirstQuery(newSearchString) {
5569
+ const newState = super.getStateBeforeFirstQuery(newSearchString);
5570
+ const { items } = this.state.getLatestValue();
5478
5571
  return {
5479
- ...current,
5480
- lastQueryError: void 0,
5481
- // reset lastQueryError that can be overridden by the stateUpdate
5482
- ...stateUpdate,
5483
- isLoading: false,
5484
- items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5572
+ ...newState,
5573
+ items
5574
+ // preserve items to avoid flickering
5485
5575
  };
5486
5576
  }
5487
- async executeQuery({ direction }) {
5488
- if (!this.canExecuteQuery(direction)) return;
5489
- const isFirstPage = typeof this.items === "undefined";
5490
- if (isFirstPage) {
5491
- this.state.next(this.getStateBeforeFirstQuery());
5492
- } else {
5493
- this.state.partialNext({ isLoading: true });
5494
- }
5495
- const stateUpdate = {};
5496
- try {
5497
- const results = await this.query({ direction });
5498
- if (!results) return;
5499
- const { items, next, prev } = results;
5500
- if (isFirstPage && (next || prev)) {
5501
- this._isCursorPagination = true;
5577
+ query(searchQuery) {
5578
+ const channelConfig = this.channel.getConfig();
5579
+ const commands = channelConfig?.commands || [];
5580
+ const selectedCommands = commands.filter(
5581
+ (command) => !!(command.name && command.name.toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1)
5582
+ );
5583
+ selectedCommands.sort((a, b) => {
5584
+ let nameA = a.name?.toLowerCase();
5585
+ let nameB = b.name?.toLowerCase();
5586
+ if (nameA?.indexOf(searchQuery) === 0) {
5587
+ nameA = `0${nameA}`;
5502
5588
  }
5503
- if (this._isCursorPagination) {
5504
- stateUpdate.cursor = { next: next || null, prev: prev || null };
5505
- stateUpdate.hasNext = !!next;
5506
- stateUpdate.hasPrev = !!prev;
5507
- } else {
5508
- stateUpdate.offset = (this.offset ?? 0) + items.length;
5509
- stateUpdate.hasNext = items.length === this.pageSize;
5589
+ if (nameB?.indexOf(searchQuery) === 0) {
5590
+ nameB = `0${nameB}`;
5510
5591
  }
5511
- stateUpdate.items = await this.filterQueryResults(items);
5512
- } catch (e) {
5513
- stateUpdate.lastQueryError = e;
5514
- } finally {
5515
- this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
5516
- }
5592
+ if (nameA != null && nameB != null) {
5593
+ if (nameA < nameB) {
5594
+ return -1;
5595
+ }
5596
+ if (nameA > nameB) {
5597
+ return 1;
5598
+ }
5599
+ }
5600
+ return 0;
5601
+ });
5602
+ return {
5603
+ items: selectedCommands.map((command) => ({
5604
+ ...command,
5605
+ id: command.name
5606
+ })),
5607
+ next: null
5608
+ };
5517
5609
  }
5518
- cancelScheduledQuery() {
5519
- this._executeQueryDebounced.cancel();
5610
+ filterQueryResults(items) {
5611
+ return items;
5520
5612
  }
5521
- resetState() {
5522
- this.state.next(this.initialState);
5613
+ };
5614
+ var DEFAULT_OPTIONS = { minChars: 1, trigger: "/" };
5615
+ var createCommandsMiddleware = (channel, options) => {
5616
+ const finalOptions = mergeWith(DEFAULT_OPTIONS, options ?? {});
5617
+ let searchSource = new CommandSearchSource(channel);
5618
+ if (options?.searchSource) {
5619
+ searchSource = options.searchSource;
5620
+ searchSource.resetState();
5523
5621
  }
5622
+ searchSource.activate();
5623
+ return {
5624
+ id: "stream-io/text-composer/commands-middleware",
5625
+ handlers: {
5626
+ onChange: ({ state, next, complete, forward }) => {
5627
+ if (!state.selection) return forward();
5628
+ const finalText = state.text.slice(0, state.selection.end);
5629
+ const commandName = getCompleteCommandInString(finalText);
5630
+ if (commandName) {
5631
+ const command = searchSource?.query(commandName).items[0];
5632
+ const composer = options?.composer;
5633
+ if (command && !composer?.isCommandDisabled(command)) {
5634
+ return next({
5635
+ ...state,
5636
+ command,
5637
+ suggestions: void 0
5638
+ });
5639
+ }
5640
+ }
5641
+ const triggerWithToken = getTriggerCharWithToken({
5642
+ trigger: finalOptions.trigger,
5643
+ text: finalText,
5644
+ acceptTrailingSpaces: false,
5645
+ isCommand: true
5646
+ });
5647
+ const newSearchTriggerred = triggerWithToken && triggerWithToken.length === finalOptions.minChars;
5648
+ if (newSearchTriggerred) {
5649
+ searchSource.resetStateAndActivate();
5650
+ }
5651
+ const triggerWasRemoved = !triggerWithToken || triggerWithToken.length < finalOptions.minChars;
5652
+ if (triggerWasRemoved) {
5653
+ const hasStaleSuggestions = state.suggestions?.trigger === finalOptions.trigger;
5654
+ const newState = { ...state };
5655
+ if (hasStaleSuggestions) {
5656
+ delete newState.suggestions;
5657
+ }
5658
+ return next(newState);
5659
+ }
5660
+ return complete({
5661
+ ...state,
5662
+ command: null,
5663
+ suggestions: {
5664
+ query: triggerWithToken.slice(1),
5665
+ searchSource,
5666
+ trigger: finalOptions.trigger
5667
+ }
5668
+ });
5669
+ },
5670
+ onSuggestionItemSelect: ({ state, next, forward }) => {
5671
+ const { selectedSuggestion } = state.change ?? {};
5672
+ if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger)
5673
+ return forward();
5674
+ const composer = options?.composer;
5675
+ if (composer && notifyCommandDisabled(composer, selectedSuggestion)) {
5676
+ return forward();
5677
+ }
5678
+ searchSource.resetStateAndActivate();
5679
+ return next({
5680
+ ...state,
5681
+ ...insertItemWithTrigger({
5682
+ insertText: `/${selectedSuggestion.name} `,
5683
+ selection: state.selection,
5684
+ text: state.text,
5685
+ trigger: finalOptions.trigger
5686
+ }),
5687
+ command: selectedSuggestion,
5688
+ suggestions: void 0
5689
+ });
5690
+ }
5691
+ }
5692
+ };
5524
5693
  };
5525
5694
 
5526
- // src/pagination/FilterBuilder.ts
5527
- var FilterBuilder = class {
5528
- constructor(params) {
5529
- this.context = new StateStore(params?.initialContext ?? {});
5530
- this.filterConfig = new StateStore(
5531
- params?.initialFilterConfig ?? {}
5532
- );
5533
- }
5534
- updateFilterConfig(config) {
5535
- this.filterConfig.partialNext(config);
5695
+ // src/messageComposer/middleware/messageComposer/compositionValidation.ts
5696
+ var getCommandByName = (searchSource, commandName) => {
5697
+ if (!commandName) return;
5698
+ const normalizedCommandName = commandName.toLowerCase();
5699
+ return searchSource.query(normalizedCommandName).items.find((command) => command.name?.toLowerCase() === normalizedCommandName);
5700
+ };
5701
+ var getDisabledRawCommand = (composer, searchSource, text) => {
5702
+ const rawCommand = getCommandByName(searchSource, getRawCommandName(text));
5703
+ if (rawCommand && composer.isCommandDisabled(rawCommand)) {
5704
+ return rawCommand;
5536
5705
  }
5537
- enableFilter(filterKey) {
5538
- const config = this.filterConfig.getLatestValue();
5539
- if (config[filterKey]) {
5540
- this.filterConfig.partialNext({
5541
- [filterKey]: {
5542
- ...config[filterKey],
5543
- enabled: true
5706
+ };
5707
+ var createCompositionValidationMiddleware = (composer) => {
5708
+ const commandSearchSource = new CommandSearchSource(composer.channel);
5709
+ return {
5710
+ id: "stream-io/message-composer-middleware/data-validation",
5711
+ handlers: {
5712
+ compose: async ({
5713
+ state,
5714
+ discard,
5715
+ forward
5716
+ }) => {
5717
+ const { maxLengthOnSend } = composer.config.text ?? {};
5718
+ const inputText = state.message.text ?? "";
5719
+ const disabledRawCommand = getDisabledRawCommand(
5720
+ composer,
5721
+ commandSearchSource,
5722
+ inputText
5723
+ );
5724
+ if (disabledRawCommand) {
5725
+ notifyCommandDisabled(composer, disabledRawCommand);
5726
+ return await discard();
5727
+ }
5728
+ const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
5729
+ if (composer.compositionIsEmpty || hasExceededMaxLength) {
5730
+ return await discard();
5544
5731
  }
5732
+ return await forward();
5733
+ }
5734
+ }
5735
+ };
5736
+ };
5737
+ var createDraftCompositionValidationMiddleware = (composer) => ({
5738
+ id: "stream-io/message-composer-middleware/draft-data-validation",
5739
+ handlers: {
5740
+ compose: async ({
5741
+ state,
5742
+ discard,
5743
+ forward
5744
+ }) => {
5745
+ const hasData = !textIsEmpty(state.draft.text ?? "") || state.draft.attachments?.length || state.draft.poll_id || state.draft.quoted_message_id;
5746
+ const shouldCreateDraft = composer.lastChangeOriginIsLocal && hasData;
5747
+ if (!shouldCreateDraft) {
5748
+ return await discard();
5749
+ }
5750
+ return await forward();
5751
+ }
5752
+ }
5753
+ });
5754
+
5755
+ // src/messageComposer/middleware/messageComposer/linkPreviews.ts
5756
+ var createLinkPreviewsCompositionMiddleware = (composer) => ({
5757
+ id: "stream-io/message-composer-middleware/link-previews",
5758
+ handlers: {
5759
+ compose: ({
5760
+ state,
5761
+ next,
5762
+ forward
5763
+ }) => {
5764
+ const { linkPreviewsManager } = composer;
5765
+ if (!linkPreviewsManager) return forward();
5766
+ linkPreviewsManager.cancelURLEnrichment();
5767
+ const someLinkPreviewsLoading = linkPreviewsManager.loadingPreviews.length > 0;
5768
+ const someLinkPreviewsDismissed = linkPreviewsManager.dismissedPreviews.length > 0;
5769
+ const linkPreviews = linkPreviewsManager.loadingPreviews.length > 0 ? [] : linkPreviewsManager.loadedPreviews.map(
5770
+ (preview) => LinkPreviewsManager.getPreviewData(preview)
5771
+ );
5772
+ const attachments = (state.message.attachments ?? []).concat(
5773
+ linkPreviews
5774
+ );
5775
+ if (!attachments.length) return forward();
5776
+ const sendOptions = { ...state.sendOptions };
5777
+ const skip_enrich_url = !someLinkPreviewsLoading && linkPreviews.length > 0 || someLinkPreviewsDismissed;
5778
+ if (skip_enrich_url) {
5779
+ sendOptions.skip_enrich_url = true;
5780
+ }
5781
+ return next({
5782
+ ...state,
5783
+ message: {
5784
+ ...state.message,
5785
+ attachments
5786
+ },
5787
+ localMessage: {
5788
+ ...state.localMessage,
5789
+ attachments
5790
+ },
5791
+ sendOptions
5545
5792
  });
5546
5793
  }
5547
5794
  }
5548
- disableFilter(filterKey) {
5549
- const config = this.filterConfig.getLatestValue();
5550
- if (config[filterKey]) {
5551
- this.filterConfig.partialNext({
5552
- [filterKey]: {
5553
- ...config[filterKey],
5554
- enabled: false
5795
+ });
5796
+ var createDraftLinkPreviewsCompositionMiddleware = (composer) => ({
5797
+ id: "stream-io/message-composer-middleware/draft-link-previews",
5798
+ handlers: {
5799
+ compose: ({
5800
+ state,
5801
+ next,
5802
+ forward
5803
+ }) => {
5804
+ const { linkPreviewsManager } = composer;
5805
+ if (!linkPreviewsManager) return forward();
5806
+ linkPreviewsManager.cancelURLEnrichment();
5807
+ const linkPreviews = linkPreviewsManager.loadedPreviews.map(
5808
+ (preview) => LinkPreviewsManager.getPreviewData(preview)
5809
+ );
5810
+ if (!linkPreviews.length) return forward();
5811
+ return next({
5812
+ ...state,
5813
+ draft: {
5814
+ ...state.draft,
5815
+ attachments: (state.draft.attachments ?? []).concat(linkPreviews)
5555
5816
  }
5556
5817
  });
5557
5818
  }
5558
5819
  }
5559
- updateContext(newContext) {
5560
- this.context.partialNext(newContext);
5561
- }
5562
- buildFilters(params) {
5563
- const filters = {
5564
- ...params?.baseFilters ?? {}
5565
- };
5566
- const filterConfig = this.filterConfig.getLatestValue();
5567
- for (const key in filterConfig) {
5568
- const configItem = filterConfig[key];
5569
- if (!configItem?.enabled) continue;
5570
- const generated = configItem.generate({
5571
- ...this.context.getLatestValue(),
5572
- ...params?.context ?? {}
5573
- });
5574
- if (generated) Object.assign(filters, generated);
5575
- }
5576
- return filters;
5577
- }
5578
- };
5820
+ });
5579
5821
 
5580
- // src/pagination/ReminderPaginator.ts
5581
- var ReminderPaginator = class extends BasePaginator {
5582
- constructor(client, options) {
5583
- super(options);
5584
- this.query = async ({
5585
- direction
5822
+ // src/messageComposer/middleware/messageComposer/textComposer.ts
5823
+ var createTextComposerCompositionMiddleware = (composer) => ({
5824
+ id: "stream-io/message-composer-middleware/text-composition",
5825
+ handlers: {
5826
+ compose: ({
5827
+ state,
5828
+ next,
5829
+ forward
5586
5830
  }) => {
5587
- const cursor = this.cursor?.[direction];
5588
- const {
5589
- reminders: items,
5590
- next,
5591
- prev
5592
- } = await this.client.queryReminders({
5593
- filter: this.filters,
5594
- sort: this.sort,
5595
- limit: this.pageSize,
5596
- [direction]: cursor
5831
+ if (!composer.textComposer) return forward();
5832
+ const { mentionedUsers, text } = composer.textComposer;
5833
+ const mentioned_users = Array.from(
5834
+ new Set(
5835
+ mentionedUsers.filter(
5836
+ ({ id, name }) => text.includes(`@${id}`) || text.includes(`@${name}`)
5837
+ )
5838
+ )
5839
+ );
5840
+ if (!text && mentioned_users.length === 0) return forward();
5841
+ return next({
5842
+ ...state,
5843
+ localMessage: {
5844
+ ...state.localMessage,
5845
+ mentioned_users,
5846
+ text
5847
+ },
5848
+ message: {
5849
+ ...state.message,
5850
+ mentioned_users: mentioned_users.map((u) => u.id),
5851
+ text
5852
+ }
5597
5853
  });
5598
- return { items, next, prev };
5599
- };
5600
- this.filterQueryResults = (items) => items;
5601
- this.client = client;
5602
- }
5603
- get filters() {
5604
- return this._filters;
5605
- }
5606
- get sort() {
5607
- return this._sort;
5608
- }
5609
- set filters(filters) {
5610
- this._filters = filters;
5611
- this.resetState();
5612
- }
5613
- set sort(sort) {
5614
- this._sort = sort;
5615
- this.resetState();
5854
+ }
5616
5855
  }
5617
- };
5618
-
5619
- // src/search/UserSearchSource.ts
5620
- var UserSearchSource = class extends BaseSearchSource {
5621
- constructor(client, options, filterBuilderOptions = {}) {
5622
- super(options);
5623
- this.type = "users";
5624
- this.client = client;
5625
- this.filterBuilder = new FilterBuilder({
5626
- initialFilterConfig: {
5627
- $or: {
5628
- enabled: true,
5629
- generate: ({ searchQuery }) => searchQuery ? {
5630
- $or: [
5631
- { id: { $autocomplete: searchQuery } },
5632
- { name: { $autocomplete: searchQuery } }
5633
- ]
5634
- } : null
5856
+ });
5857
+ var createDraftTextComposerCompositionMiddleware = (composer) => ({
5858
+ id: "stream-io/message-composer-middleware/draft-text-composition",
5859
+ handlers: {
5860
+ compose: ({
5861
+ state,
5862
+ next,
5863
+ forward
5864
+ }) => {
5865
+ if (!composer.textComposer) return forward();
5866
+ const { maxLengthOnSend } = composer.config.text ?? {};
5867
+ const { mentionedUsers, text: inputText } = composer.textComposer;
5868
+ const mentioned_users = mentionedUsers.length ? Array.from(
5869
+ new Set(
5870
+ mentionedUsers.filter(
5871
+ ({ id, name }) => inputText.includes(`@${id}`) || inputText.includes(`@${name}`)
5872
+ )
5873
+ )
5874
+ ) : void 0;
5875
+ const text = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend ? inputText.slice(0, maxLengthOnSend) : inputText;
5876
+ return next({
5877
+ ...state,
5878
+ draft: {
5879
+ ...state.draft,
5880
+ mentioned_users: mentioned_users?.map((u) => u.id),
5881
+ text
5635
5882
  }
5636
- },
5637
- ...filterBuilderOptions
5638
- });
5639
- }
5640
- async query(searchQuery) {
5641
- const filters = this.filterBuilder.buildFilters({
5642
- baseFilters: this.filters,
5643
- context: { searchQuery }
5644
- });
5645
- const sort = { id: 1, ...this.sort };
5646
- const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5647
- const { users } = await this.client.queryUsers(filters, sort, options);
5648
- return { items: users };
5649
- }
5650
- filterQueryResults(items) {
5651
- return items.filter((u) => u.id !== this.client.user?.id);
5652
- }
5653
- };
5654
-
5655
- // src/search/ChannelSearchSource.ts
5656
- var ChannelSearchSource = class extends BaseSearchSource {
5657
- constructor(client, options, filterBuilderOptions = {}) {
5658
- super(options);
5659
- this.type = "channels";
5660
- this.client = client;
5661
- this.filterBuilder = new FilterBuilder({
5662
- ...filterBuilderOptions,
5663
- initialFilterConfig: {
5664
- name: {
5665
- enabled: true,
5666
- generate: ({ searchQuery }) => searchQuery ? { name: { $autocomplete: searchQuery } } : null
5667
- },
5668
- ...filterBuilderOptions.initialFilterConfig
5669
- }
5670
- });
5671
- }
5672
- async query(searchQuery) {
5673
- const filters = this.filterBuilder.buildFilters({
5674
- baseFilters: {
5675
- ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5676
- ...this.filters
5677
- },
5678
- context: { searchQuery }
5679
- });
5680
- const sort = this.sort ?? {};
5681
- const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5682
- const items = await this.client.queryChannels(filters, sort, options);
5683
- return { items };
5684
- }
5685
- filterQueryResults(items) {
5686
- return items;
5883
+ });
5884
+ }
5687
5885
  }
5688
- };
5886
+ });
5689
5887
 
5690
- // src/search/MessageSearchSource.ts
5691
- var MessageSearchSource = class extends BaseSearchSource {
5692
- constructor(client, options, filterBuilderOptions) {
5693
- super(options);
5694
- this.type = "messages";
5695
- this.client = client;
5696
- this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5697
- this.messageSearchFilterBuilder = new FilterBuilder({
5698
- ...filterBuilderOptions?.messageSearch,
5699
- initialFilterConfig: {
5700
- text: {
5701
- enabled: true,
5702
- generate: ({ searchQuery }) => searchQuery ? { text: searchQuery } : null
5703
- },
5704
- ...filterBuilderOptions?.messageSearch?.initialFilterConfig
5888
+ // src/messageComposer/middleware/messageComposer/messageComposerState.ts
5889
+ var createMessageComposerStateCompositionMiddleware = (composer) => ({
5890
+ id: "stream-io/message-composer-middleware/own-state",
5891
+ handlers: {
5892
+ compose: ({
5893
+ state,
5894
+ next
5895
+ }) => {
5896
+ const payload = {};
5897
+ if (composer.quotedMessage) {
5898
+ payload.quoted_message_id = composer.quotedMessage.id;
5705
5899
  }
5706
- });
5707
- this.channelQueryFilterBuilder = new FilterBuilder({
5708
- ...filterBuilderOptions?.channelQuery,
5709
- initialFilterConfig: {
5710
- cid: {
5711
- enabled: true,
5712
- generate: ({ cids }) => cids ? { cid: { $in: cids } } : null
5713
- },
5714
- ...filterBuilderOptions?.channelQuery?.initialFilterConfig
5900
+ if (composer.pollId) {
5901
+ payload.poll_id = composer.pollId;
5715
5902
  }
5716
- });
5903
+ if (composer.showReplyInChannel) {
5904
+ payload.show_in_channel = true;
5905
+ }
5906
+ return next({
5907
+ ...state,
5908
+ localMessage: {
5909
+ ...state.localMessage,
5910
+ ...payload,
5911
+ quoted_message: composer.quotedMessage ?? void 0
5912
+ },
5913
+ message: {
5914
+ ...state.message,
5915
+ ...payload
5916
+ }
5917
+ });
5918
+ }
5717
5919
  }
5718
- async query(searchQuery) {
5719
- if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
5720
- const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5721
- baseFilters: {
5722
- ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5723
- ...this.messageSearchChannelFilters
5724
- },
5725
- context: { searchQuery }
5726
- });
5727
- const messageFilters = this.messageSearchFilterBuilder.buildFilters({
5728
- baseFilters: {
5729
- type: "regular",
5730
- ...this.messageSearchFilters
5731
- },
5732
- context: { searchQuery }
5733
- });
5734
- const sort = {
5735
- created_at: -1,
5736
- ...this.messageSearchSort
5737
- };
5738
- const options = {
5739
- limit: this.pageSize,
5740
- next: this.next,
5741
- sort
5742
- };
5743
- const { next, results } = await this.client.search(
5744
- channelFilters,
5745
- messageFilters,
5746
- options
5747
- );
5748
- const items = results.map(({ message }) => message);
5749
- const cids = Array.from(
5750
- items.reduce((acc, message) => {
5751
- if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
5752
- return acc;
5753
- }, /* @__PURE__ */ new Set())
5754
- );
5755
- if (cids.length > 0) {
5756
- const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
5757
- baseFilters: this.channelQueryFilters,
5758
- context: { cids }
5920
+ });
5921
+ var createDraftMessageComposerStateCompositionMiddleware = (composer) => ({
5922
+ id: "stream-io/message-composer-middleware/draft-own-state",
5923
+ handlers: {
5924
+ compose: ({
5925
+ state,
5926
+ next
5927
+ }) => {
5928
+ const payload = {};
5929
+ if (composer.quotedMessage) {
5930
+ payload.quoted_message_id = composer.quotedMessage.id;
5931
+ }
5932
+ if (composer.pollId) {
5933
+ payload.poll_id = composer.pollId;
5934
+ }
5935
+ if (composer.showReplyInChannel) {
5936
+ payload.show_in_channel = true;
5937
+ }
5938
+ return next({
5939
+ ...state,
5940
+ draft: {
5941
+ ...state.draft,
5942
+ ...payload
5943
+ }
5759
5944
  });
5760
- await this.client.queryChannels(
5761
- channelQueryFilters,
5762
- {
5763
- last_message_at: -1,
5764
- ...this.channelQuerySort
5765
- },
5766
- this.channelQueryOptions
5767
- );
5768
5945
  }
5769
- return { items, next };
5770
5946
  }
5771
- filterQueryResults(items) {
5772
- return items;
5947
+ });
5948
+
5949
+ // src/messageComposer/middleware/messageComposer/userDataInjection.ts
5950
+ var createUserDataInjectionMiddleware = (composer) => ({
5951
+ id: "stream-io/message-composer-middleware/user-data-injection",
5952
+ handlers: {
5953
+ compose: ({
5954
+ state,
5955
+ next,
5956
+ forward
5957
+ }) => {
5958
+ if (!composer.client.user) {
5959
+ return forward();
5960
+ }
5961
+ const { channel_mutes, devices, mutes, ...messageUser } = composer.client.user;
5962
+ return next({
5963
+ ...state,
5964
+ localMessage: {
5965
+ ...state.localMessage,
5966
+ user: messageUser,
5967
+ user_id: messageUser.id
5968
+ }
5969
+ });
5970
+ }
5773
5971
  }
5774
- };
5972
+ });
5775
5973
 
5776
- // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
5777
- var getTriggerCharWithToken = ({
5778
- trigger,
5779
- text,
5780
- isCommand = false,
5781
- acceptTrailingSpaces = true
5782
- }) => {
5783
- const escapedTrigger = escapeRegExp(trigger);
5784
- const triggerNorWhitespace = `[^\\s${escapedTrigger}]*`;
5785
- const match = text.match(
5786
- new RegExp(
5787
- isCommand ? `^[${escapedTrigger}]${triggerNorWhitespace}$` : acceptTrailingSpaces ? `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}\\s?${triggerNorWhitespace}$` : `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}$`,
5788
- "g"
5789
- )
5790
- );
5791
- return match && match[match.length - 1].trim();
5792
- };
5793
- var getCompleteCommandInString = (text) => {
5794
- const match = text.match(/^\/(\S+)\s+.*/);
5795
- const commandName = match && match[1];
5796
- return commandName;
5974
+ // src/messageComposer/middleware/messageComposer/pollOnly.ts
5975
+ var pollLocalMessageNullifiedFields = {
5976
+ attachments: [],
5977
+ mentioned_users: [],
5978
+ parent_id: void 0,
5979
+ quoted_message: void 0,
5980
+ text: ""
5797
5981
  };
5798
- var insertItemWithTrigger = ({
5799
- insertText,
5800
- selection,
5801
- text,
5802
- trigger
5803
- }) => {
5804
- const beforeCursor = text.slice(0, selection.end);
5805
- const afterCursor = text.slice(selection.end);
5806
- const lastIndex = beforeCursor.lastIndexOf(trigger);
5807
- const newText = beforeCursor.slice(0, lastIndex) + insertText + afterCursor;
5808
- return {
5809
- text: newText,
5810
- selection: {
5811
- start: lastIndex + insertText.length,
5812
- end: lastIndex + insertText.length
5982
+ var createPollOnlyCompositionMiddleware = (composer) => ({
5983
+ id: "stream-io/message-composer-middleware/poll-only",
5984
+ handlers: {
5985
+ compose: ({
5986
+ state,
5987
+ complete,
5988
+ forward
5989
+ }) => {
5990
+ const pollId = composer.pollId;
5991
+ const isEditingMessage = !!composer.editedMessage;
5992
+ const isComposingThreadReply = !!composer.threadId;
5993
+ if (!pollId || isComposingThreadReply || isEditingMessage) return forward();
5994
+ return complete({
5995
+ ...state,
5996
+ localMessage: {
5997
+ ...state.localMessage,
5998
+ ...pollLocalMessageNullifiedFields,
5999
+ poll_id: pollId
6000
+ },
6001
+ message: {
6002
+ id: state.localMessage.id,
6003
+ poll_id: pollId
6004
+ }
6005
+ });
6006
+ }
6007
+ }
6008
+ });
6009
+
6010
+ // src/messageComposer/middleware/messageComposer/sharedLocation.ts
6011
+ var createSharedLocationCompositionMiddleware = (composer) => ({
6012
+ id: "stream-io/message-composer-middleware/shared-location",
6013
+ handlers: {
6014
+ compose: ({
6015
+ state,
6016
+ next,
6017
+ forward
6018
+ }) => {
6019
+ const { locationComposer } = composer;
6020
+ const location = locationComposer.validLocation;
6021
+ if (!locationComposer || !location || !composer.client.user) return forward();
6022
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6023
+ return next({
6024
+ ...state,
6025
+ localMessage: {
6026
+ ...state.localMessage,
6027
+ shared_location: {
6028
+ ...location,
6029
+ channel_cid: composer.channel.cid,
6030
+ created_at: timestamp,
6031
+ updated_at: timestamp,
6032
+ user_id: composer.client.user.id
6033
+ }
6034
+ },
6035
+ message: {
6036
+ ...state.message,
6037
+ shared_location: location
6038
+ }
6039
+ });
5813
6040
  }
5814
- };
5815
- };
5816
- var replaceWordWithEntity = async ({
5817
- caretPosition,
5818
- getEntityString,
5819
- text
5820
- }) => {
5821
- const lastWordRegex = /([^\s]+)(\s*)$/;
5822
- const match = lastWordRegex.exec(text.slice(0, caretPosition));
5823
- if (!match) return text;
5824
- const lastWord = match[1];
5825
- if (!lastWord) return text;
5826
- const spaces = match[2];
5827
- const newWord = await getEntityString(lastWord);
5828
- if (newWord == null) return text;
5829
- const textBeforeWord = text.slice(0, caretPosition - match[0].length);
5830
- const textAfterCaret = text.slice(caretPosition, -1);
5831
- return textBeforeWord + newWord + spaces + textAfterCaret;
5832
- };
5833
- function escapeRegExp(text) {
5834
- return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, "\\$&");
5835
- }
5836
- var getTokenizedSuggestionDisplayName = ({
5837
- displayName,
5838
- searchToken
5839
- }) => ({
5840
- tokenizedDisplayName: {
5841
- token: searchToken,
5842
- parts: searchToken ? displayName.split(new RegExp(`(${escapeRegExp(searchToken)})`, "gi")).filter(Boolean) : [displayName]
5843
6041
  }
5844
6042
  });
5845
6043
 
5846
- // src/messageComposer/middleware/textComposer/commands.ts
5847
- var CommandSearchSource = class extends BaseSearchSourceSync {
5848
- constructor(channel, options) {
5849
- super(options);
5850
- this.type = "commands";
5851
- this.canExecuteQuery = (newSearchString) => {
5852
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5853
- return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
5854
- };
5855
- this.channel = channel;
6044
+ // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
6045
+ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6046
+ constructor({ composer }) {
6047
+ super();
6048
+ this.use([
6049
+ createUserDataInjectionMiddleware(composer),
6050
+ createPollOnlyCompositionMiddleware(composer),
6051
+ createTextComposerCompositionMiddleware(composer),
6052
+ createAttachmentsCompositionMiddleware(composer),
6053
+ createLinkPreviewsCompositionMiddleware(composer),
6054
+ createSharedLocationCompositionMiddleware(composer),
6055
+ createMessageComposerStateCompositionMiddleware(composer),
6056
+ createCustomDataCompositionMiddleware(composer),
6057
+ createCompositionValidationMiddleware(composer),
6058
+ createCompositionDataCleanupMiddleware(composer)
6059
+ ]);
5856
6060
  }
5857
- getStateBeforeFirstQuery(newSearchString) {
5858
- const newState = super.getStateBeforeFirstQuery(newSearchString);
5859
- const { items } = this.state.getLatestValue();
5860
- return {
5861
- ...newState,
5862
- items
5863
- // preserve items to avoid flickering
5864
- };
6061
+ };
6062
+ var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6063
+ constructor({ composer }) {
6064
+ super();
6065
+ this.use([
6066
+ createDraftTextComposerCompositionMiddleware(composer),
6067
+ createDraftAttachmentsCompositionMiddleware(composer),
6068
+ createDraftLinkPreviewsCompositionMiddleware(composer),
6069
+ createDraftMessageComposerStateCompositionMiddleware(composer),
6070
+ createDraftCustomDataCompositionMiddleware(composer),
6071
+ createDraftCompositionValidationMiddleware(composer)
6072
+ ]);
5865
6073
  }
5866
- query(searchQuery) {
5867
- const channelConfig = this.channel.getConfig();
5868
- const commands = channelConfig?.commands || [];
5869
- const selectedCommands = commands.filter(
5870
- (command) => !!(command.name && command.name.toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1)
5871
- );
5872
- selectedCommands.sort((a, b) => {
5873
- let nameA = a.name?.toLowerCase();
5874
- let nameB = b.name?.toLowerCase();
5875
- if (nameA?.indexOf(searchQuery) === 0) {
5876
- nameA = `0${nameA}`;
5877
- }
5878
- if (nameB?.indexOf(searchQuery) === 0) {
5879
- nameB = `0${nameB}`;
6074
+ };
6075
+
6076
+ // src/messageComposer/middleware/messageComposer/commandInjection.ts
6077
+ var createCommandInjectionMiddleware = (composer) => ({
6078
+ handlers: {
6079
+ compose: ({
6080
+ forward,
6081
+ next,
6082
+ state
6083
+ }) => {
6084
+ const command = composer.textComposer.command;
6085
+ if (!command) {
6086
+ return forward();
5880
6087
  }
5881
- if (nameA != null && nameB != null) {
5882
- if (nameA < nameB) {
5883
- return -1;
6088
+ const { text } = state.localMessage;
6089
+ const injection = `/${command?.name}`;
6090
+ const enrichedText = `${injection} ${text}`;
6091
+ return next({
6092
+ ...state,
6093
+ localMessage: {
6094
+ ...state.localMessage,
6095
+ text: enrichedText
6096
+ },
6097
+ message: {
6098
+ ...state.message,
6099
+ text: enrichedText
5884
6100
  }
5885
- if (nameA > nameB) {
5886
- return 1;
6101
+ });
6102
+ }
6103
+ },
6104
+ id: "stream-io/message-composer-middleware/command-string-injection"
6105
+ });
6106
+ var createDraftCommandInjectionMiddleware = (composer) => ({
6107
+ handlers: {
6108
+ compose: ({
6109
+ forward,
6110
+ next,
6111
+ state
6112
+ }) => {
6113
+ const command = composer.textComposer.command;
6114
+ if (!command) {
6115
+ return forward();
6116
+ }
6117
+ const { text } = state.draft;
6118
+ const injection = `/${command?.name}`;
6119
+ const enrichedText = `${injection} ${text}`;
6120
+ return next({
6121
+ ...state,
6122
+ draft: {
6123
+ ...state.draft,
6124
+ text: enrichedText
5887
6125
  }
6126
+ });
6127
+ }
6128
+ },
6129
+ id: "stream-io/message-composer-middleware/draft-command-string-injection"
6130
+ });
6131
+
6132
+ // src/messageComposer/middleware/textComposer/activeCommandGuard.ts
6133
+ var createActiveCommandGuardMiddleware = () => ({
6134
+ handlers: {
6135
+ onChange: ({ complete, forward, state }) => {
6136
+ if (state.command) {
6137
+ return complete(state);
5888
6138
  }
5889
- return 0;
5890
- });
5891
- return {
5892
- items: selectedCommands.map((c) => ({ ...c, id: c.name })),
5893
- next: null
5894
- };
5895
- }
5896
- filterQueryResults(items) {
5897
- return items;
5898
- }
6139
+ return forward();
6140
+ },
6141
+ onSuggestionItemSelect: ({ forward }) => forward()
6142
+ },
6143
+ id: "stream-io/text-composer/active-command-guard"
6144
+ });
6145
+
6146
+ // src/messageComposer/middleware/textComposer/commandEffects.ts
6147
+ var emptyCommandStateToRestore = {
6148
+ selection: { start: 0, end: 0 },
6149
+ text: ""
5899
6150
  };
5900
- var DEFAULT_OPTIONS = { minChars: 1, trigger: "/" };
5901
- var createCommandsMiddleware = (channel, options) => {
5902
- const finalOptions = mergeWith(DEFAULT_OPTIONS, options ?? {});
5903
- let searchSource = new CommandSearchSource(channel);
5904
- if (options?.searchSource) {
5905
- searchSource = options.searchSource;
5906
- searchSource.resetState();
5907
- }
5908
- searchSource.activate();
5909
- return {
5910
- id: "stream-io/text-composer/commands-middleware",
5911
- handlers: {
5912
- onChange: ({ state, next, complete, forward }) => {
5913
- if (!state.selection) return forward();
5914
- const finalText = state.text.slice(0, state.selection.end);
5915
- const commandName = getCompleteCommandInString(finalText);
5916
- if (commandName) {
5917
- const command = searchSource?.query(commandName).items[0];
5918
- if (command) {
5919
- return next({
5920
- ...state,
5921
- command,
5922
- suggestions: void 0
5923
- });
5924
- }
5925
- }
5926
- const triggerWithToken = getTriggerCharWithToken({
5927
- trigger: finalOptions.trigger,
5928
- text: finalText,
5929
- acceptTrailingSpaces: false,
5930
- isCommand: true
5931
- });
5932
- const newSearchTriggerred = triggerWithToken && triggerWithToken.length === finalOptions.minChars;
5933
- if (newSearchTriggerred) {
5934
- searchSource.resetStateAndActivate();
5935
- }
5936
- const triggerWasRemoved = !triggerWithToken || triggerWithToken.length < finalOptions.minChars;
5937
- if (triggerWasRemoved) {
5938
- const hasStaleSuggestions = state.suggestions?.trigger === finalOptions.trigger;
5939
- const newState = { ...state };
5940
- if (hasStaleSuggestions) {
5941
- delete newState.suggestions;
5942
- }
5943
- return next(newState);
5944
- }
5945
- return complete({
5946
- ...state,
5947
- command: null,
5948
- suggestions: {
5949
- query: triggerWithToken.slice(1),
5950
- searchSource,
5951
- trigger: finalOptions.trigger
5952
- }
5953
- });
5954
- },
5955
- onSuggestionItemSelect: ({ state, next, forward }) => {
5956
- const { selectedSuggestion } = state.change ?? {};
5957
- if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger)
5958
- return forward();
5959
- searchSource.resetStateAndActivate();
5960
- return next({
5961
- ...state,
5962
- ...insertItemWithTrigger({
5963
- insertText: `/${selectedSuggestion.name} `,
5964
- selection: state.selection,
5965
- text: state.text,
5966
- trigger: finalOptions.trigger
5967
- }),
5968
- command: selectedSuggestion,
5969
- suggestions: void 0
5970
- });
6151
+ var createCommandActivationEffect = (command) => ({
6152
+ command,
6153
+ stateToRestore: emptyCommandStateToRestore,
6154
+ type: "command.activate"
6155
+ });
6156
+ var isCommandResponse = (suggestion) => typeof suggestion?.name === "string";
6157
+ var createCommandEffectsMiddleware = () => ({
6158
+ handlers: {
6159
+ onChange: ({ forward }) => forward(),
6160
+ onSuggestionItemSelect: ({ state, next, forward }) => {
6161
+ const { selectedSuggestion } = state.change ?? {};
6162
+ if (!isCommandResponse(selectedSuggestion) || !state.command || state.command.name !== selectedSuggestion.name) {
6163
+ return forward();
5971
6164
  }
6165
+ return next({
6166
+ ...state,
6167
+ effects: [...state.effects ?? [], createCommandActivationEffect(state.command)]
6168
+ });
5972
6169
  }
5973
- };
5974
- };
6170
+ },
6171
+ id: "stream-io/text-composer/command-effects-middleware"
6172
+ });
5975
6173
 
5976
6174
  // src/messageComposer/middleware/textComposer/commandStringExtraction.ts
5977
- var stripCommandFromText = (text, commandName) => text.replace(new RegExp(`^${escapeRegExp(`/${commandName}`)}\\s*`), "");
5978
6175
  var createCommandStringExtractionMiddleware = () => ({
5979
6176
  handlers: {
5980
6177
  onChange: ({ complete, forward, state }) => {
@@ -6314,7 +6511,10 @@ var TextComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6314
6511
  this.use([
6315
6512
  createTextComposerPreValidationMiddleware(composer),
6316
6513
  createMentionsMiddleware(composer.channel),
6317
- createCommandsMiddleware(composer.channel)
6514
+ createCommandsMiddleware(composer.channel, {
6515
+ composer
6516
+ }),
6517
+ createCommandEffectsMiddleware()
6318
6518
  ]);
6319
6519
  }
6320
6520
  async execute({
@@ -6363,6 +6563,10 @@ var TextComposer = class {
6363
6563
  this.initState = ({ message } = {}) => {
6364
6564
  this.state.next(initState5({ composer: this.composer, message }));
6365
6565
  };
6566
+ this.getSnapshot = (state = this.state.getLatestValue()) => state;
6567
+ this.restoreSnapshot = (snapshot) => {
6568
+ this.state.next(snapshot);
6569
+ };
6366
6570
  this.upsertMentionedUser = (user) => {
6367
6571
  const mentionedUsers = [...this.mentionedUsers];
6368
6572
  const existingUserIndex = mentionedUsers.findIndex((u) => u.id === user.id);
@@ -6383,8 +6587,27 @@ var TextComposer = class {
6383
6587
  this.state.partialNext({ mentionedUsers });
6384
6588
  };
6385
6589
  this.setCommand = (command) => {
6386
- if (command?.name === this.command?.name) return;
6387
- this.state.partialNext({ command });
6590
+ if (!command) {
6591
+ this.clearCommand();
6592
+ return;
6593
+ }
6594
+ if (command.name === this.command?.name) return;
6595
+ if (this.composer.isCommandDisabled(command)) return;
6596
+ const stateToRestore = {
6597
+ command: null
6598
+ };
6599
+ this.commitState({
6600
+ ...this.state.getLatestValue(),
6601
+ command,
6602
+ effects: [
6603
+ {
6604
+ command,
6605
+ stateToRestore,
6606
+ type: "command.activate"
6607
+ }
6608
+ ],
6609
+ suggestions: void 0
6610
+ });
6388
6611
  };
6389
6612
  this.setText = (text) => {
6390
6613
  if (!this.enabled || text === this.text) return;
@@ -6451,6 +6674,12 @@ var TextComposer = class {
6451
6674
  this.state.partialNext({ suggestions: void 0 });
6452
6675
  };
6453
6676
  // --- END STATE API ---
6677
+ this.commitState = (state) => {
6678
+ const { change, effects, ...nextState } = state;
6679
+ void change;
6680
+ this.state.next(nextState);
6681
+ this.composer.applyEffects(effects);
6682
+ };
6454
6683
  // --- START TEXT PROCESSING ----
6455
6684
  this.handleChange = async ({
6456
6685
  text,
@@ -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
  if (this.config.publishTypingEvents && text) {
6471
6700
  logChatPromiseExecution(
6472
6701
  this.channel.keystroke(this.composer.threadId ?? void 0),
@@ -6487,7 +6716,7 @@ var TextComposer = class {
6487
6716
  }
6488
6717
  });
6489
6718
  if (output?.status === "discard") return;
6490
- this.state.next(output.state);
6719
+ this.commitState(output.state);
6491
6720
  };
6492
6721
  this.composer = composer;
6493
6722
  this.state = new StateStore(initState5({ composer, message }));
@@ -6557,7 +6786,12 @@ var TextComposer = class {
6557
6786
  this.state.partialNext({ mentionedUsers: users });
6558
6787
  }
6559
6788
  clearCommand() {
6560
- this.state.partialNext({ command: null });
6789
+ if (!this.command) return;
6790
+ this.commitState({
6791
+ ...this.state.getLatestValue(),
6792
+ command: null,
6793
+ effects: [{ type: "command.clear" }]
6794
+ });
6561
6795
  }
6562
6796
  // --- END TEXT PROCESSING ----
6563
6797
  };
@@ -7048,15 +7282,28 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7048
7282
  client
7049
7283
  }) {
7050
7284
  super();
7285
+ this.snapshots = [];
7051
7286
  this.setEditedMessage = (editedMessage) => {
7052
7287
  this.state.partialNext({ editedMessage: editedMessage ?? null });
7288
+ if (editedMessage) {
7289
+ this.textComposer.clearCommand();
7290
+ }
7291
+ };
7292
+ this.getCommandDisabledReason = (command) => {
7293
+ if (this.editedMessage) return "editing";
7294
+ if (this.quotedMessage && (command.set === "moderation_set" || command.name === "moderation_set")) {
7295
+ return "quoted_message";
7296
+ }
7297
+ return void 0;
7053
7298
  };
7299
+ this.isCommandDisabled = (command) => !!this.getCommandDisabledReason(command);
7054
7300
  this.refreshId = () => {
7055
7301
  this.state.partialNext({ id: _MessageComposer.generateId() });
7056
7302
  };
7057
7303
  this.initState = ({
7058
7304
  composition
7059
7305
  } = {}) => {
7306
+ this.clearSnapshots();
7060
7307
  this.editingAuditState.partialNext(this.initEditingAuditState(composition));
7061
7308
  const message = typeof composition === "undefined" ? composition : compositionIsDraftResponse(composition) ? composition.message : formatMessage(composition);
7062
7309
  this.attachmentManager.initState({ message });
@@ -7086,6 +7333,36 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7086
7333
  }
7087
7334
  };
7088
7335
  this.initEditingAuditState = (composition) => initEditingAuditState(composition);
7336
+ this.clearSnapshots = () => {
7337
+ this.snapshots = [];
7338
+ };
7339
+ this.getSnapshot = () => ({
7340
+ attachmentManager: this.attachmentManager.getSnapshot(),
7341
+ customDataManager: this.customDataManager.getSnapshot(),
7342
+ linkPreviewsManager: this.linkPreviewsManager.getSnapshot(),
7343
+ locationComposer: this.locationComposer.getSnapshot(),
7344
+ pollComposer: this.pollComposer.getSnapshot(),
7345
+ textComposer: this.textComposer.getSnapshot()
7346
+ });
7347
+ this.restoreSnapshot = (snapshot) => {
7348
+ this.attachmentManager.restoreSnapshot(snapshot.attachmentManager);
7349
+ this.linkPreviewsManager.restoreSnapshot(snapshot.linkPreviewsManager);
7350
+ this.locationComposer.restoreSnapshot(snapshot.locationComposer);
7351
+ this.pollComposer.restoreSnapshot(snapshot.pollComposer);
7352
+ this.customDataManager.restoreSnapshot(snapshot.customDataManager);
7353
+ this.textComposer.restoreSnapshot(snapshot.textComposer);
7354
+ };
7355
+ this.captureSnapshot = (snapshot = this.getSnapshot()) => {
7356
+ if (this.snapshots.length) return;
7357
+ this.snapshots.push(snapshot);
7358
+ };
7359
+ this.popSnapshot = () => this.snapshots.pop();
7360
+ this.registerEffectHandler = (type, handler) => {
7361
+ this.effectHandlers.registerEffectHandler(type, handler);
7362
+ };
7363
+ this.applyEffects = (effects = []) => {
7364
+ this.effectHandlers.applyEffects(effects);
7365
+ };
7089
7366
  this.registerDraftEventSubscriptions = () => {
7090
7367
  const unsubscribeDraftUpdated = this.subscribeDraftUpdated();
7091
7368
  const unsubscribeDraftDeleted = this.subscribeDraftDeleted();
@@ -7265,6 +7542,10 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7265
7542
  };
7266
7543
  this.setQuotedMessage = (quotedMessage) => {
7267
7544
  this.state.partialNext({ quotedMessage });
7545
+ const activeCommand = this.textComposer.command;
7546
+ if (quotedMessage && activeCommand && this.isCommandDisabled(activeCommand)) {
7547
+ this.textComposer.clearCommand();
7548
+ }
7268
7549
  };
7269
7550
  this.toggleShowReplyInChannel = () => {
7270
7551
  this.state.partialNext({ showReplyInChannel: !this.showReplyInChannel });
@@ -7499,6 +7780,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7499
7780
  this.draftCompositionMiddlewareExecutor = new MessageDraftComposerMiddlewareExecutor({
7500
7781
  composer: this
7501
7782
  });
7783
+ this.effectHandlers = new MessageComposerEffectHandlers({ composer: this });
7502
7784
  }
7503
7785
  static evaluateContextType(compositionContext) {
7504
7786
  if (compositionContext instanceof Channel) {
@@ -15382,7 +15664,7 @@ var StreamChat = class _StreamChat {
15382
15664
  if (this.userAgent) {
15383
15665
  return this.userAgent;
15384
15666
  }
15385
- const version = "9.42.3";
15667
+ const version = "9.43.0";
15386
15668
  const clientBundle = "browser-cjs";
15387
15669
  let userAgentString = "";
15388
15670
  if (this.sdkIdentifier) {