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
package/dist/esm/index.js CHANGED
@@ -3854,6 +3854,7 @@ var isLocalVoiceRecordingAttachment = (attachment) => isVoiceRecordingAttachment
3854
3854
  var isVideoAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "video" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) !== -1);
3855
3855
  var isLocalVideoAttachment = (attachment) => isVideoAttachment(attachment) && isLocalAttachment(attachment);
3856
3856
  var isUploadedAttachment = (attachment) => isAudioAttachment(attachment) || isFileAttachment(attachment) || isImageAttachment(attachment) || isVideoAttachment(attachment) || isVoiceRecordingAttachment(attachment);
3857
+ var isSharedLocationResponse = (location) => !!location.latitude && !!location.longitude && !!location.channel_cid;
3857
3858
 
3858
3859
  // src/messageComposer/fileUtils.ts
3859
3860
  var isFile2 = (fileLike) => !!fileLike.lastModified && !("uri" in fileLike);
@@ -4642,6 +4643,9 @@ var AttachmentManager = class {
4642
4643
  if (isFileReference(fileLike) && fileLike.thumb_url) {
4643
4644
  localAttachment.thumb_url = fileLike.thumb_url;
4644
4645
  }
4646
+ if (isFileReference(fileLike) && fileLike.duration) {
4647
+ localAttachment.duration = fileLike.duration;
4648
+ }
4645
4649
  return localAttachment;
4646
4650
  };
