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
package/dist/esm/index.js CHANGED
@@ -7221,7 +7221,7 @@ var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
7221
7221
  }
7222
7222
  };
7223
7223
 
7224
- // src/search_controller.ts
7224
+ // src/search/BaseSearchSource.ts
7225
7225
  var DEFAULT_SEARCH_SOURCE_OPTIONS = {
7226
7226
  debounceMs: 300,
7227
7227
  pageSize: 10
@@ -7347,6 +7347,112 @@ var BaseSearchSource = class {
7347
7347
  this.activate();
7348
7348
  }
7349
7349
  };
7350
+
7351
+ // src/search/SearchController.ts
7352
+ var SearchController = class {
7353
+ constructor({ config, sources } = {}) {
7354
+ this.addSource = (source) => {
7355
+ this.state.partialNext({
7356
+ sources: [...this.sources, source]
7357
+ });
7358
+ };
7359
+ this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
7360
+ this.removeSource = (sourceType) => {
7361
+ const newSources = this.sources.filter((s) => s.type !== sourceType);
7362
+ if (newSources.length === this.sources.length) return;
7363
+ this.state.partialNext({ sources: newSources });
7364
+ };
7365
+ this.activateSource = (sourceType) => {
7366
+ const source = this.getSource(sourceType);
7367
+ if (!source || source.isActive) return;
7368
+ if (this.config.keepSingleActiveSource) {
7369
+ this.sources.forEach((s) => {
7370
+ if (s.type !== sourceType) {
7371
+ s.deactivate();
7372
+ }
7373
+ });
7374
+ }
7375
+ source.activate();
7376
+ this.state.partialNext({ sources: [...this.sources] });
7377
+ };
7378
+ this.deactivateSource = (sourceType) => {
7379
+ const source = this.getSource(sourceType);
7380
+ if (!source?.isActive) return;
7381
+ if (this.activeSources.length === 1) return;
7382
+ source.deactivate();
7383
+ this.state.partialNext({ sources: [...this.sources] });
7384
+ };
7385
+ this.activate = () => {
7386
+ if (!this.activeSources.length) {
7387
+ const sourcesToActivate = this.config.keepSingleActiveSource ? this.sources.slice(0, 1) : this.sources;
7388
+ sourcesToActivate.forEach((s) => s.activate());
7389
+ }
7390
+ if (this.isActive) return;
7391
+ this.state.partialNext({ isActive: true });
7392
+ };
7393
+ this.search = async (searchQuery) => {
7394
+ const searchedSources = this.activeSources;
7395
+ this.state.partialNext({
7396
+ searchQuery
7397
+ });
7398
+ await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
7399
+ };
7400
+ this.cancelSearchQueries = () => {
7401
+ this.activeSources.forEach((s) => s.cancelScheduledQuery());
7402
+ };
7403
+ this.clear = () => {
7404
+ this.cancelSearchQueries();
7405
+ this.sources.forEach(
7406
+ (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
7407
+ );
7408
+ this.state.next((current) => ({
7409
+ ...current,
7410
+ isActive: true,
7411
+ queriesInProgress: [],
7412
+ searchQuery: ""
7413
+ }));
7414
+ };
7415
+ this.exit = () => {
7416
+ this.cancelSearchQueries();
7417
+ this.sources.forEach(
7418
+ (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
7419
+ );
7420
+ this.state.next((current) => ({
7421
+ ...current,
7422
+ isActive: false,
7423
+ queriesInProgress: [],
7424
+ searchQuery: ""
7425
+ }));
7426
+ };
7427
+ this.state = new StateStore({
7428
+ isActive: false,
7429
+ searchQuery: "",
7430
+ sources: sources ?? []
7431
+ });
7432
+ this._internalState = new StateStore({});
7433
+ this.config = { keepSingleActiveSource: true, ...config };
7434
+ }
7435
+ get hasNext() {
7436
+ return this.sources.some((source) => source.hasNext);
7437
+ }
7438
+ get sources() {
7439
+ return this.state.getLatestValue().sources;
7440
+ }
7441
+ get activeSources() {
7442
+ return this.state.getLatestValue().sources.filter((s) => s.isActive);
7443
+ }
7444
+ get isActive() {
7445
+ return this.state.getLatestValue().isActive;
7446
+ }
7447
+ get searchQuery() {
7448
+ return this.state.getLatestValue().searchQuery;
7449
+ }
7450
+ get searchSourceTypes() {
7451
+ return this.sources.map((s) => s.type);
7452
+ }
7453
+ };
7454
+
7455
+ // src/search/UserSearchSource.ts
7350
7456
  var UserSearchSource = class extends BaseSearchSource {
7351
7457
  constructor(client, options) {
7352
7458
  super(options);
@@ -7370,6 +7476,8 @@ var UserSearchSource = class extends BaseSearchSource {
7370
7476
  return items.filter((u) => u.id !== this.client.user?.id);
7371
7477
  }
7372
7478
  };
7479
+
7480
+ // src/search/ChannelSearchSource.ts
7373
7481
  var ChannelSearchSource = class extends BaseSearchSource {
7374
7482
  constructor(client, options) {
7375
7483
  super(options);
@@ -7391,6 +7499,8 @@ var ChannelSearchSource = class extends BaseSearchSource {
7391
7499
  return items;
7392
7500
  }
7393
7501
  };
7502
+
7503
+ // src/search/MessageSearchSource.ts
7394
7504
  var MessageSearchSource = class extends BaseSearchSource {
7395
7505
  constructor(client, options) {
7396
7506
  super(options);
@@ -7451,108 +7561,6 @@ var MessageSearchSource = class extends BaseSearchSource {
7451
7561
  return items;
7452
7562
  }
7453
7563
  };
7454
- var SearchController = class {
7455
- constructor({ config, sources } = {}) {
7456
- this.addSource = (source) => {
7457
- this.state.partialNext({
7458
- sources: [...this.sources, source]
7459
- });
7460
- };
7461
- this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
7462
- this.removeSource = (sourceType) => {
7463
- const newSources = this.sources.filter((s) => s.type !== sourceType);
7464
- if (newSources.length === this.sources.length) return;
7465
- this.state.partialNext({ sources: newSources });
7466
- };
7467
- this.activateSource = (sourceType) => {
7468
- const source = this.getSource(sourceType);
7469
- if (!source || source.isActive) return;
7470
- if (this.config.keepSingleActiveSource) {
7471
- this.sources.forEach((s) => {
7472
- if (s.type !== sourceType) {
7473
- s.deactivate();
7474
- }
7475
- });
7476
- }
7477
- source.activate();
7478
- this.state.partialNext({ sources: [...this.sources] });
7479
- };
7480
- this.deactivateSource = (sourceType) => {
7481
- const source = this.getSource(sourceType);
7482
- if (!source?.isActive) return;
7483
- if (this.activeSources.length === 1) return;
7484
- source.deactivate();
7485
- this.state.partialNext({ sources: [...this.sources] });
7486
- };
7487
- this.activate = () => {
7488
- if (!this.activeSources.length) {
7489
- const sourcesToActivate = this.config.keepSingleActiveSource ? this.sources.slice(0, 1) : this.sources;
7490
- sourcesToActivate.forEach((s) => s.activate());
7491
- }
7492
- if (this.isActive) return;
7493
- this.state.partialNext({ isActive: true });
7494
- };
7495
- this.search = async (searchQuery) => {
7496
- const searchedSources = this.activeSources;
7497
- this.state.partialNext({
7498
- searchQuery
7499
- });
7500
- await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
7501
- };
7502
- this.cancelSearchQueries = () => {
7503
- this.activeSources.forEach((s) => s.cancelScheduledQuery());
7504
- };
7505
- this.clear = () => {
7506
- this.cancelSearchQueries();
7507
- this.sources.forEach(
7508
- (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
7509
- );
7510
- this.state.next((current) => ({
7511
- ...current,
7512
- isActive: true,
7513
- queriesInProgress: [],
7514
- searchQuery: ""
7515
- }));
7516
- };
7517
- this.exit = () => {
7518
- this.cancelSearchQueries();
7519
- this.sources.forEach(
7520
- (source) => source.state.next({ ...source.initialState, isActive: source.isActive })
7521
- );
7522
- this.state.next((current) => ({
7523
- ...current,
7524
- isActive: false,
7525
- queriesInProgress: [],
7526
- searchQuery: ""
7527
- }));
7528
- };
7529
- this.state = new StateStore({
7530
- isActive: false,
7531
- searchQuery: "",
7532
- sources: sources ?? []
7533
- });
7534
- this._internalState = new StateStore({});
7535
- this.config = { keepSingleActiveSource: true, ...config };
7536
- }
7537
- get hasNext() {
7538
- return this.sources.some((source) => source.hasNext);
7539
- }
7540
- get sources() {
7541
- return this.state.getLatestValue().sources;
7542
- }
7543
- get activeSources() {
7544
- return this.state.getLatestValue().sources.filter((s) => s.isActive);
7545
- }
7546
- get isActive() {
7547
- return this.state.getLatestValue().isActive;
7548
- }
7549
- get searchQuery() {
7550
- return this.state.getLatestValue().searchQuery;
7551
- }
7552
- get searchSourceTypes() {
7553
- return this.sources.map((s) => s.type);
7554
- }
7555
- };
7556
7564
 
7557
7565
  // src/messageComposer/middleware/textComposer/textMiddlewareUtils.ts
7558
7566
  var getTriggerCharWithToken = ({
@@ -10331,6 +10339,7 @@ var Channel = class {
10331
10339
  })
10332
10340
  };
10333
10341
  this.getClient().polls.hydratePollCache(state.messages, true);
10342
+ this.getClient().reminders.hydrateState(state.messages);
10334
10343
  if (state.draft) {
10335
10344
  this.messageComposer.initState({ composition: state.draft });
10336
10345
  }
@@ -11622,7 +11631,9 @@ var TokenManager = class {
11622
11631
  try {
11623
11632
  this.token = await this.tokenProvider();
11624
11633
  } catch (e) {
11625
- return reject(new Error(`Call to tokenProvider failed with message: ${e}`));
11634
+ return reject(
11635
+ new Error(`Call to tokenProvider failed with message: ${e}`, { cause: e })
11636
+ );
11626
11637
  }
11627
11638
  resolve(this.token);
11628
11639
  }
@@ -13372,6 +13383,458 @@ var NotificationManager = class {
13372
13383
  }
13373
13384
  };
13374
13385
 
13386
+ // src/reminders/ReminderTimer.ts
13387
+ var oneMinute = 60 * 1e3;
13388
+ var oneHour = 60 * oneMinute;
13389
+ var oneDay = 24 * oneHour;
13390
+ var oneWeek = 7 * oneDay;
13391
+ var GROUP_BOUNDS = {
13392
+ minute: { lower: oneMinute, upper: oneHour },
13393
+ hour: { lower: oneHour, upper: oneDay },
13394
+ day: { lower: oneDay, upper: oneWeek }
13395
+ };
13396
+ var DEFAULT_STOP_REFRESH_BOUNDARY_MS = 2 * oneWeek;
13397
+ var ReminderTimer = class {
13398
+ constructor({
13399
+ reminder,
13400
+ config
13401
+ }) {
13402
+ this.timeout = null;
13403
+ this.stopRefreshBoundaryMs = DEFAULT_STOP_REFRESH_BOUNDARY_MS;
13404
+ this.getRefreshIntervalLength = () => {
13405
+ if (!this.reminder.remindAt) return null;
13406
+ const distanceFromDeadlineMs = Math.abs(timeLeftMs(this.reminder.remindAt.getTime()));
13407
+ let refreshInterval;
13408
+ if (distanceFromDeadlineMs === 0) {
13409
+ refreshInterval = oneMinute;
13410
+ } else if (distanceFromDeadlineMs < GROUP_BOUNDS.minute.lower) {
13411
+ refreshInterval = distanceFromDeadlineMs;
13412
+ } else if (distanceFromDeadlineMs <= GROUP_BOUNDS.minute.upper) {
13413
+ refreshInterval = oneMinute;
13414
+ } else if (distanceFromDeadlineMs <= GROUP_BOUNDS.hour.upper) {
13415
+ refreshInterval = oneHour;
13416
+ } else {
13417
+ refreshInterval = oneDay;
13418
+ }
13419
+ return refreshInterval;
13420
+ };
13421
+ this.init = () => {
13422
+ if (!this.reminder.remindAt) return null;
13423
+ const timeoutLength = this.getRefreshIntervalLength();
13424
+ if (timeoutLength === null) return null;
13425
+ const boundaryTimestamp = this.reminder.remindAt?.getTime() + this.stopRefreshBoundaryMs;
13426
+ const timeLeftToBoundary = boundaryTimestamp - Date.now();
13427
+ if (timeLeftToBoundary <= 0) {
13428
+ this.timeout = null;
13429
+ return;
13430
+ }
13431
+ if (this.timeout) clearTimeout(this.timeout);
13432
+ this.timeout = setTimeout(() => {
13433
+ this.reminder.refreshTimeLeft();
13434
+ this.init();
13435
+ }, timeoutLength);
13436
+ };
13437
+ this.clear = () => {
13438
+ if (this.timeout) {
13439
+ clearInterval(this.timeout);
13440
+ this.timeout = null;
13441
+ }
13442
+ };
13443
+ this.reminder = reminder;
13444
+ if (typeof config?.stopRefreshBoundaryMs === "number") {
13445
+ this.stopRefreshBoundaryMs = config.stopRefreshBoundaryMs;
13446
+ }
13447
+ }
13448
+ };
13449
+
13450
+ // src/reminders/Reminder.ts
13451
+ var timeLeftMs = (remindAt) => remindAt - (/* @__PURE__ */ new Date()).getTime();
13452
+ var _Reminder = class _Reminder {
13453
+ constructor({ data, config }) {
13454
+ this.setState = (data) => {
13455
+ this.state.next((current) => {
13456
+ const newState = { ...current, ..._Reminder.toStateValue(data) };
13457
+ if (newState.remind_at) {
13458
+ newState.timeLeftMs = timeLeftMs(newState.remind_at.getTime());
13459
+ }
13460
+ return newState;
13461
+ });
13462
+ if (data.remind_at) {
13463
+ this.initTimer();
13464
+ } else if (!data.remind_at) {
13465
+ this.clearTimer();
13466
+ }
13467
+ };
13468
+ this.refreshTimeLeft = () => {
13469
+ if (!this.remindAt) return;
13470
+ this.state.partialNext({ timeLeftMs: timeLeftMs(this.remindAt.getTime()) });
13471
+ };
13472
+ this.initTimer = () => {
13473
+ this.timer.init();
13474
+ };
13475
+ this.clearTimer = () => {
13476
+ this.timer.clear();
13477
+ };
13478
+ this.state = new StateStore(_Reminder.toStateValue(data));
13479
+ this.timer = new ReminderTimer({ reminder: this, config });
13480
+ this.initTimer();
13481
+ }
13482
+ get id() {
13483
+ return this.state.getLatestValue().message_id;
13484
+ }
13485
+ get remindAt() {
13486
+ return this.state.getLatestValue().remind_at;
13487
+ }
13488
+ get timeLeftMs() {
13489
+ return this.state.getLatestValue().timeLeftMs;
13490
+ }
13491
+ };
13492
+ _Reminder.toStateValue = (data) => ({
13493
+ ...data,
13494
+ created_at: new Date(data.created_at),
13495
+ message: data.message || null,
13496
+ remind_at: data.remind_at ? new Date(data.remind_at) : null,
13497
+ timeLeftMs: data.remind_at ? timeLeftMs(new Date(data.remind_at).getTime()) : null,
13498
+ updated_at: new Date(data.updated_at),
13499
+ user: data.user || null
13500
+ });
13501
+ var Reminder = _Reminder;
13502
+
13503
+ // src/pagination/BasePaginator.ts
13504
+ var DEFAULT_PAGINATION_OPTIONS = {
13505
+ debounceMs: 300,
13506
+ pageSize: 10
13507
+ };
13508
+ var BasePaginator = class {
13509
+ constructor(options) {
13510
+ this._isCursorPagination = false;
13511
+ this.setDebounceOptions = ({ debounceMs }) => {
13512
+ this._executeQueryDebounced = debounce(this.executeQuery.bind(this), debounceMs);
13513
+ };
13514
+ this.canExecuteQuery = (direction) => !this.isLoading && direction === "next" && this.hasNext || direction === "prev" && this.hasPrev;
13515
+ this.next = () => this.executeQuery({ direction: "next" });
13516
+ this.prev = () => this.executeQuery({ direction: "prev" });
13517
+ this.nextDebounced = () => {
13518
+ this._executeQueryDebounced({ direction: "next" });
13519
+ };
13520
+ this.prevDebounced = () => {
13521
+ this._executeQueryDebounced({ direction: "prev" });
13522
+ };
13523
+ const { debounceMs, pageSize } = { ...DEFAULT_PAGINATION_OPTIONS, ...options };
13524
+ this.pageSize = pageSize;
13525
+ this.state = new StateStore(this.initialState);
13526
+ this.setDebounceOptions({ debounceMs });
13527
+ }
13528
+ get lastQueryError() {
13529
+ return this.state.getLatestValue().lastQueryError;
13530
+ }
13531
+ get hasNext() {
13532
+ return this.state.getLatestValue().hasNext;
13533
+ }
13534
+ get hasPrev() {
13535
+ return this.state.getLatestValue().hasPrev;
13536
+ }
13537
+ get hasResults() {
13538
+ return Array.isArray(this.state.getLatestValue().items);
13539
+ }
13540
+ get isLoading() {
13541
+ return this.state.getLatestValue().isLoading;
13542
+ }
13543
+ get initialState() {
13544
+ return {
13545
+ hasNext: true,
13546
+ hasPrev: true,
13547
+ //todo: check if optimistic value does not cause problems in UI
13548
+ isLoading: false,
13549
+ items: void 0,
13550
+ lastQueryError: void 0,
13551
+ cursor: void 0,
13552
+ offset: 0
13553
+ };
13554
+ }
13555
+ get items() {
13556
+ return this.state.getLatestValue().items;
13557
+ }
13558
+ get cursor() {
13559
+ return this.state.getLatestValue().cursor;
13560
+ }
13561
+ get offset() {
13562
+ return this.state.getLatestValue().offset;
13563
+ }
13564
+ getStateBeforeFirstQuery() {
13565
+ return {
13566
+ ...this.initialState,
13567
+ isLoading: true
13568
+ };
13569
+ }
13570
+ getStateAfterQuery(stateUpdate, isFirstPage) {
13571
+ const current = this.state.getLatestValue();
13572
+ return {
13573
+ ...current,
13574
+ lastQueryError: void 0,
13575
+ // reset lastQueryError that can be overridden by the stateUpdate
13576
+ ...stateUpdate,
13577
+ isLoading: false,
13578
+ items: isFirstPage ? stateUpdate.items : [...this.items ?? [], ...stateUpdate.items || []]
13579
+ };
13580
+ }
13581
+ async executeQuery({ direction }) {
13582
+ if (!this.canExecuteQuery(direction)) return;
13583
+ const isFirstPage = typeof this.items === "undefined";
13584
+ if (isFirstPage) {
13585
+ this.state.next(this.getStateBeforeFirstQuery());
13586
+ } else {
13587
+ this.state.partialNext({ isLoading: true });
13588
+ }
13589
+ const stateUpdate = {};
13590
+ try {
13591
+ const results = await this.query({ direction });
13592
+ if (!results) return;
13593
+ const { items, next, prev } = results;
13594
+ if (isFirstPage && (next || prev)) {
13595
+ this._isCursorPagination = true;
13596
+ }
13597
+ if (this._isCursorPagination) {
13598
+ stateUpdate.cursor = { next: next || null, prev: prev || null };
13599
+ stateUpdate.hasNext = !!next;
13600
+ stateUpdate.hasPrev = !!prev;
13601
+ } else {
13602
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
13603
+ stateUpdate.hasNext = items.length === this.pageSize;
13604
+ }
13605
+ stateUpdate.items = await this.filterQueryResults(items);
13606
+ } catch (e) {
13607
+ stateUpdate.lastQueryError = e;
13608
+ } finally {
13609
+ this.state.next(this.getStateAfterQuery(stateUpdate, isFirstPage));
13610
+ }
13611
+ }
13612
+ cancelScheduledQuery() {
13613
+ this._executeQueryDebounced.cancel();
13614
+ }
13615
+ resetState() {
13616
+ this.state.next(this.initialState);
13617
+ }
13618
+ };
13619
+
13620
+ // src/pagination/ReminderPaginator.ts
13621
+ var ReminderPaginator = class extends BasePaginator {
13622
+ constructor(client, options) {
13623
+ super(options);
13624
+ this.query = async ({
13625
+ direction
13626
+ }) => {
13627
+ const cursor = this.cursor?.[direction];
13628
+ const {
13629
+ reminders: items,
13630
+ next,
13631
+ prev
13632
+ } = await this.client.queryReminders({
13633
+ filter: this.filters,
13634
+ sort: this.sort,
13635
+ limit: this.pageSize,
13636
+ [direction]: cursor
13637
+ });
13638
+ return { items, next, prev };
13639
+ };
13640
+ this.filterQueryResults = (items) => items;
13641
+ this.client = client;
13642
+ }
13643
+ };
13644
+
13645
+ // src/reminders/ReminderManager.ts
13646
+ var oneMinute2 = 60 * 1e3;
13647
+ var oneHour2 = 60 * oneMinute2;
13648
+ var oneDay2 = 24 * oneHour2;
13649
+ var DEFAULT_REMINDER_MANAGER_CONFIG = {
13650
+ scheduledOffsetsMs: [
13651
+ 2 * oneMinute2,
13652
+ 30 * oneMinute2,
13653
+ oneHour2,
13654
+ 2 * oneHour2,
13655
+ 8 * oneHour2,
13656
+ oneDay2
13657
+ ]
13658
+ };
13659
+ var isReminderExistsError = (error) => error.message.match("already has reminder created for this message_id");
13660
+ var isReminderDoesNotExistError = (error) => error.message.match("reminder does not exist");
13661
+ var _ReminderManager = class _ReminderManager extends WithSubscriptions {
13662
+ constructor({ client, config }) {
13663
+ super();
13664
+ this.upsertToState = ({
13665
+ data,
13666
+ overwrite = true
13667
+ }) => {
13668
+ if (!this.client._cacheEnabled()) {
13669
+ return;
13670
+ }
13671
+ const cachedReminder = this.getFromState(data.message_id);
13672
+ if (!cachedReminder) {
13673
+ const reminder = new Reminder({
13674
+ data,
13675
+ config: { stopRefreshBoundaryMs: this.stopTimerRefreshBoundaryMs }
13676
+ });
13677
+ this.state.partialNext({
13678
+ reminders: new Map(this.reminders.set(data.message_id, reminder))
13679
+ });
13680
+ } else if (overwrite) {
13681
+ cachedReminder.setState(data);
13682
+ }
13683
+ return cachedReminder;
13684
+ };
13685
+ this.removeFromState = (messageId) => {
13686
+ const cachedReminder = this.getFromState(messageId);
13687
+ if (!cachedReminder) return;
13688
+ cachedReminder.clearTimer();
13689
+ const reminders = this.reminders;
13690
+ reminders.delete(messageId);
13691
+ this.state.partialNext({ reminders: new Map(reminders) });
13692
+ };
13693
+ this.hydrateState = (messages) => {
13694
+ messages.forEach(({ reminder }) => {
13695
+ if (reminder) {
13696
+ this.upsertToState({ data: reminder });
13697
+ }
13698
+ });
13699
+ };
13700
+ // State API END //
13701
+ // Timers API START //
13702
+ this.initTimers = () => {
13703
+ this.reminders.forEach((reminder) => reminder.initTimer());
13704
+ };
13705
+ this.clearTimers = () => {
13706
+ this.reminders.forEach((reminder) => reminder.clearTimer());
13707
+ };
13708
+ this.registerSubscriptions = () => {
13709
+ if (this.hasSubscriptions) return;
13710
+ this.addUnsubscribeFunction(this.subscribeReminderCreated());
13711
+ this.addUnsubscribeFunction(this.subscribeReminderUpdated());
13712
+ this.addUnsubscribeFunction(this.subscribeReminderDeleted());
13713
+ this.addUnsubscribeFunction(this.subscribeNotificationReminderDue());
13714
+ this.addUnsubscribeFunction(this.subscribeMessageDeleted());
13715
+ this.addUnsubscribeFunction(this.subscribeMessageUndeleted());
13716
+ this.addUnsubscribeFunction(this.subscribePaginatorStateUpdated());
13717
+ this.addUnsubscribeFunction(this.subscribeConfigStateUpdated());
13718
+ };
13719
+ this.subscribeReminderCreated = () => this.client.on("reminder.created", (event) => {
13720
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
13721
+ const { reminder } = event;
13722
+ this.upsertToState({ data: reminder });
13723
+ }).unsubscribe;
13724
+ this.subscribeReminderUpdated = () => this.client.on("reminder.updated", (event) => {
13725
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
13726
+ const { reminder } = event;
13727
+ this.upsertToState({ data: reminder });
13728
+ }).unsubscribe;
13729
+ this.subscribeReminderDeleted = () => this.client.on("reminder.deleted", (event) => {
13730
+ if (!_ReminderManager.isReminderWsEventPayload(event)) return;
13731
+ this.removeFromState(event.message_id);
13732
+ }).unsubscribe;
13733
+ this.subscribeMessageDeleted = () => this.client.on("message.deleted", (event) => {
13734
+ if (!event.message?.id) return;
13735
+ this.removeFromState(event.message.id);
13736
+ }).unsubscribe;
13737
+ this.subscribeMessageUndeleted = () => this.client.on("message.undeleted", (event) => {
13738
+ if (!event.message?.reminder) return;
13739
+ this.upsertToState({ data: event.message.reminder });
13740
+ }).unsubscribe;
13741
+ this.subscribeNotificationReminderDue = () => this.client.on("notification.reminder_due", () => null).unsubscribe;
13742
+ // todo: what should be performed on this event?
13743
+ this.subscribePaginatorStateUpdated = () => this.paginator.state.subscribeWithSelector(
13744
+ ({ items }) => [items],
13745
+ ([items]) => {
13746
+ if (!items) return;
13747
+ for (const reminder of items) {
13748
+ this.upsertToState({ data: reminder });
13749
+ }
13750
+ }
13751
+ );
13752
+ this.subscribeConfigStateUpdated = () => this.configState.subscribeWithSelector(
13753
+ ({ stopTimerRefreshBoundaryMs }) => ({ stopTimerRefreshBoundaryMs }),
13754
+ ({ stopTimerRefreshBoundaryMs }, previousValue) => {
13755
+ if (typeof stopTimerRefreshBoundaryMs === "number" && stopTimerRefreshBoundaryMs !== previousValue?.stopTimerRefreshBoundaryMs) {
13756
+ this.reminders.forEach((reminder) => {
13757
+ if (reminder.timer) {
13758
+ reminder.timer.stopRefreshBoundaryMs = stopTimerRefreshBoundaryMs;
13759
+ }
13760
+ });
13761
+ }
13762
+ }
13763
+ );
13764
+ // WS event handling END //
13765
+ // API calls START //
13766
+ this.upsertReminder = async (options) => {
13767
+ const { messageId } = options;
13768
+ if (this.getFromState(messageId)) {
13769
+ try {
13770
+ return await this.updateReminder(options);
13771
+ } catch (error) {
13772
+ if (isReminderDoesNotExistError(error)) {
13773
+ return await this.createReminder(options);
13774
+ }
13775
+ throw error;
13776
+ }
13777
+ } else {
13778
+ try {
13779
+ return await this.createReminder(options);
13780
+ } catch (error) {
13781
+ if (isReminderExistsError(error)) {
13782
+ return await this.updateReminder(options);
13783
+ }
13784
+ throw error;
13785
+ }
13786
+ }
13787
+ };
13788
+ this.createReminder = async (options) => {
13789
+ const { reminder } = await this.client.createReminder(options);
13790
+ return this.upsertToState({ data: reminder, overwrite: false });
13791
+ };
13792
+ this.updateReminder = async (options) => {
13793
+ const { reminder } = await this.client.updateReminder(options);
13794
+ return this.upsertToState({ data: reminder });
13795
+ };
13796
+ this.deleteReminder = async (messageId) => {
13797
+ await this.client.deleteReminder(messageId);
13798
+ this.removeFromState(messageId);
13799
+ };
13800
+ this.queryNextReminders = async () => {
13801
+ await this.paginator.next();
13802
+ };
13803
+ this.queryPreviousReminders = async () => {
13804
+ await this.paginator.prev();
13805
+ };
13806
+ this.client = client;
13807
+ this.configState = new StateStore({
13808
+ scheduledOffsetsMs: config?.scheduledOffsetsMs ?? DEFAULT_REMINDER_MANAGER_CONFIG.scheduledOffsetsMs
13809
+ });
13810
+ this.state = new StateStore({ reminders: /* @__PURE__ */ new Map() });
13811
+ this.paginator = new ReminderPaginator(client);
13812
+ }
13813
+ // Config API START //
13814
+ updateConfig(config) {
13815
+ this.configState.partialNext(config);
13816
+ }
13817
+ get stopTimerRefreshBoundaryMs() {
13818
+ return this.configState.getLatestValue().stopTimerRefreshBoundaryMs;
13819
+ }
13820
+ get scheduledOffsetsMs() {
13821
+ return this.configState.getLatestValue().scheduledOffsetsMs;
13822
+ }
13823
+ // Config API END //
13824
+ // State API START //
13825
+ get reminders() {
13826
+ return this.state.getLatestValue().reminders;
13827
+ }
13828
+ getFromState(messageId) {
13829
+ return this.reminders.get(messageId);
13830
+ }
13831
+ // API calls END //
13832
+ };
13833
+ // Timers API END //
13834
+ // WS event handling START //
13835
+ _ReminderManager.isReminderWsEventPayload = (event) => !!event.reminder && (event.type.startsWith("reminder.") || event.type === "notification.reminder_due");
13836
+ var ReminderManager = _ReminderManager;
13837
+
13375
13838
  // src/client.ts
13376
13839
  function isString3(x) {
13377
13840
  return typeof x === "string" || x instanceof String;
@@ -13387,6 +13850,9 @@ var StreamChat = class _StreamChat {
13387
13850
  });
13388
13851
  this._getConnectionID = () => this.wsConnection?.connectionID || this.wsFallback?.connectionID;
13389
13852
  this._hasConnectionID = () => Boolean(this._getConnectionID());
13853
+ this.setMessageComposerSetupFunction = (setupFunction) => {
13854
+ this._messageComposerSetupState.partialNext({ setupFunction });
13855
+ };
13390
13856
  /**
13391
13857
  * connectUser - Set the current user and open a WebSocket connection
13392
13858
  *
@@ -13955,9 +14421,6 @@ var StreamChat = class _StreamChat {
13955
14421
  device: this.options.device,
13956
14422
  client_request_id
13957
14423
  });
13958
- this.setMessageComposerSetupFunction = (setupFunction) => {
13959
- this._messageComposerSetupState.partialNext({ setupFunction });
13960
- };
13961
14424
  this.key = key;
13962
14425
  this.listeners = {};
13963
14426
  this.state = new ClientState({ client: this });
@@ -14012,6 +14475,7 @@ var StreamChat = class _StreamChat {
14012
14475
  this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
14013
14476
  this.threads = new ThreadManager({ client: this });
14014
14477
  this.polls = new PollManager({ client: this });
14478
+ this.reminders = new ReminderManager({ client: this });
14015
14479
  }
14016
14480
  static getInstance(key, secretOrOptions, options) {
14017
14481
  if (!_StreamChat._instance) {
@@ -14147,15 +14611,15 @@ var StreamChat = class _StreamChat {
14147
14611
  * @param {string} userID User ID. If user has no devices, it will error
14148
14612
  * @param {TestPushDataInput} [data] Overrides for push templates/message used
14149
14613
  * IE: {
14150
- messageID: 'id-of-message', // will error if message does not exist
14151
- apnTemplate: '{}', // if app doesn't have apn configured it will error
14152
- firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
14153
- firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
14154
- skipDevices: true, // skip config/device checks and sending to real devices
14155
- pushProviderName: 'staging' // one of your configured push providers
14156
- pushProviderType: 'apn' // one of supported provider types
14157
- }
14158
- */
14614
+ messageID: 'id-of-message', // will error if message does not exist
14615
+ apnTemplate: '{}', // if app doesn't have apn configured it will error
14616
+ firebaseTemplate: '{}', // if app doesn't have firebase configured it will error
14617
+ firebaseDataTemplate: '{}', // if app doesn't have firebase configured it will error
14618
+ skipDevices: true, // skip config/device checks and sending to real devices
14619
+ pushProviderName: 'staging' // one of your configured push providers
14620
+ pushProviderType: 'apn' // one of supported provider types
14621
+ }
14622
+ */
14159
14623
  async testPushSettings(userID, data = {}) {
14160
14624
  return await this.post(this.baseURL + "/check_push", {
14161
14625
  user_id: userID,
@@ -14173,10 +14637,10 @@ var StreamChat = class _StreamChat {
14173
14637
  *
14174
14638
  * @param {TestSQSDataInput} [data] Overrides SQS settings for testing if needed
14175
14639
  * IE: {
14176
- sqs_key: 'auth_key',
14177
- sqs_secret: 'auth_secret',
14178
- sqs_url: 'url_to_queue',
14179
- }
14640
+ sqs_key: 'auth_key',
14641
+ sqs_secret: 'auth_secret',
14642
+ sqs_url: 'url_to_queue',
14643
+ }
14180
14644
  */
14181
14645
  async testSQSSettings(data = {}) {
14182
14646
  return await this.post(this.baseURL + "/check_sqs", data);
@@ -14186,10 +14650,10 @@ var StreamChat = class _StreamChat {
14186
14650
  *
14187
14651
  * @param {TestSNSDataInput} [data] Overrides SNS settings for testing if needed
14188
14652
  * IE: {
14189
- sns_key: 'auth_key',
14190
- sns_secret: 'auth_secret',
14191
- sns_topic_arn: 'topic_to_publish_to',
14192
- }
14653
+ sns_key: 'auth_key',
14654
+ sns_secret: 'auth_secret',
14655
+ sns_topic_arn: 'topic_to_publish_to',
14656
+ }
14193
14657
  */
14194
14658
  async testSNSSettings(data = {}) {
14195
14659
  return await this.post(this.baseURL + "/check_sns", data);
@@ -14670,6 +15134,7 @@ var StreamChat = class _StreamChat {
14670
15134
  })
14671
15135
  };
14672
15136
  this.polls.hydratePollCache(channelState.messages, true);
15137
+ this.reminders.hydrateState(channelState.messages);
14673
15138
  }
14674
15139
  if (channelState.draft) {
14675
15140
  c.messageComposer.initState({ composition: channelState.draft });
@@ -15590,7 +16055,7 @@ var StreamChat = class _StreamChat {
15590
16055
  if (this.userAgent) {
15591
16056
  return this.userAgent;
15592
16057
  }
15593
- const version = "9.4.0";
16058
+ const version = "9.5.0";
15594
16059
  const clientBundle = "browser-esm";
15595
16060
  let userAgentString = "";
15596
16061
  if (this.sdkIdentifier) {
@@ -16629,6 +17094,56 @@ var StreamChat = class _StreamChat {
16629
17094
  };
16630
17095
  return await this.post(this.baseURL + "/drafts/query", payload);
16631
17096
  }
17097
+ /**
17098
+ * createReminder - Creates a reminder for a message
17099
+ *
17100
+ * @param {CreateReminderOptions} options The options for creating the reminder
17101
+ * @returns {Promise<ReminderAPIResponse>}
17102
+ */
17103
+ async createReminder({ messageId, ...options }) {
17104
+ return await this.post(
17105
+ `${this.baseURL}/messages/${messageId}/reminders`,
17106
+ options
17107
+ );
17108
+ }
17109
+ /**
17110
+ * updateReminder - Updates an existing reminder for a message
17111
+ *
17112
+ * @param {UpdateReminderOptions} options The options for updating the reminder
17113
+ * @returns {Promise<ReminderAPIResponse>}
17114
+ */
17115
+ async updateReminder({ messageId, ...options }) {
17116
+ return await this.patch(
17117
+ `${this.baseURL}/messages/${messageId}/reminders`,
17118
+ options
17119
+ );
17120
+ }
17121
+ /**
17122
+ * deleteReminder - Deletes a reminder for a message
17123
+ *
17124
+ * @param {string} messageId The ID of the message whose reminder to delete
17125
+ * @param {string} [userId] Optional user ID, required for server-side operations
17126
+ * @returns {Promise<APIResponse>}
17127
+ */
17128
+ async deleteReminder(messageId, userId) {
17129
+ return await this.delete(
17130
+ `${this.baseURL}/messages/${messageId}/reminders`,
17131
+ userId ? { user_id: userId } : {}
17132
+ );
17133
+ }
17134
+ /**
17135
+ * queryReminders - Queries reminders based on given filters
17136
+ *
17137
+ * @param {QueryRemindersOptions} options The options for querying reminders
17138
+ * @returns {Promise<QueryRemindersResponse>}
17139
+ */
17140
+ async queryReminders({ filter: filter2, sort, ...rest } = {}) {
17141
+ return await this.post(`${this.baseURL}/reminders/query`, {
17142
+ filter_conditions: filter2,
17143
+ sort: sort && normalizeQuerySort(sort),
17144
+ ...rest
17145
+ });
17146
+ }
16632
17147
  };
16633
17148
 
16634
17149
  // src/events.ts
@@ -16695,7 +17210,12 @@ var EVENT_MAP = {
16695
17210
  "connection.changed": true,
16696
17211
  "connection.recovered": true,
16697
17212
  "transport.changed": true,
16698
- "capabilities.changed": true
17213
+ "capabilities.changed": true,
17214
+ // Reminder events
17215
+ "reminder.created": true,
17216
+ "reminder.updated": true,
17217
+ "reminder.deleted": true,
17218
+ "notification.reminder_due": true
16699
17219
  };
16700
17220
 
16701
17221
  // src/permissions.ts
@@ -17567,6 +18087,7 @@ export {
17567
18087
  AnyResource,
17568
18088
  AnyRole,
17569
18089
  AttachmentManager,
18090
+ BasePaginator,
17570
18091
  BaseSearchSource,
17571
18092
  BuiltinPermissions,
17572
18093
  BuiltinRoles,
@@ -17584,6 +18105,9 @@ export {
17584
18105
  DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
17585
18106
  DEFAULT_COMPOSER_CONFIG,
17586
18107
  DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
18108
+ DEFAULT_PAGINATION_OPTIONS,
18109
+ DEFAULT_REMINDER_MANAGER_CONFIG,
18110
+ DEFAULT_STOP_REFRESH_BOUNDARY_MS,
17587
18111
  DEFAULT_TEXT_COMPOSER_CONFIG,
17588
18112
  Deny,
17589
18113
  DenyAll,
@@ -17616,6 +18140,10 @@ export {
17616
18140
  PollComposerCompositionMiddlewareExecutor,
17617
18141
  PollComposerStateMiddlewareExecutor,
17618
18142
  PollManager,
18143
+ Reminder,
18144
+ ReminderManager,
18145
+ ReminderPaginator,
18146
+ ReminderTimer,
17619
18147
  SearchController,
17620
18148
  Segment,
17621
18149
  StableWSConnection,
@@ -17703,6 +18231,7 @@ export {
17703
18231
  readFileAsArrayBuffer,
17704
18232
  removeDiacritics,
17705
18233
  replaceWordWithEntity,
17706
- textIsEmpty
18234
+ textIsEmpty,
18235
+ timeLeftMs
17707
18236
  };
17708
18237
  //# sourceMappingURL=index.js.map