stream-chat 9.41.1 → 9.42.1

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.
@@ -117,6 +117,7 @@ __export(index_exports, {
117
117
  ThreadManager: () => ThreadManager,
118
118
  TokenManager: () => TokenManager,
119
119
  UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT: () => UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT,
120
+ UploadManager: () => UploadManager,
120
121
  UserFromToken: () => UserFromToken,
121
122
  UserSearchSource: () => UserSearchSource,
122
123
  VALID_MAX_VOTES_VALUE_REGEX: () => VALID_MAX_VOTES_VALUE_REGEX,
@@ -3278,11 +3279,11 @@ var _AttachmentManager = class _AttachmentManager {
3278
3279
  return attachments;
3279
3280
  }
3280
3281
  }
3281
- return null;
3282
+ return stateAttachments;
3282
3283
  };
3283
3284
  this.updateAttachment = (attachmentToUpdate) => {
3284
3285
  const updatedAttachments = this.prepareAttachmentUpdate(attachmentToUpdate);
3285
- if (updatedAttachments) {
3286
+ if (updatedAttachments && updatedAttachments !== this.attachments) {
3286
3287
  this.state.partialNext({ attachments: updatedAttachments });
3287
3288
  }
3288
3289
  };
@@ -3292,15 +3293,15 @@ var _AttachmentManager = class _AttachmentManager {
3292
3293
  let hasUpdates = false;
3293
3294
  attachmentsToUpsert.forEach((attachment) => {
3294
3295
  const updatedAttachments = this.prepareAttachmentUpdate(attachment);
3295
- if (updatedAttachments) {
3296
- attachments = updatedAttachments;
3297
- hasUpdates = true;
3298
- } else {
3296
+ if (updatedAttachments === null) {
3299
3297
  const localAttachment = ensureIsLocalAttachment(attachment);
3300
3298
  if (localAttachment) {
3301
3299
  attachments.push(localAttachment);
3302
3300
  hasUpdates = true;
3303
3301
  }
3302
+ } else if (updatedAttachments !== this.attachments) {
3303
+ attachments = updatedAttachments;
3304
+ hasUpdates = true;
3304
3305
  }
3305
3306
  });
3306
3307
  if (hasUpdates) {
@@ -3308,11 +3309,15 @@ var _AttachmentManager = class _AttachmentManager {
3308
3309
  }
3309
3310
  };
3310
3311
  this.removeAttachments = (localAttachmentIds) => {
3312
+ if (!localAttachmentIds.length) return;
3311
3313
  this.state.partialNext({
3312
3314
  attachments: this.attachments.filter(
3313
3315
  (attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
3314
3316
  )
3315
3317
  });
3318
+ for (const id of localAttachmentIds) {
3319
+ this.client.uploadManager.deleteUploadRecord(id);
3320
+ }
3316
3321
  };
3317
3322
  this.getUploadConfigCheck = async (fileLike) => {
3318
3323
  const client = this.channel.getClient();
@@ -3401,13 +3406,17 @@ var _AttachmentManager = class _AttachmentManager {
3401
3406
  const percent = progressEvent.lengthComputable && progressEvent.total ? Math.round(progressEvent.loaded * 100 / progressEvent.total) : void 0;
3402
3407
  options.onProgress?.(percent);
3403
3408
  } : void 0;
3409
+ const axiosUploadConfig = progressHandler || options?.abortSignal ? {
3410
+ ...progressHandler ? { onUploadProgress: progressHandler } : {},
3411
+ ...options?.abortSignal ? { signal: options.abortSignal } : {}
3412
+ } : void 0;
3404
3413
  if (isFileReference(fileLike)) {
3405
3414
  return this.channel[isImageFile(fileLike) ? "sendImage" : "sendFile"](
3406
3415
  fileLike.uri,
3407
3416
  fileLike.name,
3408
3417
  fileLike.type,
3409
3418
  void 0,
3410
- progressHandler ? { onUploadProgress: progressHandler } : void 0
3419
+ axiosUploadConfig
3411
3420
  );
3412
3421
  }
3413
3422
  const file = isFile(fileLike) ? fileLike : createFileFromBlobs({
@@ -3415,13 +3424,7 @@ var _AttachmentManager = class _AttachmentManager {
3415
3424
  fileName: generateFileName(fileLike.type),
3416
3425
  mimeType: fileLike.type
3417
3426
  });
3418
- const { duration, ...result } = await this.channel[isImageFile(fileLike) ? "sendImage" : "sendFile"](
3419
- file,
3420
- void 0,
3421
- void 0,
3422
- void 0,
3423
- progressHandler ? { onUploadProgress: progressHandler } : void 0
3424
- );
3427
+ const { duration, ...result } = await this.channel[isImageFile(fileLike) ? "sendImage" : "sendFile"](file, void 0, void 0, void 0, axiosUploadConfig);
3425
3428
  return result;
3426
3429
  };
3427
3430
  /**
@@ -3456,33 +3459,9 @@ var _AttachmentManager = class _AttachmentManager {
3456
3459
  });
3457
3460
  return localAttachment;
3458
3461
  }
3459
- const shouldTrackProgress = this.config.trackUploadProgress;
3460
- const uploadingAttachment = {
3461
- ...attachment,
3462
- localMetadata: {
3463
- ...attachment.localMetadata,
3464
- uploadState: "uploading",
3465
- ...shouldTrackProgress && { uploadProgress: 0 }
3466
- }
3467
- };
3468
- this.upsertAttachments([uploadingAttachment]);
3469
- const uploadOptions = shouldTrackProgress ? {
3470
- onProgress: (percent) => {
3471
- this.updateAttachment({
3472
- ...uploadingAttachment,
3473
- localMetadata: {
3474
- ...uploadingAttachment.localMetadata,
3475
- uploadProgress: percent
3476
- }
3477
- });
3478
- }
3479
- } : void 0;
3480
3462
  let response;
3481
3463
  try {
3482
- response = await this.doUploadRequest(
3483
- localAttachment.localMetadata.file,
3484
- uploadOptions
3485
- );
3464
+ response = await this.upload(attachment);
3486
3465
  } catch (error) {
3487
3466
  const reason = error instanceof Error ? error.message : "unknown error";
3488
3467
  const failedAttachment = {
@@ -3551,31 +3530,10 @@ var _AttachmentManager = class _AttachmentManager {
3551
3530
  this.upsertAttachments([attachment]);
3552
3531
  return preUpload.state.attachment;
3553
3532
  }
3554
- const shouldTrackProgress = this.config.trackUploadProgress;
3555
- attachment = {
3556
- ...attachment,
3557
- localMetadata: {
3558
- ...attachment.localMetadata,
3559
- uploadState: "uploading",
3560
- ...shouldTrackProgress && { uploadProgress: 0 }
3561
- }
3562
- };
3563
- this.upsertAttachments([attachment]);
3564
- const uploadOptions = shouldTrackProgress ? {
3565
- onProgress: (percent) => {
3566
- this.updateAttachment({
3567
- ...attachment,
3568
- localMetadata: {
3569
- ...attachment.localMetadata,
3570
- uploadProgress: percent
3571
- }
3572
- });
3573
- }
3574
- } : void 0;
3575
3533
  let response;
3576
3534
  let error;
3577
3535
  try {
3578
- response = await this.doUploadRequest(file, uploadOptions);
3536
+ response = await this.upload(attachment);
3579
3537
  } catch (err) {
3580
3538
  error = err instanceof Error ? err : void 0;
3581
3539
  }
@@ -3702,6 +3660,40 @@ var _AttachmentManager = class _AttachmentManager {
3702
3660
  ({ localMetadata }) => localMetadata.uploadState === state
3703
3661
  );
3704
3662
  }
3663
+ upload(attachment) {
3664
+ const localId = attachment.localMetadata.id;
3665
+ this.upsertAttachments([
3666
+ {
3667
+ ...attachment,
3668
+ localMetadata: {
3669
+ ...attachment.localMetadata,
3670
+ uploadState: "uploading",
3671
+ uploadProgress: this.config.trackUploadProgress ? 0 : void 0
3672
+ }
3673
+ }
3674
+ ]);
3675
+ const unsubscribe = this.client.uploadManager.state.subscribeWithSelector(
3676
+ (s) => ({ upload: s.uploads[localId] }),
3677
+ ({ upload: nextUpload }) => {
3678
+ if (!nextUpload) return;
3679
+ this.updateAttachment({
3680
+ ...attachment,
3681
+ localMetadata: {
3682
+ ...attachment.localMetadata,
3683
+ uploadState: "uploading",
3684
+ uploadProgress: nextUpload.uploadProgress
3685
+ }
3686
+ });
3687
+ }
3688
+ );
3689
+ return this.client.uploadManager.upload({
3690
+ id: localId,
3691
+ channelCid: this.channel.cid,
3692
+ file: attachment.localMetadata.file
3693
+ }).finally(() => {
3694
+ unsubscribe();
3695
+ });
3696
+ }
3705
3697
  };
3706
3698
  _AttachmentManager.toLocalUploadAttachment = (fileLike) => {
3707
3699
  const file = isFileReference(fileLike) || isFile(fileLike) ? fileLike : createFileFromBlobs({
@@ -7554,9 +7546,6 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7554
7546
  return this.state.getLatestValue().showReplyInChannel;
7555
7547
  }
7556
7548
  get hasSendableData() {
7557
- if (this.client.offlineDb) {
7558
- return !this.textComposer.textIsEmpty || !!this.attachmentManager.attachments.length || !!this.pollId || !!this.locationComposer.validLocation;
7559
- }
7560
7549
  return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
7561
7550
  }
7562
7551
  get compositionIsEmpty() {
@@ -10578,6 +10567,131 @@ var StableWSConnection = class {
10578
10567
  }
10579
10568
  };
10580
10569
 
10570
+ // src/uploadManager.ts
10571
+ var initState7 = () => ({ uploads: {} });
10572
+ var upsertById = (uploads, record) => ({
10573
+ ...uploads,
10574
+ [record.id]: { ...uploads[record.id], ...record }
10575
+ });
10576
+ var updateById = (uploads, record) => {
10577
+ if (!(record.id in uploads)) return null;
10578
+ const current = uploads[record.id];
10579
+ return { ...uploads, [record.id]: { ...current, ...record } };
10580
+ };
10581
+ var UploadManager = class {
10582
+ constructor(client) {
10583
+ this.client = client;
10584
+ this.inFlightUploads = /* @__PURE__ */ new Map();
10585
+ this.getUpload = (id) => this.uploads[id];
10586
+ /**
10587
+ * Clears all upload records.
10588
+ * Invoked when the user disconnects so a later session does not inherit stale upload state.
10589
+ * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
10590
+ */
10591
+ this.reset = () => {
10592
+ for (const { abortController } of this.inFlightUploads.values()) {
10593
+ abortController.abort();
10594
+ }
10595
+ this.inFlightUploads.clear();
10596
+ this.state.next(initState7());
10597
+ };
10598
+ /**
10599
+ * Removes the upload record for `id` if present.
10600
+ * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
10601
+ */
10602
+ this.deleteUploadRecord = (id) => {
10603
+ const flight = this.inFlightUploads.get(id);
10604
+ if (flight) {
10605
+ this.inFlightUploads.delete(id);
10606
+ flight.abortController.abort();
10607
+ }
10608
+ this.state.next((current) => {
10609
+ if (!(id in current.uploads)) return current;
10610
+ const uploads = { ...current.uploads };
10611
+ delete uploads[id];
10612
+ return { ...current, uploads };
10613
+ });
10614
+ };
10615
+ /**
10616
+ * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
10617
+ * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
10618
+ * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
10619
+ */
10620
+ this.upload = ({
10621
+ id,
10622
+ channelCid,
10623
+ file
10624
+ }) => {
10625
+ const existing = this.inFlightUploads.get(id);
10626
+ if (existing) return existing.promise;
10627
+ let resolvePromise;
10628
+ let rejectPromise;
10629
+ const promise = new Promise((resolve, reject) => {
10630
+ resolvePromise = resolve;
10631
+ rejectPromise = reject;
10632
+ });
10633
+ const abortController = new AbortController();
10634
+ this.inFlightUploads.set(id, { promise, abortController });
10635
+ void (async () => {
10636
+ const attachmentManager = this.resolveAttachmentManager(channelCid);
10637
+ const trackProgress = attachmentManager.config.trackUploadProgress;
10638
+ try {
10639
+ this.upsertUpload({
10640
+ id,
10641
+ uploadProgress: trackProgress ? 0 : void 0
10642
+ });
10643
+ const onProgress = trackProgress ? (progress) => {
10644
+ this.updateUpload({
10645
+ id,
10646
+ uploadProgress: progress
10647
+ });
10648
+ } : void 0;
10649
+ const uploadRequestOptions = {
10650
+ abortSignal: abortController.signal,
10651
+ ...onProgress ? { onProgress } : {}
10652
+ };
10653
+ const response = await attachmentManager.doUploadRequest(
10654
+ file,
10655
+ uploadRequestOptions
10656
+ );
10657
+ resolvePromise(response);
10658
+ } catch (error) {
10659
+ rejectPromise(error);
10660
+ } finally {
10661
+ this.inFlightUploads.delete(id);
10662
+ this.deleteUploadRecord(id);
10663
+ }
10664
+ })();
10665
+ return promise;
10666
+ };
10667
+ this.upsertUpload = (record) => {
10668
+ this.state.partialNext({
10669
+ uploads: upsertById(this.uploads, record)
10670
+ });
10671
+ };
10672
+ this.updateUpload = (record) => {
10673
+ this.state.next((current) => {
10674
+ const nextUploads = updateById(current.uploads, record);
10675
+ if (!nextUploads) return current;
10676
+ return { ...current, uploads: nextUploads };
10677
+ });
10678
+ };
10679
+ this.state = new StateStore(initState7());
10680
+ }
10681
+ resolveAttachmentManager(channelCid) {
10682
+ const colon = channelCid.indexOf(":");
10683
+ if (colon <= 0 || colon === channelCid.length - 1) {
10684
+ throw new Error(`Invalid channelCid: ${channelCid}`);
10685
+ }
10686
+ const channelType = channelCid.slice(0, colon);
10687
+ const channelId = channelCid.slice(colon + 1);
10688
+ return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
10689
+ }
10690
+ get uploads() {
10691
+ return this.state.getLatestValue().uploads;
10692
+ }
10693
+ };
10694
+
10581
10695
  // src/signing.ts
10582
10696
  var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
10583
10697
  var import_crypto = __toESM(require("crypto"));
@@ -13050,6 +13164,7 @@ var StreamChat = class _StreamChat {
13050
13164
  this.activeChannels = {};
13051
13165
  this.state = new ClientState({ client: this });
13052
13166
  this.threads.resetState();
13167
+ this.uploadManager.reset();
13053
13168
  closePromise.finally(() => {
13054
13169
  this.tokenManager.reset();
13055
13170
  }).catch((err) => console.error(err));
@@ -13456,6 +13571,7 @@ var StreamChat = class _StreamChat {
13456
13571
  this.blockedUsers = new StateStore({ userIds: [] });
13457
13572
  this.moderation = new Moderation(this);
13458
13573
  this.notifications = options?.notifications ?? new NotificationManager();
13574
+ this.uploadManager = new UploadManager(this);
13459
13575
  if (secretOrOptions && isString2(secretOrOptions)) {
13460
13576
  this.secret = secretOrOptions;
13461
13577
  }
@@ -15245,7 +15361,7 @@ var StreamChat = class _StreamChat {
15245
15361
  if (this.userAgent) {
15246
15362
  return this.userAgent;
15247
15363
  }
15248
- const version = "9.41.1";
15364
+ const version = "9.42.1";
15249
15365
  const clientBundle = "node-cjs";
15250
15366
  let userAgentString = "";
15251
15367
  if (this.sdkIdentifier) {
@@ -17935,6 +18051,7 @@ var FixedSizeQueueCache = class {
17935
18051
  ThreadManager,
17936
18052
  TokenManager,
17937
18053
  UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT,
18054
+ UploadManager,
17938
18055
  UserFromToken,
17939
18056
  UserSearchSource,
17940
18057
  VALID_MAX_VOTES_VALUE_REGEX,