stream-chat 9.10.1 → 9.12.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 (40) hide show
  1. package/dist/cjs/index.browser.cjs +400 -21
  2. package/dist/cjs/index.browser.cjs.map +4 -4
  3. package/dist/cjs/index.node.cjs +413 -28
  4. package/dist/cjs/index.node.cjs.map +4 -4
  5. package/dist/esm/index.js +400 -21
  6. package/dist/esm/index.js.map +4 -4
  7. package/dist/types/LiveLocationManager.d.ts +54 -0
  8. package/dist/types/channel.d.ts +5 -9
  9. package/dist/types/channel_manager.d.ts +5 -1
  10. package/dist/types/client.d.ts +7 -6
  11. package/dist/types/events.d.ts +2 -0
  12. package/dist/types/index.d.ts +1 -0
  13. package/dist/types/messageComposer/LocationComposer.d.ts +34 -0
  14. package/dist/types/messageComposer/attachmentIdentity.d.ts +2 -1
  15. package/dist/types/messageComposer/configuration/configuration.d.ts +2 -1
  16. package/dist/types/messageComposer/configuration/types.d.ts +11 -0
  17. package/dist/types/messageComposer/index.d.ts +1 -0
  18. package/dist/types/messageComposer/messageComposer.d.ts +7 -2
  19. package/dist/types/messageComposer/middleware/messageComposer/index.d.ts +1 -0
  20. package/dist/types/messageComposer/middleware/messageComposer/sharedLocation.d.ts +3 -0
  21. package/dist/types/types.d.ts +52 -5
  22. package/package.json +1 -1
  23. package/src/LiveLocationManager.ts +297 -0
  24. package/src/channel.ts +37 -2
  25. package/src/channel_manager.ts +15 -2
  26. package/src/client.ts +14 -5
  27. package/src/events.ts +2 -0
  28. package/src/index.ts +1 -0
  29. package/src/messageComposer/LocationComposer.ts +94 -0
  30. package/src/messageComposer/attachmentIdentity.ts +8 -1
  31. package/src/messageComposer/attachmentManager.ts +4 -0
  32. package/src/messageComposer/configuration/configuration.ts +8 -0
  33. package/src/messageComposer/configuration/types.ts +14 -0
  34. package/src/messageComposer/index.ts +1 -0
  35. package/src/messageComposer/messageComposer.ts +81 -9
  36. package/src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts +2 -0
  37. package/src/messageComposer/middleware/messageComposer/compositionValidation.ts +1 -5
  38. package/src/messageComposer/middleware/messageComposer/index.ts +1 -0
  39. package/src/messageComposer/middleware/messageComposer/sharedLocation.ts +42 -0
  40. package/src/types.ts +55 -5
