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
@@ -3086,9 +3086,52 @@ var _AttachmentManager = class _AttachmentManager {
3086
3086
  this.setCustomUploadFn = (doUploadRequest) => {
3087
3087
  this.composer.updateConfig({ attachments: { doUploadRequest } });
3088
3088
  };
3089
+ this.cancelAttachmentUploads = (attachments) => {
3090
+ for (const { localMetadata } of attachments) {
3091
+ this.client.uploadManager.deleteUploadRecord(localMetadata.id);
3092
+ }
3093
+ };
3094
+ this.normalizeSnapshotAttachment = (attachment) => {
3095
+ if (attachment.localMetadata.uploadState !== "uploading") return attachment;
3096
+ this.client.uploadManager.deleteUploadRecord(attachment.localMetadata.id);
3097
+ return {
3098
+ ...attachment,
3099
+ localMetadata: {
3100
+ ...attachment.localMetadata,
3101
+ uploadProgress: void 0,
3102
+ uploadState: "failed"
3103
+ }
3104
+ };
3105
+ };
3089
3106
  this.initState = ({ message } = {}) => {
3107
+ this.cancelAttachmentUploads(this.attachments);
3090
3108
  this.state.next(initState({ message }));
3091
3109
  };
3110
+ this.getSnapshot = () => {
3111
+ const state = this.state.getLatestValue();
3112
+ let hasUpdates = false;
3113
+ const attachments = state.attachments.map(this.normalizeSnapshotAttachment);
3114
+ for (let i = 0; i < attachments.length; i++) {
3115
+ if (attachments[i] !== state.attachments[i]) {
3116
+ hasUpdates = true;
3117
+ break;
3118
+ }
3119
+ }
3120
+ return hasUpdates ? { ...state, attachments } : state;
3121
+ };
3122
+ this.restoreSnapshot = (snapshot) => {
3123
+ this.cancelAttachmentUploads(this.attachments);
3124
+ this.state.next(snapshot);
3125
+ };
3126
+ this.setAttachments = (attachments) => {
3127
+ this.state.partialNext({ attachments });
3128
+ };
3129
+ this.clearAttachments = () => {
3130
+ if (!this.attachments.length) return;
3131
+ this.removeAttachments(
3132
+ this.attachments.map((attachment) => attachment.localMetadata.id)
3133
+ );
3134
+ };
3092
3135
  this.getAttachmentIndex = (localId) => {
3093
3136
  const attachmentsById = this.attachmentsById;
3094
3137
  return this.attachments.indexOf(attachmentsById[localId]);
@@ -3609,6 +3652,10 @@ var CustomDataManager = class {
3609
3652
  this.initState = ({ message } = {}) => {
3610
3653
  this.state.next(initState2({ composer: this.composer, message }));
3611
3654
  };
3655
+ this.getSnapshot = () => this.state.getLatestValue();
3656
+ this.restoreSnapshot = (snapshot) => {
3657
+ this.state.next(snapshot);
3658
+ };
3612
3659
  this.composer = composer;
3613
3660
  this.state = new StateStore(initState2({ composer, message }));
3614
3661
  }
@@ -3664,8 +3711,13 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
3664
3711
  constructor({ composer, message }) {
3665
3712
  this.shouldDiscardEnrichQueries = false;
3666
3713
  this.initState = ({ message } = {}) => {
3714
+ this.cancelURLEnrichment();
3667
3715
  this.state.next(initState3({ message: this.enabled ? message : void 0 }));
3668
3716
  };
3717
+ this.getSnapshot = () => this.state.getLatestValue();
3718
+ this.restoreSnapshot = (snapshot) => {
3719
+ this.state.next(snapshot);
3720
+ };
3669
3721
  this._findAndEnrichUrls = async (text) => {
3670
3722
  if (!this.enabled) return;
3671
3723
  const urls = this.config.findURLFn(text);
@@ -3718,8 +3770,8 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
3718
3770
  );
3719
3771
  };
3720
3772
  this.cancelURLEnrichment = () => {
3721
- this.findAndEnrichUrls.cancel();
3722
- this.findAndEnrichUrls.flush();
3773
+ this.findAndEnrichUrls.cancel?.();
3774
+ this.findAndEnrichUrls.flush?.();
3723
3775
  };
3724
3776
  /**
3725
3777
  * Clears all non-dismissed previews when the text composer is cleared.
@@ -3861,6 +3913,10 @@ var LocationComposer = class {
3861
3913
  this.initState = ({ message } = {}) => {
3862
3914
  this.state.next(initState4({ message }));
3863
3915
  };
3916
+ this.getSnapshot = () => this.state.getLatestValue();
3917
+ this.restoreSnapshot = (snapshot) => {
3918
+ this.state.next(snapshot);
3919
+ };
3864
3920
  this.setData = (data) => {
3865
3921
  if (!this.config.enabled) return;
3866
3922
  if (!data.latitude || !data.longitude) return;
@@ -3897,6 +3953,63 @@ var LocationComposer = class {
3897
3953
  }
3898
3954
  };
3899
3955
 
3956
+ // src/messageComposer/MessageComposerEffectHandlers.ts
3957
+ var applyCommandActivationEffect = (effect, composer) => {
3958
+ const snapshot = composer.getSnapshot();
3959
+ if (effect.stateToRestore) {
3960
+ snapshot.textComposer = {
3961
+ ...snapshot.textComposer,
3962
+ ...effect.stateToRestore,
3963
+ command: null
3964
+ };
3965
+ }
3966
+ composer.captureSnapshot(snapshot);
3967
+ composer.textComposer.state.next({
3968
+ command: effect.command,
3969
+ mentionedUsers: [],
3970
+ suggestions: void 0,
3971
+ selection: { start: 0, end: 0 },
3972
+ text: ""
3973
+ });
3974
+ composer.attachmentManager.initState();
3975
+ composer.linkPreviewsManager.initState();
3976
+ composer.locationComposer.initState();
3977
+ composer.pollComposer.initState();
3978
+ composer.customDataManager.initState();
3979
+ };
3980
+ var applyCommandClearEffect = (_, composer) => {
3981
+ const snapshot = composer.popSnapshot();
3982
+ if (!snapshot) return;
3983
+ composer.restoreSnapshot(snapshot);
3984
+ };
3985
+ var MessageComposerEffectHandlers = class {
3986
+ constructor(options) {
3987
+ this.options = options;
3988
+ this.handlers = /* @__PURE__ */ new Map();
3989
+ this.registerDefaultHandlers = () => {
3990
+ this.registerEffectHandler(
3991
+ "command.activate",
3992
+ applyCommandActivationEffect
3993
+ );
3994
+ this.registerEffectHandler(
3995
+ "command.clear",
3996
+ applyCommandClearEffect
3997
+ );
3998
+ };
3999
+ this.registerEffectHandler = (type, handler) => {
4000
+ this.handlers.set(type, handler);
4001
+ };
4002
+ this.applyEffects = (effects = []) => {
4003
+ effects.forEach((effect) => this.applyEffect(effect));
4004
+ };
4005
+ this.applyEffect = (effect) => {
4006
+ const handler = this.handlers.get(effect.type);
4007
+ handler?.(effect, this.options.composer);
4008
+ };
4009
+ this.registerDefaultHandlers();
4010
+ }
4011
+ };
4012
+
3900
4013
  // src/messageComposer/middleware/pollComposer/state.ts
3901
4014
  var VALID_MAX_VOTES_VALUE_REGEX = /^([2-9]|10)$/;
3902
4015
  var MAX_POLL_OPTIONS = 100;
