stream-chat 9.41.1 → 9.42.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.
@@ -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({
@@ -10410,6 +10401,131 @@ var StableWSConnection = class {
10410
10401
  }
10411
10402
  };
10412
10403
 
10404
+ // src/uploadManager.ts
10405
+ var initState7 = () => ({ uploads: {} });
10406
+ var upsertById = (uploads, record) => ({
10407
+ ...uploads,
10408
+ [record.id]: { ...uploads[record.id], ...record }
10409
+ });
10410
+ var updateById = (uploads, record) => {
10411
+ if (!(record.id in uploads)) return null;
10412
+ const current = uploads[record.id];
10413
+ return { ...uploads, [record.id]: { ...current, ...record } };
10414
+ };
10415
+ var UploadManager = class {
10416
+ constructor(client) {
10417
+ this.client = client;
10418
+ this.inFlightUploads = /* @__PURE__ */ new Map();
10419
+ this.getUpload = (id) => this.uploads[id];
10420
+ /**
10421
+ * Clears all upload records.
10422
+ * Invoked when the user disconnects so a later session does not inherit stale upload state.
10423
+ * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
10424
+ */
10425
+ this.reset = () => {
10426
+ for (const { abortController } of this.inFlightUploads.values()) {
10427
+ abortController.abort();
10428
+ }
10429
+ this.inFlightUploads.clear();
10430
+ this.state.next(initState7());
10431
+ };
10432
+ /**
10433
+ * Removes the upload record for `id` if present.
10434
+ * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
10435
+ */
10436
+ this.deleteUploadRecord = (id) => {
10437
+ const flight = this.inFlightUploads.get(id);
10438
+ if (flight) {
10439
+ this.inFlightUploads.delete(id);
10440
+ flight.abortController.abort();
10441
+ }
10442
+ this.state.next((current) => {
10443
+ if (!(id in current.uploads)) return current;
10444
+ const uploads = { ...current.uploads };
10445
+ delete uploads[id];
10446
+ return { ...current, uploads };
10447
+ });
10448
+ };
10449
+ /**
10450
+ * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
10451
+ * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
10452
+ * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
10453
+ */
10454
+ this.upload = ({
10455
+ id,
10456
+ channelCid,
10457
+ file
10458
+ }) => {
10459
+ const existing = this.inFlightUploads.get(id);
10460
+ if (existing) return existing.promise;
10461
+ let resolvePromise;
10462
+ let rejectPromise;
10463
+ const promise = new Promise((resolve, reject) => {
10464
+ resolvePromise = resolve;
10465
+ rejectPromise = reject;
10466
+ });
10467
+ const abortController = new AbortController();
10468
+ this.inFlightUploads.set(id, { promise, abortController });
10469
+ void (async () => {
10470
+ const attachmentManager = this.resolveAttachmentManager(channelCid);
10471
+ const trackProgress = attachmentManager.config.trackUploadProgress;
10472
+ try {
10473
+ this.upsertUpload({
10474
+ id,
10475
+ uploadProgress: trackProgress ? 0 : void 0
10476
+ });
10477
+ const onProgress = trackProgress ? (progress) => {
10478
+ this.updateUpload({
10479
+ id,
10480
+ uploadProgress: progress
10481
+ });
10482
+ } : void 0;
10483
+ const uploadRequestOptions = {
10484
+ abortSignal: abortController.signal,
10485
+ ...onProgress ? { onProgress } : {}
10486
+ };
10487
+ const response = await attachmentManager.doUploadRequest(
10488
+ file,
10489
+ uploadRequestOptions
10490
+ );
10491
+ resolvePromise(response);
10492
+ } catch (error) {
10493
+ rejectPromise(error);
10494
+ } finally {
10495
+ this.inFlightUploads.delete(id);
10496
+ this.deleteUploadRecord(id);
10497
+ }
10498
+ })();
10499
+ return promise;
10500
+ };
10501
+ this.upsertUpload = (record) => {
10502
+ this.state.partialNext({
10503
+ uploads: upsertById(this.uploads, record)
10504
+ });
10505
+ };
10506
+ this.updateUpload = (record) => {
10507
+ this.state.next((current) => {
10508
+ const nextUploads = updateById(current.uploads, record);
10509
+ if (!nextUploads) return current;
10510
+ return { ...current, uploads: nextUploads };
10511
+ });
10512
+ };
10513
+ this.state = new StateStore(initState7());
10514
+ }
10515
+ resolveAttachmentManager(channelCid) {
10516
+ const colon = channelCid.indexOf(":");
10517
+ if (colon <= 0 || colon === channelCid.length - 1) {
10518
+ throw new Error(`Invalid channelCid: ${channelCid}`);
10519
+ }
10520
+ const channelType = channelCid.slice(0, colon);
10521
+ const channelId = channelCid.slice(colon + 1);
10522
+ return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
10523
+ }
10524
+ get uploads() {
10525
+ return this.state.getLatestValue().uploads;
10526
+ }
10527
+ };
10528
+
10413
10529
  // src/signing.ts
10414
10530
  var import_jsonwebtoken = __toESM(require_jsonwebtoken());
10415
10531
  var import_crypto = __toESM(require_crypto());
@@ -12882,6 +12998,7 @@ var StreamChat = class _StreamChat {
12882
12998
  this.activeChannels = {};
12883
12999
  this.state = new ClientState({ client: this });
12884
13000
  this.threads.resetState();
13001
+ this.uploadManager.reset();
12885
13002
  closePromise.finally(() => {
12886
13003
  this.tokenManager.reset();
12887
13004
  }).catch((err) => console.error(err));
@@ -13288,6 +13405,7 @@ var StreamChat = class _StreamChat {
13288
13405
  this.blockedUsers = new StateStore({ userIds: [] });
13289
13406
  this.moderation = new Moderation(this);
13290
13407
  this.notifications = options?.notifications ?? new NotificationManager();
13408
+ this.uploadManager = new UploadManager(this);
13291
13409
  if (secretOrOptions && isString2(secretOrOptions)) {
13292
13410
  this.secret = secretOrOptions;
13293
13411
  }
@@ -15077,7 +15195,7 @@ var StreamChat = class _StreamChat {
15077
15195
  if (this.userAgent) {
15078
15196
  return this.userAgent;
15079
15197
  }
15080
- const version = "9.41.1";
15198
+ const version = "9.42.0";
15081
15199
  const clientBundle = "browser-esm";
15082
15200
  let userAgentString = "";
15083
15201
  if (this.sdkIdentifier) {
@@ -17766,6 +17884,7 @@ export {
17766
17884
  ThreadManager,
17767
17885
  TokenManager,
17768
17886
  UPDATE_LIVE_LOCATION_REQUEST_MIN_THROTTLE_TIMEOUT,
17887
+ UploadManager,
17769
17888
  UserFromToken,
17770
17889
  UserSearchSource,
17771
17890
  VALID_MAX_VOTES_VALUE_REGEX,