4647
4651
  this.ensureLocalUploadAttachment = async (attachment) => {
@@ -6022,10 +6026,15 @@ var DEFAULT_TEXT_COMPOSER_CONFIG = {
6022
6026
  enabled: true,
6023
6027
  publishTypingEvents: true
6024
6028
  };
6029
+ var DEFAULT_LOCATION_COMPOSER_CONFIG = {
6030
+ enabled: true,
6031
+ getDeviceId: () => generateUUIDv4()
6032
+ };
6025
6033
  var DEFAULT_COMPOSER_CONFIG = {
6026
6034
  attachments: DEFAULT_ATTACHMENT_MANAGER_CONFIG,
6027
6035
  drafts: { enabled: false },
6028
6036
  linkPreviews: DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
6037
+ location: DEFAULT_LOCATION_COMPOSER_CONFIG,
6029
6038
  text: DEFAULT_TEXT_COMPOSER_CONFIG
6030
6039
  };
6031
6040
 
@@ -6281,6 +6290,54 @@ _LinkPreviewsManager.getPreviewData = (preview) => {
6281
6290
  };
6282
6291
  var LinkPreviewsManager = _LinkPreviewsManager;
6283
6292
 
6293
+ // src/messageComposer/LocationComposer.ts
6294
+ var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
6295
+ var initState4 = ({
6296
+ message
6297
+ }) => ({
6298
+ location: message?.shared_location ?? null
6299
+ });
6300
+ var LocationComposer = class {
6301
+ constructor({ composer, message }) {
6302
+ this.initState = ({ message } = {}) => {
6303
+ this.state.next(initState4({ message }));
6304
+ };
6305
+ this.setData = (data) => {
6306
+ if (!this.config.enabled) return;
6307
+ if (!data.latitude || !data.longitude) return;
6308
+ this.state.partialNext({
6309
+ location: {
6310
+ ...data,
6311
+ message_id: this.composer.id,
6312
+ created_by_device_id: this.deviceId
6313
+ }
6314
+ });
6315
+ };
6316
+ this.composer = composer;
6317
+ this.state = new StateStore(initState4({ message }));
6318
+ this._deviceId = this.config.getDeviceId();
6319
+ }
6320
+ get config() {
6321
+ return this.composer.config.location;
6322
+ }
6323
+ get deviceId() {
6324
+ return this._deviceId;
6325
+ }
6326
+ get location() {
6327
+ return this.state.getLatestValue().location;
6328
+ }
6329
+ get validLocation() {
6330
+ const { durationMs, ...location } = this.location ?? {};
6331
+ if (!!location?.created_by_device_id && location.message_id && location.latitude && location.longitude && (typeof durationMs === "undefined" || durationMs >= MIN_LIVE_LOCATION_SHARE_DURATION)) {
6332
+ return {
6333
+ ...location,
6334
+ end_at: durationMs && new Date(Date.now() + durationMs).toISOString()
6335
+ };
6336
+ }
6337
+ return null;
6338
+ }
6339
+ };
6340
+
6284
6341
  // src/utils/concurrency.ts
6285
6342
  var withoutConcurrency = createRunner(wrapWithContinuationTracking);
6286
6343
  var withCancellation = createRunner(wrapWithCancellation);
@@ -6964,9 +7021,8 @@ var createCompositionValidationMiddleware = (composer) => ({
6964
7021
  }) => {
6965
7022
  const { maxLengthOnSend } = composer.config.text ?? {};
6966
7023
  const inputText = state.message.text ?? "";
6967
- const isEmptyMessage = textIsEmpty(inputText) && !state.message.attachments?.length && !state.message.poll_id;
6968
7024
  const hasExceededMaxLength = typeof maxLengthOnSend === "number" && inputText.length > maxLengthOnSend;
6969
- if (isEmptyMessage || hasExceededMaxLength) {
7025
+ if (composer.compositionIsEmpty || hasExceededMaxLength) {
6970
7026
  return await discard();
6971
7027
  }
6972
7028
  return await forward();
@@ -7246,6 +7302,40 @@ var createPollOnlyCompositionMiddleware = (composer) => ({
7246
7302
  }
7247
7303
  });
7248
7304
 
7305
+ // src/messageComposer/middleware/messageComposer/sharedLocation.ts
7306
+ var createSharedLocationCompositionMiddleware = (composer) => ({
7307
+ id: "stream-io/message-composer-middleware/shared-location",
7308
+ handlers: {
7309
+ compose: ({
7310
+ state,
7311
+ next,
7312
+ forward
7313
+ }) => {
7314
+ const { locationComposer } = composer;
7315
+ const location = locationComposer.validLocation;
7316
+ if (!locationComposer || !location || !composer.client.user) return forward();
7317
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
7318
+ return next({
7319
+ ...state,
7320
+ localMessage: {
7321
+ ...state.localMessage,
7322
+ shared_location: {
7323
+ ...location,
7324
+ channel_cid: composer.channel.cid,
7325
+ created_at: timestamp,
7326
+ updated_at: timestamp,
7327
+ user_id: composer.client.user.id
7328
+ }
7329
+ },
7330
+ message: {
7331
+ ...state.message,
7332
+ shared_location: location
7333
+ }
7334
+ });
7335
+ }
7336
+ }
7337
+ });
7338
+
7249
7339
  // src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts
7250
7340
  var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
7251
7341
  constructor({ composer }) {
@@ -7256,6 +7346,7 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
7256
7346
  createTextComposerCompositionMiddleware(composer),
7257
7347
  createAttachmentsCompositionMiddleware(composer),
7258
7348
  createLinkPreviewsCompositionMiddleware(composer),
7349
+ createSharedLocationCompositionMiddleware(composer),
7259
7350
  createMessageComposerStateCompositionMiddleware(composer),
7260
7351
  createCustomDataCompositionMiddleware(composer),
7261
7352
  createCompositionValidationMiddleware(composer),
@@ -8297,7 +8388,7 @@ var textIsEmpty = (text) => {
8297
8388
  const trimmedText = text.trim();
8298
8389
  return trimmedText === "" || trimmedText === ">" || trimmedText === "``````" || trimmedText === "``" || trimmedText === "**" || trimmedText === "____" || trimmedText === "__" || trimmedText === "****";
8299
8390
  };
8300
- var initState4 = ({
8391
+ var initState5 = ({
8301
8392
  composer,
8302
8393
  message
8303
8394
  }) => {
@@ -8322,7 +8413,7 @@ var initState4 = ({
8322
8413
  var TextComposer = class {
8323
8414
  constructor({ composer, message }) {
8324
8415
  this.initState = ({ message } = {}) => {
8325
- this.state.next(initState4({ composer: this.composer, message }));
8416
+ this.state.next(initState5({ composer: this.composer, message }));
8326
8417
  };
8327
8418
  this.upsertMentionedUser = (user) => {
8328
8419
  const mentionedUsers = [...this.mentionedUsers];
@@ -8450,7 +8541,7 @@ var TextComposer = class {
8450
8541
  this.state.next(output.state);
8451
8542
  };
8452
8543
  this.composer = composer;
8453
- this.state = new StateStore(initState4({ composer, message }));
8544
+ this.state = new StateStore(initState5({ composer, message }));
8454
8545
  this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
8455
8546
  }
8456
8547
  get channel() {
@@ -9023,7 +9114,7 @@ var initEditingAuditState = (composition) => {
9023
9114
  }
9024
9115
  };
9025
9116
  };
9026
- var initState5 = (composition) => {
9117
+ var initState6 = (composition) => {
9027
9118
  if (!composition) {
9028
9119
  return {
9029
9120
  draftId: null,
@@ -9061,6 +9152,9 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9061
9152
  client
9062
9153
  }) {
9063
9154
  super();
9155
+ this.refreshId = () => {
9156
+ this.state.partialNext({ id: _MessageComposer.generateId() });
9157
+ };
9064
9158
  this.initState = ({
9065
9159
  composition
9066
9160
  } = {}) => {
@@ -9068,10 +9162,11 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9068
9162
  const message = typeof composition === "undefined" ? composition : compositionIsDraftResponse(composition) ? composition.message : formatMessage(composition);
9069
9163
  this.attachmentManager.initState({ message });
9070
9164
  this.linkPreviewsManager.initState({ message });
9165
+ this.locationComposer.initState({ message });
9071
9166
  this.textComposer.initState({ message });
9072
9167
  this.pollComposer.initState();
9073
9168
  this.customDataManager.initState({ message });
9074
- this.state.next(initState5(composition));
9169
+ this.state.next(initState6(composition));
9075
9170
  if (composition && !compositionIsDraftResponse(composition) && message && isLocalMessage(message)) {
9076
9171
  this.editedMessage = message;
9077
9172
  }
@@ -9111,6 +9206,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9111
9206
  this.addUnsubscribeFunction(this.subscribeTextComposerStateChanged());
9112
9207
  this.addUnsubscribeFunction(this.subscribeAttachmentManagerStateChanged());
9113
9208
  this.addUnsubscribeFunction(this.subscribeLinkPreviewsManagerStateChanged());
9209
+ this.addUnsubscribeFunction(this.subscribeLocationComposerStateChanged());
9114
9210
  this.addUnsubscribeFunction(this.subscribePollComposerStateChanged());
9115
9211
  this.addUnsubscribeFunction(this.subscribeCustomDataManagerStateChanged());
9116
9212
  this.addUnsubscribeFunction(this.subscribeMessageComposerStateChanged());
@@ -9207,6 +9303,14 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9207
9303
  return;
9208
9304
  }
9209
9305
  });
9306
+ this.subscribeLocationComposerStateChanged = () => this.locationComposer.state.subscribe((_, previousValue) => {
9307
+ if (typeof previousValue === "undefined") return;
9308
+ this.logStateUpdateTimestamp();
9309
+ if (this.compositionIsEmpty) {
9310
+ this.deleteDraft();
9311
+ return;
9312
+ }
9313
+ });
9210
9314
  this.subscribeLinkPreviewsManagerStateChanged = () => this.linkPreviewsManager.state.subscribe((_, previousValue) => {
9211
9315
  if (typeof previousValue === "undefined") return;
9212
9316
  this.logStateUpdateTimestamp();
@@ -9427,10 +9531,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9427
9531
  throw error;
9428
9532
  }
9429
9533
  };
9534
+ this.sendLocation = async () => {
9535
+ const location = this.locationComposer.validLocation;
9536
+ if (this.threadId || !location) return;
9537
+ try {
9538
+ await this.channel.sendSharedLocation(location);
9539
+ this.refreshId();
9540
+ this.locationComposer.initState();
9541
+ } catch (error) {
9542
+ this.client.notifications.addError({
9543
+ message: "Failed to share the location",
9544
+ origin: {
9545
+ emitter: "MessageComposer",
9546
+ context: { composer: this }
9547
+ },
9548
+ options: {
9549
+ type: "api:location:create:failed",
9550
+ metadata: {
9551
+ reason: error.message
9552
+ },
9553
+ originalError: error instanceof Error ? error : void 0
9554
+ }
9555
+ });
9556
+ throw error;
9557
+ }
9558
+ };
9430
9559
  this.compositionContext = compositionContext;
9431
- this.configState = new StateStore(
9432
- mergeWith(DEFAULT_COMPOSER_CONFIG, config ?? {})
9433
- );
9434
9560
  if (compositionContext instanceof Channel) {
9435
9561
  this.channel = compositionContext;
9436
9562
  } else if (compositionContext instanceof Thread) {
@@ -9443,6 +9569,21 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9443
9569
  "MessageComposer requires composition context pointing to channel (channel or context.cid)"
9444
9570
  );
9445
9571
  }
9572
+ const mergeChannelConfigCustomizer = (originalVal, channelConfigVal, key) => typeof originalVal === "object" ? void 0 : originalVal === false && key === "enabled" ? false : ["string", "number", "bigint", "boolean", "symbol"].includes(
9573
+ // prevent enabling features that are disabled server-side
9574
+ typeof channelConfigVal
9575
+ ) ? channelConfigVal : originalVal;
9576
+ this.configState = new StateStore(
9577
+ mergeWith(
9578
+ mergeWith(DEFAULT_COMPOSER_CONFIG, config ?? {}),
9579
+ {
9580
+ location: {
9581
+ enabled: this.channel.getConfig()?.shared_locations
9582
+ }
9583
+ },
9584
+ mergeChannelConfigCustomizer
9585
+ )
9586
+ );
9446
9587
  let message = void 0;
9447
9588
  if (compositionIsDraftResponse(composition)) {
9448
9589
  message = composition.message;
@@ -9452,13 +9593,14 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9452
9593
  }
9453
9594
  this.attachmentManager = new AttachmentManager({ composer: this, message });
9454
9595
  this.linkPreviewsManager = new LinkPreviewsManager({ composer: this, message });
9596
+ this.locationComposer = new LocationComposer({ composer: this, message });
9455
9597
  this.textComposer = new TextComposer({ composer: this, message });
9456
9598
  this.pollComposer = new PollComposer({ composer: this });
9457
9599
  this.customDataManager = new CustomDataManager({ composer: this, message });
9458
9600
  this.editingAuditState = new StateStore(
9459
9601
  this.initEditingAuditState(composition)
9460
9602
  );
9461
- this.state = new StateStore(initState5(composition));
9603
+ this.state = new StateStore(initState6(composition));
9462
9604
  this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
9463
9605
  composer: this
9464
9606
  });
@@ -9533,10 +9675,10 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
9533
9675
  if (this.client.offlineDb) {
9534
9676
  return !this.compositionIsEmpty;
9535
9677
  }
9536
- return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId);
9678
+ return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
9537
9679
  }
9538
9680
  get compositionIsEmpty() {
9539
- return !this.quotedMessage && this.textComposer.textIsEmpty && !this.attachmentManager.attachments.length && !this.pollId;
9681
+ return !this.quotedMessage && this.textComposer.textIsEmpty && !this.attachmentManager.attachments.length && !this.pollId && !this.locationComposer.validLocation;
9540
9682
  }
9541
9683
  get lastChangeOriginIsLocal() {
9542
9684
  const initiatedWithoutDraft = this.lastChange.draftUpdate === null;
@@ -10063,6 +10205,30 @@ var Channel = class {
10063
10205
  this.data = data.channel;
10064
10206
  return data;
10065
10207
  }
10208
+ async sendSharedLocation(location, userId) {
10209
+ const result = await this.sendMessage({
10210
+ id: location.message_id,
10211
+ shared_location: location,
10212
+ user: userId ? { id: userId } : void 0
10213
+ });
10214
+ if (location.end_at) {
10215
+ this.getClient().dispatchEvent({
10216
+ message: result.message,
10217
+ type: "live_location_sharing.started"
10218
+ });
10219
+ }
10220
+ return result;
10221
+ }
10222
+ async stopLiveLocationSharing(payload) {
10223
+ const location = await this.getClient().updateLocation({
10224
+ ...payload,
10225
+ end_at: (/* @__PURE__ */ new Date()).toISOString()
10226
+ });
10227
+ this.getClient().dispatchEvent({
10228
+ live_location: location,
10229
+ type: "live_location_sharing.stopped"
10230
+ });
10231
+ }
10066
10232
  /**
10067
10233
  * delete - Delete the channel. Messages are permanently removed.
10068
10234
  *
@@ -13309,7 +13475,8 @@ var ChannelManager = class extends WithSubscriptions {
13309
13475
  constructor({
13310
13476
  client,
13311
13477
  eventHandlerOverrides = {},
13312
- options = {}
13478
+ options = {},
13479
+ queryChannelsOverride
13313
13480
  }) {
13314
13481
  super();
13315
13482
  this.eventHandlers = /* @__PURE__ */ new Map();
@@ -13349,6 +13516,9 @@ var ChannelManager = class extends WithSubscriptions {
13349
13516
  Object.entries(truthyEventHandlerOverrides)
13350
13517
  );
13351
13518
  };
13519
+ this.setQueryChannelsRequest = (queryChannelsRequest) => {
13520
+ this.queryChannelsRequest = queryChannelsRequest;
13521
+ };
13352
13522
  this.setOptions = (options = {}) => {
13353
13523
  this.options = { ...DEFAULT_CHANNEL_MANAGER_OPTIONS, ...options };
13354
13524
  };
@@ -13359,7 +13529,7 @@ var ChannelManager = class extends WithSubscriptions {
13359
13529
  ...options
13360
13530
  };
13361
13531
  try {
13362
- const channels = await this.client.queryChannels(
13532
+ const channels = await this.queryChannelsRequest(
13363
13533
  filters,
13364
13534
  sort,
13365
13535
  options,
@@ -13475,7 +13645,7 @@ var ChannelManager = class extends WithSubscriptions {
13475
13645
  this.state.partialNext({
13476
13646
  pagination: { ...pagination, isLoading: false, isLoadingNext: true }
13477
13647
  });
13478
- const nextChannels = await this.client.queryChannels(
13648
+ const nextChannels = await this.queryChannelsRequest(
13479
13649
  filters,
13480
13650
  sort,
13481
13651
  options,
@@ -13718,6 +13888,7 @@ var ChannelManager = class extends WithSubscriptions {
13718
13888
  });
13719
13889
  this.setEventHandlerOverrides(eventHandlerOverrides);
13720
13890
  this.setOptions(options);
13891
+ this.queryChannelsRequest = queryChannelsOverride ?? ((...params) => this.client.queryChannels(...params));
13721
13892
  this.eventHandlers = new Map(
13722
13893
  Object.entries({
13723
13894
  channelDeletedHandler: this.channelDeletedHandler,
@@ -14419,8 +14590,14 @@ var StreamChat = class _StreamChat {
14419
14590
  */
14420
14591
  this.createChannelManager = ({
14421
14592
  eventHandlerOverrides = {},
14422
- options = {}
14423
- }) => new ChannelManager({ client: this, eventHandlerOverrides, options });
14593
+ options = {},
14594
+ queryChannelsOverride
14595
+ }) => new ChannelManager({
14596
+ client: this,
14597
+ eventHandlerOverrides,
14598
+ options,
14599
+ queryChannelsOverride
14600
+ });
14424
14601
  /**
14425
14602
  * Creates a new WebSocket connection with the current user. Returns empty promise, if there is an active connection
14426
14603
  */
@@ -16524,7 +16701,7 @@ var StreamChat = class _StreamChat {
16524
16701
  if (this.userAgent) {
16525
16702
  return this.userAgent;
16526
16703
  }
16527
- const version = "9.10.1";
16704
+ const version = "9.12.0";
16528
16705
  const clientBundle = "browser-esm";
16529
16706
  let userAgentString = "";
16530
16707
  if (this.sdkIdentifier) {
@@ -17616,9 +17793,9 @@ var StreamChat = class _StreamChat {
17616
17793
  /**
17617
17794
  * updateLocation - Updates a location
17618
17795
  *
17619
- * @param location UserLocation the location data to update
17796
+ * @param location SharedLocationRequest the location data to update
17620
17797
  *
17621
- * @returns {Promise<APIResponse>} The server response
17798
+ * @returns {Promise<SharedLocationResponse>} The server response
17622
17799
  */
17623
17800
  async updateLocation(location) {
17624
17801
  return await this.put(
@@ -17739,6 +17916,8 @@ var EVENT_MAP = {
17739
17916
  "connection.recovered": true,
17740
17917
  "transport.changed": true,
17741
17918
  "capabilities.changed": true,
17919
+ "live_location_sharing.started": true,
17920
+ "live_location_sharing.stopped": true,
17742
17921
  // Reminder events
17743
17922
  "reminder.created": true,
17744
17923
  "reminder.updated": true,
@@ -18594,6 +18773,200 @@ var AbstractOfflineDB = class {
18594
18773
  }
18595
18774
  };
18596
18775
 
18776
+ // src/LiveLocationManager.ts
18777
+ var isExpiredLocation = (location) => {
18778
+ const endTimeTimestamp = new Date(location.end_at).getTime();
18779
+ return endTimeTimestamp < Date.now();
18780
+ };
18781
+ function isValidLiveLocationMessage(message) {
18782
+ if (!message || message.type === "deleted" || !message.shared_location?.end_at)
18783
+ return false;
18784
+ return !isExpiredLocation(message.shared_location);
18785
+ }
18786
+ var UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT = 3e3;
18787
+ var _LiveLocationManager = class _LiveLocationManager extends WithSubscriptions {
18788
+ constructor({
18789
+ client,
18790
+ getDeviceId,
18791
+ watchLocation
18792
+ }) {
18793
+ if (!client.userID) {
18794
+ throw new Error("Live-location sharing is reserved for client-side use only");
18795
+ }
18796
+ super();
18797
+ this.registerSubscriptions = () => {
18798
+ this.incrementRefCount();
18799
+ if (this.hasSubscriptions) return;
18800
+ this.addUnsubscribeFunction(this.subscribeLiveLocationSharingUpdates());
18801
+ this.addUnsubscribeFunction(this.subscribeTargetMessagesChange());
18802
+ };
18803
+ this.unregisterSubscriptions = () => super.unregisterSubscriptions();
18804
+ this.client = client;
18805
+ this.state = new StateStore({
18806
+ messages: /* @__PURE__ */ new Map(),
18807
+ ready: false
18808
+ });
18809
+ this._deviceId = getDeviceId();
18810
+ this.getDeviceId = getDeviceId;
18811
+ this.watchLocation = watchLocation;
18812
+ }
18813
+ async init() {
18814
+ await this.assureStateInit();
18815
+ this.registerSubscriptions();
18816
+ }
18817
+ get messages() {
18818
+ return this.state.getLatestValue().messages;
18819
+ }
18820
+ get stateIsReady() {
18821
+ return this.state.getLatestValue().ready;
18822
+ }
18823
+ get deviceId() {
18824
+ if (!this._deviceId) {
18825
+ this._deviceId = this.getDeviceId();
18826
+ }
18827
+ return this._deviceId;
18828
+ }
18829
+ async assureStateInit() {
18830
+ if (this.stateIsReady) return;
18831
+ const { active_live_locations } = await this.client.getSharedLocations();
18832
+ this.state.next({
18833
+ messages: new Map(
18834
+ active_live_locations.filter((location) => !isExpiredLocation(location)).map((location) => [
18835
+ location.message_id,
18836
+ {
18837
+ ...location,
18838
+ stopSharingTimeout: setTimeout(
18839
+ () => {
18840
+ this.unregisterMessages([location.message_id]);
18841
+ },
18842
+ new Date(location.end_at).getTime() - Date.now()
18843
+ )
18844
+ }
18845
+ ])
18846
+ ),
18847
+ ready: true
18848
+ });
18849
+ }
18850
+ subscribeTargetMessagesChange() {
18851
+ let unsubscribeWatchLocation = null;
18852
+ const unsubscribe = this.state.subscribeWithSelector(
18853
+ ({ messages }) => ({ messages }),
18854
+ ({ messages }) => {
18855
+ if (!messages.size) {
18856
+ unsubscribeWatchLocation?.();
18857
+ unsubscribeWatchLocation = null;
18858
+ } else if (messages.size && !unsubscribeWatchLocation) {
18859
+ unsubscribeWatchLocation = this.subscribeWatchLocation();
18860
+ }
18861
+ }
18862
+ );
18863
+ return () => {
18864
+ unsubscribe();
18865
+ unsubscribeWatchLocation?.();
18866
+ };
18867
+ }
18868
+ subscribeWatchLocation() {
18869
+ let nextAllowedUpdateCallTimestamp = Date.now();
18870
+ const unsubscribe = this.watchLocation(({ latitude, longitude }) => {
18871
+ if (Date.now() < nextAllowedUpdateCallTimestamp) return;
18872
+ nextAllowedUpdateCallTimestamp = Date.now() + UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT;
18873
+ withCancellation(_LiveLocationManager.symbol, async () => {
18874
+ const promises = [];
18875
+ await this.assureStateInit();
18876
+ const expiredLocations = [];
18877
+ for (const [messageId, location] of this.messages) {
18878
+ if (isExpiredLocation(location)) {
18879
+ expiredLocations.push(location.message_id);
18880
+ continue;
18881
+ }
18882
+ if (location.latitude === latitude && location.longitude === longitude)
18883
+ continue;
18884
+ const promise = this.client.updateLocation({
18885
+ created_by_device_id: location.created_by_device_id,
18886
+ message_id: messageId,
18887
+ latitude,
18888
+ longitude
18889
+ });
18890
+ promises.push(promise);
18891
+ }
18892
+ this.unregisterMessages(expiredLocations);
18893
+ if (promises.length > 0) {
18894
+ await Promise.allSettled(promises);
18895
+ }
18896
+ });
18897
+ });
18898
+ return unsubscribe;
18899
+ }
18900
+ subscribeLiveLocationSharingUpdates() {
18901
+ const subscriptions = [
18902
+ ...[
18903
+ "live_location_sharing.started",
18904
+ "message.updated",
18905
+ "message.deleted"
18906
+ ].map(
18907
+ (eventType) => this.client.on(eventType, (event) => {
18908
+ if (!event.message) return;
18909
+ if (event.type === "live_location_sharing.started") {
18910
+ this.registerMessage(event.message);
18911
+ } else if (event.type === "message.updated") {
18912
+ const isRegistered = this.messages.has(event.message.id);
18913
+ if (isRegistered && !isValidLiveLocationMessage(event.message)) {
18914
+ this.unregisterMessages([event.message.id]);
18915
+ }
18916
+ this.registerMessage(event.message);
18917
+ } else {
18918
+ this.unregisterMessages([event.message.id]);
18919
+ }
18920
+ })
18921
+ ),
18922
+ this.client.on("live_location_sharing.stopped", (event) => {
18923
+ if (!event.live_location) return;
18924
+ this.unregisterMessages([event.live_location?.message_id]);
18925
+ })
18926
+ ];
18927
+ return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
18928
+ }
18929
+ registerMessage(message) {
18930
+ if (!this.client.userID || message?.user?.id !== this.client.userID || !isValidLiveLocationMessage(message))
18931
+ return;
18932
+ this.state.next((currentValue) => {
18933
+ const messages = new Map(currentValue.messages);
18934
+ messages.set(message.id, {
18935
+ ...message.shared_location,
18936
+ stopSharingTimeout: setTimeout(
18937
+ () => {
18938
+ this.unregisterMessages([message.id]);
18939
+ },
18940
+ new Date(message.shared_location.end_at).getTime() - Date.now()
18941
+ )
18942
+ });
18943
+ return {
18944
+ ...currentValue,
18945
+ messages
18946
+ };
18947
+ });
18948
+ }
18949
+ unregisterMessages(messageIds) {
18950
+ const messages = this.messages;
18951
+ const removedMessages = new Set(messageIds);
18952
+ const newMessages = new Map(
18953
+ Array.from(messages).filter(([messageId, location]) => {
18954
+ if (removedMessages.has(messageId) && location.stopSharingTimeout) {
18955
+ clearTimeout(location.stopSharingTimeout);
18956
+ location.stopSharingTimeout = null;
18957
+ }
18958
+ return !removedMessages.has(messageId);
18959
+ })
18960
+ );
18961
+ if (newMessages.size === messages.size) return;
18962
+ this.state.partialNext({
18963
+ messages: newMessages
18964
+ });
18965
+ }
18966
+ };
18967
+ _LiveLocationManager.symbol = Symbol(_LiveLocationManager.name);
18968
+ var LiveLocationManager = _LiveLocationManager;
18969
+
18597
18970
  // src/utils/FixedSizeQueueCache.ts
18598
18971
  var FixedSizeQueueCache = class {
18599
18972
  constructor(size, options) {
@@ -18672,6 +19045,7 @@ export {
18672
19045
  DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
18673
19046
  DEFAULT_COMPOSER_CONFIG,
18674
19047
  DEFAULT_LINK_PREVIEW_MANAGER_CONFIG,
19048
+ DEFAULT_LOCATION_COMPOSER_CONFIG,
18675
19049
  DEFAULT_PAGINATION_OPTIONS,
18676
19050
  DEFAULT_REMINDER_MANAGER_CONFIG,
18677
19051
  DEFAULT_STOP_REFRESH_BOUNDARY_MS,
@@ -18687,6 +19061,8 @@ export {
18687
19061
  JWTUserToken,
18688
19062
  LinkPreviewStatus,
18689
19063
  LinkPreviewsManager,
19064
+ LiveLocationManager,
19065
+ LocationComposer,
18690
19066
  MAX_POLL_OPTIONS,
18691
19067
  MODERATION_ENTITY_TYPES,
18692
19068
  MaxPriority,
@@ -18723,6 +19099,7 @@ export {
18723
19099
  Thread,
18724
19100
  ThreadManager,
18725
19101
  TokenManager,
19102
+ UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT,
18726
19103
  UserFromToken,
18727
19104
  UserSearchSource,
18728
19105
  VALID_MAX_VOTES_VALUE_REGEX,
@@ -18753,6 +19130,7 @@ export {
18753
19130
  createMentionsMiddleware,
18754
19131
  createMessageComposerStateCompositionMiddleware,
18755
19132
  createPollComposerStateMiddleware,
19133
+ createSharedLocationCompositionMiddleware,
18756
19134
  createTextComposerCompositionMiddleware,
18757
19135
  createTextComposerPreValidationMiddleware,
18758
19136
  decodeBase64,
@@ -18789,6 +19167,7 @@ export {
18789
19167
  isOwnUser,
18790
19168
  isPatch,
18791
19169
  isScrapedContent,
19170
+ isSharedLocationResponse,
18792
19171
  isTargetedOptionTextUpdate,
18793
19172
  isUploadedAttachment,
18794
19173
  isVideoAttachment,