@@ -4187,6 +4300,10 @@ var PollComposer = class {
4187
4300
  this.initState = () => {
4188
4301
  this.state.next(this.initialState);
4189
4302
  };
4303
+ this.getSnapshot = () => this.state.getLatestValue();
4304
+ this.restoreSnapshot = (snapshot) => {
4305
+ this.state.next(snapshot);
4306
+ };
4190
4307
  /**
4191
4308
  * Updates specified fields and generates relevant errors
4192
4309
  * @param data
@@ -4449,667 +4566,239 @@ var createDraftCustomDataCompositionMiddleware = (composer) => ({
4449
4566
  }
4450
4567
  });
4451
4568
 
4452
- // src/messageComposer/middleware/messageComposer/compositionValidation.ts
4453
- var createCompositionValidationMiddleware = (composer) => ({
4454
- id: "stream-io/message-composer-middleware/data-validation",
4455
- handlers: {
4456
- compose: async ({
4457
- state,
4458
- discard,
4459
- forward
4460
- }) => {
4461
- const { maxLengthOnSend } = composer.config.text ?? {};
4462
- const inputText = state.message.text ?? "";
4463
- const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
4464
- if (composer.compositionIsEmpty || hasExceededMaxLength) {
4465
- return await discard();
4466
- }
4467
- return await forward();
4468
- }
4569
+ // src/errors.ts
4570
+ var APIErrorCodes = {
4571
+ "-1": { name: "InternalSystemError", retryable: true },
4572
+ "2": { name: "AccessKeyError", retryable: false },
4573
+ "3": { name: "AuthenticationFailedError", retryable: true },
4574
+ "4": { name: "InputError", retryable: false },
4575
+ "6": { name: "DuplicateUsernameError", retryable: false },
4576
+ "9": { name: "RateLimitError", retryable: true },
4577
+ "16": { name: "DoesNotExistError", retryable: false },
4578
+ "17": { name: "NotAllowedError", retryable: false },
4579
+ "18": { name: "EventNotSupportedError", retryable: false },
4580
+ "19": { name: "ChannelFeatureNotSupportedError", retryable: false },
4581
+ "20": { name: "MessageTooLongError", retryable: false },
4582
+ "21": { name: "MultipleNestingLevelError", retryable: false },
4583
+ "22": { name: "PayloadTooBigError", retryable: false },
4584
+ "23": { name: "RequestTimeoutError", retryable: true },
4585
+ "24": { name: "MaxHeaderSizeExceededError", retryable: false },
4586
+ "40": { name: "AuthErrorTokenExpired", retryable: false },
4587
+ "41": { name: "AuthErrorTokenNotValidYet", retryable: false },
4588
+ "42": { name: "AuthErrorTokenUsedBeforeIssuedAt", retryable: false },
4589
+ "43": { name: "AuthErrorTokenSignatureInvalid", retryable: false },
4590
+ "44": { name: "CustomCommandEndpointMissingError", retryable: false },
4591
+ "45": { name: "CustomCommandEndpointCallError", retryable: true },
4592
+ "46": { name: "ConnectionIDNotFoundError", retryable: false },
4593
+ "60": { name: "CoolDownError", retryable: true },
4594
+ "69": { name: "ErrWrongRegion", retryable: false },
4595
+ "70": { name: "ErrQueryChannelPermissions", retryable: false },
4596
+ "71": { name: "ErrTooManyConnections", retryable: true },
4597
+ "99": { name: "AppSuspendedError", retryable: false }
4598
+ };
4599
+ function isAPIError(error) {
4600
+ return error.code !== void 0;
4601
+ }
4602
+ function isErrorRetryable(error) {
4603
+ if (!error.code) return false;
4604
+ const err = APIErrorCodes[`${error.code}`];
4605
+ if (!err) return false;
4606
+ return err.retryable;
4607
+ }
4608
+ function isConnectionIDError(error) {
4609
+ return error.code === 46;
4610
+ }
4611
+ function isWSFailure(err) {
4612
+ if (typeof err.isWSFailure === "boolean") {
4613
+ return err.isWSFailure;
4469
4614
  }
4470
- });
4471
- var createDraftCompositionValidationMiddleware = (composer) => ({
4472
- id: "stream-io/message-composer-middleware/draft-data-validation",
4473
- handlers: {
4474
- compose: async ({
4475
- state,
4476
- discard,
4477
- forward
4478
- }) => {
4479
- const hasData = !textIsEmpty(state.draft.text ?? "") || state.draft.attachments?.length || state.draft.poll_id || state.draft.quoted_message_id;
4480
- const shouldCreateDraft = composer.lastChangeOriginIsLocal && hasData;
4481
- if (!shouldCreateDraft) {
4482
- return await discard();
4483
- }
4484
- return await forward();
4485
- }
4615
+ try {
4616
+ return JSON.parse(err.message).isWSFailure;
4617
+ } catch (_) {
4618
+ return false;
4486
4619
  }
4487
- });
4620
+ }
4621
+ function isErrorResponse(res) {
4622
+ return !res.status || res.status < 200 || 300 <= res.status;
4623
+ }
4488
4624
 
4489
- // src/messageComposer/middleware/messageComposer/linkPreviews.ts
4490
- var createLinkPreviewsCompositionMiddleware = (composer) => ({
4491
- id: "stream-io/message-composer-middleware/link-previews",
4492
- handlers: {
4493
- compose: ({
4494
- state,
4495
- next,
4496
- forward
4497
- }) => {
4498
- const { linkPreviewsManager } = composer;
4499
- if (!linkPreviewsManager) return forward();
4500
- linkPreviewsManager.cancelURLEnrichment();
4501
- const someLinkPreviewsLoading = linkPreviewsManager.loadingPreviews.length > 0;
4502
- const someLinkPreviewsDismissed = linkPreviewsManager.dismissedPreviews.length > 0;
4503
- const linkPreviews = linkPreviewsManager.loadingPreviews.length > 0 ? [] : linkPreviewsManager.loadedPreviews.map(
4504
- (preview) => LinkPreviewsManager.getPreviewData(preview)
4505
- );
4506
- const attachments = (state.message.attachments ?? []).concat(
4507
- linkPreviews
4508
- );
4509
- if (!attachments.length) return forward();
4510
- const sendOptions = { ...state.sendOptions };
4511
- const skip_enrich_url = !someLinkPreviewsLoading && linkPreviews.length > 0 || someLinkPreviewsDismissed;
4512
- if (skip_enrich_url) {
4513
- sendOptions.skip_enrich_url = true;
4514
- }
4515
- return next({
4516
- ...state,
4517
- message: {
4518
- ...state.message,
4519
- attachments
4520
- },
4521
- localMessage: {
4522
- ...state.localMessage,
4523
- attachments
4524
- },
4525
- sendOptions
4526
- });
4527
- }
4625
+ // src/search/BaseSearchSource.ts
4626
+ var DEFAULT_SEARCH_SOURCE_OPTIONS = {
4627
+ debounceMs: 300,
4628
+ pageSize: 10
4629
+ };
4630
+ var BaseSearchSourceBase = class {
4631
+ constructor(options) {
4632
+ this.activate = () => {
4633
+ if (this.isActive) return;
4634
+ this.state.partialNext({ isActive: true });
4635
+ };
4636
+ this.deactivate = () => {
4637
+ if (!this.isActive) return;
4638
+ this.state.partialNext({ isActive: false });
4639
+ };
4640
+ this.canExecuteQuery = (newSearchString) => {
4641
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
4642
+ const searchString = newSearchString ?? this.searchQuery;
4643
+ return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && searchString);
4644
+ };
4645
+ const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4646
+ this.pageSize = pageSize;
4647
+ this.state = new StateStore(this.initialState);
4528
4648
  }
4529
- });
4530
- var createDraftLinkPreviewsCompositionMiddleware = (composer) => ({
4531
- id: "stream-io/message-composer-middleware/draft-link-previews",
4532
- handlers: {
4533
- compose: ({
4534
- state,
4535
- next,
4536
- forward
4537
- }) => {
4538
- const { linkPreviewsManager } = composer;
4539
- if (!linkPreviewsManager) return forward();
4540
- linkPreviewsManager.cancelURLEnrichment();
4541
- const linkPreviews = linkPreviewsManager.loadedPreviews.map(
4542
- (preview) => LinkPreviewsManager.getPreviewData(preview)
4543
- );
4544
- if (!linkPreviews.length) return forward();
4545
- return next({
4546
- ...state,
4547
- draft: {
4548
- ...state.draft,
4549
- attachments: (state.draft.attachments ?? []).concat(linkPreviews)
4550
- }
4551
- });
4552
- }
4649
+ get lastQueryError() {
4650
+ return this.state.getLatestValue().lastQueryError;
4553
4651
  }
4554
- });
4555
-
4556
- // src/messageComposer/middleware/messageComposer/textComposer.ts
4557
- var createTextComposerCompositionMiddleware = (composer) => ({
4558
- id: "stream-io/message-composer-middleware/text-composition",
4559
- handlers: {
4560
- compose: ({
4561
- state,
4562
- next,
4563
- forward
4564
- }) => {
4565
- if (!composer.textComposer) return forward();
4566
- const { mentionedUsers, text } = composer.textComposer;
4567
- const mentioned_users = Array.from(
4568
- new Set(
4569
- mentionedUsers.filter(
4570
- ({ id, name }) => text.includes(`@${id}`) || text.includes(`@${name}`)
4571
- )
4572
- )
4573
- );
4574
- if (!text && mentioned_users.length === 0) return forward();
4575
- return next({
4576
- ...state,
4577
- localMessage: {
4578
- ...state.localMessage,
4579
- mentioned_users,
4580
- text
4581
- },
4582
- message: {
4583
- ...state.message,
4584
- mentioned_users: mentioned_users.map((u) => u.id),
4585
- text
4586
- }
4587
- });
4588
- }
4652
+ get hasNext() {
4653
+ return this.state.getLatestValue().hasNext;
4589
4654
  }
4590
- });
4591
- var createDraftTextComposerCompositionMiddleware = (composer) => ({
4592
- id: "stream-io/message-composer-middleware/draft-text-composition",
4593
- handlers: {
4594
- compose: ({
4595
- state,
4596
- next,
4597
- forward
4598
- }) => {
4599
- if (!composer.textComposer) return forward();
4600
- const { maxLengthOnSend } = composer.config.text ?? {};
4601
- const { mentionedUsers, text: inputText } = composer.textComposer;
4602
- const mentioned_users = mentionedUsers.length ? Array.from(
4603
- new Set(
4604
- mentionedUsers.filter(
4605
- ({ id, name }) => inputText.includes(`@${id}`) || inputText.includes(`@${name}`)
4606
- )
4607
- )
4608
- ) : void 0;
4609
- const text = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend ? inputText.slice(0, maxLengthOnSend) : inputText;
4610
- return next({
4611
- ...state,
4612
- draft: {
4613
- ...state.draft,
4614
- mentioned_users: mentioned_users?.map((u) => u.id),
4615
- text
4616
- }
4617
- });
4655
+ get hasResults() {
4656
+ return Array.isArray(this.state.getLatestValue().items);
4657
+ }
4658
+ get isActive() {
4659
+ return this.state.getLatestValue().isActive;
4660
+ }
4661
+ get isLoading() {
4662
+ return this.state.getLatestValue().isLoading;
4663
+ }
4664
+ get initialState() {
4665
+ return {
4666
+ hasNext: true,
4667
+ isActive: false,
4668
+ isLoading: false,
4669
+ items: void 0,
4670
+ lastQueryError: void 0,
4671
+ next: void 0,
4672
+ offset: 0,
4673
+ searchQuery: ""
4674
+ };
4675
+ }
4676
+ get items() {
4677
+ return this.state.getLatestValue().items;
4678
+ }
4679
+ get next() {
4680
+ return this.state.getLatestValue().next;
4681
+ }
4682
+ get offset() {
4683
+ return this.state.getLatestValue().offset;
4684
+ }
4685
+ get searchQuery() {
4686
+ return this.state.getLatestValue().searchQuery;
4687
+ }
4688
+ getStateBeforeFirstQuery(newSearchString) {
4689
+ return {
4690
+ ...this.initialState,
4691
+ isActive: this.isActive,
4692
+ isLoading: true,
4693
+ searchQuery: newSearchString
4694
+ };
4695
+ }
4696
+ getStateAfterQuery(stateUpdate, isFirstPage) {
4697
+ const current = this.state.getLatestValue();
4698
+ return {
4699
+ ...current,
4700
+ lastQueryError: void 0,
4701
+ // reset lastQueryError that can be overridden by the stateUpdate
4702
+ ...stateUpdate,
4703
+ isLoading: false,
4704
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
4705
+ };
4706
+ }
4707
+ prepareStateForQuery(newSearchString) {
4708
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
4709
+ const searchString = newSearchString ?? this.searchQuery;
4710
+ if (hasNewSearchQuery) {
4711
+ this.state.next(this.getStateBeforeFirstQuery(newSearchString ?? ""));
4712
+ } else {
4713
+ this.state.partialNext({ isLoading: true });
4618
4714
  }
4715
+ return { searchString, hasNewSearchQuery };
4619
4716
  }
4620
- });
4621
-
4622
- // src/messageComposer/middleware/messageComposer/messageComposerState.ts
4623
- var createMessageComposerStateCompositionMiddleware = (composer) => ({
4624
- id: "stream-io/message-composer-middleware/own-state",
4625
- handlers: {
4626
- compose: ({
4627
- state,
4628
- next
4629
- }) => {
4630
- const payload = {};
4631
- if (composer.quotedMessage) {
4632
- payload.quoted_message_id = composer.quotedMessage.id;
4633
- }
4634
- if (composer.pollId) {
4635
- payload.poll_id = composer.pollId;
4636
- }
4637
- if (composer.showReplyInChannel) {
4638
- payload.show_in_channel = true;
4639
- }
4640
- return next({
4641
- ...state,
4642
- localMessage: {
4643
- ...state.localMessage,
4644
- ...payload,
4645
- quoted_message: composer.quotedMessage ?? void 0
4646
- },
4647
- message: {
4648
- ...state.message,
4649
- ...payload
4650
- }
4651
- });
4717
+ updatePaginationStateFromQuery(result) {
4718
+ const { items, next } = result;
4719
+ const stateUpdate = {};
4720
+ if (Object.prototype.hasOwnProperty.call(result, "next")) {
4721
+ stateUpdate.next = next;
4722
+ stateUpdate.hasNext = !!next;
4723
+ } else {
4724
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
4725
+ stateUpdate.hasNext = items.length === this.pageSize;
4652
4726
  }
4727
+ return stateUpdate;
4653
4728
  }
4654
- });
4655
- var createDraftMessageComposerStateCompositionMiddleware = (composer) => ({
4656
- id: "stream-io/message-composer-middleware/draft-own-state",
4657
- handlers: {
4658
- compose: ({
4659
- state,
4660
- next
4661
- }) => {
4662
- const payload = {};
4663
- if (composer.quotedMessage) {
4664
- payload.quoted_message_id = composer.quotedMessage.id;
4665
- }
4666
- if (composer.pollId) {
4667
- payload.poll_id = composer.pollId;
4668
- }
4669
- if (composer.showReplyInChannel) {
4670
- payload.show_in_channel = true;
4729
+ resetState() {
4730
+ this.state.next(this.initialState);
4731
+ }
4732
+ resetStateAndActivate() {
4733
+ this.resetState();
4734
+ this.activate();
4735
+ }
4736
+ };
4737
+ var BaseSearchSource = class extends BaseSearchSourceBase {
4738
+ constructor(options) {
4739
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4740
+ super(options);
4741
+ this.setDebounceOptions = ({ debounceMs }) => {
4742
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4743
+ };
4744
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4745
+ this.setDebounceOptions({ debounceMs });
4746
+ }
4747
+ async executeQuery(newSearchString) {
4748
+ if (!this.canExecuteQuery(newSearchString)) return;
4749
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4750
+ let stateUpdate = {};
4751
+ try {
4752
+ const results = await this.query(searchString);
4753
+ if (!results) return;
4754
+ const { items } = results;
4755
+ stateUpdate = this.updatePaginationStateFromQuery(results);
4756
+ stateUpdate.items = await this.filterQueryResults(items);
4757
+ } catch (e) {
4758
+ stateUpdate.lastQueryError = e;
4759
+ if (isAPIError(e) && !isErrorRetryable(e)) {
4760
+ stateUpdate.hasNext = false;
4671
4761
  }
4672
- return next({
4673
- ...state,
4674
- draft: {
4675
- ...state.draft,
4676
- ...payload
4677
- }
4678
- });
4762
+ } finally {
4763
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4679
4764
  }
4680
4765
  }
4681
- });
4682
-
4683
- // src/messageComposer/middleware/messageComposer/userDataInjection.ts
4684
- var createUserDataInjectionMiddleware = (composer) => ({
4685
- id: "stream-io/message-composer-middleware/user-data-injection",
4686
- handlers: {
4687
- compose: ({
4688
- state,
4689
- next,
4690
- forward
4691
- }) => {
4692
- if (!composer.client.user) {
4693
- return forward();
4766
+ cancelScheduledQuery() {
4767
+ this.searchDebounced.cancel();
4768
+ }
4769
+ };
4770
+ var BaseSearchSourceSync = class extends BaseSearchSourceBase {
4771
+ constructor(options) {
4772
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4773
+ super(options);
4774
+ this.setDebounceOptions = ({ debounceMs }) => {
4775
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4776
+ };
4777
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4778
+ this.setDebounceOptions({ debounceMs });
4779
+ }
4780
+ executeQuery(newSearchString) {
4781
+ if (!this.canExecuteQuery(newSearchString)) return;
4782
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4783
+ let stateUpdate = {};
4784
+ try {
4785
+ const results = this.query(searchString);
4786
+ if (!results) return;
4787
+ const { items } = results;
4788
+ stateUpdate = this.updatePaginationStateFromQuery(results);
4789
+ stateUpdate.items = this.filterQueryResults(items);
4790
+ } catch (e) {
4791
+ stateUpdate.lastQueryError = e;
4792
+ if (isAPIError(e) && !isErrorRetryable(e)) {
4793
+ stateUpdate.hasNext = false;
4694
4794
  }
4695
- const { channel_mutes, devices, mutes, ...messageUser } = composer.client.user;
4696
- return next({
4697
- ...state,
4698
- localMessage: {
4699
- ...state.localMessage,
4700
- user: messageUser,
4701
- user_id: messageUser.id
4702
- }
4703
- });
4795
+ } finally {
4796
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4704
4797
  }
4705
4798
  }
4706
- });
4707
-
4708
- // src/messageComposer/middleware/messageComposer/pollOnly.ts
4709
- var pollLocalMessageNullifiedFields = {
4710
- attachments: [],
4711
- mentioned_users: [],
4712
- parent_id: void 0,
4713
- quoted_message: void 0,
4714
- text: ""
4715
- };
4716
- var createPollOnlyCompositionMiddleware = (composer) => ({
4717
- id: "stream-io/message-composer-middleware/poll-only",
4718
- handlers: {
4719
- compose: ({
4720
- state,
4721
- complete,
4722
- forward
4723
- }) => {
4724
- const pollId = composer.pollId;
4725
- const isEditingMessage = !!composer.editedMessage;
4726
- const isComposingThreadReply = !!composer.threadId;
4727
- if (!pollId || isComposingThreadReply || isEditingMessage) return forward();
4728
- return complete({
4729
- ...state,
4730
- localMessage: {
4731
- ...state.localMessage,
4732
- ...pollLocalMessageNullifiedFields,
4733
- poll_id: pollId
4734
- },
4735
- message: {
4736
- id: state.localMessage.id,
4737
- poll_id: pollId
4738
- }
4739
- });
4740
- }
4741
- }
4742
- });
4743
-
4744
- // src/messageComposer/middleware/messageComposer/sharedLocation.ts
4745
- var createSharedLocationCompositionMiddleware = (composer) => ({
4746
- id: "stream-io/message-composer-middleware/shared-location",
4747
- handlers: {
4748
- compose: ({
4749
- state,
4750
- next,
4751
- forward
4752
- }) => {
4753
- const { locationComposer } = composer;
4754
- const location = locationComposer.validLocation;
4755
- if (!locationComposer || !location || !composer.client.user) return forward();
4756
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4757
- return next({
4758
- ...state,
4759
- localMessage: {
4760
- ...state.localMessage,
4761
- shared_location: {
4762
- ...location,
4763
- channel_cid: composer.channel.cid,
4764
- created_at: timestamp,
4765
- updated_at: timestamp,
4766
- user_id: composer.client.user.id
4767
- }
4768
- },
4769
- message: {
4770
- ...state.message,
4771
- shared_location: location
4772
- }
4773
- });
4774
- }
4775
- }
4776
- });
4777
-
4778
- // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
4779
- var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
4780
- constructor({ composer }) {
4781
- super();
4782
- this.use([
4783
- createUserDataInjectionMiddleware(composer),
4784
- createPollOnlyCompositionMiddleware(composer),
4785
- createTextComposerCompositionMiddleware(composer),
4786
- createAttachmentsCompositionMiddleware(composer),
4787
- createLinkPreviewsCompositionMiddleware(composer),
4788
- createSharedLocationCompositionMiddleware(composer),
4789
- createMessageComposerStateCompositionMiddleware(composer),
4790
- createCustomDataCompositionMiddleware(composer),
4791
- createCompositionValidationMiddleware(composer),
4792
- createCompositionDataCleanupMiddleware(composer)
4793
- ]);
4794
- }
4795
- };
4796
- var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
4797
- constructor({ composer }) {
4798
- super();
4799
- this.use([
4800
- createDraftTextComposerCompositionMiddleware(composer),
4801
- createDraftAttachmentsCompositionMiddleware(composer),
4802
- createDraftLinkPreviewsCompositionMiddleware(composer),
4803
- createDraftMessageComposerStateCompositionMiddleware(composer),
4804
- createDraftCustomDataCompositionMiddleware(composer),
4805
- createDraftCompositionValidationMiddleware(composer)
4806
- ]);
4807
- }
4808
- };
4809
-
4810
- // src/messageComposer/middleware/messageComposer/commandInjection.ts
4811
- var createCommandInjectionMiddleware = (composer) => ({
4812
- handlers: {
4813
- compose: ({
4814
- forward,
4815
- next,
4816
- state
4817
- }) => {
4818
- const command = composer.textComposer.command;
4819
- if (!command) {
4820
- return forward();
4821
- }
4822
- const { text } = state.localMessage;
4823
- const injection = `/${command?.name}`;
4824
- const enrichedText = `${injection} ${text}`;
4825
- return next({
4826
- ...state,
4827
- localMessage: {
4828
- ...state.localMessage,
4829
- text: enrichedText
4830
- },
4831
- message: {
4832
- ...state.message,
4833
- text: enrichedText
4834
- }
4835
- });
4836
- }
4837
- },
4838
- id: "stream-io/message-composer-middleware/command-string-injection"
4839
- });
4840
- var createDraftCommandInjectionMiddleware = (composer) => ({
4841
- handlers: {
4842
- compose: ({
4843
- forward,
4844
- next,
4845
- state
4846
- }) => {
4847
- const command = composer.textComposer.command;
4848
- if (!command) {
4849
- return forward();
4850
- }
4851
- const { text } = state.draft;
4852
- const injection = `/${command?.name}`;
4853
- const enrichedText = `${injection} ${text}`;
4854
- return next({
4855
- ...state,
4856
- draft: {
4857
- ...state.draft,
4858
- text: enrichedText
4859
- }
4860
- });
4861
- }
4862
- },
4863
- id: "stream-io/message-composer-middleware/draft-command-string-injection"
4864
- });
4865
-
4866
- // src/messageComposer/middleware/textComposer/activeCommandGuard.ts
4867
- var createActiveCommandGuardMiddleware = () => ({
4868
- handlers: {
4869
- onChange: ({ complete, forward, state }) => {
4870
- if (state.command) {
4871
- return complete(state);
4872
- }
4873
- return forward();
4874
- },
4875
- onSuggestionItemSelect: ({ forward }) => forward()
4876
- },
4877
- id: "stream-io/text-composer/active-command-guard"
4878
- });
4879
-
4880
- // src/errors.ts
4881
- var APIErrorCodes = {
4882
- "-1": { name: "InternalSystemError", retryable: true },
4883
- "2": { name: "AccessKeyError", retryable: false },
4884
- "3": { name: "AuthenticationFailedError", retryable: true },
4885
- "4": { name: "InputError", retryable: false },
4886
- "6": { name: "DuplicateUsernameError", retryable: false },
4887
- "9": { name: "RateLimitError", retryable: true },
4888
- "16": { name: "DoesNotExistError", retryable: false },
4889
- "17": { name: "NotAllowedError", retryable: false },
4890
- "18": { name: "EventNotSupportedError", retryable: false },
4891
- "19": { name: "ChannelFeatureNotSupportedError", retryable: false },
4892
- "20": { name: "MessageTooLongError", retryable: false },
4893
- "21": { name: "MultipleNestingLevelError", retryable: false },
4894
- "22": { name: "PayloadTooBigError", retryable: false },
4895
- "23": { name: "RequestTimeoutError", retryable: true },
4896
- "24": { name: "MaxHeaderSizeExceededError", retryable: false },
4897
- "40": { name: "AuthErrorTokenExpired", retryable: false },
4898
- "41": { name: "AuthErrorTokenNotValidYet", retryable: false },
4899
- "42": { name: "AuthErrorTokenUsedBeforeIssuedAt", retryable: false },
4900
- "43": { name: "AuthErrorTokenSignatureInvalid", retryable: false },
4901
- "44": { name: "CustomCommandEndpointMissingError", retryable: false },
4902
- "45": { name: "CustomCommandEndpointCallError", retryable: true },
4903
- "46": { name: "ConnectionIDNotFoundError", retryable: false },
4904
- "60": { name: "CoolDownError", retryable: true },
4905
- "69": { name: "ErrWrongRegion", retryable: false },
4906
- "70": { name: "ErrQueryChannelPermissions", retryable: false },
4907
- "71": { name: "ErrTooManyConnections", retryable: true },
4908
- "99": { name: "AppSuspendedError", retryable: false }
4909
- };
4910
- function isAPIError(error) {
4911
- return error.code !== void 0;
4912
- }
4913
- function isErrorRetryable(error) {
4914
- if (!error.code) return false;
4915
- const err = APIErrorCodes[`${error.code}`];
4916
- if (!err) return false;
4917
- return err.retryable;
4918
- }
4919
- function isConnectionIDError(error) {
4920
- return error.code === 46;
4921
- }
4922
- function isWSFailure(err) {
4923
- if (typeof err.isWSFailure === "boolean") {
4924
- return err.isWSFailure;
4925
- }
4926
- try {
4927
- return JSON.parse(err.message).isWSFailure;
4928
- } catch (_) {
4929
- return false;
4930
- }
4931
- }
4932
- function isErrorResponse(res) {
4933
- return !res.status || res.status < 200 || 300 <= res.status;
4934
- }
4935
-
4936
- // src/search/BaseSearchSource.ts
4937
- var DEFAULT_SEARCH_SOURCE_OPTIONS = {
4938
- debounceMs: 300,
4939
- pageSize: 10
4940
- };
4941
- var BaseSearchSourceBase = class {
4942
- constructor(options) {
4943
- this.activate = () => {
4944
- if (this.isActive) return;
4945
- this.state.partialNext({ isActive: true });
4946
- };
4947
- this.deactivate = () => {
4948
- if (!this.isActive) return;
4949
- this.state.partialNext({ isActive: false });
4950
- };
4951
- this.canExecuteQuery = (newSearchString) => {
4952
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
4953
- const searchString = newSearchString ?? this.searchQuery;
4954
- return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && searchString);
4955
- };
4956
- const { pageSize } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4957
- this.pageSize = pageSize;
4958
- this.state = new StateStore(this.initialState);
4959
- }
4960
- get lastQueryError() {
4961
- return this.state.getLatestValue().lastQueryError;
4962
- }
4963
- get hasNext() {
4964
- return this.state.getLatestValue().hasNext;
4965
- }
4966
- get hasResults() {
4967
- return Array.isArray(this.state.getLatestValue().items);
4968
- }
4969
- get isActive() {
4970
- return this.state.getLatestValue().isActive;
4971
- }
4972
- get isLoading() {
4973
- return this.state.getLatestValue().isLoading;
4974
- }
4975
- get initialState() {
4976
- return {
4977
- hasNext: true,
4978
- isActive: false,
4979
- isLoading: false,
4980
- items: void 0,
4981
- lastQueryError: void 0,
4982
- next: void 0,
4983
- offset: 0,
4984
- searchQuery: ""
4985
- };
4986
- }
4987
- get items() {
4988
- return this.state.getLatestValue().items;
4989
- }
4990
- get next() {
4991
- return this.state.getLatestValue().next;
4992
- }
4993
- get offset() {
4994
- return this.state.getLatestValue().offset;
4995
- }
4996
- get searchQuery() {
4997
- return this.state.getLatestValue().searchQuery;
4998
- }
4999
- getStateBeforeFirstQuery(newSearchString) {
5000
- return {
5001
- ...this.initialState,
5002
- isActive: this.isActive,
5003
- isLoading: true,
5004
- searchQuery: newSearchString
5005
- };
5006
- }
5007
- getStateAfterQuery(stateUpdate, isFirstPage) {
5008
- const current = this.state.getLatestValue();
5009
- return {
5010
- ...current,
5011
- lastQueryError: void 0,
5012
- // reset lastQueryError that can be overridden by the stateUpdate
5013
- ...stateUpdate,
5014
- isLoading: false,
5015
- items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5016
- };
5017
- }
5018
- prepareStateForQuery(newSearchString) {
5019
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5020
- const searchString = newSearchString ?? this.searchQuery;
5021
- if (hasNewSearchQuery) {
5022
- this.state.next(this.getStateBeforeFirstQuery(newSearchString ?? ""));
5023
- } else {
5024
- this.state.partialNext({ isLoading: true });
5025
- }
5026
- return { searchString, hasNewSearchQuery };
5027
- }
5028
- updatePaginationStateFromQuery(result) {
5029
- const { items, next } = result;
5030
- const stateUpdate = {};
5031
- if (Object.prototype.hasOwnProperty.call(result, "next")) {
5032
- stateUpdate.next = next;
5033
- stateUpdate.hasNext = !!next;
5034
- } else {
5035
- stateUpdate.offset = (this.offset ?? 0) + items.length;
5036
- stateUpdate.hasNext = items.length === this.pageSize;
5037
- }
5038
- return stateUpdate;
5039
- }
5040
- resetState() {
5041
- this.state.next(this.initialState);
5042
- }
5043
- resetStateAndActivate() {
5044
- this.resetState();
5045
- this.activate();
5046
- }
5047
- };
5048
- var BaseSearchSource = class extends BaseSearchSourceBase {
5049
- constructor(options) {
5050
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5051
- super(options);
5052
- this.setDebounceOptions = ({ debounceMs }) => {
5053
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5054
- };
5055
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
5056
- this.setDebounceOptions({ debounceMs });
5057
- }
5058
- async executeQuery(newSearchString) {
5059
- if (!this.canExecuteQuery(newSearchString)) return;
5060
- const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
5061
- let stateUpdate = {};
5062
- try {
5063
- const results = await this.query(searchString);
5064
- if (!results) return;
5065
- const { items } = results;
5066
- stateUpdate = this.updatePaginationStateFromQuery(results);
5067
- stateUpdate.items = await this.filterQueryResults(items);
5068
- } catch (e) {
5069
- stateUpdate.lastQueryError = e;
5070
- if (isAPIError(e) && !isErrorRetryable(e)) {
5071
- stateUpdate.hasNext = false;
5072
- }
5073
- } finally {
5074
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
5075
- }
5076
- }
5077
- cancelScheduledQuery() {
5078
- this.searchDebounced.cancel();
5079
- }
5080
- };
5081
- var BaseSearchSourceSync = class extends BaseSearchSourceBase {
5082
- constructor(options) {
5083
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
5084
- super(options);
5085
- this.setDebounceOptions = ({ debounceMs }) => {
5086
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5087
- };
5088
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
5089
- this.setDebounceOptions({ debounceMs });
5090
- }
5091
- executeQuery(newSearchString) {
5092
- if (!this.canExecuteQuery(newSearchString)) return;
5093
- const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
5094
- let stateUpdate = {};
5095
- try {
5096
- const results = this.query(searchString);
5097
- if (!results) return;
5098
- const { items } = results;
5099
- stateUpdate = this.updatePaginationStateFromQuery(results);
5100
- stateUpdate.items = this.filterQueryResults(items);
5101
- } catch (e) {
5102
- stateUpdate.lastQueryError = e;
5103
- if (isAPIError(e) && !isErrorRetryable(e)) {
5104
- stateUpdate.hasNext = false;
5105
- }
5106
- } finally {
5107
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
5108
- }
5109
- }
5110
- cancelScheduledQuery() {
5111
- this.searchDebounced.cancel();
5112
- }
4799
+ cancelScheduledQuery() {
4800
+ this.searchDebounced.cancel();
4801
+ }
5113
4802
  };
5114
4803
 
5115
4804
  // src/search/SearchController.ts
@@ -5205,586 +4894,1095 @@ var SearchController = class {
5205
4894
  get activeSources() {
5206
4895
  return this.state.getLatestValue().sources.filter((s) => s.isActive);
5207
4896
  }
5208
- get isActive() {
5209
- return this.state.getLatestValue().isActive;
4897
+ get isActive() {
4898
+ return this.state.getLatestValue().isActive;
4899
+ }
4900
+ get searchQuery() {
4901
+ return this.state.getLatestValue().searchQuery;
4902
+ }
4903
+ get searchSourceTypes() {
4904
+ return this.sources.map((s) => s.type);
4905
+ }
4906
+ };
4907
+
4908
+ // src/pagination/BasePaginator.ts
4909
+ var DEFAULT_PAGINATION_OPTIONS = {
4910
+ debounceMs: 300,
4911
+ pageSize: 10
4912
+ };
4913
+ var BasePaginator = class {
4914
+ constructor(options) {
4915
+ this._isCursorPagination = false;
4916
+ this.setDebounceOptions = ({ debounceMs }) => {
4917
+ this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4918
+ };
4919
+ this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
4920
+ this.next = () => this.executeQuery({ direction: "next" });
4921
+ this.prev = () => this.executeQuery({ direction: "prev" });
4922
+ this.nextDebounced = () => {
4923
+ this._executeQueryDebounced({ direction: "next" });
4924
+ };
4925
+ this.prevDebounced = () => {
4926
+ this._executeQueryDebounced({ direction: "prev" });
4927
+ };
4928
+ const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
4929
+ this.pageSize = pageSize;
4930
+ this.state = new StateStore(this.initialState);
4931
+ this.setDebounceOptions({ debounceMs });
4932
+ }
4933
+ get lastQueryError() {
4934
+ return this.state.getLatestValue().lastQueryError;
4935
+ }
4936
+ get hasNext() {
4937
+ return this.state.getLatestValue().hasNext;
4938
+ }
4939
+ get hasPrev() {
4940
+ return this.state.getLatestValue().hasPrev;
4941
+ }
4942
+ get hasResults() {
4943
+ return Array.isArray(this.state.getLatestValue().items);
4944
+ }
4945
+ get isLoading() {
4946
+ return this.state.getLatestValue().isLoading;
4947
+ }
4948
+ get initialState() {
4949
+ return {
4950
+ hasNext: true,
4951
+ hasPrev: true,
4952
+ //todo: check if optimistic value does not cause problems in UI
4953
+ isLoading: false,
4954
+ items: void 0,
4955
+ lastQueryError: void 0,
4956
+ cursor: void 0,
4957
+ offset: 0
4958
+ };
4959
+ }
4960
+ get items() {
4961
+ return this.state.getLatestValue().items;
4962
+ }
4963
+ get cursor() {
4964
+ return this.state.getLatestValue().cursor;
4965
+ }
4966
+ get offset() {
4967
+ return this.state.getLatestValue().offset;
4968
+ }
4969
+ getStateBeforeFirstQuery() {
4970
+ return {
4971
+ ...this.initialState,
4972
+ isLoading: true
4973
+ };
4974
+ }
4975
+ getStateAfterQuery(stateUpdate, isFirstPage) {
4976
+ const current = this.state.getLatestValue();
4977
+ return {
4978
+ ...current,
4979
+ lastQueryError: void 0,
4980
+ // reset lastQueryError that can be overridden by the stateUpdate
4981
+ ...stateUpdate,
4982
+ isLoading: false,
4983
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
4984
+ };
4985
+ }
4986
+ async executeQuery({ direction }) {
4987
+ if (!this.canExecuteQuery(direction)) return;
4988
+ const isFirstPage = typeof this.items === "undefined";
4989
+ if (isFirstPage) {
4990
+ this.state.next(this.getStateBeforeFirstQuery());
4991
+ } else {
4992
+ this.state.partialNext({ isLoading: true });
4993
+ }
4994
+ const stateUpdate = {};
4995
+ try {
4996
+ const results = await this.query({ direction });
4997
+ if (!results) return;
4998
+ const { items, next, prev } = results;
4999
+ if (isFirstPage && (next || prev)) {
5000
+ this._isCursorPagination = true;
5001
+ }
5002
+ if (this._isCursorPagination) {
5003
+ stateUpdate.cursor = { next: next || null, prev: prev || null };
5004
+ stateUpdate.hasNext = !!next;
5005
+ stateUpdate.hasPrev = !!prev;
5006
+ } else {
5007
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
5008
+ stateUpdate.hasNext = items.length === this.pageSize;
5009
+ }
5010
+ stateUpdate.items = await this.filterQueryResults(items);
5011
+ } catch (e) {
5012
+ stateUpdate.lastQueryError = e;
5013
+ } finally {
5014
+ this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
5015
+ }
5016
+ }
5017
+ cancelScheduledQuery() {
5018
+ this._executeQueryDebounced.cancel();
5019
+ }
5020
+ resetState() {
5021
+ this.state.next(this.initialState);
5022
+ }
5023
+ };
5024
+
5025
+ // src/pagination/FilterBuilder.ts
5026
+ var FilterBuilder = class {
5027
+ constructor(params) {
5028
+ this.context = new StateStore(params?.initialContext ?? {});
5029
+ this.filterConfig = new StateStore(
5030
+ params?.initialFilterConfig ?? {}
5031
+ );
5032
+ }
5033
+ updateFilterConfig(config) {
5034
+ this.filterConfig.partialNext(config);
5035
+ }
5036
+ enableFilter(filterKey) {
5037
+ const config = this.filterConfig.getLatestValue();
5038
+ if (config[filterKey]) {
5039
+ this.filterConfig.partialNext({
5040
+ [filterKey]: {
5041
+ ...config[filterKey],
5042
+ enabled: true
5043
+ }
5044
+ });
5045
+ }
5046
+ }
5047
+ disableFilter(filterKey) {
5048
+ const config = this.filterConfig.getLatestValue();
5049
+ if (config[filterKey]) {
5050
+ this.filterConfig.partialNext({
5051
+ [filterKey]: {
5052
+ ...config[filterKey],
5053
+ enabled: false
5054
+ }
5055
+ });
5056
+ }
5057
+ }
5058
+ updateContext(newContext) {
5059
+ this.context.partialNext(newContext);
5060
+ }
5061
+ buildFilters(params) {
5062
+ const filters = {
5063
+ ...params?.baseFilters ?? {}
5064
+ };
5065
+ const filterConfig = this.filterConfig.getLatestValue();
5066
+ for (const key in filterConfig) {
5067
+ const configItem = filterConfig[key];
5068
+ if (!configItem?.enabled) continue;
5069
+ const generated = configItem.generate({
5070
+ ...this.context.getLatestValue(),
5071
+ ...params?.context ?? {}
5072
+ });
5073
+ if (generated) Object.assign(filters, generated);
5074
+ }
5075
+ return filters;
5076
+ }
5077
+ };
5078
+
5079
+ // src/pagination/ReminderPaginator.ts
5080
+ var ReminderPaginator = class extends BasePaginator {
5081
+ constructor(client, options) {
5082
+ super(options);
5083
+ this.query = async ({
5084
+ direction
5085
+ }) => {
5086
+ const cursor = this.cursor?.[direction];
5087
+ const {
5088
+ reminders: items,
5089
+ next,
5090
+ prev
5091
+ } = await this.client.queryReminders({
5092
+ filter: this.filters,
5093
+ sort: this.sort,
5094
+ limit: this.pageSize,
5095
+ [direction]: cursor
5096
+ });
5097
+ return { items, next, prev };
5098
+ };
5099
+ this.filterQueryResults = (items) => items;
5100
+ this.client = client;
5101
+ }
5102
+ get filters() {
5103
+ return this._filters;
5104
+ }
5105
+ get sort() {
5106
+ return this._sort;
5210
5107
  }
5211
- get searchQuery() {
5212
- return this.state.getLatestValue().searchQuery;
5108
+ set filters(filters) {
5109
+ this._filters = filters;
5110
+ this.resetState();
5213
5111
  }
5214
- get searchSourceTypes() {
5215
- return this.sources.map((s) => s.type);
5112
+ set sort(sort) {
5113
+ this._sort = sort;
5114
+ this.resetState();
5216
5115
  }
5217
5116
  };
5218
5117
 
5219
- // src/pagination/BasePaginator.ts
5220
- var DEFAULT_PAGINATION_OPTIONS = {
5221
- debounceMs: 300,
5222
- pageSize: 10
5223
- };
5224
- var BasePaginator = class {
5225
- constructor(options) {
5226
- this._isCursorPagination = false;
5227
- this.setDebounceOptions = ({ debounceMs }) => {
5228
- this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
5229
- };
5230
- this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
5231
- this.next = () => this.executeQuery({ direction: "next" });
5232
- this.prev = () => this.executeQuery({ direction: "prev" });
5233
- this.nextDebounced = () => {
5234
- this._executeQueryDebounced({ direction: "next" });
5235
- };
5236
- this.prevDebounced = () => {
5237
- this._executeQueryDebounced({ direction: "prev" });
5238
- };
5239
- const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
5240
- this.pageSize = pageSize;
5241
- this.state = new StateStore(this.initialState);
5242
- this.setDebounceOptions({ debounceMs });
5118
+ // src/search/UserSearchSource.ts
5119
+ var UserSearchSource = class extends BaseSearchSource {
5120
+ constructor(client, options, filterBuilderOptions = {}) {
5121
+ super(options);
5122
+ this.type = "users";
5123
+ this.client = client;
5124
+ this.filterBuilder = new FilterBuilder({
5125
+ initialFilterConfig: {
5126
+ $or: {
5127
+ enabled: true,
5128
+ generate: ({ searchQuery }) => searchQuery ? {
5129
+ $or: [
5130
+ { id: { $autocomplete: searchQuery } },
5131
+ { name: { $autocomplete: searchQuery } }
5132
+ ]
5133
+ } : null
5134
+ }
5135
+ },
5136
+ ...filterBuilderOptions
5137
+ });
5243
5138
  }
5244
- get lastQueryError() {
5245
- return this.state.getLatestValue().lastQueryError;
5139
+ async query(searchQuery) {
5140
+ const filters = this.filterBuilder.buildFilters({
5141
+ baseFilters: this.filters,
5142
+ context: { searchQuery }
5143
+ });
5144
+ const sort = { id: 1, ...this.sort };
5145
+ const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5146
+ const { users } = await this.client.queryUsers(filters, sort, options);
5147
+ return { items: users };
5246
5148
  }
5247
- get hasNext() {
5248
- return this.state.getLatestValue().hasNext;
5149
+ filterQueryResults(items) {
5150
+ return items.filter((u) => u.id !== this.client.user?.id);
5249
5151
  }
5250
- get hasPrev() {
5251
- return this.state.getLatestValue().hasPrev;
5152
+ };
5153
+
5154
+ // src/search/ChannelSearchSource.ts
5155
+ var ChannelSearchSource = class extends BaseSearchSource {
5156
+ constructor(client, options, filterBuilderOptions = {}) {
5157
+ super(options);
5158
+ this.type = "channels";
5159
+ this.client = client;
5160
+ this.filterBuilder = new FilterBuilder({
5161
+ ...filterBuilderOptions,
5162
+ initialFilterConfig: {
5163
+ name: {
5164
+ enabled: true,
5165
+ generate: ({ searchQuery }) => searchQuery ? { name: { $autocomplete: searchQuery } } : null
5166
+ },
5167
+ ...filterBuilderOptions.initialFilterConfig
5168
+ }
5169
+ });
5252
5170
  }
5253
- get hasResults() {
5254
- return Array.isArray(this.state.getLatestValue().items);
5171
+ async query(searchQuery) {
5172
+ const filters = this.filterBuilder.buildFilters({
5173
+ baseFilters: {
5174
+ ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5175
+ ...this.filters
5176
+ },
5177
+ context: { searchQuery }
5178
+ });
5179
+ const sort = this.sort ?? {};
5180
+ const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5181
+ const items = await this.client.queryChannels(filters, sort, options);
5182
+ return { items };
5255
5183
  }
5256
- get isLoading() {
5257
- return this.state.getLatestValue().isLoading;
5184
+ filterQueryResults(items) {
5185
+ return items;
5258
5186
  }
5259
- get initialState() {
5260
- return {
5261
- hasNext: true,
5262
- hasPrev: true,
5263
- //todo: check if optimistic value does not cause problems in UI
5264
- isLoading: false,
5265
- items: void 0,
5266
- lastQueryError: void 0,
5267
- cursor: void 0,
5268
- offset: 0
5269
- };
5187
+ };
5188
+
5189
+ // src/search/MessageSearchSource.ts
5190
+ var MessageSearchSource = class extends BaseSearchSource {
5191
+ constructor(client, options, filterBuilderOptions) {
5192
+ super(options);
5193
+ this.type = "messages";
5194
+ this.client = client;
5195
+ this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5196
+ this.messageSearchFilterBuilder = new FilterBuilder({
5197
+ ...filterBuilderOptions?.messageSearch,
5198
+ initialFilterConfig: {
5199
+ text: {
5200
+ enabled: true,
5201
+ generate: ({ searchQuery }) => searchQuery ? { text: searchQuery } : null
5202
+ },
5203
+ ...filterBuilderOptions?.messageSearch?.initialFilterConfig
5204
+ }
5205
+ });
5206
+ this.channelQueryFilterBuilder = new FilterBuilder({
5207
+ ...filterBuilderOptions?.channelQuery,
5208
+ initialFilterConfig: {
5209
+ cid: {
5210
+ enabled: true,
5211
+ generate: ({ cids }) => cids ? { cid: { $in: cids } } : null
5212
+ },
5213
+ ...filterBuilderOptions?.channelQuery?.initialFilterConfig
5214
+ }
5215
+ });
5270
5216
  }
5271
- get items() {
5272
- return this.state.getLatestValue().items;
5217
+ async query(searchQuery) {
5218
+ if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
5219
+ const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5220
+ baseFilters: {
5221
+ ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5222
+ ...this.messageSearchChannelFilters
5223
+ },
5224
+ context: { searchQuery }
5225
+ });
5226
+ const messageFilters = this.messageSearchFilterBuilder.buildFilters({
5227
+ baseFilters: {
5228
+ type: "regular",
5229
+ ...this.messageSearchFilters
5230
+ },
5231
+ context: { searchQuery }
5232
+ });
5233
+ const sort = {
5234
+ created_at: -1,
5235
+ ...this.messageSearchSort
5236
+ };
5237
+ const options = {
5238
+ limit: this.pageSize,
5239
+ next: this.next,
5240
+ sort
5241
+ };
5242
+ const { next, results } = await this.client.search(
5243
+ channelFilters,
5244
+ messageFilters,
5245
+ options
5246
+ );
5247
+ const items = results.map(({ message }) => message);
5248
+ const cids = Array.from(
5249
+ items.reduce((acc, message) => {
5250
+ if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
5251
+ return acc;
5252
+ }, /* @__PURE__ */ new Set())
5253
+ );
5254
+ if (cids.length > 0) {
5255
+ const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
5256
+ baseFilters: this.channelQueryFilters,
5257
+ context: { cids }
5258
+ });
5259
+ await this.client.queryChannels(
5260
+ channelQueryFilters,
5261
+ {
5262
+ last_message_at: -1,
5263
+ ...this.channelQuerySort
5264
+ },
5265
+ this.channelQueryOptions
5266
+ );
5267
+ }
5268
+ return { items, next };
5273
5269
  }
