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
@@ -159,6 +159,7 @@ __export(index_exports, {
159
159
  AnyResource: () => AnyResource,
160
160
  AnyRole: () => AnyRole,
161
161
  AttachmentManager: () => AttachmentManager,
162
+ BasePaginator: () => BasePaginator,
162
163
  BaseSearchSource: () => BaseSearchSource,
163
164
  BuiltinPermissions: () => BuiltinPermissions,
164
165
  BuiltinRoles: () => BuiltinRoles,
@@ -176,6 +177,9 @@ __export(index_exports, {
176
177
  DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS: () => DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
177
178
  DEFAULT_COMPOSER_CONFIG: () => DEFAULT_COMPOSER_CONFIG,
178
179
  DEFAULT_LINK_PREVIEW_MANAGER_CONFIG: () => DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
180
+ DEFAULT_PAGINATION_OPTIONS: () => DEFAULT_PAGINATION_OPTIONS,
181
+ DEFAULT_REMINDER_MANAGER_CONFIG: () => DEFAULT_REMINDER_MANAGER_CONFIG,
182
+ DEFAULT_STOP_REFRESH_BOUNDARY_MS: () => DEFAULT_STOP_REFRESH_BOUNDARY_MS,
179
183
  DEFAULT_TEXT_COMPOSER_CONFIG: () => DEFAULT_TEXT_COMPOSER_CONFIG,
180
184
  Deny: () => Deny,
181
185
  DenyAll: () => DenyAll,
@@ -208,6 +212,10 @@ __export(index_exports, {
208
212
  PollComposerCompositionMiddlewareExecutor: () => PollComposerCompositionMiddlewareExecutor,
209
213
  PollComposerStateMiddlewareExecutor: () => PollComposerStateMiddlewareExecutor,
210
214
  PollManager: () => PollManager,
215
+ Reminder: () => Reminder,
216
+ ReminderManager: () => ReminderManager,
217
+ ReminderPaginator: () => ReminderPaginator,
218
+ ReminderTimer: () => ReminderTimer,
211
219
  SearchController: () => SearchController,
212
220
  Segment: () => Segment,
213
221
  StableWSConnection: () => StableWSConnection,
@@ -295,7 +303,8 @@ __export(index_exports, {
295
303
  readFileAsArrayBuffer: () => readFileAsArrayBuffer,
296
304
  removeDiacritics: () => removeDiacritics,
297
305
  replaceWordWithEntity: () => replaceWordWithEntity,
298
- textIsEmpty: () => textIsEmpty
306
+ textIsEmpty: () => textIsEmpty,
307
+ timeLeftMs: () => timeLeftMs
299
308
  });
300
309
  module.exports = __toCommonJS(index_exports);
301
310
 
@@ -6241,7 +6250,7 @@ var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6241
6250
  }
6242
6251
  };
6243
6252
 
6244
- // src/search_controller.ts
6253
+ // src/search/BaseSearchSource.ts
6245
6254
  var DEFAULT_SEARCH_SOURCE_OPTIONS = {
6246
6255
  debounceMs: 300,
6247
6256
  pageSize: 10
@@ -6367,6 +6376,112 @@ var BaseSearchSource = class {
6367
6376
  this.activate();
6368
6377
  }
6369
6378
  };
6379
+
6380
+ // src/search/SearchController.ts
6381
+ var SearchController = class {
6382
+ constructor({ config, sources } = {}) {
6383
+ this.addSource = (source) => {
6384
+ this.state.partialNext({
6385
+ sources: [...this.sources, source]
6386
+ });
6387
+ };
6388
+ this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
6389
+ this.removeSource = (sourceType) => {
6390
+ const newSources = this.sources.filter((s) => s.type !== sourceType);
6391
+ if (newSources.length === this.sources.length) return;
6392
+ this.state.partialNext({ sources: newSources });
6393
+ };
6394
+ this.activateSource = (sourceType) => {
6395
+ const source = this.getSource(sourceType);
6396
+ if (!source || source.isActive) return;
6397
+ if (this.config.keepSingleActiveSource) {
6398
+ this.sources.forEach((s) => {
6399
+ if (s.type !== sourceType) {
6400
+ s.deactivate();
6401
+ }
6402
+ });
6403
+ }
6404
+ source.activate();
6405
+ this.state.partialNext({ sources: [...this.sources] });
6406
+ };
6407
+ this.deactivateSource = (sourceType) => {
6408
+ const source = this.getSource(sourceType);
6409
+ if (!source?.isActive) return;
6410
+ if (this.activeSources.length === 1) return;
6411
+ source.deactivate();
6412
+ this.state.partialNext({ sources: [...this.sources] });
6413
+ };
6414
+ this.activate = () => {
6415
+ if (!this.activeSources.length) {
6416
+ const sourcesToActivate = this.config.keepSingleActiveSource ? this.sources.slice(0, 1) : this.sources;
6417
+ sourcesToActivate.forEach((s) => s.activate());
6418
+ }
6419
+ if (this.isActive) return;
6420
+ this.state.partialNext({ isActive: true });
6421
+ };
6422
+ this.search = async (searchQuery) => {
6423
+ const searchedSources = this.activeSources;
6424
+ this.state.partialNext({
6425
+ searchQuery
6426
+ });
6427
+ await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
6428
+ };
6429
+ this.cancelSearchQueries = () => {
6430
+ this.activeSources.forEach((s) => s.cancelScheduledQuery());
6431
+ };
6432
+ this.clear = () => {
6433
+ this.cancelSearchQueries();
6434
+ this.sources.forEach(
6435
+ (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
6436
+ );
6437
+ this.state.next((current) => ({
6438
+ ...current,
6439
+ isActive: true,
6440
+ queriesInProgress: [],
6441
+ searchQuery: ""
6442
+ }));
6443
+ };
6444
+ this.exit = () => {
6445
+ this.cancelSearchQueries();
6446
+ this.sources.forEach(
6447
+ (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
6448
+ );
6449
+ this.state.next((current) => ({
6450
+ ...current,
6451
+ isActive: false,
6452
+ queriesInProgress: [],
6453
+ searchQuery: ""
6454
+ }));
6455
+ };
6456
+ this.state = new StateStore({
6457
+ isActive: false,
6458
+ searchQuery: "",
6459
+ sources: sources ?? []
6460
+ });
6461
+ this._internalState = new StateStore({});
6462
+ this.config = { keepSingleActiveSource: true, ...config };
6463
+ }
6464
+ get hasNext() {
6465
+ return this.sources.some((source) => source.hasNext);
6466
+ }
6467
+ get sources() {
6468
+ return this.state.getLatestValue().sources;
6469
+ }
6470
+ get activeSources() {
6471
+ return this.state.getLatestValue().sources.filter((s) => s.isActive);
6472
+ }
6473
+ get isActive() {
6474
+ return this.state.getLatestValue().isActive;
6475
+ }
6476
+ get searchQuery() {
6477
+ return this.state.getLatestValue().searchQuery;
6478
+ }
6479
+ get searchSourceTypes() {
6480
+ return this.sources.map((s) => s.type);
6481
+ }
6482
+ };
6483
+
6484
+ // src/search/UserSearchSource.ts
6370
6485
  var UserSearchSource = class extends BaseSearchSource {
6371
6486
  constructor(client, options) {
6372
6487
  super(options);
@@ -6390,6 +6505,8 @@ var UserSearchSource = class extends BaseSearchSource {
6390
6505
  return items.filter((u) => u.id !== this.client.user?.id);
6391
6506
  }
6392
6507
  };
6508
+
6509
+ // src/search/ChannelSearchSource.ts
6393
6510
  var ChannelSearchSource = class extends BaseSearchSource {
6394
6511
  constructor(client, options) {
6395
6512
  super(options);
@@ -6411,6 +6528,8 @@ var ChannelSearchSource = class extends BaseSearchSource {
6411
6528
  return items;
6412
6529
  }
6413
6530
  };
6531
+
6532
+ // src/search/MessageSearchSource.ts
6414
6533
  var MessageSearchSource = class extends BaseSearchSource {
6415
6534
  constructor(client, options) {
6416
6535
  super(options);
@@ -6471,108 +6590,6 @@ var MessageSearchSource = class extends BaseSearchSource {
6471
6590
  return items;
6472
6591
  }
6473
6592
  };
6474
- var SearchController = class {
6475
- constructor({ config, sources } = {}) {
6476
- this.addSource = (source) => {
6477
- this.state.partialNext({
6478
- sources: [...this.sources, source]
6479
- });
6480
- };
6481
- this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
6482
- this.removeSource = (sourceType) => {
6483
- const newSources = this.sources.filter((s) => s.type !== sourceType);
6484
- if (newSources.length === this.sources.length) return;
6485
- this.state.partialNext({ sources: newSources });
6486
- };
6487
- this.activateSource = (sourceType) => {
6488
- const source = this.getSource(sourceType);
6489
- if (!source || source.isActive) return;
6490
- if (this.config.keepSingleActiveSource) {
6491
- this.sources.forEach((s) => {
6492
- if (s.type !== sourceType) {
6493
- s.deactivate();
6494
- }
6495
- });
6496
- }
6497
- source.activate();
6498
- this.state.partialNext({ sources: [...this.sources] });
6499
- };
6500
- this.deactivateSource = (sourceType) => {
6501
- const source = this.getSource(sourceType);
6502
- if (!source?.isActive) return;
6503
- if (this.activeSources.length === 1) return;
6504
- source.deactivate();
6505
- this.state.partialNext({ sources: [...this.sources] });
6506
- };
6507
- this.activate = () => {
6508
- if (!this.activeSources.length) {
6509
- const sourcesToActivate = this.config.keepSingleActiveSource ? this.sources.slice(0, 1) : this.sources;
6510
- sourcesToActivate.forEach((s) => s.activate());
6511
- }
6512
- if (this.isActive) return;
6513
- this.state.partialNext({ isActive: true });
6514
- };
6515
- this.search = async (searchQuery) => {
6516
- const searchedSources = this.activeSources;
6517
- this.state.partialNext({
6518
- searchQuery
6519
- });
6520
- await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
6521
- };
6522
- this.cancelSearchQueries = () => {
6523
- this.activeSources.forEach((s) => s.cancelScheduledQuery());
6524
- };
6525
- this.clear = () => {
6526
- this.cancelSearchQueries();
6527
- this.sources.forEach(
6528
- (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
6529
- );
6530
- this.state.next((current) => ({
6531
- ...current,
6532
- isActive: true,
6533
- queriesInProgress: [],
6534
- searchQuery: ""
6535
- }));
6536
- };
6537
- this.exit = () => {
6538
- this.cancelSearchQueries();
6539
- this.sources.forEach(
6540
- (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
6541
- );
6542
- this.state.next((current) => ({
6543
- ...current,
6544
- isActive: false,
6545
- queriesInProgress: [],
6546
- searchQuery: ""
6547
- }));
6548
- };
6549
- this.state = new StateStore({
6550
- isActive: false,
6551
- searchQuery: "",
6552
- sources: sources ?? []
6553
- });
6554
- this._internalState = new StateStore({});
6555
- this.config = { keepSingleActiveSource: true, ...config };
6556
- }
6557
- get hasNext() {
6558
- return this.sources.some((source) => source.hasNext);
6559
- }
6560
- get sources() {
6561
- return this.state.getLatestValue().sources;
6562
- }
6563
- get activeSources() {
6564
- return this.state.getLatestValue().sources.filter((s) => s.isActive);
6565
- }
6566
- get isActive() {
6567
- return this.state.getLatestValue().isActive;
6568
- }
6569
- get searchQuery() {
6570
- return this.state.getLatestValue().searchQuery;
6571
- }
6572
- get searchSourceTypes() {
6573
- return this.sources.map((s) => s.type);
6574
- }
6575
- };
6576
6593
 
6577
6594
  // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
6578
6595
  var getTriggerCharWithToken = ({
@@ -9351,6 +9368,7 @@ var Channel = class {
9351
9368
  })
9352
9369
  };
9353
9370
  this.getClient().polls.hydratePollCache(state.messages, true);
9371
+ this.getClient().reminders.hydrateState(state.messages);
9354
9372
  if (state.draft) {
9355
9373
  this.messageComposer.initState({ composition: state.draft });
9356
9374
  }
@@ -10642,7 +10660,9 @@ var TokenManager = class {
10642
10660
  try {
10643
10661
  this.token = await this.tokenProvider();
10644
10662
  } catch (e) {
10645
- return reject(new Error(`Call to tokenProvider failed with message: ${e}`));
10663
+ return reject(
10664
+ new Error(`Call to tokenProvider failed with message: ${e}`, { cause: e })
10665
+ );
10646
10666
  }
10647
10667
  resolve(this.token);
10648
10668
  }
@@ -12392,6 +12412,458 @@ var NotificationManager = class {
12392
12412
  }
12393
12413
  };
12394
12414
 
12415
+ // src/reminders/ReminderTimer.ts
12416
+ var oneMinute = 60 * 1e3;
12417
+ var oneHour = 60 * oneMinute;
12418
+ var oneDay = 24 * oneHour;
12419
+ var oneWeek = 7 * oneDay;
12420
+ var GROUP_BOUNDS = {
12421
+ minute: { lower: oneMinute, upper: oneHour },
12422
+ hour: { lower: oneHour, upper: oneDay },
12423
+ day: { lower: oneDay, upper: oneWeek }
12424
+ };
12425
+ var DEFAULT_STOP_REFRESH_BOUNDARY_MS = 2 * oneWeek;
12426
+ var ReminderTimer = class {
12427
+ constructor({
12428
+ reminder,
12429
+ config
12430
+ }) {
12431
+ this.timeout = null;
12432
+ this.stopRefreshBoundaryMs = DEFAULT_STOP_REFRESH_BOUNDARY_MS;
12433
+ this.getRefreshIntervalLength = () => {
12434
+ if (!this.reminder.remindAt) return null;
12435
+ const distanceFromDeadlineMs = Math.abs(timeLeftMs(this.reminder.remindAt.getTime()));
12436
+ let refreshInterval;
12437
+ if (distanceFromDeadlineMs === 0) {
12438
+ refreshInterval = oneMinute;
12439
+ } else if (distanceFromDeadlineMs < GROUP_BOUNDS.minute.lower) {
12440
+ refreshInterval = distanceFromDeadlineMs;
12441
+ } else if (distanceFromDeadlineMs <= GROUP_BOUNDS.minute.upper) {
12442
+ refreshInterval = oneMinute;
12443
+ } else if (distanceFromDeadlineMs <= GROUP_BOUNDS.hour.upper) {
12444
+ refreshInterval = oneHour;
12445
+ } else {
12446
+ refreshInterval = oneDay;
12447
+ }
12448
+ return refreshInterval;
12449
+ };
12450
+ this.init = () => {
12451
+ if (!this.reminder.remindAt) return null;
12452
+ const timeoutLength = this.getRefreshIntervalLength();
12453
+ if (timeoutLength === null) return null;
12454
+ const boundaryTimestamp = this.reminder.remindAt?.getTime() + this.stopRefreshBoundaryMs;
12455
+ const timeLeftToBoundary = boundaryTimestamp - Date.now();
12456
+ if (timeLeftToBoundary <= 0) {
12457
+ this.timeout = null;
12458
+ return;
12459
+ }
12460
+ if (this.timeout) clearTimeout(this.timeout);
12461
+ this.timeout = setTimeout(() => {
12462
+ this.reminder.refreshTimeLeft();
12463
+ this.init();
12464
+ }, timeoutLength);
12465
+ };
12466
+ this.clear = () => {
12467
+ if (this.timeout) {
12468
+ clearInterval(this.timeout);
12469
+ this.timeout = null;
12470
+ }
12471
+ };
12472
+ this.reminder = reminder;
12473
+ if (typeof config?.stopRefreshBoundaryMs === "number") {
12474
+ this.stopRefreshBoundaryMs = config.stopRefreshBoundaryMs;
12475
+ }
12476
+ }
12477
+ };
12478
+
12479
+ // src/reminders/Reminder.ts
12480
+ var timeLeftMs = (remindAt) => remindAt - (/* @__PURE__ */ new Date()).getTime();
12481
+ var _Reminder = class _Reminder {
12482
+ constructor({ data, config }) {
12483
+ this.setState = (data) => {
12484
+ this.state.next((current) => {
12485
+ const newState = { ...current, ..._Reminder.toStateValue(data) };
12486
+ if (newState.remind_at) {
12487
+ newState.timeLeftMs = timeLeftMs(newState.remind_at.getTime());
12488
+ }
12489
+ return newState;
12490
+ });
12491
+ if (data.remind_at) {
12492
+ this.initTimer();
12493
+ } else if (!data.remind_at) {
12494
+ this.clearTimer();
12495
+ }
12496
+ };
12497
+ this.refreshTimeLeft = () => {
12498
+ if (!this.remindAt) return;
12499
+ this.state.partialNext({ timeLeftMs: timeLeftMs(this.remindAt.getTime()) });
12500
+ };
12501
+ this.initTimer = () => {
12502
+ this.timer.init();
12503
+ };
12504
+ this.clearTimer = () => {
12505
+ this.timer.clear();
12506
+ };
12507
+ this.state = new StateStore(_Reminder.toStateValue(data));
12508
+ this.timer = new ReminderTimer({ reminder: this, config });
12509
+ this.initTimer();
12510
+ }
12511
+ get id() {
12512
+ return this.state.getLatestValue().message_id;
12513
+ }
12514
+ get remindAt() {
12515
+ return this.state.getLatestValue().remind_at;
12516
+ }
12517
+ get timeLeftMs() {
12518
+ return this.state.getLatestValue().timeLeftMs;
12519
+ }
12520
+ };
12521
+ _Reminder.toStateValue = (data) => ({
12522
+ ...data,
12523
+ created_at: new Date(data.created_at),
12524
+ message: data.message || null,
12525
+ remind_at: data.remind_at ? new Date(data.remind_at) : null,
12526
+ timeLeftMs: data.remind_at ? timeLeftMs(new Date(data.remind_at).getTime()) : null,
12527
+ updated_at: new Date(data.updated_at),
12528
+ user: data.user || null
12529
+ });
12530
+ var Reminder = _Reminder;
12531
+
12532
+ // src/pagination/BasePaginator.ts
12533
+ var DEFAULT_PAGINATION_OPTIONS = {
12534
+ debounceMs: 300,
12535
+ pageSize: 10
12536
+ };
12537
+ var BasePaginator = class {
12538
+ constructor(options) {
12539
+ this._isCursorPagination = false;
12540
+ this.setDebounceOptions = ({ debounceMs }) => {
12541
+ this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
12542
+ };
12543
+ this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
12544
+ this.next = () => this.executeQuery({ direction: "next" });
12545
+ this.prev = () => this.executeQuery({ direction: "prev" });
12546
+ this.nextDebounced = () => {
12547
+ this._executeQueryDebounced({ direction: "next" });
12548
+ };
12549
+ this.prevDebounced = () => {
12550
+ this._executeQueryDebounced({ direction: "prev" });
12551
+ };
12552
+ const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
12553
+ this.pageSize = pageSize;
12554
+ this.state = new StateStore(this.initialState);
12555
+ this.setDebounceOptions({ debounceMs });
12556
+ }
12557
+ get lastQueryError() {
12558
+ return this.state.getLatestValue().lastQueryError;
12559
+ }
12560
+ get hasNext() {
12561
+ return this.state.getLatestValue().hasNext;
12562
+ }
12563
+ get hasPrev() {
12564
+ return this.state.getLatestValue().hasPrev;
12565
+ }
12566
+ get hasResults() {
12567
+ return Array.isArray(this.state.getLatestValue().items);
12568
+ }
12569
+ get isLoading() {
12570
+ return this.state.getLatestValue().isLoading;
12571
+ }
12572
+ get initialState() {
12573
+ return {
12574
+ hasNext: true,
12575
+ hasPrev: true,
12576
+ //todo: check if optimistic value does not cause problems in UI
12577
+ isLoading: false,
12578
+ items: void 0,
12579
+ lastQueryError: void 0,
12580
+ cursor: void 0,
12581
+ offset: 0
12582
+ };
12583
+ }
12584
+ get items() {
12585
+ return this.state.getLatestValue().items;
12586
+ }
12587
+ get cursor() {
12588
+ return this.state.getLatestValue().cursor;
12589
+ }
12590
+ get offset() {
12591
+ return this.state.getLatestValue().offset;
12592
+ }
12593
+ getStateBeforeFirstQuery() {
12594
+ return {
12595
+ ...this.initialState,
12596
+ isLoading: true
12597
+ };
12598
+ }
12599
+ getStateAfterQuery(stateUpdate, isFirstPage) {
12600
+ const current = this.state.getLatestValue();
12601
+ return {
12602
+ ...current,
12603
+ lastQueryError: void 0,
12604
+ // reset lastQueryError that can be overridden by the stateUpdate
12605
+ ...stateUpdate,
12606
+ isLoading: false,
12607
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
12608
+ };
12609
+ }
12610
+ async executeQuery({ direction }) {
12611
+ if (!this.canExecuteQuery(direction)) return;
12612
+ const isFirstPage = typeof this.items === "undefined";
12613
+ if (isFirstPage) {
12614
+ this.state.next(this.getStateBeforeFirstQuery());
12615
+ } else {
12616
+ this.state.partialNext({ isLoading: true });
12617
+ }
12618
+ const stateUpdate = {};
12619
+ try {
12620
+ const results = await this.query({ direction });
12621
+ if (!results) return;
12622
+ const { items, next, prev } = results;
12623
+ if (isFirstPage && (next || prev)) {
12624
+ this._isCursorPagination = true;
12625
+ }
12626
+ if (this._isCursorPagination) {
12627
+ stateUpdate.cursor = { next: next || null, prev: prev || null };
12628
+ stateUpdate.hasNext = !!next;
12629
+ stateUpdate.hasPrev = !!prev;
12630
+ } else {
12631
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
12632
+ stateUpdate.hasNext = items.length === this.pageSize;
12633
+ }
12634
+ stateUpdate.items = await this.filterQueryResults(items);
12635
+ } catch (e) {
12636
+ stateUpdate.lastQueryError = e;
12637
+ } finally {
12638
+ this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
12639
+ }
12640
+ }
12641
+ cancelScheduledQuery() {
12642
+ this._executeQueryDebounced.cancel();
12643
+ }
12644
+ resetState() {
12645
+ this.state.next(this.initialState);
12646
+ }
12647
+ };
12648
+
12649
+ // src/pagination/ReminderPaginator.ts
12650
+ var ReminderPaginator = class extends BasePaginator {
12651
+ constructor(client, options) {
12652
+ super(options);
12653
+ this.query = async ({
12654
+ direction
12655
+ }) => {
12656
+ const cursor = this.cursor?.[direction];
12657
+ const {
12658
+ reminders: items,
12659
+ next,
12660
+ prev
12661
+ } = await this.client.queryReminders({
12662
+ filter: this.filters,
12663
+ sort: this.sort,
12664
+ limit: this.pageSize,
12665
+ [direction]: cursor
12666
+ });
12667
+ return { items, next, prev };
12668
+ };
12669
+ this.filterQueryResults = (items) => items;
12670
+ this.client = client;
12671
+ }
12672
+ };
12673
+
12674
+ // src/reminders/ReminderManager.ts
12675
+ var oneMinute2 = 60 * 1e3;
12676
+ var oneHour2 = 60 * oneMinute2;
12677
+ var oneDay2 = 24 * oneHour2;
12678
+ var DEFAULT_REMINDER_MANAGER_CONFIG = {
12679
+ scheduledOffsetsMs: [
12680
+ 2 * oneMinute2,
12681
+ 30 * oneMinute2,
12682
+ oneHour2,
12683
+ 2 * oneHour2,
12684
+ 8 * oneHour2,
12685
+ oneDay2
12686
+ ]
12687
+ };
12688
+ var isReminderExistsError = (error) => error.message.match("already has reminder created for this message_id");
12689
+ var isReminderDoesNotExistError = (error) => error.message.match("reminder does not exist");
12690
+ var _ReminderManager = class _ReminderManager extends WithSubscriptions {
12691
+ constructor({ client, config }) {
12692
+ super();
12693
+ this.upsertToState = ({
12694
+ data,
12695
+ overwrite = true
12696
+ }) => {
12697
+ if (!this.client._cacheEnabled()) {
12698
+ return;
12699
+ }
12700
+ const cachedReminder = this.getFromState(data.message_id);
12701
+ if (!cachedReminder) {
12702
+ const reminder = new Reminder({
12703
+ data,
12704
+ config: { stopRefreshBoundaryMs: this.stopTimerRefreshBoundaryMs }
12705
+ });
12706
+ this.state.partialNext({
12707
+ reminders: new Map(this.reminders.set(data.message_id, reminder))
12708
+ });
12709
+ } else if (overwrite) {
12710
+ cachedReminder.setState(data);
12711
+ }
12712
+ return cachedReminder;
12713
+ };
12714
+ this.removeFromState = (messageId) => {
12715
+ const cachedReminder = this.getFromState(messageId);
12716
+ if (!cachedReminder) return;
12717
+ cachedReminder.clearTimer();
12718
+ const reminders = this.reminders;
12719
+ reminders.delete(messageId);
12720
+ this.state.partialNext({ reminders: new Map(reminders) });
12721
+ };
12722
+ this.hydrateState = (messages) => {
12723
+ messages.forEach(({ reminder }) => {
12724
+ if (reminder) {
12725
+ this.upsertToState({ data: reminder });
12726
+ }
12727
+ });
12728
+ };
12729
+ // State API END //
12730
+ // Timers API START //
12731
+ this.initTimers = () => {
12732
+ this.reminders.forEach((reminder) => reminder.initTimer());
12733
+ };
12734
+ this.clearTimers = () => {
12735
+ this.reminders.forEach((reminder) => reminder.clearTimer());
12736
+ };
12737
+ this.registerSubscriptions = () => {
12738
+ if (this.hasSubscriptions) return;
12739
+ this.addUnsubscribeFunction(this.subscribeReminderCreated());
12740
+ this.addUnsubscribeFunction(this.subscribeReminderUpdated());
12741
+ this.addUnsubscribeFunction(this.subscribeReminderDeleted());
12742
+ this.addUnsubscribeFunction(this.subscribeNotificationReminderDue());
12743
+ this.addUnsubscribeFunction(this.subscribeMessageDeleted());
12744
+ this.addUnsubscribeFunction(this.subscribeMessageUndeleted());
12745
+ this.addUnsubscribeFunction(this.subscribePaginatorStateUpdated());
12746
+ this.addUnsubscribeFunction(this.subscribeConfigStateUpdated());
12747
+ };
12748
+ this.subscribeReminderCreated = () => this.client.on("reminder.created", (event) => {
12749
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
12750
+ const { reminder } = event;
12751
+ this.upsertToState({ data: reminder });
12752
+ }).unsubscribe;
12753
+ this.subscribeReminderUpdated = () => this.client.on("reminder.updated", (event) => {
12754
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
12755
+ const { reminder } = event;
12756
+ this.upsertToState({ data: reminder });
12757
+ }).unsubscribe;
12758
+ this.subscribeReminderDeleted = () => this.client.on("reminder.deleted", (event) => {
12759
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
12760
+ this.removeFromState(event.message_id);
12761
+ }).unsubscribe;
12762
+ this.subscribeMessageDeleted = () => this.client.on("message.deleted", (event) => {
12763
+ if (!event.message?.id) return;
12764
+ this.removeFromState(event.message.id);
12765
+ }).unsubscribe;
12766
+ this.subscribeMessageUndeleted = () => this.client.on("message.undeleted", (event) => {
12767
+ if (!event.message?.reminder) return;
12768
+ this.upsertToState({ data: event.message.reminder });
12769
+ }).unsubscribe;
12770
+ this.subscribeNotificationReminderDue = () => this.client.on("notification.reminder_due", () => null).unsubscribe;
12771
+ // todo: what should be performed on this event?
12772
+ this.subscribePaginatorStateUpdated = () => this.paginator.state.subscribeWithSelector(
12773
+ ({ items }) => [items],
12774
+ ([items]) => {
12775
+ if (!items) return;
12776
+ for (const reminder of items) {
12777
+ this.upsertToState({ data: reminder });
12778
+ }
12779
+ }
12780
+ );
12781
+ this.subscribeConfigStateUpdated = () => this.configState.subscribeWithSelector(
12782
+ ({ stopTimerRefreshBoundaryMs }) => ({ stopTimerRefreshBoundaryMs }),
12783
+ ({ stopTimerRefreshBoundaryMs }, previousValue) => {
12784
+ if (typeof stopTimerRefreshBoundaryMs === "number" && stopTimerRefreshBoundaryMs !== previousValue?.stopTimerRefreshBoundaryMs) {
12785
+ this.reminders.forEach((reminder) => {
12786
+ if (reminder.timer) {
12787
+ reminder.timer.stopRefreshBoundaryMs = stopTimerRefreshBoundaryMs;
12788
+ }
12789
+ });
12790
+ }
12791
+ }
12792
+ );
12793
+ // WS event handling END //
12794
+ // API calls START //
12795
+ this.upsertReminder = async (options) => {
12796
+ const { messageId } = options;
12797
+ if (this.getFromState(messageId)) {
12798
+ try {
12799
+ return await this.updateReminder(options);
12800
+ } catch (error) {
12801
+ if (isReminderDoesNotExistError(error)) {
12802
+ return await this.createReminder(options);
12803
+ }
12804
+ throw error;
12805
+ }
12806
+ } else {
12807
+ try {
12808
+ return await this.createReminder(options);
12809
+ } catch (error) {
12810
+ if (isReminderExistsError(error)) {
12811
+ return await this.updateReminder(options);
12812
+ }
12813
+ throw error;
12814
+ }
12815
+ }
12816
+ };
12817
+ this.createReminder = async (options) => {
12818
+ const { reminder } = await this.client.createReminder(options);
12819
+ return this.upsertToState({ data: reminder, overwrite: false });
12820
+ };
12821
+ this.updateReminder = async (options) => {
12822
+ const { reminder } = await this.client.updateReminder(options);
12823
+ return this.upsertToState({ data: reminder });
12824
+ };
12825
+ this.deleteReminder = async (messageId) => {
12826
+ await this.client.deleteReminder(messageId);
12827
+ this.removeFromState(messageId);
12828
+ };
12829
+ this.queryNextReminders = async () => {
12830
+ await this.paginator.next();
12831
+ };
12832
+ this.queryPreviousReminders = async () => {
12833
+ await this.paginator.prev();
12834
+ };
12835
+ this.client = client;
12836
+ this.configState = new StateStore({
12837
+ scheduledOffsetsMs: config?.scheduledOffsetsMs ?? DEFAULT_REMINDER_MANAGER_CONFIG.scheduledOffsetsMs
12838
+ });
12839
+ this.state = new StateStore({ reminders: /* @__PURE__ */ new Map() });
12840
+ this.paginator = new ReminderPaginator(client);
12841
+ }
12842
+ // Config API START //
12843
+ updateConfig(config) {
12844
+ this.configState.partialNext(config);
12845
+ }
12846
+ get stopTimerRefreshBoundaryMs() {
12847
+ return this.configState.getLatestValue().stopTimerRefreshBoundaryMs;
12848
+ }
12849
+ get scheduledOffsetsMs() {
12850
+ return this.configState.getLatestValue().scheduledOffsetsMs;
12851
+ }
12852
+ // Config API END //
12853
+ // State API START //
12854
+ get reminders() {
12855
+ return this.state.getLatestValue().reminders;
12856
+ }
12857
+ getFromState(messageId) {
12858
+ return this.reminders.get(messageId);
12859
+ }
12860
+ // API calls END //
12861
+ };
12862
+ // Timers API END //
12863
+ // WS event handling START //
12864
+ _ReminderManager.isReminderWsEventPayload = (event) => !!event.reminder && (event.type.startsWith("reminder.") || event.type === "notification.reminder_due");
12865
+ var ReminderManager = _ReminderManager;
12866
+
12395
12867
  // src/client.ts
12396
12868
  function isString3(x) {
12397
12869
  return typeof x === "string" || x instanceof String;
@@ -12407,6 +12879,9 @@ var StreamChat = class _StreamChat {
12407
12879
  });
12408
12880
  this._getConnectionID = () => this.wsConnection?.connectionID || this.wsFallback?.connectionID;
12409
12881
  this._hasConnectionID = () => Boolean(this._getConnectionID());
12882
+ this.setMessageComposerSetupFunction = (setupFunction) => {
12883
+ this._messageComposerSetupState.partialNext({ setupFunction });
12884
+ };
12410
12885
  /**
12411
12886
  * connectUser - Set the current user and open a WebSocket connection
12412
12887
  *
@@ -12975,9 +13450,6 @@ var StreamChat = class _StreamChat {
12975
13450
  device: this.options.device,
12976
13451
  client_request_id
12977
13452
  });
12978
- this.setMessageComposerSetupFunction = (setupFunction) => {
12979
- this._messageComposerSetupState.partialNext({ setupFunction });
12980
- };
12981
13453
  this.key = key;
12982
13454
  this.listeners = {};
12983
13455
  this.state = new ClientState({ client: this });
@@ -13032,6 +13504,7 @@ var StreamChat = class _StreamChat {
13032
13504
  this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
13033
13505
  this.threads = new ThreadManager({ client: this });
13034
13506
  this.polls = new PollManager({ client: this });
13507
+ this.reminders = new ReminderManager({ client: this });
13035
13508
  }
13036
13509
  static getInstance(key, secretOrOptions, options) {
13037
13510
  if (!_StreamChat._instance) {
@@ -13167,15 +13640,15 @@ var StreamChat = class _StreamChat {
13167
13640
  * @param {string} userID User ID. If user has no devices, it will error
13168
13641
  * @param {TestPushDataInput} [data] Overrides for push templates/message used
13169
13642
  * IE: {
13170
- messageID: 'id-of-message', // will error if message does not exist
13171
- apnTemplate: '{}', // if app doesn't have apn configured it will error
13172
- firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
13173
- firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
13174
- skipDevices: true, // skip config/device checks and sending to real devices
13175
- pushProviderName: 'staging' // one of your configured push providers
13176
- pushProviderType: 'apn' // one of supported provider types
13177
- }
13178
- */
13643
+ messageID: 'id-of-message', // will error if message does not exist
13644
+ apnTemplate: '{}', // if app doesn't have apn configured it will error
13645
+ firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
13646
+ firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
13647
+ skipDevices: true, // skip config/device checks and sending to real devices
13648
+ pushProviderName: 'staging' // one of your configured push providers
13649
+ pushProviderType: 'apn' // one of supported provider types
13650
+ }
13651
+ */
13179
13652
  async testPushSettings(userID, data = {}) {
13180
13653
  return await this.post(this.baseURL + "/check_push", {
13181
13654
  user_id: userID,
@@ -13193,10 +13666,10 @@ var StreamChat = class _StreamChat {
13193
13666
  *
13194
13667
  * @param {TestSQSDataInput} [data] Overrides SQS settings for testing if needed
13195
13668
  * IE: {
13196
- sqs_key: 'auth_key',
13197
- sqs_secret: 'auth_secret',
13198
- sqs_url: 'url_to_queue',
13199
- }
13669
+ sqs_key: 'auth_key',
13670
+ sqs_secret: 'auth_secret',
13671
+ sqs_url: 'url_to_queue',
13672
+ }
13200
13673
  */
13201
13674
  async testSQSSettings(data = {}) {
13202
13675
  return await this.post(this.baseURL + "/check_sqs", data);
@@ -13206,10 +13679,10 @@ var StreamChat = class _StreamChat {
13206
13679
  *
13207
13680
  * @param {TestSNSDataInput} [data] Overrides SNS settings for testing if needed
13208
13681
  * IE: {
13209
- sns_key: 'auth_key',
13210
- sns_secret: 'auth_secret',
13211
- sns_topic_arn: 'topic_to_publish_to',
13212
- }
13682
+ sns_key: 'auth_key',
13683
+ sns_secret: 'auth_secret',
13684
+ sns_topic_arn: 'topic_to_publish_to',
13685
+ }
13213
13686
  */
13214
13687
  async testSNSSettings(data = {}) {
13215
13688
  return await this.post(this.baseURL + "/check_sns", data);
@@ -13690,6 +14163,7 @@ var StreamChat = class _StreamChat {
13690
14163
  })
13691
14164
  };
13692
14165
  this.polls.hydratePollCache(channelState.messages, true);
14166
+ this.reminders.hydrateState(channelState.messages);
13693
14167
  }
13694
14168
  if (channelState.draft) {
13695
14169
  c.messageComposer.initState({ composition: channelState.draft });
@@ -14610,7 +15084,7 @@ var StreamChat = class _StreamChat {
14610
15084
  if (this.userAgent) {
14611
15085
  return this.userAgent;
14612
15086
  }
14613
- const version = "9.4.0";
15087
+ const version = "9.5.0";
14614
15088
  const clientBundle = "browser-cjs";
14615
15089
  let userAgentString = "";
14616
15090
  if (this.sdkIdentifier) {
@@ -15649,6 +16123,56 @@ var StreamChat = class _StreamChat {
15649
16123
  };
15650
16124
  return await this.post(this.baseURL + "/drafts/query", payload);
15651
16125
  }
16126
+ /**
16127
+ * createReminder - Creates a reminder for a message
16128
+ *
16129
+ * @param {CreateReminderOptions} options The options for creating the reminder
16130
+ * @returns {Promise<ReminderAPIResponse>}
16131
+ */
16132
+ async createReminder({ messageId, ...options }) {
16133
+ return await this.post(
16134
+ `${this.baseURL}/messages/${messageId}/reminders`,
16135
+ options
16136
+ );
16137
+ }
16138
+ /**
16139
+ * updateReminder - Updates an existing reminder for a message
16140
+ *
16141
+ * @param {UpdateReminderOptions} options The options for updating the reminder
16142
+ * @returns {Promise<ReminderAPIResponse>}
16143
+ */
16144
+ async updateReminder({ messageId, ...options }) {
16145
+ return await this.patch(
16146
+ `${this.baseURL}/messages/${messageId}/reminders`,
16147
+ options
16148
+ );
16149
+ }
16150
+ /**
16151
+ * deleteReminder - Deletes a reminder for a message
16152
+ *
16153
+ * @param {string} messageId The ID of the message whose reminder to delete
16154
+ * @param {string} [userId] Optional user ID, required for server-side operations
16155
+ * @returns {Promise<APIResponse>}
16156
+ */
16157
+ async deleteReminder(messageId, userId) {
16158
+ return await this.delete(
16159
+ `${this.baseURL}/messages/${messageId}/reminders`,
16160
+ userId ? { user_id: userId } : {}
16161
+ );
16162
+ }
16163
+ /**
16164
+ * queryReminders - Queries reminders based on given filters
16165
+ *
16166
+ * @param {QueryRemindersOptions} options The options for querying reminders
16167
+ * @returns {Promise<QueryRemindersResponse>}
16168
+ */
16169
+ async queryReminders({ filter: filter2, sort, ...rest } = {}) {
16170
+ return await this.post(`${this.baseURL}/reminders/query`, {
16171
+ filter_conditions: filter2,
16172
+ sort: sort && normalizeQuerySort(sort),
16173
+ ...rest
16174
+ });
16175
+ }
15652
16176
  };
15653
16177
 
15654
16178
  // src/events.ts
@@ -15715,7 +16239,12 @@ var EVENT_MAP = {
15715
16239
  "connection.changed": true,
15716
16240
  "connection.recovered": true,
15717
16241
  "transport.changed": true,
15718
- "capabilities.changed": true
16242
+ "capabilities.changed": true,
16243
+ // Reminder events
16244
+ "reminder.created": true,
16245
+ "reminder.updated": true,
16246
+ "reminder.deleted": true,
16247
+ "notification.reminder_due": true
15719
16248
  };
15720
16249
 
15721
16250
  // src/permissions.ts