stream-chat 9.4.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cjs/index.browser.cjs +656 -127
  2. package/dist/cjs/index.browser.cjs.map +4 -4
  3. package/dist/cjs/index.node.cjs +666 -128
  4. package/dist/cjs/index.node.cjs.map +4 -4
  5. package/dist/esm/index.js +656 -127
  6. package/dist/esm/index.js.map +4 -4
  7. package/dist/types/client.d.ts +52 -19
  8. package/dist/types/events.d.ts +4 -0
  9. package/dist/types/index.d.ts +3 -1
  10. package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +2 -2
  11. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +2 -2
  12. package/dist/types/messageComposer/middleware/textComposer/types.d.ts +1 -1
  13. package/dist/types/pagination/BasePaginator.d.ts +69 -0
  14. package/dist/types/pagination/ReminderPaginator.d.ts +12 -0
  15. package/dist/types/pagination/index.d.ts +2 -0
  16. package/dist/types/reminders/Reminder.d.ts +37 -0
  17. package/dist/types/reminders/ReminderManager.d.ts +65 -0
  18. package/dist/types/reminders/ReminderTimer.d.ts +17 -0
  19. package/dist/types/reminders/index.d.ts +3 -0
  20. package/dist/types/search/BaseSearchSource.d.ts +87 -0
  21. package/dist/types/search/ChannelSearchSource.d.ts +17 -0
  22. package/dist/types/search/MessageSearchSource.d.ts +23 -0
  23. package/dist/types/search/SearchController.d.ts +44 -0
  24. package/dist/types/search/UserSearchSource.d.ts +16 -0
  25. package/dist/types/search/index.d.ts +5 -0
  26. package/dist/types/types.d.ts +43 -0
  27. package/package.json +1 -1
  28. package/src/channel.ts +2 -1
  29. package/src/client.ts +108 -39
  30. package/src/events.ts +6 -0
  31. package/src/index.ts +3 -1
  32. package/src/messageComposer/middleware/textComposer/commands.ts +2 -2
  33. package/src/messageComposer/middleware/textComposer/mentions.ts +2 -2
  34. package/src/messageComposer/middleware/textComposer/types.ts +1 -1
  35. package/src/pagination/BasePaginator.ts +184 -0
  36. package/src/pagination/ReminderPaginator.ts +38 -0
  37. package/src/pagination/index.ts +2 -0
  38. package/src/reminders/Reminder.ts +89 -0
  39. package/src/reminders/ReminderManager.ts +284 -0
  40. package/src/reminders/ReminderTimer.ts +86 -0
  41. package/src/reminders/index.ts +3 -0
  42. package/src/search/BaseSearchSource.ts +227 -0
  43. package/src/search/ChannelSearchSource.ts +34 -0
  44. package/src/search/MessageSearchSource.ts +88 -0
  45. package/src/search/SearchController.ts +154 -0
  46. package/src/search/UserSearchSource.ts +35 -0
  47. package/src/search/index.ts +5 -0
  48. package/src/token_manager.ts +3 -1
  49. package/src/types.ts +88 -0
  50. package/dist/types/search_controller.d.ts +0 -174
  51. package/src/search_controller.ts +0 -523