5274
- get cursor() {
5275
- return this.state.getLatestValue().cursor;
5270
+ filterQueryResults(items) {
5271
+ return items;
5276
5272
  }
5277
- get offset() {
5278
- return this.state.getLatestValue().offset;
5273
+ };
5274
+
5275
+ // src/messageComposer/middleware/textComposer/commandUtils.ts
5276
+ function escapeCommandRegExp(text) {
5277
+ return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, "\\$&");
5278
+ }
5279
+ var getRawCommandName = (text) => text?.match(/^\/(\S+)(?:\s.*)?$/)?.[1];
5280
+ var getCompleteCommandInString = (text) => {
5281
+ const match = text.match(/^\/(\S+)\s+.*/);
5282
+ const commandName = match && match[1];
5283
+ return commandName;
5284
+ };
5285
+ var stripCommandFromText = (text, commandName) => text.replace(new RegExp(`^${escapeCommandRegExp(`/${commandName}`)}\\s*`), "");
5286
+ var notifyCommandDisabled = (composer, command) => {
5287
+ const disabledReason = composer.getCommandDisabledReason(command);
5288
+ if (!disabledReason) return;
5289
+ composer.client.notifications.addWarning({
5290
+ message: disabledReason === "editing" ? "Command not available while editing" : "Command not available while replying",
5291
+ origin: {
5292
+ emitter: "MessageComposer",
5293
+ context: { command, composer }
5294
+ },
5295
+ options: {
5296
+ type: "validation:command:disabled",
5297
+ metadata: {
5298
+ command: command.name,
5299
+ reason: disabledReason
5300
+ }
5301
+ }
5302
+ });
5303
+ return true;
5304
+ };
5305
+
5306
+ // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
5307
+ var getTriggerCharWithToken = ({
5308
+ trigger,
5309
+ text,
5310
+ isCommand = false,
5311
+ acceptTrailingSpaces = true
5312
+ }) => {
5313
+ const escapedTrigger = escapeCommandRegExp(trigger);
5314
+ const triggerNorWhitespace = `[^\\s${escapedTrigger}]*`;
5315
+ const match = text.match(
5316
+ new RegExp(
5317
+ isCommand ? `^[${escapedTrigger}]${triggerNorWhitespace}$` : acceptTrailingSpaces ? `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}\\s?${triggerNorWhitespace}$` : `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}$`,
5318
+ "g"
5319
+ )
5320
+ );
5321
+ return match && match[match.length - 1].trim();
5322
+ };
5323
+ var insertItemWithTrigger = ({
5324
+ insertText,
5325
+ selection,
5326
+ text,
5327
+ trigger
5328
+ }) => {
5329
+ const beforeCursor = text.slice(0, selection.end);
5330
+ const afterCursor = text.slice(selection.end);
5331
+ const lastIndex = beforeCursor.lastIndexOf(trigger);
5332
+ const newText = beforeCursor.slice(0, lastIndex) + insertText + afterCursor;
5333
+ return {
5334
+ text: newText,
5335
+ selection: {
5336
+ start: lastIndex + insertText.length,
5337
+ end: lastIndex + insertText.length
5338
+ }
5339
+ };
5340
+ };
5341
+ var replaceWordWithEntity = async ({
5342
+ caretPosition,
5343
+ getEntityString,
5344
+ text
5345
+ }) => {
5346
+ const lastWordRegex = /([^\s]+)(\s*)$/;
5347
+ const match = lastWordRegex.exec(text.slice(0, caretPosition));
5348
+ if (!match) return text;
5349
+ const lastWord = match[1];
5350
+ if (!lastWord) return text;
5351
+ const spaces = match[2];
5352
+ const newWord = await getEntityString(lastWord);
5353
+ if (newWord == null) return text;
5354
+ const textBeforeWord = text.slice(0, caretPosition - match[0].length);
5355
+ const textAfterCaret = text.slice(caretPosition, -1);
5356
+ return textBeforeWord + newWord + spaces + textAfterCaret;
5357
+ };
5358
+ var getTokenizedSuggestionDisplayName = ({
5359
+ displayName,
5360
+ searchToken
5361
+ }) => ({
5362
+ tokenizedDisplayName: {
5363
+ token: searchToken,
5364
+ parts: searchToken ? displayName.split(new RegExp(`(${escapeCommandRegExp(searchToken)})`, "gi")).filter(Boolean) : [displayName]
5279
5365
  }
5280
- getStateBeforeFirstQuery() {
5281
- return {
5282
- ...this.initialState,
5283
- isLoading: true
5366
+ });
5367
+
5368
+ // src/messageComposer/middleware/textComposer/commands.ts
5369
+ var CommandSearchSource = class extends BaseSearchSourceSync {
5370
+ constructor(channel, options) {
5371
+ super(options);
5372
+ this.type = "commands";
5373
+ this.canExecuteQuery = (newSearchString) => {
5374
+ const hasNewSearchQuery = typeof newSearchString !== "undefined";
5375
+ return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
5284
5376
  };
5377
+ this.channel = channel;
5285
5378
  }
5286
- getStateAfterQuery(stateUpdate, isFirstPage) {
5287
- const current = this.state.getLatestValue();
5379
+ getStateBeforeFirstQuery(newSearchString) {
5380
+ const newState = super.getStateBeforeFirstQuery(newSearchString);
5381
+ const { items } = this.state.getLatestValue();
5288
5382
  return {
5289
- ...current,
5290
- lastQueryError: void 0,
5291
- // reset lastQueryError that can be overridden by the stateUpdate
5292
- ...stateUpdate,
5293
- isLoading: false,
5294
- items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
5383
+ ...newState,
5384
+ items
5385
+ // preserve items to avoid flickering
5295
5386
  };
5296
5387
  }
5297
- async executeQuery({ direction }) {
5298
- if (!this.canExecuteQuery(direction)) return;
5299
- const isFirstPage = typeof this.items === "undefined";
5300
- if (isFirstPage) {
5301
- this.state.next(this.getStateBeforeFirstQuery());
5302
- } else {
5303
- this.state.partialNext({ isLoading: true });
5304
- }
5305
- const stateUpdate = {};
5306
- try {
5307
- const results = await this.query({ direction });
5308
- if (!results) return;
5309
- const { items, next, prev } = results;
5310
- if (isFirstPage && (next || prev)) {
5311
- this._isCursorPagination = true;
5388
+ query(searchQuery) {
5389
+ const channelConfig = this.channel.getConfig();
5390
+ const commands = channelConfig?.commands || [];
5391
+ const selectedCommands = commands.filter(
5392
+ (command) => !!(command.name && command.name.toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1)
5393
+ );
5394
+ selectedCommands.sort((a, b) => {
5395
+ let nameA = a.name?.toLowerCase();
5396
+ let nameB = b.name?.toLowerCase();
5397
+ if (nameA?.indexOf(searchQuery) === 0) {
5398
+ nameA = `0${nameA}`;
5312
5399
  }
5313
- if (this._isCursorPagination) {
5314
- stateUpdate.cursor = { next: next || null, prev: prev || null };
5315
- stateUpdate.hasNext = !!next;
5316
- stateUpdate.hasPrev = !!prev;
5317
- } else {
5318
- stateUpdate.offset = (this.offset ?? 0) + items.length;
5319
- stateUpdate.hasNext = items.length === this.pageSize;
5400
+ if (nameB?.indexOf(searchQuery) === 0) {
5401
+ nameB = `0${nameB}`;
5320
5402
  }
5321
- stateUpdate.items = await this.filterQueryResults(items);
5322
- } catch (e) {
5323
- stateUpdate.lastQueryError = e;
5324
- } finally {
5325
- this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
5326
- }
5403
+ if (nameA != null && nameB != null) {
5404
+ if (nameA < nameB) {
5405
+ return -1;
5406
+ }
5407
+ if (nameA > nameB) {
5408
+ return 1;
5409
+ }
5410
+ }
5411
+ return 0;
5412
+ });
5413
+ return {
5414
+ items: selectedCommands.map((command) => ({
5415
+ ...command,
5416
+ id: command.name
5417
+ })),
5418
+ next: null
5419
+ };
5327
5420
  }
5328
- cancelScheduledQuery() {
5329
- this._executeQueryDebounced.cancel();
5421
+ filterQueryResults(items) {
5422
+ return items;
5330
5423
  }
5331
- resetState() {
5332
- this.state.next(this.initialState);
5424
+ };
5425
+ var DEFAULT_OPTIONS = { minChars: 1, trigger: "/" };
5426
+ var createCommandsMiddleware = (channel, options) => {
5427
+ const finalOptions = mergeWith(DEFAULT_OPTIONS, options ?? {});
5428
+ let searchSource = new CommandSearchSource(channel);
5429
+ if (options?.searchSource) {
5430
+ searchSource = options.searchSource;
5431
+ searchSource.resetState();
5333
5432
  }
5433
+ searchSource.activate();
5434
+ return {
5435
+ id: "stream-io/text-composer/commands-middleware",
5436
+ handlers: {
5437
+ onChange: ({ state, next, complete, forward }) => {
5438
+ if (!state.selection) return forward();
5439
+ const finalText = state.text.slice(0, state.selection.end);
5440
+ const commandName = getCompleteCommandInString(finalText);
5441
+ if (commandName) {
5442
+ const command = searchSource?.query(commandName).items[0];
5443
+ const composer = options?.composer;
5444
+ if (command && !composer?.isCommandDisabled(command)) {
5445
+ return next({
5446
+ ...state,
5447
+ command,
5448
+ suggestions: void 0
5449
+ });
5450
+ }
5451
+ }
5452
+ const triggerWithToken = getTriggerCharWithToken({
5453
+ trigger: finalOptions.trigger,
5454
+ text: finalText,
5455
+ acceptTrailingSpaces: false,
5456
+ isCommand: true
5457
+ });
5458
+ const newSearchTriggerred = triggerWithToken && triggerWithToken.length === finalOptions.minChars;
5459
+ if (newSearchTriggerred) {
5460
+ searchSource.resetStateAndActivate();
5461
+ }
5462
+ const triggerWasRemoved = !triggerWithToken || triggerWithToken.length < finalOptions.minChars;
5463
+ if (triggerWasRemoved) {
5464
+ const hasStaleSuggestions = state.suggestions?.trigger === finalOptions.trigger;
5465
+ const newState = { ...state };
5466
+ if (hasStaleSuggestions) {
5467
+ delete newState.suggestions;
5468
+ }
5469
+ return next(newState);
5470
+ }
5471
+ return complete({
5472
+ ...state,
5473
+ command: null,
5474
+ suggestions: {
5475
+ query: triggerWithToken.slice(1),
5476
+ searchSource,
5477
+ trigger: finalOptions.trigger
5478
+ }
5479
+ });
5480
+ },
5481
+ onSuggestionItemSelect: ({ state, next, forward }) => {
5482
+ const { selectedSuggestion } = state.change ?? {};
5483
+ if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger)
5484
+ return forward();
5485
+ const composer = options?.composer;
5486
+ if (composer && notifyCommandDisabled(composer, selectedSuggestion)) {
5487
+ return forward();
5488
+ }
5489
+ searchSource.resetStateAndActivate();
5490
+ return next({
5491
+ ...state,
5492
+ ...insertItemWithTrigger({
5493
+ insertText: `/${selectedSuggestion.name} `,
5494
+ selection: state.selection,
5495
+ text: state.text,
5496
+ trigger: finalOptions.trigger
5497
+ }),
5498
+ command: selectedSuggestion,
5499
+ suggestions: void 0
5500
+ });
5501
+ }
5502
+ }
5503
+ };
5334
5504
  };
5335
5505
 
5336
- // src/pagination/FilterBuilder.ts
5337
- var FilterBuilder = class {
5338
- constructor(params) {
5339
- this.context = new StateStore(params?.initialContext ?? {});
5340
- this.filterConfig = new StateStore(
5341
- params?.initialFilterConfig ?? {}
5342
- );
5343
- }
5344
- updateFilterConfig(config) {
5345
- this.filterConfig.partialNext(config);
5506
+ // src/messageComposer/middleware/messageComposer/compositionValidation.ts
5507
+ var getCommandByName = (searchSource, commandName) => {
5508
+ if (!commandName) return;
5509
+ const normalizedCommandName = commandName.toLowerCase();
5510
+ return searchSource.query(normalizedCommandName).items.find((command) => command.name?.toLowerCase() === normalizedCommandName);
5511
+ };
5512
+ var getDisabledRawCommand = (composer, searchSource, text) => {
5513
+ const rawCommand = getCommandByName(searchSource, getRawCommandName(text));
5514
+ if (rawCommand && composer.isCommandDisabled(rawCommand)) {
5515
+ return rawCommand;
5346
5516
  }
5347
- enableFilter(filterKey) {
5348
- const config = this.filterConfig.getLatestValue();
5349
- if (config[filterKey]) {
5350
- this.filterConfig.partialNext({
5351
- [filterKey]: {
5352
- ...config[filterKey],
5353
- enabled: true
5517
+ };
5518
+ var createCompositionValidationMiddleware = (composer) => {
5519
+ const commandSearchSource = new CommandSearchSource(composer.channel);
5520
+ return {
5521
+ id: "stream-io/message-composer-middleware/data-validation",
5522
+ handlers: {
5523
+ compose: async ({
5524
+ state,
5525
+ discard,
5526
+ forward
5527
+ }) => {
5528
+ const { maxLengthOnSend } = composer.config.text ?? {};
5529
+ const inputText = state.message.text ?? "";
5530
+ const disabledRawCommand = getDisabledRawCommand(
5531
+ composer,
5532
+ commandSearchSource,
5533
+ inputText
5534
+ );
5535
+ if (disabledRawCommand) {
5536
+ notifyCommandDisabled(composer, disabledRawCommand);
5537
+ return await discard();
5538
+ }
5539
+ const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
5540
+ if (composer.compositionIsEmpty || hasExceededMaxLength) {
5541
+ return await discard();
5354
5542
  }
5543
+ return await forward();
5544
+ }
5545
+ }
5546
+ };
5547
+ };
5548
+ var createDraftCompositionValidationMiddleware = (composer) => ({
5549
+ id: "stream-io/message-composer-middleware/draft-data-validation",
5550
+ handlers: {
5551
+ compose: async ({
5552
+ state,
5553
+ discard,
5554
+ forward
5555
+ }) => {
5556
+ const hasData = !textIsEmpty(state.draft.text ?? "") || state.draft.attachments?.length || state.draft.poll_id || state.draft.quoted_message_id;
5557
+ const shouldCreateDraft = composer.lastChangeOriginIsLocal && hasData;
5558
+ if (!shouldCreateDraft) {
5559
+ return await discard();
5560
+ }
5561
+ return await forward();
5562
+ }
5563
+ }
5564
+ });
5565
+
5566
+ // src/messageComposer/middleware/messageComposer/linkPreviews.ts
5567
+ var createLinkPreviewsCompositionMiddleware = (composer) => ({
5568
+ id: "stream-io/message-composer-middleware/link-previews",
5569
+ handlers: {
5570
+ compose: ({
5571
+ state,
5572
+ next,
5573
+ forward
5574
+ }) => {
5575
+ const { linkPreviewsManager } = composer;
5576
+ if (!linkPreviewsManager) return forward();
5577
+ linkPreviewsManager.cancelURLEnrichment();
5578
+ const someLinkPreviewsLoading = linkPreviewsManager.loadingPreviews.length > 0;
5579
+ const someLinkPreviewsDismissed = linkPreviewsManager.dismissedPreviews.length > 0;
5580
+ const linkPreviews = linkPreviewsManager.loadingPreviews.length > 0 ? [] : linkPreviewsManager.loadedPreviews.map(
5581
+ (preview) => LinkPreviewsManager.getPreviewData(preview)
5582
+ );
5583
+ const attachments = (state.message.attachments ?? []).concat(
5584
+ linkPreviews
5585
+ );
5586
+ if (!attachments.length) return forward();
5587
+ const sendOptions = { ...state.sendOptions };
5588
+ const skip_enrich_url = !someLinkPreviewsLoading && linkPreviews.length > 0 || someLinkPreviewsDismissed;
5589
+ if (skip_enrich_url) {
5590
+ sendOptions.skip_enrich_url = true;
5591
+ }
5592
+ return next({
5593
+ ...state,
5594
+ message: {
5595
+ ...state.message,
5596
+ attachments
5597
+ },
5598
+ localMessage: {
5599
+ ...state.localMessage,
5600
+ attachments
5601
+ },
5602
+ sendOptions
5355
5603
  });
5356
5604
  }
5357
5605
  }
5358
- disableFilter(filterKey) {
5359
- const config = this.filterConfig.getLatestValue();
5360
- if (config[filterKey]) {
5361
- this.filterConfig.partialNext({
5362
- [filterKey]: {
5363
- ...config[filterKey],
5364
- enabled: false
5606
+ });
5607
+ var createDraftLinkPreviewsCompositionMiddleware = (composer) => ({
5608
+ id: "stream-io/message-composer-middleware/draft-link-previews",
5609
+ handlers: {
5610
+ compose: ({
5611
+ state,
5612
+ next,
5613
+ forward
5614
+ }) => {
5615
+ const { linkPreviewsManager } = composer;
5616
+ if (!linkPreviewsManager) return forward();
5617
+ linkPreviewsManager.cancelURLEnrichment();
5618
+ const linkPreviews = linkPreviewsManager.loadedPreviews.map(
5619
+ (preview) => LinkPreviewsManager.getPreviewData(preview)
5620
+ );
5621
+ if (!linkPreviews.length) return forward();
5622
+ return next({
5623
+ ...state,
5624
+ draft: {
5625
+ ...state.draft,
5626
+ attachments: (state.draft.attachments ?? []).concat(linkPreviews)
5365
5627
  }
5366
5628
  });
5367
5629
  }
5368
5630
  }
5369
- updateContext(newContext) {
5370
- this.context.partialNext(newContext);
5371
- }
5372
- buildFilters(params) {
5373
- const filters = {
5374
- ...params?.baseFilters ?? {}
5375
- };
5376
- const filterConfig = this.filterConfig.getLatestValue();
5377
- for (const key in filterConfig) {
5378
- const configItem = filterConfig[key];
5379
- if (!configItem?.enabled) continue;
5380
- const generated = configItem.generate({
5381
- ...this.context.getLatestValue(),
5382
- ...params?.context ?? {}
5383
- });
5384
- if (generated) Object.assign(filters, generated);
5385
- }
5386
- return filters;
5387
- }
5388
- };
5631
+ });
5389
5632
 
5390
- // src/pagination/ReminderPaginator.ts
5391
- var ReminderPaginator = class extends BasePaginator {
5392
- constructor(client, options) {
5393
- super(options);
5394
- this.query = async ({
5395
- direction
5633
+ // src/messageComposer/middleware/messageComposer/textComposer.ts
5634
+ var createTextComposerCompositionMiddleware = (composer) => ({
5635
+ id: "stream-io/message-composer-middleware/text-composition",
5636
+ handlers: {
5637
+ compose: ({
5638
+ state,
5639
+ next,
5640
+ forward
5396
5641
  }) => {
5397
- const cursor = this.cursor?.[direction];
5398
- const {
5399
- reminders: items,
5400
- next,
5401
- prev
5402
- } = await this.client.queryReminders({
5403
- filter: this.filters,
5404
- sort: this.sort,
5405
- limit: this.pageSize,
5406
- [direction]: cursor
5642
+ if (!composer.textComposer) return forward();
5643
+ const { mentionedUsers, text } = composer.textComposer;
5644
+ const mentioned_users = Array.from(
5645
+ new Set(
5646
+ mentionedUsers.filter(
5647
+ ({ id, name }) => text.includes(`@${id}`) || text.includes(`@${name}`)
5648
+ )
5649
+ )
5650
+ );
5651
+ if (!text && mentioned_users.length === 0) return forward();
5652
+ return next({
5653
+ ...state,
5654
+ localMessage: {
5655
+ ...state.localMessage,
5656
+ mentioned_users,
5657
+ text
5658
+ },
5659
+ message: {
5660
+ ...state.message,
5661
+ mentioned_users: mentioned_users.map((u) => u.id),
5662
+ text
5663
+ }
5407
5664
  });
5408
- return { items, next, prev };
5409
- };
5410
- this.filterQueryResults = (items) => items;
5411
- this.client = client;
5412
- }
5413
- get filters() {
5414
- return this._filters;
5415
- }
5416
- get sort() {
5417
- return this._sort;
5418
- }
5419
- set filters(filters) {
5420
- this._filters = filters;
5421
- this.resetState();
5422
- }
5423
- set sort(sort) {
5424
- this._sort = sort;
5425
- this.resetState();
5665
+ }
5426
5666
  }
5427
- };
5428
-
5429
- // src/search/UserSearchSource.ts
5430
- var UserSearchSource = class extends BaseSearchSource {
5431
- constructor(client, options, filterBuilderOptions = {}) {
5432
- super(options);
5433
- this.type = "users";
5434
- this.client = client;
5435
- this.filterBuilder = new FilterBuilder({
5436
- initialFilterConfig: {
5437
- $or: {
5438
- enabled: true,
5439
- generate: ({ searchQuery }) => searchQuery ? {
5440
- $or: [
5441
- { id: { $autocomplete: searchQuery } },
5442
- { name: { $autocomplete: searchQuery } }
5443
- ]
5444
- } : null
5667
+ });
5668
+ var createDraftTextComposerCompositionMiddleware = (composer) => ({
5669
+ id: "stream-io/message-composer-middleware/draft-text-composition",
5670
+ handlers: {
5671
+ compose: ({
5672
+ state,
5673
+ next,
5674
+ forward
5675
+ }) => {
5676
+ if (!composer.textComposer) return forward();
5677
+ const { maxLengthOnSend } = composer.config.text ?? {};
5678
+ const { mentionedUsers, text: inputText } = composer.textComposer;
5679
+ const mentioned_users = mentionedUsers.length ? Array.from(
5680
+ new Set(
5681
+ mentionedUsers.filter(
5682
+ ({ id, name }) => inputText.includes(`@${id}`) || inputText.includes(`@${name}`)
5683
+ )
5684
+ )
5685
+ ) : void 0;
5686
+ const text = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend ? inputText.slice(0, maxLengthOnSend) : inputText;
5687
+ return next({
5688
+ ...state,
5689
+ draft: {
5690
+ ...state.draft,
5691
+ mentioned_users: mentioned_users?.map((u) => u.id),
5692
+ text
5445
5693
  }
5446
- },
5447
- ...filterBuilderOptions
5448
- });
5449
- }
5450
- async query(searchQuery) {
5451
- const filters = this.filterBuilder.buildFilters({
5452
- baseFilters: this.filters,
5453
- context: { searchQuery }
5454
- });
5455
- const sort = { id: 1, ...this.sort };
5456
- const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5457
- const { users } = await this.client.queryUsers(filters, sort, options);
5458
- return { items: users };
5459
- }
5460
- filterQueryResults(items) {
5461
- return items.filter((u) => u.id !== this.client.user?.id);
5462
- }
5463
- };
5464
-
5465
- // src/search/ChannelSearchSource.ts
5466
- var ChannelSearchSource = class extends BaseSearchSource {
5467
- constructor(client, options, filterBuilderOptions = {}) {
5468
- super(options);
5469
- this.type = "channels";
5470
- this.client = client;
5471
- this.filterBuilder = new FilterBuilder({
5472
- ...filterBuilderOptions,
5473
- initialFilterConfig: {
5474
- name: {
5475
- enabled: true,
5476
- generate: ({ searchQuery }) => searchQuery ? { name: { $autocomplete: searchQuery } } : null
5477
- },
5478
- ...filterBuilderOptions.initialFilterConfig
5479
- }
5480
- });
5481
- }
5482
- async query(searchQuery) {
5483
- const filters = this.filterBuilder.buildFilters({
5484
- baseFilters: {
5485
- ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5486
- ...this.filters
5487
- },
5488
- context: { searchQuery }
5489
- });
5490
- const sort = this.sort ?? {};
5491
- const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5492
- const items = await this.client.queryChannels(filters, sort, options);
5493
- return { items };
5494
- }
5495
- filterQueryResults(items) {
5496
- return items;
5694
+ });
5695
+ }
5497
5696
  }
5498
- };
5697
+ });
5499
5698
 
5500
- // src/search/MessageSearchSource.ts
5501
- var MessageSearchSource = class extends BaseSearchSource {
5502
- constructor(client, options, filterBuilderOptions) {
5503
- super(options);
5504
- this.type = "messages";
5505
- this.client = client;
5506
- this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5507
- this.messageSearchFilterBuilder = new FilterBuilder({
5508
- ...filterBuilderOptions?.messageSearch,
5509
- initialFilterConfig: {
5510
- text: {
5511
- enabled: true,
5512
- generate: ({ searchQuery }) => searchQuery ? { text: searchQuery } : null
5513
- },
5514
- ...filterBuilderOptions?.messageSearch?.initialFilterConfig
5699
+ // src/messageComposer/middleware/messageComposer/messageComposerState.ts
5700
+ var createMessageComposerStateCompositionMiddleware = (composer) => ({
5701
+ id: "stream-io/message-composer-middleware/own-state",
5702
+ handlers: {
5703
+ compose: ({
5704
+ state,
5705
+ next
5706
+ }) => {
5707
+ const payload = {};
5708
+ if (composer.quotedMessage) {
5709
+ payload.quoted_message_id = composer.quotedMessage.id;
5515
5710
  }
5516
- });
5517
- this.channelQueryFilterBuilder = new FilterBuilder({
5518
- ...filterBuilderOptions?.channelQuery,
5519
- initialFilterConfig: {
5520
- cid: {
5521
- enabled: true,
5522
- generate: ({ cids }) => cids ? { cid: { $in: cids } } : null
5523
- },
5524
- ...filterBuilderOptions?.channelQuery?.initialFilterConfig
5711
+ if (composer.pollId) {
5712
+ payload.poll_id = composer.pollId;
5525
5713
  }
5526
- });
5714
+ if (composer.showReplyInChannel) {
5715
+ payload.show_in_channel = true;
5716
+ }
5717
+ return next({
5718
+ ...state,
5719
+ localMessage: {
5720
+ ...state.localMessage,
5721
+ ...payload,
5722
+ quoted_message: composer.quotedMessage ?? void 0
5723
+ },
5724
+ message: {
5725
+ ...state.message,
5726
+ ...payload
5727
+ }
5728
+ });
5729
+ }
5527
5730
  }
5528
- async query(searchQuery) {
5529
- if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
5530
- const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5531
- baseFilters: {
5532
- ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
5533
- ...this.messageSearchChannelFilters
5534
- },
5535
- context: { searchQuery }
5536
- });
5537
- const messageFilters = this.messageSearchFilterBuilder.buildFilters({
5538
- baseFilters: {
5539
- type: "regular",
5540
- ...this.messageSearchFilters
5541
- },
5542
- context: { searchQuery }
5543
- });
5544
- const sort = {
5545
- created_at: -1,
5546
- ...this.messageSearchSort
5547
- };
5548
- const options = {
5549
- limit: this.pageSize,
5550
- next: this.next,
5551
- sort
5552
- };
5553
- const { next, results } = await this.client.search(
5554
- channelFilters,
5555
- messageFilters,
5556
- options
5557
- );
5558
- const items = results.map(({ message }) => message);
5559
- const cids = Array.from(
5560
- items.reduce((acc, message) => {
5561
- if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
5562
- return acc;
5563
- }, /* @__PURE__ */ new Set())
5564
- );
5565
- if (cids.length > 0) {
5566
- const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
5567
- baseFilters: this.channelQueryFilters,
5568
- context: { cids }
5731
+ });
5732
+ var createDraftMessageComposerStateCompositionMiddleware = (composer) => ({
5733
+ id: "stream-io/message-composer-middleware/draft-own-state",
5734
+ handlers: {
5735
+ compose: ({
5736
+ state,
5737
+ next
5738
+ }) => {
5739
+ const payload = {};
5740
+ if (composer.quotedMessage) {
5741
+ payload.quoted_message_id = composer.quotedMessage.id;
5742
+ }
5743
+ if (composer.pollId) {
5744
+ payload.poll_id = composer.pollId;
5745
+ }
5746
+ if (composer.showReplyInChannel) {
5747
+ payload.show_in_channel = true;
5748
+ }
5749
+ return next({
5750
+ ...state,
5751
+ draft: {
5752
+ ...state.draft,
5753
+ ...payload
5754
+ }
5569
5755
  });
5570
- await this.client.queryChannels(
5571
- channelQueryFilters,
5572
- {
5573
- last_message_at: -1,
5574
- ...this.channelQuerySort
5575
- },
5576
- this.channelQueryOptions
5577
- );
5578
5756
  }
5579
- return { items, next };
5580
5757
  }
5581
- filterQueryResults(items) {
5582
- return items;
5758
+ });
5759
+
5760
+ // src/messageComposer/middleware/messageComposer/userDataInjection.ts
5761
+ var createUserDataInjectionMiddleware = (composer) => ({
5762
+ id: "stream-io/message-composer-middleware/user-data-injection",
5763
+ handlers: {
5764
+ compose: ({
5765
+ state,
5766
+ next,
5767
+ forward
5768
+ }) => {
5769
+ if (!composer.client.user) {
5770
+ return forward();
5771
+ }
5772
+ const { channel_mutes, devices, mutes, ...messageUser } = composer.client.user;
5773
+ return next({
5774
+ ...state,
5775
+ localMessage: {
5776
+ ...state.localMessage,
5777
+ user: messageUser,
5778
+ user_id: messageUser.id
5779
+ }
5780
+ });
5781
+ }
5583
5782
  }
5584
- };
5783
+ });
5585
5784
 
5586
- // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
5587
- var getTriggerCharWithToken = ({
5588
- trigger,
5589
- text,
5590
- isCommand = false,
5591
- acceptTrailingSpaces = true
5592
- }) => {
5593
- const escapedTrigger = escapeRegExp(trigger);
5594
- const triggerNorWhitespace = `[^\\s${escapedTrigger}]*`;
5595
- const match = text.match(
5596
- new RegExp(
5597
- isCommand ? `^[${escapedTrigger}]${triggerNorWhitespace}$` : acceptTrailingSpaces ? `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}\\s?${triggerNorWhitespace}$` : `(?!^|\\W)?[${escapedTrigger}]${triggerNorWhitespace}$`,
5598
- "g"
5599
- )
5600
- );
5601
- return match && match[match.length - 1].trim();
5602
- };
5603
- var getCompleteCommandInString = (text) => {
5604
- const match = text.match(/^\/(\S+)\s+.*/);
5605
- const commandName = match && match[1];
5606
- return commandName;
5785
+ // src/messageComposer/middleware/messageComposer/pollOnly.ts
5786
+ var pollLocalMessageNullifiedFields = {
5787
+ attachments: [],
5788
+ mentioned_users: [],
5789
+ parent_id: void 0,
5790
+ quoted_message: void 0,
5791
+ text: ""
5607
5792
  };
5608
- var insertItemWithTrigger = ({
5609
- insertText,
5610
- selection,
5611
- text,
5612
- trigger
5613
- }) => {
5614
- const beforeCursor = text.slice(0, selection.end);
5615
- const afterCursor = text.slice(selection.end);
5616
- const lastIndex = beforeCursor.lastIndexOf(trigger);
5617
- const newText = beforeCursor.slice(0, lastIndex) + insertText + afterCursor;
5618
- return {
5619
- text: newText,
5620
- selection: {
5621
- start: lastIndex + insertText.length,
5622
- end: lastIndex + insertText.length
5793
+ var createPollOnlyCompositionMiddleware = (composer) => ({
5794
+ id: "stream-io/message-composer-middleware/poll-only",
5795
+ handlers: {
5796
+ compose: ({
5797
+ state,
5798
+ complete,
5799
+ forward
5800
+ }) => {
5801
+ const pollId = composer.pollId;
5802
+ const isEditingMessage = !!composer.editedMessage;
5803
+ const isComposingThreadReply = !!composer.threadId;
5804
+ if (!pollId || isComposingThreadReply || isEditingMessage) return forward();
5805
+ return complete({
5806
+ ...state,
5807
+ localMessage: {
5808
+ ...state.localMessage,
5809
+ ...pollLocalMessageNullifiedFields,
5810
+ poll_id: pollId
5811
+ },
5812
+ message: {
5813
+ id: state.localMessage.id,
5814
+ poll_id: pollId
5815
+ }
5816
+ });
5817
+ }
5818
+ }
5819
+ });
5820
+
5821
+ // src/messageComposer/middleware/messageComposer/sharedLocation.ts
5822
+ var createSharedLocationCompositionMiddleware = (composer) => ({
5823
+ id: "stream-io/message-composer-middleware/shared-location",
5824
+ handlers: {
5825
+ compose: ({
5826
+ state,
5827
+ next,
5828
+ forward
5829
+ }) => {
5830
+ const { locationComposer } = composer;
5831
+ const location = locationComposer.validLocation;
5832
+ if (!locationComposer || !location || !composer.client.user) return forward();
5833
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
5834
+ return next({
5835
+ ...state,
5836
+ localMessage: {
5837
+ ...state.localMessage,
5838
+ shared_location: {
5839
+ ...location,
5840
+ channel_cid: composer.channel.cid,
5841
+ created_at: timestamp,
5842
+ updated_at: timestamp,
5843
+ user_id: composer.client.user.id
5844
+ }
5845
+ },
5846
+ message: {
5847
+ ...state.message,
5848
+ shared_location: location
5849
+ }
5850
+ });
5623
5851
  }
5624
- };
5625
- };
5626
- var replaceWordWithEntity = async ({
5627
- caretPosition,
5628
- getEntityString,
5629
- text
5630
- }) => {
5631
- const lastWordRegex = /([^\s]+)(\s*)$/;
5632
- const match = lastWordRegex.exec(text.slice(0, caretPosition));
5633
- if (!match) return text;
5634
- const lastWord = match[1];
5635
- if (!lastWord) return text;
5636
- const spaces = match[2];
5637
- const newWord = await getEntityString(lastWord);
5638
- if (newWord == null) return text;
5639
- const textBeforeWord = text.slice(0, caretPosition - match[0].length);
5640
- const textAfterCaret = text.slice(caretPosition, -1);
5641
- return textBeforeWord + newWord + spaces + textAfterCaret;
5642
- };
5643
- function escapeRegExp(text) {
5644
- return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, "\\$&");
5645
- }
5646
- var getTokenizedSuggestionDisplayName = ({
5647
- displayName,
5648
- searchToken
5649
- }) => ({
5650
- tokenizedDisplayName: {
5651
- token: searchToken,
5652
- parts: searchToken ? displayName.split(new RegExp(`(${escapeRegExp(searchToken)})`, "gi")).filter(Boolean) : [displayName]
5653
5852
  }
5654
5853
  });
5655
5854
 
5656
- // src/messageComposer/middleware/textComposer/commands.ts
5657
- var CommandSearchSource = class extends BaseSearchSourceSync {
5658
- constructor(channel, options) {
5659
- super(options);
5660
- this.type = "commands";
5661
- this.canExecuteQuery = (newSearchString) => {
5662
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
5663
- return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
5664
- };
5665
- this.channel = channel;
5855
+ // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
5856
+ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5857
+ constructor({ composer }) {
5858
+ super();
5859
+ this.use([
5860
+ createUserDataInjectionMiddleware(composer),
5861
+ createPollOnlyCompositionMiddleware(composer),
5862
+ createTextComposerCompositionMiddleware(composer),
5863
+ createAttachmentsCompositionMiddleware(composer),
5864
+ createLinkPreviewsCompositionMiddleware(composer),
5865
+ createSharedLocationCompositionMiddleware(composer),
5866
+ createMessageComposerStateCompositionMiddleware(composer),
5867
+ createCustomDataCompositionMiddleware(composer),
5868
+ createCompositionValidationMiddleware(composer),
5869
+ createCompositionDataCleanupMiddleware(composer)
5870
+ ]);
5666
5871
  }
5667
- getStateBeforeFirstQuery(newSearchString) {
5668
- const newState = super.getStateBeforeFirstQuery(newSearchString);
5669
- const { items } = this.state.getLatestValue();
5670
- return {
5671
- ...newState,
5672
- items
5673
- // preserve items to avoid flickering
5674
- };
5872
+ };
5873
+ var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5874
+ constructor({ composer }) {
5875
+ super();
5876
+ this.use([
5877
+ createDraftTextComposerCompositionMiddleware(composer),
5878
+ createDraftAttachmentsCompositionMiddleware(composer),
5879
+ createDraftLinkPreviewsCompositionMiddleware(composer),
5880
+ createDraftMessageComposerStateCompositionMiddleware(composer),
5881
+ createDraftCustomDataCompositionMiddleware(composer),
5882
+ createDraftCompositionValidationMiddleware(composer)
5883
+ ]);
5675
5884
  }
5676
- query(searchQuery) {
5677
- const channelConfig = this.channel.getConfig();
5678
- const commands = channelConfig?.commands || [];
5679
- const selectedCommands = commands.filter(
5680
- (command) => !!(command.name && command.name.toLowerCase().indexOf(searchQuery.toLowerCase()) !== -1)
5681
- );
5682
- selectedCommands.sort((a, b) => {
5683
- let nameA = a.name?.toLowerCase();
5684
- let nameB = b.name?.toLowerCase();
5685
- if (nameA?.indexOf(searchQuery) === 0) {
5686
- nameA = `0${nameA}`;
5687
- }
5688
- if (nameB?.indexOf(searchQuery) === 0) {
5689
- nameB = `0${nameB}`;
5885
+ };
5886
+
5887
+ // src/messageComposer/middleware/messageComposer/commandInjection.ts
5888
+ var createCommandInjectionMiddleware = (composer) => ({
5889
+ handlers: {
5890
+ compose: ({
5891
+ forward,
5892
+ next,
5893
+ state
5894
+ }) => {
5895
+ const command = composer.textComposer.command;
5896
+ if (!command) {
5897
+ return forward();
5690
5898
  }
5691
- if (nameA != null && nameB != null) {
5692
- if (nameA < nameB) {
5693
- return -1;
5899
+ const { text } = state.localMessage;
5900
+ const injection = `/${command?.name}`;
5901
+ const enrichedText = `${injection} ${text}`;
5902
+ return next({
5903
+ ...state,
5904
+ localMessage: {
5905
+ ...state.localMessage,
5906
+ text: enrichedText
5907
+ },
5908
+ message: {
5909
+ ...state.message,
5910
+ text: enrichedText
5694
5911
  }
5695
- if (nameA > nameB) {
5696
- return 1;
5912
+ });
5913
+ }
5914
+ },
5915
+ id: "stream-io/message-composer-middleware/command-string-injection"
5916
+ });
5917
+ var createDraftCommandInjectionMiddleware = (composer) => ({
5918
+ handlers: {
5919
+ compose: ({
5920
+ forward,
5921
+ next,
5922
+ state
5923
+ }) => {
5924
+ const command = composer.textComposer.command;
5925
+ if (!command) {
5926
+ return forward();
5927
+ }
5928
+ const { text } = state.draft;
5929
+ const injection = `/${command?.name}`;
5930
+ const enrichedText = `${injection} ${text}`;
5931
+ return next({
5932
+ ...state,
5933
+ draft: {
5934
+ ...state.draft,
5935
+ text: enrichedText
5697
5936
  }
5937
+ });
5938
+ }
5939
+ },
5940
+ id: "stream-io/message-composer-middleware/draft-command-string-injection"
5941
+ });
5942
+
5943
+ // src/messageComposer/middleware/textComposer/activeCommandGuard.ts
5944
+ var createActiveCommandGuardMiddleware = () => ({
5945
+ handlers: {
5946
+ onChange: ({ complete, forward, state }) => {
5947
+ if (state.command) {
5948
+ return complete(state);
5698
5949
  }
5699
- return 0;
5700
- });
5701
- return {
5702
- items: selectedCommands.map((c) => ({ ...c, id: c.name })),
5703
- next: null
5704
- };
5705
- }
5706
- filterQueryResults(items) {
5707
- return items;
5708
- }
5950
+ return forward();
5951
+ },
5952
+ onSuggestionItemSelect: ({ forward }) => forward()
5953
+ },
5954
+ id: "stream-io/text-composer/active-command-guard"
5955
+ });
5956
+
5957
+ // src/messageComposer/middleware/textComposer/commandEffects.ts
5958
+ var emptyCommandStateToRestore = {
5959
+ selection: { start: 0, end: 0 },
5960
+ text: ""
5709
5961
  };
5710
- var DEFAULT_OPTIONS = { minChars: 1, trigger: "/" };
5711
- var createCommandsMiddleware = (channel, options) => {
5712
- const finalOptions = mergeWith(DEFAULT_OPTIONS, options ?? {});
5713
- let searchSource = new CommandSearchSource(channel);
5714
- if (options?.searchSource) {
5715
- searchSource = options.searchSource;
5716
- searchSource.resetState();
5717
- }
5718
- searchSource.activate();
5719
- return {
5720
- id: "stream-io/text-composer/commands-middleware",
5721
- handlers: {
5722
- onChange: ({ state, next, complete, forward }) => {
5723
- if (!state.selection) return forward();
5724
- const finalText = state.text.slice(0, state.selection.end);
5725
- const commandName = getCompleteCommandInString(finalText);
5726
- if (commandName) {
5727
- const command = searchSource?.query(commandName).items[0];
5728
- if (command) {
5729
- return next({
5730
- ...state,
5731
- command,
5732
- suggestions: void 0
5733
- });
5734
- }
5735
- }
5736
- const triggerWithToken = getTriggerCharWithToken({
5737
- trigger: finalOptions.trigger,
5738
- text: finalText,
5739
- acceptTrailingSpaces: false,
5740
- isCommand: true
5741
- });
5742
- const newSearchTriggerred = triggerWithToken && triggerWithToken.length === finalOptions.minChars;
5743
- if (newSearchTriggerred) {
5744
- searchSource.resetStateAndActivate();
5745
- }
5746
- const triggerWasRemoved = !triggerWithToken || triggerWithToken.length < finalOptions.minChars;
5747
- if (triggerWasRemoved) {
5748
- const hasStaleSuggestions = state.suggestions?.trigger === finalOptions.trigger;
5749
- const newState = { ...state };
5750
- if (hasStaleSuggestions) {
5751
- delete newState.suggestions;
5752
- }
5753
- return next(newState);
5754
- }
5755
- return complete({
5756
- ...state,
5757
- command: null,
5758
- suggestions: {
5759
- query: triggerWithToken.slice(1),
5760
- searchSource,
5761
- trigger: finalOptions.trigger
5762
- }
5763
- });
5764
- },
5765
- onSuggestionItemSelect: ({ state, next, forward }) => {
5766
- const { selectedSuggestion } = state.change ?? {};
5767
- if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger)
5768
- return forward();
5769
- searchSource.resetStateAndActivate();
5770
- return next({
5771
- ...state,
5772
- ...insertItemWithTrigger({
5773
- insertText: `/${selectedSuggestion.name} `,
5774
- selection: state.selection,
5775
- text: state.text,
5776
- trigger: finalOptions.trigger
5777
- }),
5778
- command: selectedSuggestion,
5779
- suggestions: void 0
5780
- });
5962
+ var createCommandActivationEffect = (command) => ({
5963
+ command,
5964
+ stateToRestore: emptyCommandStateToRestore,
5965
+ type: "command.activate"
5966
+ });
5967
+ var isCommandResponse = (suggestion) => typeof suggestion?.name === "string";
5968
+ var createCommandEffectsMiddleware = () => ({
5969
+ handlers: {
5970
+ onChange: ({ forward }) => forward(),
5971
+ onSuggestionItemSelect: ({ state, next, forward }) => {
5972
+ const { selectedSuggestion } = state.change ?? {};
5973
+ if (!isCommandResponse(selectedSuggestion) || !state.command || state.command.name !== selectedSuggestion.name) {
5974
+ return forward();
5781
5975
  }
5976
+ return next({
5977
+ ...state,
5978
+ effects: [...state.effects ?? [], createCommandActivationEffect(state.command)]
5979
+ });
5782
5980
  }
5783
- };
5784
- };
5981
+ },
5982
+ id: "stream-io/text-composer/command-effects-middleware"
5983
+ });
5785
5984
 
5786
5985
  // src/messageComposer/middleware/textComposer/commandStringExtraction.ts
5787
- var stripCommandFromText = (text, commandName) => text.replace(new RegExp(`^${escapeRegExp(`/${commandName}`)}\\s*`), "");
5788
5986
  var createCommandStringExtractionMiddleware = () => ({
5789
5987
  handlers: {
5790
5988
  onChange: ({ complete, forward, state }) => {
@@ -6124,7 +6322,10 @@ var TextComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6124
6322
  this.use([
6125
6323
  createTextComposerPreValidationMiddleware(composer),
6126
6324
  createMentionsMiddleware(composer.channel),
6127
- createCommandsMiddleware(composer.channel)
6325
+ createCommandsMiddleware(composer.channel, {
6326
+ composer
6327
+ }),
6328
+ createCommandEffectsMiddleware()
6128
6329
  ]);
6129
6330
  }
6130
6331
  async execute({
@@ -6173,6 +6374,10 @@ var TextComposer = class {
6173
6374
  this.initState = ({ message } = {}) => {
6174
6375
  this.state.next(initState5({ composer: this.composer, message }));
6175
6376
  };
6377
+ this.getSnapshot = (state = this.state.getLatestValue()) => state;
6378
+ this.restoreSnapshot = (snapshot) => {
6379
+ this.state.next(snapshot);
6380
+ };
6176
6381
  this.upsertMentionedUser = (user) => {
6177
6382
  const mentionedUsers = [...this.mentionedUsers];
6178
6383
  const existingUserIndex = mentionedUsers.findIndex((u) => u.id === user.id);
@@ -6193,8 +6398,27 @@ var TextComposer = class {
6193
6398
  this.state.partialNext({ mentionedUsers });
6194
6399
  };
6195
6400
  this.setCommand = (command) => {
6196
- if (command?.name === this.command?.name) return;
6197
- this.state.partialNext({ command });
6401
+ if (!command) {
6402
+ this.clearCommand();
6403
+ return;
6404
+ }
6405
+ if (command.name === this.command?.name) return;
6406
+ if (this.composer.isCommandDisabled(command)) return;
6407
+ const stateToRestore = {
6408
+ command: null
6409
+ };
6410
+ this.commitState({
6411
+ ...this.state.getLatestValue(),
6412
+ command,
6413
+ effects: [
6414
+ {
6415
+ command,
6416
+ stateToRestore,
6417
+ type: "command.activate"
6418
+ }
6419
+ ],
6420
+ suggestions: void 0
6421
+ });
6198
6422
  };
6199
6423
  this.setText = (text) => {
6200
6424
  if (!this.enabled || text === this.text) return;
@@ -6261,6 +6485,12 @@ var TextComposer = class {
6261
6485
  this.state.partialNext({ suggestions: void 0 });
6262
6486
  };
6263
6487
  // --- END STATE API ---
6488
+ this.commitState = (state) => {
6489
+ const { change, effects, ...nextState } = state;
6490
+ void change;
6491
+ this.state.next(nextState);
6492
+ this.composer.applyEffects(effects);
6493
+ };
6264
6494
  // --- START TEXT PROCESSING ----
6265
6495
  this.handleChange = async ({
6266
6496
  text,
@@ -6276,7 +6506,7 @@ var TextComposer = class {
6276
6506
  }
6277
6507
  });
6278
6508
  if (output.status === "discard") return;
6279
- this.state.next(output.state);
6509
+ this.commitState(output.state);
6280
6510
  if (this.config.publishTypingEvents && text) {
6281
6511
  logChatPromiseExecution(
6282
6512
  this.channel.keystroke(this.composer.threadId ?? void 0),
@@ -6297,7 +6527,7 @@ var TextComposer = class {
6297
6527
  }
6298
6528
  });
6299
6529
  if (output?.status === "discard") return;
6300
- this.state.next(output.state);
6530
+ this.commitState(output.state);
6301
6531
  };
6302
6532
  this.composer = composer;
6303
6533
  this.state = new StateStore(initState5({ composer, message }));
@@ -6367,7 +6597,12 @@ var TextComposer = class {
6367
6597
  this.state.partialNext({ mentionedUsers: users });
6368
6598
  }
6369
6599
  clearCommand() {
6370
- this.state.partialNext({ command: null });
6600
+ if (!this.command) return;
6601
+ this.commitState({
6602
+ ...this.state.getLatestValue(),
6603
+ command: null,
6604
+ effects: [{ type: "command.clear" }]
6605
+ });
6371
6606
  }
6372
6607
  // --- END TEXT PROCESSING ----
6373
6608
  };
@@ -6858,15 +7093,28 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
6858
7093
  client
6859
7094
  }) {
6860
7095
  super();
7096
+ this.snapshots = [];
6861
7097
  this.setEditedMessage = (editedMessage) => {
6862
7098
  this.state.partialNext({ editedMessage: editedMessage ?? null });
7099
+ if (editedMessage) {
7100
+ this.textComposer.clearCommand();
7101
+ }
7102
+ };
7103
+ this.getCommandDisabledReason = (command) => {
7104
+ if (this.editedMessage) return "editing";
7105
+ if (this.quotedMessage && (command.set === "moderation_set" || command.name === "moderation_set")) {
7106
+ return "quoted_message";
7107
+ }
7108
+ return void 0;
6863
7109
  };
7110
+ this.isCommandDisabled = (command) => !!this.getCommandDisabledReason(command);
6864
7111
  this.refreshId = () => {
6865
7112
  this.state.partialNext({ id: _MessageComposer.generateId() });
6866
7113
  };
6867
7114
  this.initState = ({
6868
7115
  composition
6869
7116
  } = {}) => {
7117
+ this.clearSnapshots();
6870
7118
  this.editingAuditState.partialNext(this.initEditingAuditState(composition));
6871
7119
  const message = typeof composition === "undefined" ? composition : compositionIsDraftResponse(composition) ? composition.message : formatMessage(composition);
6872
7120
  this.attachmentManager.initState({ message });
@@ -6896,6 +7144,36 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
6896
7144
  }
6897
7145
  };
6898
7146
  this.initEditingAuditState = (composition) => initEditingAuditState(composition);
7147
+ this.clearSnapshots = () => {
7148
+ this.snapshots = [];
7149
+ };
7150
+ this.getSnapshot = () => ({
7151
+ attachmentManager: this.attachmentManager.getSnapshot(),
7152
+ customDataManager: this.customDataManager.getSnapshot(),
7153
+ linkPreviewsManager: this.linkPreviewsManager.getSnapshot(),
7154
+ locationComposer: this.locationComposer.getSnapshot(),
7155
+ pollComposer: this.pollComposer.getSnapshot(),
7156
+ textComposer: this.textComposer.getSnapshot()
7157
+ });
7158
+ this.restoreSnapshot = (snapshot) => {
7159
+ this.attachmentManager.restoreSnapshot(snapshot.attachmentManager);
7160
+ this.linkPreviewsManager.restoreSnapshot(snapshot.linkPreviewsManager);
7161
+ this.locationComposer.restoreSnapshot(snapshot.locationComposer);
7162
+ this.pollComposer.restoreSnapshot(snapshot.pollComposer);
7163
+ this.customDataManager.restoreSnapshot(snapshot.customDataManager);
7164
+ this.textComposer.restoreSnapshot(snapshot.textComposer);
7165
+ };
7166
+ this.captureSnapshot = (snapshot = this.getSnapshot()) => {
7167
+ if (this.snapshots.length) return;
7168
+ this.snapshots.push(snapshot);
7169
+ };
7170
+ this.popSnapshot = () => this.snapshots.pop();
7171
+ this.registerEffectHandler = (type, handler) => {
7172
+ this.effectHandlers.registerEffectHandler(type, handler);
7173
+ };
7174
+ this.applyEffects = (effects = []) => {
7175
+ this.effectHandlers.applyEffects(effects);
7176
+ };
6899
7177
  this.registerDraftEventSubscriptions = () => {
6900
7178
  const unsubscribeDraftUpdated = this.subscribeDraftUpdated();
6901
7179
  const unsubscribeDraftDeleted = this.subscribeDraftDeleted();
@@ -7075,6 +7353,10 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7075
7353
  };
7076
7354
  this.setQuotedMessage = (quotedMessage) => {
7077
7355
  this.state.partialNext({ quotedMessage });
7356
+ const activeCommand = this.textComposer.command;
7357
+ if (quotedMessage && activeCommand && this.isCommandDisabled(activeCommand)) {
7358
+ this.textComposer.clearCommand();
7359
+ }
7078
7360
  };
7079
7361
  this.toggleShowReplyInChannel = () => {
7080
7362
  this.state.partialNext({ showReplyInChannel: !this.showReplyInChannel });
@@ -7309,6 +7591,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7309
7591
  this.draftCompositionMiddlewareExecutor = new MessageDraftComposerMiddlewareExecutor({
7310
7592
  composer: this
7311
7593
  });
7594
+ this.effectHandlers = new MessageComposerEffectHandlers({ composer: this });
7312
7595
  }
7313
7596
  static evaluateContextType(compositionContext) {
7314
7597
  if (compositionContext instanceof Channel) {
@@ -15192,7 +15475,7 @@ var StreamChat = class _StreamChat {
15192
15475
  if (this.userAgent) {
15193
15476
  return this.userAgent;
15194
15477
  }
15195
- const version = "9.42.3";
15478
+ const version = "9.43.0";
15196
15479
  const clientBundle = "browser-esm";
15197
15480
  let userAgentString = "";
15198
15481
  if (this.sdkIdentifier) {
@@ -17895,6 +18178,7 @@ export {
17895
18178
  createActiveCommandGuardMiddleware,
17896
18179
  createAttachmentsCompositionMiddleware,
17897
18180
  createBlockedAttachmentUploadNotificationMiddleware,
18181
+ createCommandEffectsMiddleware,
17898
18182
  createCommandInjectionMiddleware,
17899
18183
  createCommandStringExtractionMiddleware,
17900
18184
  createCommandsMiddleware,
@@ -17924,13 +18208,11 @@ export {
17924
18208
  defaultPollFieldChangeEventValidators,
17925
18209
  encodeBase64,
17926
18210
  ensureIsLocalAttachment,
17927
- escapeRegExp,
17928
18211
  extractPollData,
17929
18212
  extractPollEnrichedData,
17930
18213
  formatMessage,
17931
18214
  generateFileName,
17932
18215
  getAttachmentTypeFromMimeType,
17933
- getCompleteCommandInString,
17934
18216
  getExtensionFromMimeType,
17935
18217
  getTokenizedSuggestionDisplayName,
17936
18218
  getTriggerCharWithToken,