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.
@@ -3110,11 +3110,11 @@ var _AttachmentManager = class _AttachmentManager {
3110
3110
  return attachments;
3111
3111
  }
3112
3112
  }
3113
- return null;
3113
+ return stateAttachments;
3114
3114
  };
3115
3115
  this.updateAttachment = (attachmentToUpdate) => {
3116
3116
  const updatedAttachments = this.prepareAttachmentUpdate(attachmentToUpdate);
3117
- if (updatedAttachments) {
3117
+ if (updatedAttachments && updatedAttachments !== this.attachments) {
3118
3118
  this.state.partialNext({ attachments: updatedAttachments });
3119
3119
  }
3120
3120
  };
@@ -3124,15 +3124,15 @@ var _AttachmentManager = class _AttachmentManager {
3124
3124
  let hasUpdates = false;
3125
3125
  attachmentsToUpsert.forEach((attachment) => {
3126
3126
  const updatedAttachments = this.prepareAttachmentUpdate(attachment);
3127
- if (updatedAttachments) {
3128
- attachments = updatedAttachments;
3129
- hasUpdates = true;
3130
- } else {
3127
+ if (updatedAttachments === null) {
3131
3128
  const localAttachment = ensureIsLocalAttachment(attachment);
3132
3129
  if (localAttachment) {
3133
3130
  attachments.push(localAttachment);
3134
3131
  hasUpdates = true;
3135
3132
  }
3133
+ } else if (updatedAttachments !== this.attachments) {
3134
+ attachments = updatedAttachments;
3135
+ hasUpdates = true;
3136
3136
  }
3137
3137
  });
3138
3138
  if (hasUpdates) {
@@ -3140,11 +3140,15 @@ var _AttachmentManager = class _AttachmentManager {
3140
3140
  }
3141
3141
  };
3142
3142
  this.removeAttachments = (localAttachmentIds) => {
3143
+ if (!localAttachmentIds.length) return;
3143
3144
  this.state.partialNext({
3144
3145
  attachments: this.attachments.filter(
3145
3146
  (attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
3146
3147
  )
3147
3148
  });
3149
+ for (const id of localAttachmentIds) {
3150
+ this.client.uploadManager.deleteUploadRecord(id);
3151
+ }
3148
3152
  };
3149
3153
  this.getUploadConfigCheck = async (fileLike) => {
3150
3154
  const client = this.channel.getClient();
@@ -3233,13 +3237,17 @@ var _AttachmentManager = class _AttachmentManager {
3233
3237
  const percent = progressEvent.lengthComputable && progressEvent.total ? Math.round(progressEvent.loaded * 100 / progressEvent.total) : void 0;
3234
3238
  options.onProgress?.(percent);
3235
3239
  } : void 0;
3240
+ const axiosUploadConfig = progressHandler || options?.abortSignal ? {
3241
+ ...progressHandler ? { onUploadProgress: progressHandler } : {},
3242
+ ...options?.abortSignal ? { signal: options.abortSignal } : {}
3243
+ } : void 0;
3236
3244
  if (isFileReference(fileLike)) {
3237
3245
  return this.channel[isImageFile(fileLike) ? "sendImage" : "sendFile"](
3238
3246
  fileLike.uri,
3239
3247
  fileLike.name,
3240
3248
  fileLike.type,
3241
3249
  void 0,
3242
- progressHandler ? { onUploadProgress: progressHandler } : void 0
3250
+ axiosUploadConfig
3243
3251
  );
3244
3252
  }
3245
3253
  const file = isFile(fileLike) ? fileLike : createFileFromBlobs({
@@ -3247,13 +3255,7 @@ var _AttachmentManager = class _AttachmentManager {
3247
3255
  fileName: generateFileName(fileLike.type),
3248
3256
  mimeType: fileLike.type
3249
3257
  });
3250
- const { duration, ...result } = await this.channel[isImageFile(fileLike) ? "sendImage" : "sendFile"](
3251
- file,
3252
- void 0,
3253
- void 0,
3254
- void 0,
3255
- progressHandler ? { onUploadProgress: progressHandler } : void 0
3256
- );
3258
+ const { duration, ...result } = await this.channel[isImageFile(fileLike) ? "sendImage" : "sendFile"](file, void 0, void 0, void 0, axiosUploadConfig);
3257
3259
  return result;
3258
3260
  };
3259
3261
  /**
@@ -3288,33 +3290,9 @@ var _AttachmentManager = class _AttachmentManager {
3288
3290
  });
3289
3291
  return localAttachment;
3290
3292
  }
3291
- const shouldTrackProgress = this.config.trackUploadProgress;
3292
- const uploadingAttachment = {
3293
- ...attachment,
3294
- localMetadata: {
3295
- ...attachment.localMetadata,
3296
- uploadState: "uploading",
3297
- ...shouldTrackProgress && { uploadProgress: 0 }
3298
- }
3299
- };
3300
- this.upsertAttachments([uploadingAttachment]);
3301
- const uploadOptions = shouldTrackProgress ? {
3302
- onProgress: (percent) => {
3303
- this.updateAttachment({
3304
- ...uploadingAttachment,
3305
- localMetadata: {
3306
- ...uploadingAttachment.localMetadata,
3307
- uploadProgress: percent
3308
- }
3309
- });
3310
- }
3311
- } : void 0;
3312
3293
  let response;
3313
3294
  try {
3314
- response = await this.doUploadRequest(
3315
- localAttachment.localMetadata.file,
3316
- uploadOptions
3317
- );
3295
+ response = await this.upload(attachment);
3318
3296
  } catch (error) {
3319
3297
  const reason = error instanceof Error ? error.message : "unknown error";
3320
3298
  const failedAttachment = {
@@ -3383,31 +3361,10 @@ var _AttachmentManager = class _AttachmentManager {
3383
3361
  this.upsertAttachments([attachment]);
3384
3362
  return preUpload.state.attachment;
3385
3363
  }
3386
- const shouldTrackProgress = this.config.trackUploadProgress;
3387
- attachment = {
3388
- ...attachment,
3389
- localMetadata: {
3390
- ...attachment.localMetadata,
3391
- uploadState: "uploading",
3392
- ...shouldTrackProgress && { uploadProgress: 0 }
3393
- }
3394
- };
3395
- this.upsertAttachments([attachment]);
3396
- const uploadOptions = shouldTrackProgress ? {
3397
- onProgress: (percent) => {
3398
- this.updateAttachment({
3399
- ...attachment,
3400
- localMetadata: {
3401
- ...attachment.localMetadata,
3402
- uploadProgress: percent
3403
- }
3404
- });
3405
- }
3406
- } : void 0;
3407
3364
  let response;
3408
3365
  let error;
3409
3366
  try {
3410
- response = await this.doUploadRequest(file, uploadOptions);
3367
+ response = await this.upload(attachment);
3411
3368
  } catch (err) {
3412
3369
  error = err instanceof Error ? err : void 0;
3413
3370
  }
@@ -3534,6 +3491,40 @@ var _AttachmentManager = class _AttachmentManager {
3534
3491
  ({ localMetadata }) => localMetadata.uploadState === state
3535
3492
  );
3536
3493
  }
3494
+ upload(attachment) {
3495
+ const localId = attachment.localMetadata.id;
3496
+ this.upsertAttachments([
3497
+ {
3498
+ ...attachment,
3499
+ localMetadata: {
3500
+ ...attachment.localMetadata,
3501
+ uploadState: "uploading",
3502
+ uploadProgress: this.config.trackUploadProgress ? 0 : void 0
3503
+ }
3504
+ }
3505
+ ]);
3506
+ const unsubscribe = this.client.uploadManager.state.subscribeWithSelector(
3507
+ (s) => ({ upload: s.uploads[localId] }),
3508
+ ({ upload: nextUpload }) => {
3509
+ if (!nextUpload) return;
3510
+ this.updateAttachment({
3511
+ ...attachment,
3512
+ localMetadata: {
3513
+ ...attachment.localMetadata,
3514
+ uploadState: "uploading",
3515
+ uploadProgress: nextUpload.uploadProgress
3516
+ }
3517
+ });
3518
+ }
3519
+ );
3520
+ return this.client.uploadManager.upload({
3521
+ id: localId,
3522
+ channelCid: this.channel.cid,
3523
+ file: attachment.localMetadata.file
3524
+ }).finally(() => {
3525
+ unsubscribe();
3526
+ });
3527
+ }
3537
3528
  };
3538
3529
  _AttachmentManager.toLocalUploadAttachment = (fileLike) => {
3539
3530
  const file = isFileReference(fileLike) || isFile(fileLike) ? fileLike : createFileFromBlobs({
@@ -7386,9 +7377,6 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7386
7377
  return this.state.getLatestValue().showReplyInChannel;
7387
7378
  }
7388
7379
  get hasSendableData() {
7389
- if (this.client.offlineDb) {
7390
- return !this.textComposer.textIsEmpty || !!this.attachmentManager.attachments.length || !!this.pollId || !!this.locationComposer.validLocation;
7391
- }
7392
7380
  return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
7393
7381
  }
7394
7382
  get compositionIsEmpty() {
@@ -10410,6 +10398,131 @@ var StableWSConnection = class {
10410
10398
  }
10411
10399
  };
10412
10400
 
10401
+ // src/uploadManager.ts
10402
+ var initState7 = () => ({ uploads: {} });
10403
+ var upsertById = (uploads, record) => ({
10404
+ ...uploads,
10405
+ [record.id]: { ...uploads[record.id], ...record }
10406
+ });
10407
+ var updateById = (uploads, record) => {
10408
+ if (!(record.id in uploads)) return null;
10409
+ const current = uploads[record.id];
10410
+ return { ...uploads, [record.id]: { ...current, ...record } };
10411
+ };
10412
+ var UploadManager = class {
10413
+ constructor(client) {
10414
+ this.client = client;
10415
+ this.inFlightUploads = /* @__PURE__ */ new Map();
10416
+ this.getUpload = (id) => this.uploads[id];
10417
+ /**
10418
+ * Clears all upload records.
10419
+ * Invoked when the user disconnects so a later session does not inherit stale upload state.
10420
+ * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
10421
+ */
10422
+ this.reset = () => {
10423
+ for (const { abortController } of this.inFlightUploads.values()) {
10424
+ abortController.abort();
10425
+ }
10426
+ this.inFlightUploads.clear();
10427
+ this.state.next(initState7());
10428
+ };
10429
+ /**
10430
+ * Removes the upload record for `id` if present.
10431
+ * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
10432
+ */
10433
+ this.deleteUploadRecord = (id) => {
10434
+ const flight = this.inFlightUploads.get(id);
10435
+ if (flight) {
10436
+ this.inFlightUploads.delete(id);
10437
+ flight.abortController.abort();
10438
+ }
10439
+ this.state.next((current) => {
10440
+ if (!(id in current.uploads)) return current;
10441
+ const uploads = { ...current.uploads };
10442
+ delete uploads[id];
10443
+ return { ...current, uploads };
10444
+ });
10445
+ };
10446
+ /**
10447
+ * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
10448
+ * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
10449
+ * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
10450
+ */
10451
+ this.upload = ({
10452
+ id,
10453
+ channelCid,
10454
+ file
10455
+ }) => {
10456
+ const existing = this.inFlightUploads.get(id);
10457
+ if (existing) return existing.promise;
10458
+ let resolvePromise;
10459
+ let rejectPromise;
10460
+ const promise = new Promise((resolve, reject) => {
10461
+ resolvePromise = resolve;
10462
+ rejectPromise = reject;
10463
+ });
10464
+ const abortController = new AbortController();
10465
+ this.inFlightUploads.set(id, { promise, abortController });
10466
+ void (async () => {
10467
+ const attachmentManager = this.resolveAttachmentManager(channelCid);
10468
+ const trackProgress = attachmentManager.config.trackUploadProgress;
10469
+ try {
10470
+ this.upsertUpload({
10471
+ id,
10472
+ uploadProgress: trackProgress ? 0 : void 0
10473
+ });
10474
+ const onProgress = trackProgress ? (progress) => {
10475
+ this.updateUpload({
10476
+ id,
10477
+ uploadProgress: progress
10478
+ });
10479
+ } : void 0;
10480
+ const uploadRequestOptions = {
10481
+ abortSignal: abortController.signal,
10482
+ ...onProgress ? { onProgress } : {}
10483
+ };
10484
+ const response = await attachmentManager.doUploadRequest(
10485
+ file,
10486
+ uploadRequestOptions
10487
+ );
10488
+ resolvePromise(response);
10489
+ } catch (error) {
10490
+ rejectPromise(error);
10491
+ } finally {
10492
+ this.inFlightUploads.delete(id);
10493
+ this.deleteUploadRecord(id);
10494
+ }
10495
+ })();
10496
+ return promise;
10497
+ };
10498
+ this.upsertUpload = (record) => {
10499
+ this.state.partialNext({
10500
+ uploads: upsertById(this.uploads, record)
10501
+ });
10502
+ };
10503
+ this.updateUpload = (record) => {
10504
+ this.state.next((current) => {
10505
+ const nextUploads = updateById(current.uploads, record);
10506
+ if (!nextUploads) return current;
10507
+ return { ...current, uploads: nextUploads };
10508
+ });
10509
+ };
10510
+ this.state = new StateStore(initState7());
10511
+ }
10512
+ resolveAttachmentManager(channelCid) {
10513
+ const colon = channelCid.indexOf(":");
10514
+ if (colon <= 0 || colon === channelCid.length - 1) {
10515
+ throw new Error(`Invalid channelCid: ${channelCid}`);
10516
+ }
10517
+ const channelType = channelCid.slice(0, colon);
10518
+ const channelId = channelCid.slice(colon + 1);
10519
+ return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
10520
+ }
10521
+ get uploads() {
10522
+ return this.state.getLatestValue().uploads;
10523
+ }
10524
+ };
10525
+
10413
10526
  // src/signing.ts
10414
10527
  var import_jsonwebtoken = __toESM(require_jsonwebtoken());
10415
10528
  var import_crypto = __toESM(require_crypto());
@@ -12882,6 +12995,7 @@ var StreamChat = class _StreamChat {
12882
12995
  this.activeChannels = {};
12883
12996
  this.state = new ClientState({ client: this });
12884
12997
  this.threads.resetState();
12998
+ this.uploadManager.reset();
12885
12999
  closePromise.finally(() => {
12886
13000
  this.tokenManager.reset();
12887
13001
  }).catch((err) => console.error(err));
@@ -13288,6 +13402,7 @@ var StreamChat = class _StreamChat {
13288
13402
  this.blockedUsers = new StateStore({ userIds: [] });
13289
13403
  this.moderation = new Moderation(this);
13290
13404
  this.notifications = options?.notifications ?? new NotificationManager();
13405
+ this.uploadManager = new UploadManager(this);
13291
13406
  if (secretOrOptions && isString2(secretOrOptions)) {
13292
13407
  this.secret = secretOrOptions;
13293
13408
  }
@@ -15077,7 +15192,7 @@ var StreamChat = class _StreamChat {
15077
15192
  if (this.userAgent) {
15078
15193
  return this.userAgent;
15079
15194
  }
15080
- const version = "9.41.1";
15195
+ const version = "9.42.1";
15081
15196
  const clientBundle = "browser-esm";
15082
15197
  let userAgentString = "";
15083
15198
  if (this.sdkIdentifier) {
@@ -17766,6 +17881,7 @@ export {
17766
17881
  ThreadManager,
17767
17882
  TokenManager,
17768
17883
  UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT,
17884
+ UploadManager,
17769
17885
  UserFromToken,
17770
17886
  UserSearchSource,
17771
17887
  VALID_MAX_VOTES_VALUE_REGEX,