@@ -10670,6 +10670,7 @@ __export(index_exports, {
10670
10670
  AnyResource: () => AnyResource,
10671
10671
  AnyRole: () => AnyRole,
10672
10672
  AttachmentManager: () => AttachmentManager,
10673
+ BasePaginator: () => BasePaginator,
10673
10674
  BaseSearchSource: () => BaseSearchSource,
10674
10675
  BuiltinPermissions: () => BuiltinPermissions,
10675
10676
  BuiltinRoles: () => BuiltinRoles,
@@ -10687,6 +10688,9 @@ __export(index_exports, {
10687
10688
  DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS: () => DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
10688
10689
  DEFAULT_COMPOSER_CONFIG: () => DEFAULT_COMPOSER_CONFIG,
10689
10690
  DEFAULT_LINK_PREVIEW_MANAGER_CONFIG: () => DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
10691
+ DEFAULT_PAGINATION_OPTIONS: () => DEFAULT_PAGINATION_OPTIONS,
10692
+ DEFAULT_REMINDER_MANAGER_CONFIG: () => DEFAULT_REMINDER_MANAGER_CONFIG,
10693
+ DEFAULT_STOP_REFRESH_BOUNDARY_MS: () => DEFAULT_STOP_REFRESH_BOUNDARY_MS,
10690
10694
  DEFAULT_TEXT_COMPOSER_CONFIG: () => DEFAULT_TEXT_COMPOSER_CONFIG,
10691
10695
  Deny: () => Deny,
10692
10696
  DenyAll: () => DenyAll,
@@ -10719,6 +10723,10 @@ __export(index_exports, {
10719
10723
  PollComposerCompositionMiddlewareExecutor: () => PollComposerCompositionMiddlewareExecutor,
10720
10724
  PollComposerStateMiddlewareExecutor: () => PollComposerStateMiddlewareExecutor,
10721
10725
  PollManager: () => PollManager,
10726
+ Reminder: () => Reminder,
10727
+ ReminderManager: () => ReminderManager,
10728
+ ReminderPaginator: () => ReminderPaginator,
10729
+ ReminderTimer: () => ReminderTimer,
10722
10730
  SearchController: () => SearchController,
10723
10731
  Segment: () => Segment,
10724
10732
  StableWSConnection: () => StableWSConnection,
@@ -10806,7 +10814,8 @@ __export(index_exports, {
10806
10814
  readFileAsArrayBuffer: () => readFileAsArrayBuffer,
10807
10815
  removeDiacritics: () => removeDiacritics,
10808
10816
  replaceWordWithEntity: () => replaceWordWithEntity,
10809
- textIsEmpty: () => textIsEmpty
10817
+ textIsEmpty: () => textIsEmpty,
10818
+ timeLeftMs: () => timeLeftMs
10810
10819
  });
10811
10820
  module.exports = __toCommonJS(index_exports);
10812
10821
 
@@ -17584,7 +17593,7 @@ var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
17584
17593
  }
17585
17594
  };
17586
17595
 
17587
- // src/search_controller.ts
17596
+ // src/search/BaseSearchSource.ts
17588
17597
  var DEFAULT_SEARCH_SOURCE_OPTIONS = {
17589
17598
  debounceMs: 300,
17590
17599
  pageSize: 10
@@ -17710,6 +17719,112 @@ var BaseSearchSource = class {
17710
17719
  this.activate();
17711
17720
  }
17712
17721
  };
17722
+
17723
+ // src/search/SearchController.ts
17724
+ var SearchController = class {
17725
+ constructor({ config, sources } = {}) {
17726
+ this.addSource = (source) => {
17727
+ this.state.partialNext({
17728
+ sources: [...this.sources, source]
17729
+ });
17730
+ };
17731
+ this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
17732
+ this.removeSource = (sourceType) => {
17733
+ const newSources = this.sources.filter((s) => s.type !== sourceType);
17734
+ if (newSources.length === this.sources.length) return;
17735
+ this.state.partialNext({ sources: newSources });
17736
+ };
17737
+ this.activateSource = (sourceType) => {
17738
+ const source = this.getSource(sourceType);
17739
+ if (!source || source.isActive) return;
17740
+ if (this.config.keepSingleActiveSource) {
17741
+ this.sources.forEach((s) => {
17742
+ if (s.type !== sourceType) {
17743
+ s.deactivate();
17744
+ }
17745
+ });
17746
+ }
17747
+ source.activate();
17748
+ this.state.partialNext({ sources: [...this.sources] });
17749
+ };
17750
+ this.deactivateSource = (sourceType) => {
17751
+ const source = this.getSource(sourceType);
17752
+ if (!source?.isActive) return;
17753
+ if (this.activeSources.length === 1) return;
17754
+ source.deactivate();
17755
+ this.state.partialNext({ sources: [...this.sources] });
17756
+ };
17757
+ this.activate = () => {
17758
+ if (!this.activeSources.length) {
17759
+ const sourcesToActivate = this.config.keepSingleActiveSource ? this.sources.slice(0, 1) : this.sources;
17760
+ sourcesToActivate.forEach((s) => s.activate());
17761
+ }
17762
+ if (this.isActive) return;
17763
+ this.state.partialNext({ isActive: true });
17764
+ };
17765
+ this.search = async (searchQuery) => {
17766
+ const searchedSources = this.activeSources;
17767
+ this.state.partialNext({
17768
+ searchQuery
17769
+ });
17770
+ await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
17771
+ };
17772
+ this.cancelSearchQueries = () => {
17773
+ this.activeSources.forEach((s) => s.cancelScheduledQuery());
17774
+ };
17775
+ this.clear = () => {
17776
+ this.cancelSearchQueries();
17777
+ this.sources.forEach(
17778
+ (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
17779
+ );
17780
+ this.state.next((current) => ({
17781
+ ...current,
17782
+ isActive: true,
17783
+ queriesInProgress: [],
17784
+ searchQuery: ""
17785
+ }));
17786
+ };
17787
+ this.exit = () => {
17788
+ this.cancelSearchQueries();
17789
+ this.sources.forEach(
17790
+ (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
17791
+ );
17792
+ this.state.next((current) => ({
17793
+ ...current,
17794
+ isActive: false,
17795
+ queriesInProgress: [],
17796
+ searchQuery: ""
17797
+ }));
17798
+ };
17799
+ this.state = new StateStore({
17800
+ isActive: false,
17801
+ searchQuery: "",
17802
+ sources: sources ?? []
17803
+ });
17804
+ this._internalState = new StateStore({});
17805
+ this.config = { keepSingleActiveSource: true, ...config };
17806
+ }
17807
+ get hasNext() {
17808
+ return this.sources.some((source) => source.hasNext);
17809
+ }
17810
+ get sources() {
17811
+ return this.state.getLatestValue().sources;
17812
+ }
17813
+ get activeSources() {
17814
+ return this.state.getLatestValue().sources.filter((s) => s.isActive);
17815
+ }
17816
+ get isActive() {
17817
+ return this.state.getLatestValue().isActive;
17818
+ }
17819
+ get searchQuery() {
17820
+ return this.state.getLatestValue().searchQuery;
17821
+ }
17822
+ get searchSourceTypes() {
17823
+ return this.sources.map((s) => s.type);
17824
+ }
17825
+ };
17826
+
17827
+ // src/search/UserSearchSource.ts
17713
17828
  var UserSearchSource = class extends BaseSearchSource {
17714
17829
  constructor(client, options) {
17715
17830
  super(options);
@@ -17733,6 +17848,8 @@ var UserSearchSource = class extends BaseSearchSource {
17733
17848
  return items.filter((u) => u.id !== this.client.user?.id);
17734
17849
  }
17735
17850
  };
17851
+
17852
+ // src/search/ChannelSearchSource.ts
17736
17853
  var ChannelSearchSource = class extends BaseSearchSource {
17737
17854
  constructor(client, options) {
17738
17855
  super(options);
@@ -17754,6 +17871,8 @@ var ChannelSearchSource = class extends BaseSearchSource {
17754
17871
  return items;
17755
17872
  }
17756
17873
  };
17874
+
17875
+ // src/search/MessageSearchSource.ts
17757
17876
  var MessageSearchSource = class extends BaseSearchSource {
17758
17877
  constructor(client, options) {
17759
17878
  super(options);
@@ -17814,108 +17933,6 @@ var MessageSearchSource = class extends BaseSearchSource {
17814
17933
  return items;
17815
17934
  }
17816
17935
  };
17817
- var SearchController = class {
17818
- constructor({ config, sources } = {}) {
17819
- this.addSource = (source) => {
17820
- this.state.partialNext({
17821
- sources: [...this.sources, source]
17822
- });
17823
- };
17824
- this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
17825
- this.removeSource = (sourceType) => {
17826
- const newSources = this.sources.filter((s) => s.type !== sourceType);
17827
- if (newSources.length === this.sources.length) return;
17828
- this.state.partialNext({ sources: newSources });
17829
- };
17830
- this.activateSource = (sourceType) => {
17831
- const source = this.getSource(sourceType);
17832
- if (!source || source.isActive) return;
17833
- if (this.config.keepSingleActiveSource) {
17834
- this.sources.forEach((s) => {
17835
- if (s.type !== sourceType) {
17836
- s.deactivate();
17837
- }
17838
- });
17839
- }
17840
- source.activate();
17841
- this.state.partialNext({ sources: [...this.sources] });
17842
- };
17843
- this.deactivateSource = (sourceType) => {
17844
- const source = this.getSource(sourceType);
17845
- if (!source?.isActive) return;
17846
- if (this.activeSources.length === 1) return;
17847
- source.deactivate();
17848
- this.state.partialNext({ sources: [...this.sources] });
17849
- };
17850
- this.activate = () => {
17851
- if (!this.activeSources.length) {
17852
- const sourcesToActivate = this.config.keepSingleActiveSource ? this.sources.slice(0, 1) : this.sources;
17853
- sourcesToActivate.forEach((s) => s.activate());
17854
- }
17855
- if (this.isActive) return;
17856
- this.state.partialNext({ isActive: true });
17857
- };
17858
- this.search = async (searchQuery) => {
17859
- const searchedSources = this.activeSources;
17860
- this.state.partialNext({
17861
- searchQuery
17862
- });
17863
- await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
17864
- };
17865
- this.cancelSearchQueries = () => {
17866
- this.activeSources.forEach((s) => s.cancelScheduledQuery());
17867
- };
17868
- this.clear = () => {
17869
- this.cancelSearchQueries();
17870
- this.sources.forEach(
17871
- (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
17872
- );
17873
- this.state.next((current) => ({
17874
- ...current,
17875
- isActive: true,
17876
- queriesInProgress: [],
17877
- searchQuery: ""
17878
- }));
17879
- };
17880
- this.exit = () => {
17881
- this.cancelSearchQueries();
17882
- this.sources.forEach(
17883
- (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
17884
- );
17885
- this.state.next((current) => ({
17886
- ...current,
17887
- isActive: false,
17888
- queriesInProgress: [],
17889
- searchQuery: ""
17890
- }));
17891
- };
17892
- this.state = new StateStore({
17893
- isActive: false,
17894
- searchQuery: "",
17895
- sources: sources ?? []
17896
- });
17897
- this._internalState = new StateStore({});
17898
- this.config = { keepSingleActiveSource: true, ...config };
17899
- }
17900
- get hasNext() {
17901
- return this.sources.some((source) => source.hasNext);
17902
- }
17903
- get sources() {
17904
- return this.state.getLatestValue().sources;
17905
- }
17906
- get activeSources() {
17907
- return this.state.getLatestValue().sources.filter((s) => s.isActive);
17908
- }
17909
- get isActive() {
17910
- return this.state.getLatestValue().isActive;
17911
- }
17912
- get searchQuery() {
17913
- return this.state.getLatestValue().searchQuery;
17914
- }
17915
- get searchSourceTypes() {
17916
- return this.sources.map((s) => s.type);
17917
- }
17918
- };
17919
17936
 
17920
17937
  // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
17921
17938
  var getTriggerCharWithToken = ({
@@ -20694,6 +20711,7 @@ var Channel = class {
20694
20711
  })
20695
20712
  };
20696
20713
  this.getClient().polls.hydratePollCache(state.messages, true);
20714
+ this.getClient().reminders.hydrateState(state.messages);
20697
20715
  if (state.draft) {
20698
20716
  this.messageComposer.initState({ composition: state.draft });
20699
20717
  }
@@ -21973,7 +21991,9 @@ var TokenManager = class {
21973
21991
  try {
21974
21992
  this.token = await this.tokenProvider();
21975
21993
  } catch (e) {
21976
- return reject(new Error(`Call to tokenProvider failed with message: ${e}`));
21994
+ return reject(
21995
+ new Error(`Call to tokenProvider failed with message: ${e}`, { cause: e })
21996
+ );
21977
21997
  }
21978
21998
  resolve(this.token);
21979
21999
  }
@@ -23723,6 +23743,458 @@ var NotificationManager = class {
23723
23743
  }
23724
23744
  };
23725
23745
 
23746
+ // src/reminders/ReminderTimer.ts
23747
+ var oneMinute = 60 * 1e3;
23748
+ var oneHour = 60 * oneMinute;
23749
+ var oneDay = 24 * oneHour;
23750
+ var oneWeek = 7 * oneDay;
23751
+ var GROUP_BOUNDS = {
23752
+ minute: { lower: oneMinute, upper: oneHour },
23753
+ hour: { lower: oneHour, upper: oneDay },
23754
+ day: { lower: oneDay, upper: oneWeek }
23755
+ };
23756
+ var DEFAULT_STOP_REFRESH_BOUNDARY_MS = 2 * oneWeek;
23757
+ var ReminderTimer = class {
23758
+ constructor({
23759
+ reminder,
23760
+ config
23761
+ }) {
23762
+ this.timeout = null;
23763
+ this.stopRefreshBoundaryMs = DEFAULT_STOP_REFRESH_BOUNDARY_MS;
23764
+ this.getRefreshIntervalLength = () => {
23765
+ if (!this.reminder.remindAt) return null;
23766
+ const distanceFromDeadlineMs = Math.abs(timeLeftMs(this.reminder.remindAt.getTime()));
23767
+ let refreshInterval;
23768
+ if (distanceFromDeadlineMs === 0) {
23769
+ refreshInterval = oneMinute;
23770
+ } else if (distanceFromDeadlineMs < GROUP_BOUNDS.minute.lower) {
23771
+ refreshInterval = distanceFromDeadlineMs;
23772
+ } else if (distanceFromDeadlineMs <= GROUP_BOUNDS.minute.upper) {
23773
+ refreshInterval = oneMinute;
23774
+ } else if (distanceFromDeadlineMs <= GROUP_BOUNDS.hour.upper) {
23775
+ refreshInterval = oneHour;
23776
+ } else {
23777
+ refreshInterval = oneDay;
23778
+ }
23779
+ return refreshInterval;
23780
+ };
23781
+ this.init = () => {
23782
+ if (!this.reminder.remindAt) return null;
23783
+ const timeoutLength = this.getRefreshIntervalLength();
23784
+ if (timeoutLength === null) return null;
23785
+ const boundaryTimestamp = this.reminder.remindAt?.getTime() + this.stopRefreshBoundaryMs;
23786
+ const timeLeftToBoundary = boundaryTimestamp - Date.now();
23787
+ if (timeLeftToBoundary <= 0) {
23788
+ this.timeout = null;
23789
+ return;
23790
+ }
23791
+ if (this.timeout) clearTimeout(this.timeout);
23792
+ this.timeout = setTimeout(() => {
23793
+ this.reminder.refreshTimeLeft();
23794
+ this.init();
23795
+ }, timeoutLength);
23796
+ };
23797
+ this.clear = () => {
23798
+ if (this.timeout) {
23799
+ clearInterval(this.timeout);
23800
+ this.timeout = null;
23801
+ }
23802
+ };
23803
+ this.reminder = reminder;
23804
+ if (typeof config?.stopRefreshBoundaryMs === "number") {
23805
+ this.stopRefreshBoundaryMs = config.stopRefreshBoundaryMs;
23806
+ }
23807
+ }
23808
+ };
23809
+
23810
+ // src/reminders/Reminder.ts
23811
+ var timeLeftMs = (remindAt) => remindAt - (/* @__PURE__ */ new Date()).getTime();
23812
+ var _Reminder = class _Reminder {
23813
+ constructor({ data, config }) {
23814
+ this.setState = (data) => {
23815
+ this.state.next((current) => {
23816
+ const newState = { ...current, ..._Reminder.toStateValue(data) };
23817
+ if (newState.remind_at) {
23818
+ newState.timeLeftMs = timeLeftMs(newState.remind_at.getTime());
23819
+ }
23820
+ return newState;
23821
+ });
23822
+ if (data.remind_at) {
23823
+ this.initTimer();
23824
+ } else if (!data.remind_at) {
23825
+ this.clearTimer();
23826
+ }
23827
+ };
23828
+ this.refreshTimeLeft = () => {
23829
+ if (!this.remindAt) return;
23830
+ this.state.partialNext({ timeLeftMs: timeLeftMs(this.remindAt.getTime()) });
23831
+ };
23832
+ this.initTimer = () => {
23833
+ this.timer.init();
23834
+ };
23835
+ this.clearTimer = () => {
23836
+ this.timer.clear();
23837
+ };
23838
+ this.state = new StateStore(_Reminder.toStateValue(data));
23839
+ this.timer = new ReminderTimer({ reminder: this, config });
23840
+ this.initTimer();
23841
+ }
23842
+ get id() {
23843
+ return this.state.getLatestValue().message_id;
23844
+ }
23845
+ get remindAt() {
23846
+ return this.state.getLatestValue().remind_at;
23847
+ }
23848
+ get timeLeftMs() {
23849
+ return this.state.getLatestValue().timeLeftMs;
23850
+ }
23851
+ };
23852
+ _Reminder.toStateValue = (data) => ({
23853
+ ...data,
23854
+ created_at: new Date(data.created_at),
23855
+ message: data.message || null,
23856
+ remind_at: data.remind_at ? new Date(data.remind_at) : null,
23857
+ timeLeftMs: data.remind_at ? timeLeftMs(new Date(data.remind_at).getTime()) : null,
23858
+ updated_at: new Date(data.updated_at),
23859
+ user: data.user || null
23860
+ });
23861
+ var Reminder = _Reminder;
23862
+
23863
+ // src/pagination/BasePaginator.ts
23864
+ var DEFAULT_PAGINATION_OPTIONS = {
23865
+ debounceMs: 300,
23866
+ pageSize: 10
23867
+ };
23868
+ var BasePaginator = class {
23869
+ constructor(options) {
23870
+ this._isCursorPagination = false;
23871
+ this.setDebounceOptions = ({ debounceMs }) => {
23872
+ this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
23873
+ };
23874
+ this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
23875
+ this.next = () => this.executeQuery({ direction: "next" });
23876
+ this.prev = () => this.executeQuery({ direction: "prev" });
23877
+ this.nextDebounced = () => {
23878
+ this._executeQueryDebounced({ direction: "next" });
23879
+ };
23880
+ this.prevDebounced = () => {
23881
+ this._executeQueryDebounced({ direction: "prev" });
23882
+ };
23883
+ const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
23884
+ this.pageSize = pageSize;
23885
+ this.state = new StateStore(this.initialState);
23886
+ this.setDebounceOptions({ debounceMs });
23887
+ }
23888
+ get lastQueryError() {
23889
+ return this.state.getLatestValue().lastQueryError;
23890
+ }
23891
+ get hasNext() {
23892
+ return this.state.getLatestValue().hasNext;
23893
+ }
23894
+ get hasPrev() {
23895
+ return this.state.getLatestValue().hasPrev;
23896
+ }
23897
+ get hasResults() {
23898
+ return Array.isArray(this.state.getLatestValue().items);
23899
+ }
23900
+ get isLoading() {
23901
+ return this.state.getLatestValue().isLoading;
23902
+ }
23903
+ get initialState() {
23904
+ return {
23905
+ hasNext: true,
23906
+ hasPrev: true,
23907
+ //todo: check if optimistic value does not cause problems in UI
23908
+ isLoading: false,
23909
+ items: void 0,
23910
+ lastQueryError: void 0,
23911
+ cursor: void 0,
23912
+ offset: 0
23913
+ };
23914
+ }
23915
+ get items() {
23916
+ return this.state.getLatestValue().items;
23917
+ }
23918
+ get cursor() {
23919
+ return this.state.getLatestValue().cursor;
23920
+ }
23921
+ get offset() {
23922
+ return this.state.getLatestValue().offset;
23923
+ }
23924
+ getStateBeforeFirstQuery() {
23925
+ return {
23926
+ ...this.initialState,
23927
+ isLoading: true
23928
+ };
23929
+ }
23930
+ getStateAfterQuery(stateUpdate, isFirstPage) {
23931
+ const current = this.state.getLatestValue();
23932
+ return {
23933
+ ...current,
23934
+ lastQueryError: void 0,
23935
+ // reset lastQueryError that can be overridden by the stateUpdate
23936
+ ...stateUpdate,
23937
+ isLoading: false,
23938
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
23939
+ };
23940
+ }
23941
+ async executeQuery({ direction }) {
23942
+ if (!this.canExecuteQuery(direction)) return;
23943
+ const isFirstPage = typeof this.items === "undefined";
23944
+ if (isFirstPage) {
23945
+ this.state.next(this.getStateBeforeFirstQuery());
23946
+ } else {
23947
+ this.state.partialNext({ isLoading: true });
23948
+ }
23949
+ const stateUpdate = {};
23950
+ try {
23951
+ const results = await this.query({ direction });
23952
+ if (!results) return;
23953
+ const { items, next, prev } = results;
23954
+ if (isFirstPage && (next || prev)) {
23955
+ this._isCursorPagination = true;
23956
+ }
23957
+ if (this._isCursorPagination) {
23958
+ stateUpdate.cursor = { next: next || null, prev: prev || null };
23959
+ stateUpdate.hasNext = !!next;
23960
+ stateUpdate.hasPrev = !!prev;
23961
+ } else {
23962
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
23963
+ stateUpdate.hasNext = items.length === this.pageSize;
23964
+ }
23965
+ stateUpdate.items = await this.filterQueryResults(items);
23966
+ } catch (e) {
23967
+ stateUpdate.lastQueryError = e;
23968
+ } finally {
23969
+ this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
23970
+ }
23971
+ }
23972
+ cancelScheduledQuery() {
23973
+ this._executeQueryDebounced.cancel();
23974
+ }
23975
+ resetState() {
23976
+ this.state.next(this.initialState);
23977
+ }
23978
+ };
23979
+
23980
+ // src/pagination/ReminderPaginator.ts
23981
+ var ReminderPaginator = class extends BasePaginator {
23982
+ constructor(client, options) {
23983
+ super(options);
23984
+ this.query = async ({
23985
+ direction
23986
+ }) => {
23987
+ const cursor = this.cursor?.[direction];
23988
+ const {
23989
+ reminders: items,
23990
+ next,
23991
+ prev
23992
+ } = await this.client.queryReminders({
23993
+ filter: this.filters,
23994
+ sort: this.sort,
23995
+ limit: this.pageSize,
23996
+ [direction]: cursor
23997
+ });
23998
+ return { items, next, prev };
23999
+ };
24000
+ this.filterQueryResults = (items) => items;
24001
+ this.client = client;
24002
+ }
24003
+ };
24004
+
24005
+ // src/reminders/ReminderManager.ts
24006
+ var oneMinute2 = 60 * 1e3;
24007
+ var oneHour2 = 60 * oneMinute2;
24008
+ var oneDay2 = 24 * oneHour2;
24009
+ var DEFAULT_REMINDER_MANAGER_CONFIG = {
24010
+ scheduledOffsetsMs: [
24011
+ 2 * oneMinute2,
24012
+ 30 * oneMinute2,
24013
+ oneHour2,
24014
+ 2 * oneHour2,
24015
+ 8 * oneHour2,
24016
+ oneDay2
24017
+ ]
24018
+ };
24019
+ var isReminderExistsError = (error) => error.message.match("already has reminder created for this message_id");
24020
+ var isReminderDoesNotExistError = (error) => error.message.match("reminder does not exist");
24021
+ var _ReminderManager = class _ReminderManager extends WithSubscriptions {
24022
+ constructor({ client, config }) {
24023
+ super();
24024
+ this.upsertToState = ({
24025
+ data,
24026
+ overwrite = true
24027
+ }) => {
24028
+ if (!this.client._cacheEnabled()) {
24029
+ return;
24030
+ }
24031
+ const cachedReminder = this.getFromState(data.message_id);
24032
+ if (!cachedReminder) {
24033
+ const reminder = new Reminder({
24034
+ data,
24035
+ config: { stopRefreshBoundaryMs: this.stopTimerRefreshBoundaryMs }
24036
+ });
24037
+ this.state.partialNext({
24038
+ reminders: new Map(this.reminders.set(data.message_id, reminder))
24039
+ });
24040
+ } else if (overwrite) {
24041
+ cachedReminder.setState(data);
24042
+ }
24043
+ return cachedReminder;
24044
+ };
24045
+ this.removeFromState = (messageId) => {
24046
+ const cachedReminder = this.getFromState(messageId);
24047
+ if (!cachedReminder) return;
24048
+ cachedReminder.clearTimer();
24049
+ const reminders = this.reminders;
24050
+ reminders.delete(messageId);
24051
+ this.state.partialNext({ reminders: new Map(reminders) });
24052
+ };
24053
+ this.hydrateState = (messages) => {
24054
+ messages.forEach(({ reminder }) => {
24055
+ if (reminder) {
24056
+ this.upsertToState({ data: reminder });
24057
+ }
24058
+ });
24059
+ };
24060
+ // State API END //
24061
+ // Timers API START //
24062
+ this.initTimers = () => {
24063
+ this.reminders.forEach((reminder) => reminder.initTimer());
24064
+ };
24065
+ this.clearTimers = () => {
24066
+ this.reminders.forEach((reminder) => reminder.clearTimer());
24067
+ };
24068
+ this.registerSubscriptions = () => {
24069
+ if (this.hasSubscriptions) return;
24070
+ this.addUnsubscribeFunction(this.subscribeReminderCreated());
24071
+ this.addUnsubscribeFunction(this.subscribeReminderUpdated());
24072
+ this.addUnsubscribeFunction(this.subscribeReminderDeleted());
24073
+ this.addUnsubscribeFunction(this.subscribeNotificationReminderDue());
24074
+ this.addUnsubscribeFunction(this.subscribeMessageDeleted());
24075
+ this.addUnsubscribeFunction(this.subscribeMessageUndeleted());
24076
+ this.addUnsubscribeFunction(this.subscribePaginatorStateUpdated());
24077
+ this.addUnsubscribeFunction(this.subscribeConfigStateUpdated());
24078
+ };
24079
+ this.subscribeReminderCreated = () => this.client.on("reminder.created", (event) => {
24080
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
24081
+ const { reminder } = event;
24082
+ this.upsertToState({ data: reminder });
24083
+ }).unsubscribe;
24084
+ this.subscribeReminderUpdated = () => this.client.on("reminder.updated", (event) => {
24085
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
24086
+ const { reminder } = event;
24087
+ this.upsertToState({ data: reminder });
24088
+ }).unsubscribe;
24089
+ this.subscribeReminderDeleted = () => this.client.on("reminder.deleted", (event) => {
24090
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
24091
+ this.removeFromState(event.message_id);
24092
+ }).unsubscribe;
24093
+ this.subscribeMessageDeleted = () => this.client.on("message.deleted", (event) => {
24094
+ if (!event.message?.id) return;
24095
+ this.removeFromState(event.message.id);
24096
+ }).unsubscribe;
24097
+ this.subscribeMessageUndeleted = () => this.client.on("message.undeleted", (event) => {
24098
+ if (!event.message?.reminder) return;
24099
+ this.upsertToState({ data: event.message.reminder });
24100
+ }).unsubscribe;
24101
+ this.subscribeNotificationReminderDue = () => this.client.on("notification.reminder_due", () => null).unsubscribe;
24102
+ // todo: what should be performed on this event?
24103
+ this.subscribePaginatorStateUpdated = () => this.paginator.state.subscribeWithSelector(
24104
+ ({ items }) => [items],
24105
+ ([items]) => {
24106
+ if (!items) return;
24107
+ for (const reminder of items) {
24108
+ this.upsertToState({ data: reminder });
24109
+ }
24110
+ }
24111
+ );
24112
+ this.subscribeConfigStateUpdated = () => this.configState.subscribeWithSelector(
24113
+ ({ stopTimerRefreshBoundaryMs }) => ({ stopTimerRefreshBoundaryMs }),
24114
+ ({ stopTimerRefreshBoundaryMs }, previousValue) => {
24115
+ if (typeof stopTimerRefreshBoundaryMs === "number" && stopTimerRefreshBoundaryMs !== previousValue?.stopTimerRefreshBoundaryMs) {
24116
+ this.reminders.forEach((reminder) => {
24117
+ if (reminder.timer) {
24118
+ reminder.timer.stopRefreshBoundaryMs = stopTimerRefreshBoundaryMs;
24119
+ }
24120
+ });
24121
+ }
24122
+ }
24123
+ );
24124
+ // WS event handling END //
24125
+ // API calls START //
24126
+ this.upsertReminder = async (options) => {
24127
+ const { messageId } = options;
24128
+ if (this.getFromState(messageId)) {
24129
+ try {
24130
+ return await this.updateReminder(options);
24131
+ } catch (error) {
24132
+ if (isReminderDoesNotExistError(error)) {
24133
+ return await this.createReminder(options);
24134
+ }
24135
+ throw error;
24136
+ }
24137
+ } else {
24138
+ try {
24139
+ return await this.createReminder(options);
24140
+ } catch (error) {
24141
+ if (isReminderExistsError(error)) {
24142
+ return await this.updateReminder(options);
24143
+ }
24144
+ throw error;
24145
+ }
24146
+ }
24147
+ };
24148
+ this.createReminder = async (options) => {
24149
+ const { reminder } = await this.client.createReminder(options);
24150
+ return this.upsertToState({ data: reminder, overwrite: false });
24151
+ };
24152
+ this.updateReminder = async (options) => {
24153
+ const { reminder } = await this.client.updateReminder(options);
24154
+ return this.upsertToState({ data: reminder });
24155
+ };
24156
+ this.deleteReminder = async (messageId) => {
24157
+ await this.client.deleteReminder(messageId);
24158
+ this.removeFromState(messageId);
24159
+ };
24160
+ this.queryNextReminders = async () => {
24161
+ await this.paginator.next();
24162
+ };
24163
+ this.queryPreviousReminders = async () => {
24164
+ await this.paginator.prev();
24165
+ };
24166
+ this.client = client;
24167
+ this.configState = new StateStore({
24168
+ scheduledOffsetsMs: config?.scheduledOffsetsMs ?? DEFAULT_REMINDER_MANAGER_CONFIG.scheduledOffsetsMs
24169
+ });
24170
+ this.state = new StateStore({ reminders: /* @__PURE__ */ new Map() });
24171
+ this.paginator = new ReminderPaginator(client);
24172
+ }
24173
+ // Config API START //
24174
+ updateConfig(config) {
24175
+ this.configState.partialNext(config);
24176
+ }
24177
+ get stopTimerRefreshBoundaryMs() {
24178
+ return this.configState.getLatestValue().stopTimerRefreshBoundaryMs;
24179
+ }
24180
+ get scheduledOffsetsMs() {
24181
+ return this.configState.getLatestValue().scheduledOffsetsMs;
24182
+ }
24183
+ // Config API END //
24184
+ // State API START //
24185
+ get reminders() {
24186
+ return this.state.getLatestValue().reminders;
24187
+ }
24188
+ getFromState(messageId) {
24189
+ return this.reminders.get(messageId);
24190
+ }
24191
+ // API calls END //
24192
+ };
24193
+ // Timers API END //
24194
+ // WS event handling START //
24195
+ _ReminderManager.isReminderWsEventPayload = (event) => !!event.reminder && (event.type.startsWith("reminder.") || event.type === "notification.reminder_due");
24196
+ var ReminderManager = _ReminderManager;
24197
+
23726
24198
  // src/client.ts
23727
24199
  function isString3(x) {
23728
24200
  return typeof x === "string" || x instanceof String;
@@ -23738,6 +24210,9 @@ var StreamChat = class _StreamChat {
23738
24210
  });
23739
24211
  this._getConnectionID = () => this.wsConnection?.connectionID || this.wsFallback?.connectionID;
23740
24212
  this._hasConnectionID = () => Boolean(this._getConnectionID());
24213
+ this.setMessageComposerSetupFunction = (setupFunction) => {
24214
+ this._messageComposerSetupState.partialNext({ setupFunction });
24215
+ };
23741
24216
  /**
23742
24217
  * connectUser - Set the current user and open a WebSocket connection
23743
24218
  *
@@ -24306,9 +24781,6 @@ var StreamChat = class _StreamChat {
24306
24781
  device: this.options.device,
24307
24782
  client_request_id
24308
24783
  });
24309
- this.setMessageComposerSetupFunction = (setupFunction) => {
24310
- this._messageComposerSetupState.partialNext({ setupFunction });
24311
- };
24312
24784
  this.key = key;
24313
24785
  this.listeners = {};
24314
24786
  this.state = new ClientState({ client: this });
@@ -24363,6 +24835,7 @@ var StreamChat = class _StreamChat {
24363
24835
  this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
24364
24836
  this.threads = new ThreadManager({ client: this });
24365
24837
  this.polls = new PollManager({ client: this });
24838
+ this.reminders = new ReminderManager({ client: this });
24366
24839
  }
24367
24840
  static getInstance(key, secretOrOptions, options) {
24368
24841
  if (!_StreamChat._instance) {
@@ -24498,15 +24971,15 @@ var StreamChat = class _StreamChat {
24498
24971
  * @param {string} userID User ID. If user has no devices, it will error
24499
24972
  * @param {TestPushDataInput} [data] Overrides for push templates/message used
24500
24973
  * IE: {
24501
- messageID: 'id-of-message', // will error if message does not exist
24502
- apnTemplate: '{}', // if app doesn't have apn configured it will error
24503
- firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
24504
- firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
24505
- skipDevices: true, // skip config/device checks and sending to real devices
24506
- pushProviderName: 'staging' // one of your configured push providers
24507
- pushProviderType: 'apn' // one of supported provider types
24508
- }
24509
- */
24974
+ messageID: 'id-of-message', // will error if message does not exist
24975
+ apnTemplate: '{}', // if app doesn't have apn configured it will error
24976
+ firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
24977
+ firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
24978
+ skipDevices: true, // skip config/device checks and sending to real devices
24979
+ pushProviderName: 'staging' // one of your configured push providers
24980
+ pushProviderType: 'apn' // one of supported provider types
24981
+ }
24982
+ */
24510
24983
  async testPushSettings(userID, data = {}) {
24511
24984
  return await this.post(this.baseURL + "/check_push", {
24512
24985
  user_id: userID,
@@ -24524,10 +24997,10 @@ var StreamChat = class _StreamChat {
24524
24997
  *
24525
24998
  * @param {TestSQSDataInput} [data] Overrides SQS settings for testing if needed
24526
24999
  * IE: {
24527
- sqs_key: 'auth_key',
24528
- sqs_secret: 'auth_secret',
24529
- sqs_url: 'url_to_queue',
24530
- }
25000
+ sqs_key: 'auth_key',
25001
+ sqs_secret: 'auth_secret',
25002
+ sqs_url: 'url_to_queue',
25003
+ }
24531
25004
  */
24532
25005
  async testSQSSettings(data = {}) {
24533
25006
  return await this.post(this.baseURL + "/check_sqs", data);
@@ -24537,10 +25010,10 @@ var StreamChat = class _StreamChat {
24537
25010
  *
24538
25011
  * @param {TestSNSDataInput} [data] Overrides SNS settings for testing if needed
24539
25012
  * IE: {
24540
- sns_key: 'auth_key',
24541
- sns_secret: 'auth_secret',
24542
- sns_topic_arn: 'topic_to_publish_to',
24543
- }
25013
+ sns_key: 'auth_key',
25014
+ sns_secret: 'auth_secret',
25015
+ sns_topic_arn: 'topic_to_publish_to',
25016
+ }
24544
25017
  */
24545
25018
  async testSNSSettings(data = {}) {
24546
25019
  return await this.post(this.baseURL + "/check_sns", data);
@@ -25021,6 +25494,7 @@ var StreamChat = class _StreamChat {
25021
25494
  })
25022
25495
  };
25023
25496
  this.polls.hydratePollCache(channelState.messages, true);
25497
+ this.reminders.hydrateState(channelState.messages);
25024
25498
  }
25025
25499
  if (channelState.draft) {
25026
25500
  c.messageComposer.initState({ composition: channelState.draft });
@@ -25941,7 +26415,7 @@ var StreamChat = class _StreamChat {
25941
26415
  if (this.userAgent) {
25942
26416
  return this.userAgent;
25943
26417
  }
25944
- const version = "9.4.0";
26418
+ const version = "9.5.0";
25945
26419
  const clientBundle = "node-cjs";
25946
26420
  let userAgentString = "";
25947
26421
  if (this.sdkIdentifier) {
@@ -26980,6 +27454,56 @@ var StreamChat = class _StreamChat {
26980
27454
  };
26981
27455
  return await this.post(this.baseURL + "/drafts/query", payload);
26982
27456
  }
27457
+ /**
27458
+ * createReminder - Creates a reminder for a message
27459
+ *
27460
+ * @param {CreateReminderOptions} options The options for creating the reminder
27461
+ * @returns {Promise<ReminderAPIResponse>}
27462
+ */
27463
+ async createReminder({ messageId, ...options }) {
27464
+ return await this.post(
27465
+ `${this.baseURL}/messages/${messageId}/reminders`,
27466
+ options
27467
+ );
27468
+ }
27469
+ /**
27470
+ * updateReminder - Updates an existing reminder for a message
27471
+ *
27472
+ * @param {UpdateReminderOptions} options The options for updating the reminder
27473
+ * @returns {Promise<ReminderAPIResponse>}
27474
+ */
27475
+ async updateReminder({ messageId, ...options }) {
27476
+ return await this.patch(
27477
+ `${this.baseURL}/messages/${messageId}/reminders`,
27478
+ options
27479
+ );
27480
+ }
27481
+ /**
27482
+ * deleteReminder - Deletes a reminder for a message
27483
+ *
27484
+ * @param {string} messageId The ID of the message whose reminder to delete
27485
+ * @param {string} [userId] Optional user ID, required for server-side operations
27486
+ * @returns {Promise<APIResponse>}
27487
+ */
27488
+ async deleteReminder(messageId, userId) {
27489
+ return await this.delete(
27490
+ `${this.baseURL}/messages/${messageId}/reminders`,
27491
+ userId ? { user_id: userId } : {}
27492
+ );
27493
+ }
27494
+ /**
27495
+ * queryReminders - Queries reminders based on given filters
27496
+ *
27497
+ * @param {QueryRemindersOptions} options The options for querying reminders
27498
+ * @returns {Promise<QueryRemindersResponse>}
27499
+ */
27500
+ async queryReminders({ filter: filter2, sort, ...rest } = {}) {
27501
+ return await this.post(`${this.baseURL}/reminders/query`, {
27502
+ filter_conditions: filter2,
27503
+ sort: sort && normalizeQuerySort(sort),
27504
+ ...rest
27505
+ });
27506
+ }
26983
27507
  };
26984
27508
 
26985
27509
  // src/events.ts
@@ -27046,7 +27570,12 @@ var EVENT_MAP = {
27046
27570
  "connection.changed": true,
27047
27571
  "connection.recovered": true,
27048
27572
  "transport.changed": true,
27049
- "capabilities.changed": true
27573
+ "capabilities.changed": true,
27574
+ // Reminder events
27575
+ "reminder.created": true,
27576
+ "reminder.updated": true,
27577
+ "reminder.deleted": true,
27578
+ "notification.reminder_due": true
27050
27579
  };
27051
27580
 
27052
27581
  // src/permissions.ts
@@ -27919,6 +28448,7 @@ var FixedSizeQueueCache = class {
27919
28448
  AnyResource,
27920
28449
  AnyRole,
27921
28450
  AttachmentManager,
28451
+ BasePaginator,
27922
28452
  BaseSearchSource,
27923
28453
  BuiltinPermissions,
27924
28454
  BuiltinRoles,
@@ -27936,6 +28466,9 @@ var FixedSizeQueueCache = class {
27936
28466
  DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
27937
28467
  DEFAULT_COMPOSER_CONFIG,
27938
28468
  DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
28469
+ DEFAULT_PAGINATION_OPTIONS,
28470
+ DEFAULT_REMINDER_MANAGER_CONFIG,
28471
+ DEFAULT_STOP_REFRESH_BOUNDARY_MS,
27939
28472
  DEFAULT_TEXT_COMPOSER_CONFIG,
27940
28473
  Deny,
27941
28474
  DenyAll,
@@ -27968,6 +28501,10 @@ var FixedSizeQueueCache = class {
27968
28501
  PollComposerCompositionMiddlewareExecutor,
27969
28502
  PollComposerStateMiddlewareExecutor,
27970
28503
  PollManager,
28504
+ Reminder,
28505
+ ReminderManager,
28506
+ ReminderPaginator,
28507
+ ReminderTimer,
27971
28508
  SearchController,
27972
28509
  Segment,
27973
28510
  StableWSConnection,
@@ -28055,7 +28592,8 @@ var FixedSizeQueueCache = class {
28055
28592
  readFileAsArrayBuffer,
28056
28593
  removeDiacritics,
28057
28594
  replaceWordWithEntity,
28058
- textIsEmpty
28595
+ textIsEmpty,
28596
+ timeLeftMs
28059
28597
  });
28060
28598
  /*! Bundled license information:
28061
28599