@@ -178,6 +178,7 @@ __export(index_exports, {
178
178
  DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS: () => DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
179
179
  DEFAULT_COMPOSER_CONFIG: () => DEFAULT_COMPOSER_CONFIG,
180
180
  DEFAULT_LINK_PREVIEW_MANAGER_CONFIG: () => DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
181
+ DEFAULT_LOCATION_COMPOSER_CONFIG: () => DEFAULT_LOCATION_COMPOSER_CONFIG,
181
182
  DEFAULT_PAGINATION_OPTIONS: () => DEFAULT_PAGINATION_OPTIONS,
182
183
  DEFAULT_REMINDER_MANAGER_CONFIG: () => DEFAULT_REMINDER_MANAGER_CONFIG,
183
184
  DEFAULT_STOP_REFRESH_BOUNDARY_MS: () => DEFAULT_STOP_REFRESH_BOUNDARY_MS,
@@ -193,6 +194,8 @@ __export(index_exports, {
193
194
  JWTUserToken: () => JWTUserToken,
194
195
  LinkPreviewStatus: () => LinkPreviewStatus,
195
196
  LinkPreviewsManager: () => LinkPreviewsManager,
197
+ LiveLocationManager: () => LiveLocationManager,
198
+ LocationComposer: () => LocationComposer,
196
199
  MAX_POLL_OPTIONS: () => MAX_POLL_OPTIONS,
197
200
  MODERATION_ENTITY_TYPES: () => MODERATION_ENTITY_TYPES,
198
201
  MaxPriority: () => MaxPriority,
@@ -229,6 +232,7 @@ __export(index_exports, {
229
232
  Thread: () => Thread,
230
233
  ThreadManager: () => ThreadManager,
231
234
  TokenManager: () => TokenManager,
235
+ UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT: () => UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT,
232
236
  UserFromToken: () => UserFromToken,
233
237
  UserSearchSource: () => UserSearchSource,
234
238
  VALID_MAX_VOTES_VALUE_REGEX: () => VALID_MAX_VOTES_VALUE_REGEX,
@@ -259,6 +263,7 @@ __export(index_exports, {
259
263
  createMentionsMiddleware: () => createMentionsMiddleware,
260
264
  createMessageComposerStateCompositionMiddleware: () => createMessageComposerStateCompositionMiddleware,
261
265
  createPollComposerStateMiddleware: () => createPollComposerStateMiddleware,
266
+ createSharedLocationCompositionMiddleware: () => createSharedLocationCompositionMiddleware,
262
267
  createTextComposerCompositionMiddleware: () => createTextComposerCompositionMiddleware,
263
268
  createTextComposerPreValidationMiddleware: () => createTextComposerPreValidationMiddleware,
264
269
  decodeBase64: () => decodeBase64,
@@ -295,6 +300,7 @@ __export(index_exports, {
295
300
  isOwnUser: () => isOwnUser,
296
301
  isPatch: () => isPatch,
297
302
  isScrapedContent: () => isScrapedContent,
303
+ isSharedLocationResponse: () => isSharedLocationResponse,
298
304
  isTargetedOptionTextUpdate: () => isTargetedOptionTextUpdate,
299
305
  isUploadedAttachment: () => isUploadedAttachment,
300
306
  isVideoAttachment: () => isVideoAttachment,
@@ -4019,6 +4025,7 @@ var isLocalVoiceRecordingAttachment = (attachment) => isVoiceRecordingAttachment
4019
4025
  var isVideoAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "video" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) !== -1);
4020
4026
  var isLocalVideoAttachment = (attachment) => isVideoAttachment(attachment) && isLocalAttachment(attachment);
4021
4027
  var isUploadedAttachment = (attachment) => isAudioAttachment(attachment) || isFileAttachment(attachment) || isImageAttachment(attachment) || isVideoAttachment(attachment) || isVoiceRecordingAttachment(attachment);
4028
+ var isSharedLocationResponse = (location) => !!location.latitude && !!location.longitude && !!location.channel_cid;
4022
4029
 
4023
4030
  // src/messageComposer/fileUtils.ts
4024
4031
  var isFile2 = (fileLike) => !!fileLike.lastModified && !("uri" in fileLike);
@@ -4807,6 +4814,9 @@ var AttachmentManager = class {
4807
4814
  if (isFileReference(fileLike) && fileLike.thumb_url) {
4808
4815
  localAttachment.thumb_url = fileLike.thumb_url;
4809
4816
  }
4817
+ if (isFileReference(fileLike) && fileLike.duration) {
4818
+ localAttachment.duration = fileLike.duration;
4819
+ }
4810
4820
  return localAttachment;
4811
4821
  };
4812
4822
  this.ensureLocalUploadAttachment = async (attachment) => {
@@ -5058,10 +5068,15 @@ var DEFAULT_TEXT_COMPOSER_CONFIG = {
5058
5068
  enabled: true,
5059
5069
  publishTypingEvents: true
5060
5070
  };
5071
+ var DEFAULT_LOCATION_COMPOSER_CONFIG = {
5072
+ enabled: true,
5073
+ getDeviceId: () => generateUUIDv4()
5074
+ };
5061
5075
  var DEFAULT_COMPOSER_CONFIG = {
5062
5076
  attachments: DEFAULT_ATTACHMENT_MANAGER_CONFIG,
5063
5077
  drafts: { enabled: false },
5064
5078
  linkPreviews: DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
5079
+ location: DEFAULT_LOCATION_COMPOSER_CONFIG,
5065
5080
  text: DEFAULT_TEXT_COMPOSER_CONFIG
5066
5081
  };
5067
5082
 
@@ -5317,6 +5332,54 @@ _LinkPreviewsManager.getPreviewData = (preview) => {
5317
5332
  };
5318
5333
  var LinkPreviewsManager = _LinkPreviewsManager;
5319
5334
 
5335
+ // src/messageComposer/LocationComposer.ts
5336
+ var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
5337
+ var initState4 = ({
5338
+ message
5339
+ }) => ({
5340
+ location: message?.shared_location ?? null
5341
+ });
5342
+ var LocationComposer = class {
5343
+ constructor({ composer, message }) {
5344
+ this.initState = ({ message } = {}) => {
5345
+ this.state.next(initState4({ message }));
5346
+ };
5347
+ this.setData = (data) => {
5348
+ if (!this.config.enabled) return;
5349
+ if (!data.latitude || !data.longitude) return;
5350
+ this.state.partialNext({
5351
+ location: {
5352
+ ...data,
5353
+ message_id: this.composer.id,
5354
+ created_by_device_id: this.deviceId
5355
+ }
5356
+ });
5357
+ };
5358
+ this.composer = composer;
5359
+ this.state = new StateStore(initState4({ message }));
5360
+ this._deviceId = this.config.getDeviceId();
5361
+ }
5362
+ get config() {
5363
+ return this.composer.config.location;
5364
+ }
5365
+ get deviceId() {
5366
+ return this._deviceId;
5367
+ }
5368
+ get location() {
5369
+ return this.state.getLatestValue().location;
5370
+ }
5371
+ get validLocation() {
5372
+ const { durationMs, ...location } = this.location ?? {};
5373
+ if (!!location?.created_by_device_id && location.message_id && location.latitude && location.longitude && (typeof durationMs === "undefined" || durationMs >= MIN_LIVE_LOCATION_SHARE_DURATION)) {
5374
+ return {
5375
+ ...location,
5376
+ end_at: durationMs && new Date(Date.now() + durationMs).toISOString()
5377
+ };
5378
+ }
5379
+ return null;
5380
+ }
5381
+ };
5382
+
5320
5383
  // src/utils/concurrency.ts
5321
5384
  var withoutConcurrency = createRunner(wrapWithContinuationTracking);
5322
5385
  var withCancellation = createRunner(wrapWithCancellation);
@@ -6000,9 +6063,8 @@ var createCompositionValidationMiddleware = (composer) => ({
6000
6063
  }) => {
6001
6064
  const { maxLengthOnSend } = composer.config.text ?? {};
6002
6065
  const inputText = state.message.text ?? "";
6003
- const isEmptyMessage = textIsEmpty(inputText) && !state.message.attachments?.length && !state.message.poll_id;
6004
6066
  const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
6005
- if (isEmptyMessage || hasExceededMaxLength) {
6067
+ if (composer.compositionIsEmpty || hasExceededMaxLength) {
6006
6068
  return await discard();
6007
6069
  }
6008
6070
  return await forward();
@@ -6282,6 +6344,40 @@ var createPollOnlyCompositionMiddleware = (composer) => ({
6282
6344
  }
6283
6345
  });
6284
6346
 
6347
+ // src/messageComposer/middleware/messageComposer/sharedLocation.ts
6348
+ var createSharedLocationCompositionMiddleware = (composer) => ({
6349
+ id: "stream-io/message-composer-middleware/shared-location",
6350
+ handlers: {
6351
+ compose: ({
6352
+ state,
6353
+ next,
6354
+ forward
6355
+ }) => {
6356
+ const { locationComposer } = composer;
6357
+ const location = locationComposer.validLocation;
6358
+ if (!locationComposer || !location || !composer.client.user) return forward();
6359
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6360
+ return next({
6361
+ ...state,
6362
+ localMessage: {
6363
+ ...state.localMessage,
6364
+ shared_location: {
6365
+ ...location,
6366
+ channel_cid: composer.channel.cid,
6367
+ created_at: timestamp,
6368
+ updated_at: timestamp,
6369
+ user_id: composer.client.user.id
6370
+ }
6371
+ },
6372
+ message: {
6373
+ ...state.message,
6374
+ shared_location: location
6375
+ }
6376
+ });
6377
+ }
6378
+ }
6379
+ });
6380
+
6285
6381
  // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
6286
6382
  var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6287
6383
  constructor({ composer }) {
@@ -6292,6 +6388,7 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
6292
6388
  createTextComposerCompositionMiddleware(composer),
6293
6389
  createAttachmentsCompositionMiddleware(composer),
6294
6390
  createLinkPreviewsCompositionMiddleware(composer),
6391
+ createSharedLocationCompositionMiddleware(composer),
6295
6392
  createMessageComposerStateCompositionMiddleware(composer),
6296
6393
  createCustomDataCompositionMiddleware(composer),
6297
6394
  createCompositionValidationMiddleware(composer),
@@ -7333,7 +7430,7 @@ var textIsEmpty = (text) => {
7333
7430
  const trimmedText = text.trim();
7334
7431
  return trimmedText === "" || trimmedText === ">" || trimmedText === "``````" || trimmedText === "``" || trimmedText === "**" || trimmedText === "____" || trimmedText === "__" || trimmedText === "****";
7335
7432
  };
7336
- var initState4 = ({
7433
+ var initState5 = ({
7337
7434
  composer,
7338
7435
  message
7339
7436
  }) => {
@@ -7358,7 +7455,7 @@ var initState4 = ({
7358
7455
  var TextComposer = class {
7359
7456
  constructor({ composer, message }) {
7360
7457
  this.initState = ({ message } = {}) => {
7361
- this.state.next(initState4({ composer: this.composer, message }));
7458
+ this.state.next(initState5({ composer: this.composer, message }));
7362
7459
  };
7363
7460
  this.upsertMentionedUser = (user) => {
7364
7461
  const mentionedUsers = [...this.mentionedUsers];
@@ -7486,7 +7583,7 @@ var TextComposer = class {
7486
7583
  this.state.next(output.state);
7487
7584
  };
7488
7585
  this.composer = composer;
7489
- this.state = new StateStore(initState4({ composer, message }));
7586
+ this.state = new StateStore(initState5({ composer, message }));
7490
7587
  this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
7491
7588
  }
7492
7589
  get channel() {
@@ -8059,7 +8156,7 @@ var initEditingAuditState = (composition) => {
8059
8156
  }
8060
8157
  };
8061
8158
  };
8062
- var initState5 = (composition) => {
8159
+ var initState6 = (composition) => {
8063
8160
  if (!composition) {
8064
8161
  return {
8065
8162
  draftId: null,
@@ -8097,6 +8194,9 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8097
8194
  client
8098
8195
  }) {
8099
8196
  super();
8197
+ this.refreshId = () => {
8198
+ this.state.partialNext({ id: _MessageComposer.generateId() });
8199
+ };
8100
8200
  this.initState = ({
8101
8201
  composition
8102
8202
  } = {}) => {
@@ -8104,10 +8204,11 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8104
8204
  const message = typeof composition === "undefined" ? composition : compositionIsDraftResponse(composition) ? composition.message : formatMessage(composition);
8105
8205
  this.attachmentManager.initState({ message });
8106
8206
  this.linkPreviewsManager.initState({ message });
8207
+ this.locationComposer.initState({ message });
8107
8208
  this.textComposer.initState({ message });
8108
8209
  this.pollComposer.initState();
8109
8210
  this.customDataManager.initState({ message });
8110
- this.state.next(initState5(composition));
8211
+ this.state.next(initState6(composition));
8111
8212
  if (composition && !compositionIsDraftResponse(composition) && message && isLocalMessage(message)) {
8112
8213
  this.editedMessage = message;
8113
8214
  }
@@ -8147,6 +8248,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8147
8248
  this.addUnsubscribeFunction(this.subscribeTextComposerStateChanged());
8148
8249
  this.addUnsubscribeFunction(this.subscribeAttachmentManagerStateChanged());
8149
8250
  this.addUnsubscribeFunction(this.subscribeLinkPreviewsManagerStateChanged());
8251
+ this.addUnsubscribeFunction(this.subscribeLocationComposerStateChanged());
8150
8252
  this.addUnsubscribeFunction(this.subscribePollComposerStateChanged());
8151
8253
  this.addUnsubscribeFunction(this.subscribeCustomDataManagerStateChanged());
8152
8254
  this.addUnsubscribeFunction(this.subscribeMessageComposerStateChanged());
@@ -8243,6 +8345,14 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8243
8345
  return;
8244
8346
  }
8245
8347
  });
8348
+ this.subscribeLocationComposerStateChanged = () => this.locationComposer.state.subscribe((_, previousValue) => {
8349
+ if (typeof previousValue === "undefined") return;
8350
+ this.logStateUpdateTimestamp();
8351
+ if (this.compositionIsEmpty) {
8352
+ this.deleteDraft();
8353
+ return;
8354
+ }
8355
+ });
8246
8356
  this.subscribeLinkPreviewsManagerStateChanged = () => this.linkPreviewsManager.state.subscribe((_, previousValue) => {
8247
8357
  if (typeof previousValue === "undefined") return;
8248
8358
  this.logStateUpdateTimestamp();
@@ -8463,10 +8573,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8463
8573
  throw error;
8464
8574
  }
8465
8575
  };
8576
+ this.sendLocation = async () => {
8577
+ const location = this.locationComposer.validLocation;
8578
+ if (this.threadId || !location) return;
8579
+ try {
8580
+ await this.channel.sendSharedLocation(location);
8581
+ this.refreshId();
8582
+ this.locationComposer.initState();
8583
+ } catch (error) {
8584
+ this.client.notifications.addError({
8585
+ message: "Failed to share the location",
8586
+ origin: {
8587
+ emitter: "MessageComposer",
8588
+ context: { composer: this }
8589
+ },
8590
+ options: {
8591
+ type: "api:location:create:failed",
8592
+ metadata: {
8593
+ reason: error.message
8594
+ },
8595
+ originalError: error instanceof Error ? error : void 0
8596
+ }
8597
+ });
8598
+ throw error;
8599
+ }
8600
+ };
8466
8601
  this.compositionContext = compositionContext;
8467
- this.configState = new StateStore(
8468
- mergeWith(DEFAULT_COMPOSER_CONFIG, config ?? {})
8469
- );
8470
8602
  if (compositionContext instanceof Channel) {
8471
8603
  this.channel = compositionContext;
8472
8604
  } else if (compositionContext instanceof Thread) {
@@ -8479,6 +8611,21 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8479
8611
  "MessageComposer requires composition context pointing to channel (channel or context.cid)"
8480
8612
  );
8481
8613
  }
8614
+ const mergeChannelConfigCustomizer = (originalVal, channelConfigVal, key) => typeof originalVal === "object" ? void 0 : originalVal === false && key === "enabled" ? false : ["string", "number", "bigint", "boolean", "symbol"].includes(
8615
+ // prevent enabling features that are disabled server-side
8616
+ typeof channelConfigVal
8617
+ ) ? channelConfigVal : originalVal;
8618
+ this.configState = new StateStore(
8619
+ mergeWith(
8620
+ mergeWith(DEFAULT_COMPOSER_CONFIG, config ?? {}),
8621
+ {
8622
+ location: {
8623
+ enabled: this.channel.getConfig()?.shared_locations
8624
+ }
8625
+ },
8626
+ mergeChannelConfigCustomizer
8627
+ )
8628
+ );
8482
8629
  let message = void 0;
8483
8630
  if (compositionIsDraftResponse(composition)) {
8484
8631
  message = composition.message;
@@ -8488,13 +8635,14 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8488
8635
  }
8489
8636
  this.attachmentManager = new AttachmentManager({ composer: this, message });
8490
8637
  this.linkPreviewsManager = new LinkPreviewsManager({ composer: this, message });
8638
+ this.locationComposer = new LocationComposer({ composer: this, message });
8491
8639
  this.textComposer = new TextComposer({ composer: this, message });
8492
8640
  this.pollComposer = new PollComposer({ composer: this });
8493
8641
  this.customDataManager = new CustomDataManager({ composer: this, message });
8494
8642
  this.editingAuditState = new StateStore(
8495
8643
  this.initEditingAuditState(composition)
8496
8644
  );
8497
- this.state = new StateStore(initState5(composition));
8645
+ this.state = new StateStore(initState6(composition));
8498
8646
  this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
8499
8647
  composer: this
8500
8648
  });
@@ -8569,10 +8717,10 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8569
8717
  if (this.client.offlineDb) {
8570
8718
  return !this.compositionIsEmpty;
8571
8719
  }
8572
- return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId);
8720
+ return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
8573
8721
  }
8574
8722
  get compositionIsEmpty() {
8575
- return !this.quotedMessage && this.textComposer.textIsEmpty && !this.attachmentManager.attachments.length && !this.pollId;
8723
+ return !this.quotedMessage && this.textComposer.textIsEmpty && !this.attachmentManager.attachments.length && !this.pollId && !this.locationComposer.validLocation;
8576
8724
  }
8577
8725
  get lastChangeOriginIsLocal() {
8578
8726
  const initiatedWithoutDraft = this.lastChange.draftUpdate === null;
@@ -9099,6 +9247,30 @@ var Channel = class {
9099
9247
  this.data = data.channel;
9100
9248
  return data;
9101
9249
  }
9250
+ async sendSharedLocation(location, userId) {
9251
+ const result = await this.sendMessage({
9252
+ id: location.message_id,
9253
+ shared_location: location,
9254
+ user: userId ? { id: userId } : void 0
9255
+ });
9256
+ if (location.end_at) {
9257
+ this.getClient().dispatchEvent({
9258
+ message: result.message,
9259
+ type: "live_location_sharing.started"
9260
+ });
9261
+ }
9262
+ return result;
9263
+ }
9264
+ async stopLiveLocationSharing(payload) {
9265
+ const location = await this.getClient().updateLocation({
9266
+ ...payload,
9267
+ end_at: (/* @__PURE__ */ new Date()).toISOString()
9268
+ });
9269
+ this.getClient().dispatchEvent({
9270
+ live_location: location,
9271
+ type: "live_location_sharing.stopped"
9272
+ });
9273
+ }
9102
9274
  /**
9103
9275
  * delete - Delete the channel. Messages are permanently removed.
9104
9276
  *
@@ -12345,7 +12517,8 @@ var ChannelManager = class extends WithSubscriptions {
12345
12517
  constructor({
12346
12518
  client,
12347
12519
  eventHandlerOverrides = {},
12348
- options = {}
12520
+ options = {},
12521
+ queryChannelsOverride
12349
12522
  }) {
12350
12523
  super();
12351
12524
  this.eventHandlers = /* @__PURE__ */ new Map();
@@ -12385,6 +12558,9 @@ var ChannelManager = class extends WithSubscriptions {
12385
12558
  Object.entries(truthyEventHandlerOverrides)
12386
12559
  );
12387
12560
  };
12561
+ this.setQueryChannelsRequest = (queryChannelsRequest) => {
12562
+ this.queryChannelsRequest = queryChannelsRequest;
12563
+ };
12388
12564
  this.setOptions = (options = {}) => {
12389
12565
  this.options = { ...DEFAULT_CHANNEL_MANAGER_OPTIONS, ...options };
12390
12566
  };
@@ -12395,7 +12571,7 @@ var ChannelManager = class extends WithSubscriptions {
12395
12571
  ...options
12396
12572
  };
12397
12573
  try {
12398
- const channels = await this.client.queryChannels(
12574
+ const channels = await this.queryChannelsRequest(
12399
12575
  filters,
12400
12576
  sort,
12401
12577
  options,
@@ -12511,7 +12687,7 @@ var ChannelManager = class extends WithSubscriptions {
12511
12687
  this.state.partialNext({
12512
12688
  pagination: { ...pagination, isLoading: false, isLoadingNext: true }
12513
12689
  });
12514
- const nextChannels = await this.client.queryChannels(
12690
+ const nextChannels = await this.queryChannelsRequest(
12515
12691
  filters,
12516
12692
  sort,
12517
12693
  options,
@@ -12754,6 +12930,7 @@ var ChannelManager = class extends WithSubscriptions {
12754
12930
  });
12755
12931
  this.setEventHandlerOverrides(eventHandlerOverrides);
12756
12932
  this.setOptions(options);
12933
+ this.queryChannelsRequest = queryChannelsOverride ?? ((...params) => this.client.queryChannels(...params));
12757
12934
  this.eventHandlers = new Map(
12758
12935
  Object.entries({
12759
12936
  channelDeletedHandler: this.channelDeletedHandler,
@@ -13455,8 +13632,14 @@ var StreamChat = class _StreamChat {
13455
13632
  */
13456
13633
  this.createChannelManager = ({
13457
13634
  eventHandlerOverrides = {},
13458
- options = {}
13459
- }) => new ChannelManager({ client: this, eventHandlerOverrides, options });
13635
+ options = {},
13636
+ queryChannelsOverride
13637
+ }) => new ChannelManager({
13638
+ client: this,
13639
+ eventHandlerOverrides,
13640
+ options,
13641
+ queryChannelsOverride
13642
+ });
13460
13643
  /**
13461
13644
  * Creates a new WebSocket connection with the current user. Returns empty promise, if there is an active connection
13462
13645
  */
@@ -15560,7 +15743,7 @@ var StreamChat = class _StreamChat {
15560
15743
  if (this.userAgent) {
15561
15744
  return this.userAgent;
15562
15745
  }
15563
- const version = "9.10.1";
15746
+ const version = "9.12.0";
15564
15747
  const clientBundle = "browser-cjs";
15565
15748
  let userAgentString = "";
15566
15749
  if (this.sdkIdentifier) {
@@ -16652,9 +16835,9 @@ var StreamChat = class _StreamChat {
16652
16835
  /**
16653
16836
  * updateLocation - Updates a location
16654
16837
  *
16655
- * @param location UserLocation the location data to update
16838
+ * @param location SharedLocationRequest the location data to update
16656
16839
  *
16657
- * @returns {Promise<APIResponse>} The server response
16840
+ * @returns {Promise<SharedLocationResponse>} The server response
16658
16841
  */
16659
16842
  async updateLocation(location) {
16660
16843
  return await this.put(
@@ -16775,6 +16958,8 @@ var EVENT_MAP = {
16775
16958
  "connection.recovered": true,
16776
16959
  "transport.changed": true,
16777
16960
  "capabilities.changed": true,
16961
+ "live_location_sharing.started": true,
16962
+ "live_location_sharing.stopped": true,
16778
16963
  // Reminder events
16779
16964
  "reminder.created": true,
16780
16965
  "reminder.updated": true,
@@ -17630,6 +17815,200 @@ var AbstractOfflineDB = class {
17630
17815
  }
17631
17816
  };
17632
17817
 
17818
+ // src/LiveLocationManager.ts
17819
+ var isExpiredLocation = (location) => {
17820
+ const endTimeTimestamp = new Date(location.end_at).getTime();
17821
+ return endTimeTimestamp < Date.now();
17822
+ };
17823
+ function isValidLiveLocationMessage(message) {
17824
+ if (!message || message.type === "deleted" || !message.shared_location?.end_at)
17825
+ return false;
17826
+ return !isExpiredLocation(message.shared_location);
17827
+ }
17828
+ var UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT = 3e3;
17829
+ var _LiveLocationManager = class _LiveLocationManager extends WithSubscriptions {
17830
+ constructor({
17831
+ client,
17832
+ getDeviceId,
17833
+ watchLocation
17834
+ }) {
17835
+ if (!client.userID) {
17836
+ throw new Error("Live-location sharing is reserved for client-side use only");
17837
+ }
17838
+ super();
17839
+ this.registerSubscriptions = () => {
17840
+ this.incrementRefCount();
17841
+ if (this.hasSubscriptions) return;
17842
+ this.addUnsubscribeFunction(this.subscribeLiveLocationSharingUpdates());
17843
+ this.addUnsubscribeFunction(this.subscribeTargetMessagesChange());
17844
+ };
17845
+ this.unregisterSubscriptions = () => super.unregisterSubscriptions();
17846
+ this.client = client;
17847
+ this.state = new StateStore({
17848
+ messages: /* @__PURE__ */ new Map(),
17849
+ ready: false
17850
+ });
17851
+ this._deviceId = getDeviceId();
17852
+ this.getDeviceId = getDeviceId;
17853
+ this.watchLocation = watchLocation;
17854
+ }
17855
+ async init() {
17856
+ await this.assureStateInit();
17857
+ this.registerSubscriptions();
17858
+ }
17859
+ get messages() {
17860
+ return this.state.getLatestValue().messages;
17861
+ }
17862
+ get stateIsReady() {
17863
+ return this.state.getLatestValue().ready;
17864
+ }
17865
+ get deviceId() {
17866
+ if (!this._deviceId) {
17867
+ this._deviceId = this.getDeviceId();
17868
+ }
17869
+ return this._deviceId;
17870
+ }
17871
+ async assureStateInit() {
17872
+ if (this.stateIsReady) return;
17873
+ const { active_live_locations } = await this.client.getSharedLocations();
17874
+ this.state.next({
17875
+ messages: new Map(
17876
+ active_live_locations.filter((location) => !isExpiredLocation(location)).map((location) => [
17877
+ location.message_id,
17878
+ {
17879
+ ...location,
17880
+ stopSharingTimeout: setTimeout(
17881
+ () => {
17882
+ this.unregisterMessages([location.message_id]);
17883
+ },
17884
+ new Date(location.end_at).getTime() - Date.now()
17885
+ )
17886
+ }
17887
+ ])
17888
+ ),
17889
+ ready: true
17890
+ });
17891
+ }
17892
+ subscribeTargetMessagesChange() {
17893
+ let unsubscribeWatchLocation = null;
17894
+ const unsubscribe = this.state.subscribeWithSelector(
17895
+ ({ messages }) => ({ messages }),
17896
+ ({ messages }) => {
17897
+ if (!messages.size) {
17898
+ unsubscribeWatchLocation?.();
17899
+ unsubscribeWatchLocation = null;
17900
+ } else if (messages.size && !unsubscribeWatchLocation) {
17901
+ unsubscribeWatchLocation = this.subscribeWatchLocation();
17902
+ }
17903
+ }
17904
+ );
17905
+ return () => {
17906
+ unsubscribe();
17907
+ unsubscribeWatchLocation?.();
17908
+ };
17909
+ }
17910
+ subscribeWatchLocation() {
17911
+ let nextAllowedUpdateCallTimestamp = Date.now();
17912
+ const unsubscribe = this.watchLocation(({ latitude, longitude }) => {
17913
+ if (Date.now() < nextAllowedUpdateCallTimestamp) return;
17914
+ nextAllowedUpdateCallTimestamp = Date.now() + UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT;
17915
+ withCancellation(_LiveLocationManager.symbol, async () => {
17916
+ const promises = [];
17917
+ await this.assureStateInit();
17918
+ const expiredLocations = [];
17919
+ for (const [messageId, location] of this.messages) {
17920
+ if (isExpiredLocation(location)) {
17921
+ expiredLocations.push(location.message_id);
17922
+ continue;
17923
+ }
17924
+ if (location.latitude === latitude && location.longitude === longitude)
17925
+ continue;
17926
+ const promise = this.client.updateLocation({
17927
+ created_by_device_id: location.created_by_device_id,
17928
+ message_id: messageId,
17929
+ latitude,
17930
+ longitude
17931
+ });
17932
+ promises.push(promise);
17933
+ }
17934
+ this.unregisterMessages(expiredLocations);
17935
+ if (promises.length > 0) {
17936
+ await Promise.allSettled(promises);
17937
+ }
17938
+ });
17939
+ });
17940
+ return unsubscribe;
17941
+ }
17942
+ subscribeLiveLocationSharingUpdates() {
17943
+ const subscriptions = [
17944
+ ...[
17945
+ "live_location_sharing.started",
17946
+ "message.updated",
17947
+ "message.deleted"
17948
+ ].map(
17949
+ (eventType) => this.client.on(eventType, (event) => {
17950
+ if (!event.message) return;
17951
+ if (event.type === "live_location_sharing.started") {
17952
+ this.registerMessage(event.message);
17953
+ } else if (event.type === "message.updated") {
17954
+ const isRegistered = this.messages.has(event.message.id);
17955
+ if (isRegistered && !isValidLiveLocationMessage(event.message)) {
17956
+ this.unregisterMessages([event.message.id]);
17957
+ }
17958
+ this.registerMessage(event.message);
17959
+ } else {
17960
+ this.unregisterMessages([event.message.id]);
17961
+ }
17962
+ })
17963
+ ),
17964
+ this.client.on("live_location_sharing.stopped", (event) => {
17965
+ if (!event.live_location) return;
17966
+ this.unregisterMessages([event.live_location?.message_id]);
17967
+ })
17968
+ ];
17969
+ return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
17970
+ }
17971
+ registerMessage(message) {
17972
+ if (!this.client.userID || message?.user?.id !== this.client.userID || !isValidLiveLocationMessage(message))
17973
+ return;
17974
+ this.state.next((currentValue) => {
17975
+ const messages = new Map(currentValue.messages);
17976
+ messages.set(message.id, {
17977
+ ...message.shared_location,
17978
+ stopSharingTimeout: setTimeout(
17979
+ () => {
17980
+ this.unregisterMessages([message.id]);
17981
+ },
17982
+ new Date(message.shared_location.end_at).getTime() - Date.now()
17983
+ )
17984
+ });
17985
+ return {
17986
+ ...currentValue,
17987
+ messages
17988
+ };
17989
+ });
17990
+ }
17991
+ unregisterMessages(messageIds) {
17992
+ const messages = this.messages;
17993
+ const removedMessages = new Set(messageIds);
17994
+ const newMessages = new Map(
17995
+ Array.from(messages).filter(([messageId, location]) => {
17996
+ if (removedMessages.has(messageId) && location.stopSharingTimeout) {
17997
+ clearTimeout(location.stopSharingTimeout);
17998
+ location.stopSharingTimeout = null;
17999
+ }
18000
+ return !removedMessages.has(messageId);
18001
+ })
18002
+ );
18003
+ if (newMessages.size === messages.size) return;
18004
+ this.state.partialNext({
18005
+ messages: newMessages
18006
+ });
18007
+ }
18008
+ };
18009
+ _LiveLocationManager.symbol = Symbol(_LiveLocationManager.name);
18010
+ var LiveLocationManager = _LiveLocationManager;
18011
+
17633
18012
  // src/utils/FixedSizeQueueCache.ts
17634
18013
  var FixedSizeQueueCache = class {
17635
18014
  constructor(size, options) {