stream-chat 9.42.3 → 9.43.1

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