stream-chat 9.51.0 → 9.52.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.
- package/dist/cjs/index.browser.js +418 -215
- package/dist/cjs/index.browser.js.map +4 -4
- package/dist/cjs/index.node.js +427 -216
- package/dist/cjs/index.node.js.map +4 -4
- package/dist/esm/index.mjs +416 -213
- package/dist/esm/index.mjs.map +4 -4
- package/dist/types/channel.d.ts +5 -3
- package/dist/types/client.d.ts +10 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/messageComposer/attachmentIdentity.d.ts +4 -0
- package/dist/types/messageComposer/attachmentManager.d.ts +8 -0
- package/dist/types/messageComposer/messageComposer.d.ts +13 -0
- package/dist/types/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.d.ts +20 -1
- package/dist/types/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.d.ts +6 -1
- package/dist/types/messageComposer/middleware/messageComposer/attachments.d.ts +24 -0
- package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +22 -1
- package/dist/types/messageComposer/types.d.ts +5 -0
- package/dist/types/middleware.d.ts +8 -0
- package/dist/types/uploadManager.d.ts +30 -0
- package/dist/types/utils.d.ts +29 -6
- package/package.json +1 -1
- package/src/channel.ts +42 -26
- package/src/client.ts +27 -14
- package/src/index.ts +12 -0
- package/src/messageComposer/attachmentIdentity.ts +27 -0
- package/src/messageComposer/attachmentManager.ts +61 -12
- package/src/messageComposer/messageComposer.ts +43 -8
- package/src/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.ts +1 -1
- package/src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts +58 -33
- package/src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts +4 -0
- package/src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts +9 -0
- package/src/messageComposer/middleware/messageComposer/attachments.ts +109 -0
- package/src/messageComposer/middleware/messageComposer/types.ts +22 -1
- package/src/messageComposer/types.ts +5 -0
- package/src/messageDelivery/MessageDeliveryReporter.ts +5 -4
- package/src/middleware.ts +11 -0
- package/src/offline-support/offline_support_api.ts +4 -0
- package/src/uploadManager.ts +45 -0
- package/src/utils.ts +102 -7
package/dist/esm/index.mjs
CHANGED
|
@@ -316,7 +316,7 @@ var ChannelBatchUpdater = class {
|
|
|
316
316
|
|
|
317
317
|
// src/client.ts
|
|
318
318
|
var import_https = __toESM(require_https());
|
|
319
|
-
import
|
|
319
|
+
import axios4 from "axios";
|
|
320
320
|
|
|
321
321
|
// src/utils.ts
|
|
322
322
|
import FormData from "form-data";
|
|
@@ -364,6 +364,22 @@ function logChatPromiseExecution(promise, name) {
|
|
|
364
364
|
console.warn(`failed to do ${name}, ran into error: `, error);
|
|
365
365
|
});
|
|
366
366
|
}
|
|
367
|
+
var noopLogger = () => null;
|
|
368
|
+
var logError = (logger, error, event) => {
|
|
369
|
+
const message = "Unhandled error in event listener";
|
|
370
|
+
logger("error", message, { error, event });
|
|
371
|
+
if (logger === noopLogger) console.error(message, error, event);
|
|
372
|
+
};
|
|
373
|
+
var invokeEventListener = (listener, event, logger) => {
|
|
374
|
+
try {
|
|
375
|
+
const result = listener(event);
|
|
376
|
+
if (result && typeof result.then === "function") {
|
|
377
|
+
Promise.resolve(result).catch((error) => logError(logger, error, event));
|
|
378
|
+
}
|
|
379
|
+
} catch (error) {
|
|
380
|
+
logError(logger, error, event);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
367
383
|
var sleep = (m) => new Promise((r) => setTimeout(r, m));
|
|
368
384
|
function isFunction(value) {
|
|
369
385
|
return typeof value === "function" || value instanceof Function || Object.prototype.toString.call(value) === "[object Function]";
|
|
@@ -411,7 +427,6 @@ var channelHasReadEvents = (channel) => {
|
|
|
411
427
|
return !(Array.isArray(ownCapabilities) && !ownCapabilities.includes("read-events"));
|
|
412
428
|
};
|
|
413
429
|
var channelTracksReadLocally = (channel) => !channelHasReadEvents(channel) && !!channel?.getClient().options.isLocalUnreadCountEnabled;
|
|
414
|
-
var userHasReadReceipts = (client) => client.user?.privacy_settings?.read_receipts?.enabled ?? true;
|
|
415
430
|
function addFileToFormData(uri, name, contentType) {
|
|
416
431
|
const data = new FormData();
|
|
417
432
|
if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri) || isBlobWebAPI(uri)) {
|
|
@@ -575,6 +590,38 @@ function unformatMessage(message) {
|
|
|
575
590
|
quoted_message: toMessageResponseBase(message.quoted_message)
|
|
576
591
|
};
|
|
577
592
|
}
|
|
593
|
+
var isRemoteUrl = (url) => !!url && /^https?:\/\//i.test(url.trim());
|
|
594
|
+
var sanitizeOutgoingAttachments = ({
|
|
595
|
+
client,
|
|
596
|
+
message
|
|
597
|
+
}) => {
|
|
598
|
+
const attachments = message.attachments;
|
|
599
|
+
if (!attachments?.length) return message;
|
|
600
|
+
const unresolved = [];
|
|
601
|
+
let changed = false;
|
|
602
|
+
const sanitized = attachments.reduce((acc, attachment) => {
|
|
603
|
+
const { localMetadata, ...rest } = attachment;
|
|
604
|
+
if (!localMetadata) {
|
|
605
|
+
acc.push(attachment);
|
|
606
|
+
return acc;
|
|
607
|
+
}
|
|
608
|
+
changed = true;
|
|
609
|
+
const hasRemoteSource = isRemoteUrl(rest.asset_url) || isRemoteUrl(rest.image_url) || isRemoteUrl(rest.og_scrape_url) || isRemoteUrl(rest.title_link);
|
|
610
|
+
if (hasRemoteSource) {
|
|
611
|
+
acc.push(rest);
|
|
612
|
+
} else {
|
|
613
|
+
unresolved.push(rest);
|
|
614
|
+
}
|
|
615
|
+
return acc;
|
|
616
|
+
}, []);
|
|
617
|
+
if (unresolved.length) {
|
|
618
|
+
const text = `Dropped ${unresolved.length} attachment(s) whose upload never completed. Composing with pending uploads requires awaiting them before sending.`;
|
|
619
|
+
const extraData = { attachments: unresolved, message, tags: ["attachments"] };
|
|
620
|
+
client.logger("warn", text, extraData);
|
|
621
|
+
console.warn(text, extraData);
|
|
622
|
+
}
|
|
623
|
+
return changed ? { ...message, attachments: sanitized } : message;
|
|
624
|
+
};
|
|
578
625
|
var localMessageToNewMessagePayload = (localMessage) => {
|
|
579
626
|
const {
|
|
580
627
|
// Remove all timestamp fields and client-specific fields.
|
|
@@ -2347,6 +2394,12 @@ var CooldownTimer = class extends WithSubscriptions {
|
|
|
2347
2394
|
var isScrapedContent = (attachment) => !!attachment?.og_scrape_url || !!attachment?.title_link;
|
|
2348
2395
|
var isLocalAttachment = (attachment) => !!attachment?.localMetadata?.id;
|
|
2349
2396
|
var isLocalUploadAttachment = (attachment) => !!attachment?.localMetadata?.uploadState;
|
|
2397
|
+
var PENDING_UPLOAD_STATES = [
|
|
2398
|
+
"pending",
|
|
2399
|
+
"uploading"
|
|
2400
|
+
];
|
|
2401
|
+
var isPendingUpload = (attachment) => isLocalUploadAttachment(attachment) && PENDING_UPLOAD_STATES.includes(attachment.localMetadata.uploadState);
|
|
2402
|
+
var isFinishedUpload = (attachment) => isLocalUploadAttachment(attachment) && attachment.localMetadata.uploadState === "finished";
|
|
2350
2403
|
var isFileAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "file" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) === -1 && attachment.type !== "video");
|
|
2351
2404
|
var isLocalFileAttachment = (attachment) => isFileAttachment(attachment) && isLocalAttachment(attachment);
|
|
2352
2405
|
var isImageAttachment = (attachment) => attachment.type === "image" && !isScrapedContent(attachment);
|
|
@@ -2422,7 +2475,7 @@ var ensureIsLocalAttachment = (attachment) => {
|
|
|
2422
2475
|
};
|
|
2423
2476
|
|
|
2424
2477
|
// src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts
|
|
2425
|
-
var createPostUploadAttachmentEnrichmentMiddleware = () => ({
|
|
2478
|
+
var createPostUploadAttachmentEnrichmentMiddleware = (composer) => ({
|
|
2426
2479
|
id: "stream-io/attachment-manager-middleware/post-upload-enrichment",
|
|
2427
2480
|
handlers: {
|
|
2428
2481
|
postProcess: ({
|
|
@@ -2435,9 +2488,10 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
|
|
|
2435
2488
|
if (error) return forward();
|
|
2436
2489
|
if (!attachment || !response) return discard();
|
|
2437
2490
|
const enrichedAttachment = { ...attachment };
|
|
2438
|
-
const previewUri = attachment.localMetadata
|
|
2439
|
-
|
|
2440
|
-
|
|
2491
|
+
const { id, previewUri } = attachment.localMetadata;
|
|
2492
|
+
const attachmentIsStillInComposer = !composer || !!composer.attachmentManager.attachmentsById[id];
|
|
2493
|
+
if (previewUri && attachmentIsStillInComposer) {
|
|
2494
|
+
if (previewUri.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
|
|
2441
2495
|
delete enrichedAttachment.localMetadata.previewUri;
|
|
2442
2496
|
}
|
|
2443
2497
|
if (isLocalImageAttachment(attachment)) {
|
|
@@ -2460,6 +2514,12 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
|
|
|
2460
2514
|
var withoutConcurrency = createRunner(wrapWithContinuationTracking);
|
|
2461
2515
|
var withCancellation = createRunner(wrapWithCancellation);
|
|
2462
2516
|
var pendingPromises = /* @__PURE__ */ new Map();
|
|
2517
|
+
function hasPending(tag) {
|
|
2518
|
+
return pendingPromises.has(tag);
|
|
2519
|
+
}
|
|
2520
|
+
async function settled(tag) {
|
|
2521
|
+
await pendingPromises.get(tag)?.promise;
|
|
2522
|
+
}
|
|
2463
2523
|
function createRunner(wrapper) {
|
|
2464
2524
|
return function run(tag, cb) {
|
|
2465
2525
|
const { cb: wrapped, onContinued } = wrapper(tag, cb);
|
|
@@ -2502,6 +2562,16 @@ var MiddlewareExecutor = class {
|
|
|
2502
2562
|
this.middleware = [];
|
|
2503
2563
|
this.id = generateUUIDv4();
|
|
2504
2564
|
}
|
|
2565
|
+
/**
|
|
2566
|
+
* The middleware currently installed, in execution order.
|
|
2567
|
+
*
|
|
2568
|
+
* Read-only: use `use` / `insert` / `replace` / `setOrder` / `remove` to change the chain. It
|
|
2569
|
+
* exists so components can ask what the chain is capable of - a middleware may declare
|
|
2570
|
+
* behaviour its host has to account for, and the host has no other way to find out.
|
|
2571
|
+
*/
|
|
2572
|
+
get installedMiddleware() {
|
|
2573
|
+
return this.middleware;
|
|
2574
|
+
}
|
|
2505
2575
|
use(middleware) {
|
|
2506
2576
|
this.middleware = this.middleware.concat(middleware);
|
|
2507
2577
|
return this;
|
|
@@ -2605,6 +2675,142 @@ var MiddlewareExecutor = class {
|
|
|
2605
2675
|
}
|
|
2606
2676
|
};
|
|
2607
2677
|
|
|
2678
|
+
// src/uploadManager.ts
|
|
2679
|
+
import axios from "axios";
|
|
2680
|
+
var isUploadCancellation = (error) => axios.isCancel(error) || error instanceof Error && error.name === "AbortError";
|
|
2681
|
+
var initState = () => ({ uploads: {} });
|
|
2682
|
+
var upsertById = (uploads, record) => ({
|
|
2683
|
+
...uploads,
|
|
2684
|
+
[record.id]: { ...uploads[record.id], ...record }
|
|
2685
|
+
});
|
|
2686
|
+
var updateById = (uploads, record) => {
|
|
2687
|
+
if (!(record.id in uploads)) return null;
|
|
2688
|
+
const current = uploads[record.id];
|
|
2689
|
+
return { ...uploads, [record.id]: { ...current, ...record } };
|
|
2690
|
+
};
|
|
2691
|
+
var UploadManager = class {
|
|
2692
|
+
constructor(client) {
|
|
2693
|
+
this.client = client;
|
|
2694
|
+
this.inFlightUploads = /* @__PURE__ */ new Map();
|
|
2695
|
+
this.getUpload = (id) => this.uploads[id];
|
|
2696
|
+
/**
|
|
2697
|
+
* Clears all upload records.
|
|
2698
|
+
* Invoked when the user disconnects so a later session does not inherit stale upload state.
|
|
2699
|
+
* Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
|
|
2700
|
+
*/
|
|
2701
|
+
this.reset = () => {
|
|
2702
|
+
for (const { abortController } of this.inFlightUploads.values()) {
|
|
2703
|
+
abortController.abort();
|
|
2704
|
+
}
|
|
2705
|
+
this.inFlightUploads.clear();
|
|
2706
|
+
this.state.next(initState());
|
|
2707
|
+
};
|
|
2708
|
+
/**
|
|
2709
|
+
* Removes the upload record for `id` if present.
|
|
2710
|
+
* If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
|
|
2711
|
+
*/
|
|
2712
|
+
this.deleteUploadRecord = (id) => {
|
|
2713
|
+
const flight = this.inFlightUploads.get(id);
|
|
2714
|
+
if (flight) {
|
|
2715
|
+
this.inFlightUploads.delete(id);
|
|
2716
|
+
flight.abortController.abort();
|
|
2717
|
+
}
|
|
2718
|
+
this.state.next((current) => {
|
|
2719
|
+
if (!(id in current.uploads)) return current;
|
|
2720
|
+
const uploads = { ...current.uploads };
|
|
2721
|
+
delete uploads[id];
|
|
2722
|
+
return { ...current, uploads };
|
|
2723
|
+
});
|
|
2724
|
+
};
|
|
2725
|
+
/**
|
|
2726
|
+
* Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
|
|
2727
|
+
* Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
|
|
2728
|
+
* Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
|
|
2729
|
+
*/
|
|
2730
|
+
this.upload = ({
|
|
2731
|
+
id,
|
|
2732
|
+
channelCid,
|
|
2733
|
+
file
|
|
2734
|
+
}) => {
|
|
2735
|
+
const existing = this.inFlightUploads.get(id);
|
|
2736
|
+
if (existing) return existing.promise;
|
|
2737
|
+
let resolvePromise;
|
|
2738
|
+
let rejectPromise;
|
|
2739
|
+
const promise = new Promise((resolve, reject) => {
|
|
2740
|
+
resolvePromise = resolve;
|
|
2741
|
+
rejectPromise = reject;
|
|
2742
|
+
});
|
|
2743
|
+
const abortController = new AbortController();
|
|
2744
|
+
this.inFlightUploads.set(id, { promise, abortController });
|
|
2745
|
+
void (async () => {
|
|
2746
|
+
const attachmentManager = this.resolveAttachmentManager(channelCid);
|
|
2747
|
+
const trackProgress = attachmentManager.config.trackUploadProgress;
|
|
2748
|
+
try {
|
|
2749
|
+
this.upsertUpload({
|
|
2750
|
+
uploadConfirmationPending: false,
|
|
2751
|
+
id,
|
|
2752
|
+
uploadProgress: trackProgress ? 0 : void 0
|
|
2753
|
+
});
|
|
2754
|
+
const onProgress = trackProgress ? (progress) => {
|
|
2755
|
+
this.updateUpload({
|
|
2756
|
+
id,
|
|
2757
|
+
uploadProgress: progress,
|
|
2758
|
+
// Only a number says anything about the flush, so a report without one leaves
|
|
2759
|
+
// the flag alone: `undefined` means the transport cannot measure this upload,
|
|
2760
|
+
// not that bytes were un-sent. Lowering it there would drop a UI that had
|
|
2761
|
+
// already gone indeterminate back to a determinate one with nothing to show.
|
|
2762
|
+
//
|
|
2763
|
+
// The record is removed in the `finally` below, so a record that still exists
|
|
2764
|
+
// while reporting 100% can only mean "flushed, awaiting the response".
|
|
2765
|
+
...typeof progress === "number" ? { uploadConfirmationPending: progress >= 100 } : {}
|
|
2766
|
+
});
|
|
2767
|
+
} : void 0;
|
|
2768
|
+
const uploadRequestOptions = {
|
|
2769
|
+
abortSignal: abortController.signal,
|
|
2770
|
+
...onProgress ? { onProgress } : {}
|
|
2771
|
+
};
|
|
2772
|
+
const response = await attachmentManager.doUploadRequest(
|
|
2773
|
+
file,
|
|
2774
|
+
uploadRequestOptions
|
|
2775
|
+
);
|
|
2776
|
+
resolvePromise(response);
|
|
2777
|
+
} catch (error) {
|
|
2778
|
+
rejectPromise(error);
|
|
2779
|
+
} finally {
|
|
2780
|
+
this.inFlightUploads.delete(id);
|
|
2781
|
+
this.deleteUploadRecord(id);
|
|
2782
|
+
}
|
|
2783
|
+
})();
|
|
2784
|
+
return promise;
|
|
2785
|
+
};
|
|
2786
|
+
this.upsertUpload = (record) => {
|
|
2787
|
+
this.state.partialNext({
|
|
2788
|
+
uploads: upsertById(this.uploads, record)
|
|
2789
|
+
});
|
|
2790
|
+
};
|
|
2791
|
+
this.updateUpload = (record) => {
|
|
2792
|
+
this.state.next((current) => {
|
|
2793
|
+
const nextUploads = updateById(current.uploads, record);
|
|
2794
|
+
if (!nextUploads) return current;
|
|
2795
|
+
return { ...current, uploads: nextUploads };
|
|
2796
|
+
});
|
|
2797
|
+
};
|
|
2798
|
+
this.state = new StateStore(initState());
|
|
2799
|
+
}
|
|
2800
|
+
resolveAttachmentManager(channelCid) {
|
|
2801
|
+
const colon = channelCid.indexOf(":");
|
|
2802
|
+
if (colon <= 0 || colon === channelCid.length - 1) {
|
|
2803
|
+
throw new Error(`Invalid channelCid: ${channelCid}`);
|
|
2804
|
+
}
|
|
2805
|
+
const channelType = channelCid.slice(0, colon);
|
|
2806
|
+
const channelId = channelCid.slice(colon + 1);
|
|
2807
|
+
return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
|
|
2808
|
+
}
|
|
2809
|
+
get uploads() {
|
|
2810
|
+
return this.state.getLatestValue().uploads;
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
|
|
2608
2814
|
// src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts
|
|
2609
2815
|
var createUploadErrorHandlerMiddleware = (composer) => ({
|
|
2610
2816
|
id: "stream-io/attachment-manager-middleware/upload-error",
|
|
@@ -2617,6 +2823,7 @@ var createUploadErrorHandlerMiddleware = (composer) => ({
|
|
|
2617
2823
|
const { attachment, error } = state;
|
|
2618
2824
|
if (!error) return forward();
|
|
2619
2825
|
if (!attachment) return discard();
|
|
2826
|
+
if (isUploadCancellation(error)) return forward();
|
|
2620
2827
|
const reason = error instanceof Error ? error.message : "unknown error";
|
|
2621
2828
|
composer.client.notifications.addError({
|
|
2622
2829
|
message: "Error uploading attachment",
|
|
@@ -2641,7 +2848,7 @@ var AttachmentPostUploadMiddlewareExecutor = class extends MiddlewareExecutor {
|
|
|
2641
2848
|
super();
|
|
2642
2849
|
this.use([
|
|
2643
2850
|
createUploadErrorHandlerMiddleware(composer),
|
|
2644
|
-
createPostUploadAttachmentEnrichmentMiddleware()
|
|
2851
|
+
createPostUploadAttachmentEnrichmentMiddleware(composer)
|
|
2645
2852
|
]);
|
|
2646
2853
|
}
|
|
2647
2854
|
};
|
|
@@ -3089,7 +3296,7 @@ function mergeWithDiff(target, source, customizer) {
|
|
|
3089
3296
|
}
|
|
3090
3297
|
|
|
3091
3298
|
// src/messageComposer/attachmentManager.ts
|
|
3092
|
-
var
|
|
3299
|
+
var initState2 = ({
|
|
3093
3300
|
message
|
|
3094
3301
|
}) => ({
|
|
3095
3302
|
attachments: (message?.attachments ?? [])?.filter(({ og_scrape_url }) => !og_scrape_url).map((att) => {
|
|
@@ -3117,13 +3324,14 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3117
3324
|
...attachment,
|
|
3118
3325
|
localMetadata: {
|
|
3119
3326
|
...attachment.localMetadata,
|
|
3327
|
+
uploadConfirmationPending: false,
|
|
3120
3328
|
uploadProgress: void 0,
|
|
3121
3329
|
uploadState: "failed"
|
|
3122
3330
|
}
|
|
3123
3331
|
};
|
|
3124
3332
|
};
|
|
3125
3333
|
this.initState = ({ message } = {}) => {
|
|
3126
|
-
this.state.next(
|
|
3334
|
+
this.state.next(initState2({ message }));
|
|
3127
3335
|
};
|
|
3128
3336
|
this.getSnapshot = () => {
|
|
3129
3337
|
const state = this.state.getLatestValue();
|
|
@@ -3200,8 +3408,22 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3200
3408
|
this.state.partialNext({ attachments });
|
|
3201
3409
|
}
|
|
3202
3410
|
};
|
|
3411
|
+
/**
|
|
3412
|
+
* Releases a local preview URL the SDK minted, if it minted one.
|
|
3413
|
+
*
|
|
3414
|
+
* Only `blob:` URLs are ours to revoke: `toLocalUploadAttachment` puts a
|
|
3415
|
+
* `URL.createObjectURL` result in `previewUri` for a `File`, but for a React Native
|
|
3416
|
+
* `FileReference` it stores the caller's own native file URI, which must be left alone.
|
|
3417
|
+
*/
|
|
3418
|
+
this.revokePreviewUri = (attachment) => {
|
|
3419
|
+
const previewUri = attachment.localMetadata?.previewUri;
|
|
3420
|
+
if (previewUri?.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
|
|
3421
|
+
};
|
|
3203
3422
|
this.removeAttachments = (localAttachmentIds) => {
|
|
3204
3423
|
if (!localAttachmentIds.length) return;
|
|
3424
|
+
const removedAttachments = this.attachments.filter(
|
|
3425
|
+
(attachment) => localAttachmentIds.includes(attachment.localMetadata?.id)
|
|
3426
|
+
);
|
|
3205
3427
|
this.state.partialNext({
|
|
3206
3428
|
attachments: this.attachments.filter(
|
|
3207
3429
|
(attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
|
|
@@ -3210,6 +3432,7 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3210
3432
|
for (const id of localAttachmentIds) {
|
|
3211
3433
|
this.client.uploadManager.deleteUploadRecord(id);
|
|
3212
3434
|
}
|
|
3435
|
+
removedAttachments.forEach(this.revokePreviewUri);
|
|
3213
3436
|
};
|
|
3214
3437
|
this.getUploadConfigCheck = async (fileLike) => {
|
|
3215
3438
|
const client = this.channel.getClient();
|
|
@@ -3361,21 +3584,27 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3361
3584
|
localMetadata: {
|
|
3362
3585
|
...attachment.localMetadata,
|
|
3363
3586
|
uploadState: "failed",
|
|
3364
|
-
uploadProgress: void 0
|
|
3587
|
+
uploadProgress: void 0,
|
|
3588
|
+
// `false`, not `undefined`: these updates go through `updateAttachment`, which merges
|
|
3589
|
+
// via `mergeWith` — and that skips `undefined` source values, so `undefined` would
|
|
3590
|
+
// leave a stale `true` in place.
|
|
3591
|
+
uploadConfirmationPending: false
|
|
3365
3592
|
}
|
|
3366
3593
|
};
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3594
|
+
if (!isUploadCancellation(error)) {
|
|
3595
|
+
this.client.notifications.addError({
|
|
3596
|
+
message: "Error uploading attachment",
|
|
3597
|
+
origin: {
|
|
3598
|
+
emitter: "AttachmentManager",
|
|
3599
|
+
context: { attachment, failedAttachment }
|
|
3600
|
+
},
|
|
3601
|
+
options: {
|
|
3602
|
+
type: "api:attachment:upload:failed",
|
|
3603
|
+
metadata: { reason },
|
|
3604
|
+
originalError: error instanceof Error ? error : void 0
|
|
3605
|
+
}
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3379
3608
|
this.updateAttachment(failedAttachment);
|
|
3380
3609
|
return failedAttachment;
|
|
3381
3610
|
}
|
|
@@ -3388,7 +3617,9 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3388
3617
|
localMetadata: {
|
|
3389
3618
|
...attachment.localMetadata,
|
|
3390
3619
|
uploadState: "finished",
|
|
3391
|
-
uploadProgress: void 0
|
|
3620
|
+
uploadProgress: void 0,
|
|
3621
|
+
// See the note in the failure branch: `undefined` cannot clear through `mergeWith`.
|
|
3622
|
+
uploadConfirmationPending: false
|
|
3392
3623
|
}
|
|
3393
3624
|
};
|
|
3394
3625
|
const previewUri = uploadedAttachment.localMetadata.previewUri;
|
|
@@ -3437,7 +3668,9 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3437
3668
|
localMetadata: {
|
|
3438
3669
|
...attachment.localMetadata,
|
|
3439
3670
|
uploadState: error ? "failed" : "finished",
|
|
3440
|
-
uploadProgress: void 0
|
|
3671
|
+
uploadProgress: void 0,
|
|
3672
|
+
// See the note in `uploadAttachment`: `undefined` cannot clear through `mergeWith`.
|
|
3673
|
+
uploadConfirmationPending: false
|
|
3441
3674
|
}
|
|
3442
3675
|
},
|
|
3443
3676
|
error,
|
|
@@ -3462,7 +3695,7 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3462
3695
|
);
|
|
3463
3696
|
};
|
|
3464
3697
|
this.composer = composer;
|
|
3465
|
-
this.state = new StateStore(
|
|
3698
|
+
this.state = new StateStore(initState2({ message }));
|
|
3466
3699
|
this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
|
|
3467
3700
|
this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
|
|
3468
3701
|
composer
|
|
@@ -3567,7 +3800,8 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3567
3800
|
localMetadata: {
|
|
3568
3801
|
...attachment.localMetadata,
|
|
3569
3802
|
uploadState: "uploading",
|
|
3570
|
-
uploadProgress: this.config.trackUploadProgress ? 0 : void 0
|
|
3803
|
+
uploadProgress: this.config.trackUploadProgress ? 0 : void 0,
|
|
3804
|
+
uploadConfirmationPending: false
|
|
3571
3805
|
}
|
|
3572
3806
|
}
|
|
3573
3807
|
]);
|
|
@@ -3580,7 +3814,8 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3580
3814
|
localMetadata: {
|
|
3581
3815
|
...attachment.localMetadata,
|
|
3582
3816
|
uploadState: "uploading",
|
|
3583
|
-
uploadProgress: nextUpload.uploadProgress
|
|
3817
|
+
uploadProgress: nextUpload.uploadProgress,
|
|
3818
|
+
uploadConfirmationPending: nextUpload.uploadConfirmationPending
|
|
3584
3819
|
}
|
|
3585
3820
|
});
|
|
3586
3821
|
}
|
|
@@ -3634,6 +3869,50 @@ var localAttachmentToAttachment = (localAttachment) => {
|
|
|
3634
3869
|
const { localMetadata, ...attachment } = localAttachment;
|
|
3635
3870
|
return attachment;
|
|
3636
3871
|
};
|
|
3872
|
+
var composeWithPendingUploads = ({
|
|
3873
|
+
composer,
|
|
3874
|
+
state
|
|
3875
|
+
}) => {
|
|
3876
|
+
const composerIsKeptAsDraft = !!composer.pollId;
|
|
3877
|
+
const relevantAttachments = composer.attachmentManager.attachments.filter(
|
|
3878
|
+
(attachment) => isFinishedUpload(attachment) || !composerIsKeptAsDraft && isPendingUpload(attachment)
|
|
3879
|
+
);
|
|
3880
|
+
const localAttachments = (state.localMessage.attachments ?? []).concat(
|
|
3881
|
+
relevantAttachments.map(
|
|
3882
|
+
(attachment) => isPendingUpload(attachment) ? attachment : localAttachmentToAttachment(attachment)
|
|
3883
|
+
)
|
|
3884
|
+
);
|
|
3885
|
+
const messageAttachments = (state.message.attachments ?? []).concat(
|
|
3886
|
+
relevantAttachments.filter(isFinishedUpload).map(localAttachmentToAttachment)
|
|
3887
|
+
);
|
|
3888
|
+
if (!localAttachments.length && !messageAttachments.length) return state;
|
|
3889
|
+
return {
|
|
3890
|
+
...state,
|
|
3891
|
+
localMessage: localAttachments.length ? {
|
|
3892
|
+
...state.localMessage,
|
|
3893
|
+
attachments: localAttachments
|
|
3894
|
+
} : state.localMessage,
|
|
3895
|
+
message: messageAttachments.length ? {
|
|
3896
|
+
...state.message,
|
|
3897
|
+
attachments: messageAttachments
|
|
3898
|
+
} : state.message
|
|
3899
|
+
};
|
|
3900
|
+
};
|
|
3901
|
+
var createSendWithPendingUploadsAttachmentsMiddleware = (composer) => ({
|
|
3902
|
+
allowsPendingUploads: true,
|
|
3903
|
+
id: "stream-io/message-composer-middleware/attachments",
|
|
3904
|
+
handlers: {
|
|
3905
|
+
compose: ({
|
|
3906
|
+
state,
|
|
3907
|
+
next,
|
|
3908
|
+
forward
|
|
3909
|
+
}) => {
|
|
3910
|
+
const { attachmentManager } = composer;
|
|
3911
|
+
if (!attachmentManager) return forward();
|
|
3912
|
+
return next(composeWithPendingUploads({ composer, state }));
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
});
|
|
3637
3916
|
var createAttachmentsCompositionMiddleware = (composer) => ({
|
|
3638
3917
|
id: "stream-io/message-composer-middleware/attachments",
|
|
3639
3918
|
handlers: {
|
|
@@ -3850,7 +4129,7 @@ var getInitialMentions = (message) => {
|
|
|
3850
4129
|
};
|
|
3851
4130
|
var isSameMentionEntity = (first, second) => first.id === second.id && first.mentionType === second.mentionType;
|
|
3852
4131
|
var getMentionsFromState = (state) => state.mentions ?? state.mentionedUsers.map(userResponseToMentionEntity);
|
|
3853
|
-
var
|
|
4132
|
+
var initState3 = ({
|
|
3854
4133
|
composer,
|
|
3855
4134
|
message
|
|
3856
4135
|
}) => {
|
|
@@ -3879,7 +4158,7 @@ var initState2 = ({
|
|
|
3879
4158
|
var TextComposer = class {
|
|
3880
4159
|
constructor({ composer, message }) {
|
|
3881
4160
|
this.initState = ({ message } = {}) => {
|
|
3882
|
-
this.state.next(
|
|
4161
|
+
this.state.next(initState3({ composer: this.composer, message }));
|
|
3883
4162
|
};
|
|
3884
4163
|
this.getSnapshot = (state = this.state.getLatestValue()) => state;
|
|
3885
4164
|
this.restoreSnapshot = (snapshot) => {
|
|
@@ -4072,7 +4351,7 @@ var TextComposer = class {
|
|
|
4072
4351
|
this.commitState(output.state);
|
|
4073
4352
|
};
|
|
4074
4353
|
this.composer = composer;
|
|
4075
|
-
this.state = new StateStore(
|
|
4354
|
+
this.state = new StateStore(initState3({ composer, message }));
|
|
4076
4355
|
this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
|
|
4077
4356
|
}
|
|
4078
4357
|
get channel() {
|
|
@@ -5774,6 +6053,13 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
|
|
|
5774
6053
|
createCompositionDataCleanupMiddleware(composer)
|
|
5775
6054
|
]);
|
|
5776
6055
|
}
|
|
6056
|
+
/**
|
|
6057
|
+
* Narrows the base accessor to the composition middleware type, so callers can read the
|
|
6058
|
+
* declarations it carries (`allowsPendingUploads`) without casting.
|
|
6059
|
+
*/
|
|
6060
|
+
get installedMiddleware() {
|
|
6061
|
+
return super.installedMiddleware;
|
|
6062
|
+
}
|
|
5777
6063
|
};
|
|
5778
6064
|
var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
|
|
5779
6065
|
constructor({ composer }) {
|
|
@@ -6888,7 +7174,7 @@ var DEFAULT_COMPOSER_CONFIG = {
|
|
|
6888
7174
|
};
|
|
6889
7175
|
|
|
6890
7176
|
// src/messageComposer/CustomDataManager.ts
|
|
6891
|
-
var
|
|
7177
|
+
var initState4 = (options) => {
|
|
6892
7178
|
if (!options)
|
|
6893
7179
|
return { message: {}, custom: {} };
|
|
6894
7180
|
return { message: {}, custom: {} };
|
|
@@ -6897,14 +7183,14 @@ var CustomDataManager = class {
|
|
|
6897
7183
|
constructor({ composer, message }) {
|
|
6898
7184
|
this.isMessageDataEqual = (nextState, previousState) => JSON.stringify(nextState.message) === JSON.stringify(previousState?.message);
|
|
6899
7185
|
this.initState = ({ message } = {}) => {
|
|
6900
|
-
this.state.next(
|
|
7186
|
+
this.state.next(initState4({ composer: this.composer, message }));
|
|
6901
7187
|
};
|
|
6902
7188
|
this.getSnapshot = () => this.state.getLatestValue();
|
|
6903
7189
|
this.restoreSnapshot = (snapshot) => {
|
|
6904
7190
|
this.state.next(snapshot);
|
|
6905
7191
|
};
|
|
6906
7192
|
this.composer = composer;
|
|
6907
|
-
this.state = new StateStore(
|
|
7193
|
+
this.state = new StateStore(initState4({ composer, message }));
|
|
6908
7194
|
}
|
|
6909
7195
|
get customMessageData() {
|
|
6910
7196
|
return this.state.getLatestValue().message;
|
|
@@ -6940,7 +7226,7 @@ var LinkPreviewStatus = /* @__PURE__ */ ((LinkPreviewStatus2) => {
|
|
|
6940
7226
|
return LinkPreviewStatus2;
|
|
6941
7227
|
})(LinkPreviewStatus || {});
|
|
6942
7228
|
var linkPreviewArrayToMap = (linkPreviews) => new Map(linkPreviews.map((linkPreview) => [linkPreview.og_scrape_url, linkPreview]));
|
|
6943
|
-
var
|
|
7229
|
+
var initState5 = ({
|
|
6944
7230
|
message
|
|
6945
7231
|
}) => message ? {
|
|
6946
7232
|
previews: message.attachments?.reduce((acc, attachment) => {
|
|
@@ -6959,7 +7245,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
|
|
|
6959
7245
|
this.shouldDiscardEnrichQueries = false;
|
|
6960
7246
|
this.initState = ({ message } = {}) => {
|
|
6961
7247
|
this.cancelURLEnrichment();
|
|
6962
|
-
this.state.next(
|
|
7248
|
+
this.state.next(initState5({ message: this.enabled ? message : void 0 }));
|
|
6963
7249
|
};
|
|
6964
7250
|
this.getSnapshot = () => this.state.getLatestValue();
|
|
6965
7251
|
this.restoreSnapshot = (snapshot) => {
|
|
@@ -7062,7 +7348,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
|
|
|
7062
7348
|
};
|
|
7063
7349
|
this.composer = composer;
|
|
7064
7350
|
this.state = new StateStore(
|
|
7065
|
-
|
|
7351
|
+
initState5({ message: this.enabled ? message : void 0 })
|
|
7066
7352
|
);
|
|
7067
7353
|
this.findAndEnrichUrls = debounce(
|
|
7068
7354
|
this._findAndEnrichUrls.bind(this),
|
|
@@ -7150,7 +7436,7 @@ var LinkPreviewsManager = _LinkPreviewsManager;
|
|
|
7150
7436
|
|
|
7151
7437
|
// src/messageComposer/LocationComposer.ts
|
|
7152
7438
|
var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
|
|
7153
|
-
var
|
|
7439
|
+
var initState6 = ({
|
|
7154
7440
|
message
|
|
7155
7441
|
}) => ({
|
|
7156
7442
|
location: message?.shared_location ?? null
|
|
@@ -7158,7 +7444,7 @@ var initState5 = ({
|
|
|
7158
7444
|
var LocationComposer = class {
|
|
7159
7445
|
constructor({ composer, message }) {
|
|
7160
7446
|
this.initState = ({ message } = {}) => {
|
|
7161
|
-
this.state.next(
|
|
7447
|
+
this.state.next(initState6({ message }));
|
|
7162
7448
|
};
|
|
7163
7449
|
this.getSnapshot = () => this.state.getLatestValue();
|
|
7164
7450
|
this.restoreSnapshot = (snapshot) => {
|
|
@@ -7176,7 +7462,7 @@ var LocationComposer = class {
|
|
|
7176
7462
|
});
|
|
7177
7463
|
};
|
|
7178
7464
|
this.composer = composer;
|
|
7179
|
-
this.state = new StateStore(
|
|
7465
|
+
this.state = new StateStore(initState6({ message }));
|
|
7180
7466
|
this._deviceId = this.config.getDeviceId();
|
|
7181
7467
|
}
|
|
7182
7468
|
get config() {
|
|
@@ -7897,7 +8183,7 @@ var initEditingAuditState = (composition) => {
|
|
|
7897
8183
|
}
|
|
7898
8184
|
};
|
|
7899
8185
|
};
|
|
7900
|
-
var
|
|
8186
|
+
var initState7 = (composition) => {
|
|
7901
8187
|
if (!composition) {
|
|
7902
8188
|
return {
|
|
7903
8189
|
draftId: null,
|
|
@@ -7984,7 +8270,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
7984
8270
|
this.textComposer.initState({ message });
|
|
7985
8271
|
this.pollComposer.initState();
|
|
7986
8272
|
this.customDataManager.initState({ message });
|
|
7987
|
-
this.state.next(
|
|
8273
|
+
this.state.next(initState7(composition));
|
|
7988
8274
|
};
|
|
7989
8275
|
this.initStateFromChannelResponse = (channelApiResponse) => {
|
|
7990
8276
|
if (this.channel.cid !== channelApiResponse.channel.cid) {
|
|
@@ -8450,7 +8736,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
8450
8736
|
this.editingAuditState = new StateStore(
|
|
8451
8737
|
this.initEditingAuditState(composition)
|
|
8452
8738
|
);
|
|
8453
|
-
this.state = new StateStore(
|
|
8739
|
+
this.state = new StateStore(initState7(composition));
|
|
8454
8740
|
this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
|
|
8455
8741
|
composer: this
|
|
8456
8742
|
});
|
|
@@ -8529,8 +8815,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
8529
8815
|
const currentCommand = this.textComposer.command;
|
|
8530
8816
|
return !currentCommand || this.validateCommandSendability(currentCommand).ready;
|
|
8531
8817
|
}
|
|
8818
|
+
/**
|
|
8819
|
+
* Whether the installed composition middleware composes messages whose uploads are still in
|
|
8820
|
+
* flight — see `MessageCompositionMiddleware.allowsPendingUploads`.
|
|
8821
|
+
*
|
|
8822
|
+
* Derived from the chain rather than from config, so there is exactly one switch: the UI SDK
|
|
8823
|
+
* that implements the rest of the flow installs the middleware, and everything that has to
|
|
8824
|
+
* follow from that (starting with {@link hasSendableData}) follows on its own.
|
|
8825
|
+
*
|
|
8826
|
+
* **Temporary API.** In v10 sending with pending uploads becomes part of the composer
|
|
8827
|
+
* configuration (`MessageComposerConfig`, set through `updateConfig`), and this getter,
|
|
8828
|
+
* together with the middleware declaration it reads, is replaced by that config field.
|
|
8829
|
+
*/
|
|
8830
|
+
get allowsPendingUploads() {
|
|
8831
|
+
return this.compositionMiddlewareExecutor.installedMiddleware.some(
|
|
8832
|
+
(middleware) => middleware.allowsPendingUploads
|
|
8833
|
+
);
|
|
8834
|
+
}
|
|
8532
8835
|
get hasSendableData() {
|
|
8533
|
-
|
|
8836
|
+
if (!this.isCommandSendable) return false;
|
|
8837
|
+
if (this.allowsPendingUploads) {
|
|
8838
|
+
const hasSendableAttachment = this.attachmentManager.attachments.some(
|
|
8839
|
+
(attachment) => isFinishedUpload(attachment) || isPendingUpload(attachment)
|
|
8840
|
+
);
|
|
8841
|
+
return !!(!this.textComposer.textIsEmpty || hasSendableAttachment || this.pollId || this.locationComposer.validLocation);
|
|
8842
|
+
}
|
|
8843
|
+
return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
|
|
8534
8844
|
}
|
|
8535
8845
|
get compositionIsEmpty() {
|
|
8536
8846
|
return !this.quotedMessage && this.contentIsEmpty;
|
|
@@ -8669,7 +8979,6 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
|
|
|
8669
8979
|
* @param options
|
|
8670
8980
|
*/
|
|
8671
8981
|
this.markRead = async (collection, options) => {
|
|
8672
|
-
if (!userHasReadReceipts(this.client)) return null;
|
|
8673
8982
|
let result = null;
|
|
8674
8983
|
if (isChannel(collection)) {
|
|
8675
8984
|
result = await collection.markAsReadRequest(options);
|
|
@@ -8679,7 +8988,7 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
|
|
|
8679
8988
|
thread_id: collection.id
|
|
8680
8989
|
});
|
|
8681
8990
|
}
|
|
8682
|
-
this.removeCandidateFor(collection);
|
|
8991
|
+
if (result) this.removeCandidateFor(collection);
|
|
8683
8992
|
return result;
|
|
8684
8993
|
};
|
|
8685
8994
|
/**
|
|
@@ -9064,18 +9373,14 @@ var Channel = class {
|
|
|
9064
9373
|
return await this.query(defaultOptions, "latest");
|
|
9065
9374
|
};
|
|
9066
9375
|
this._callChannelListeners = (event) => {
|
|
9067
|
-
const
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
}
|
|
9072
|
-
if (channel.listeners[event.type]) {
|
|
9073
|
-
listeners.push(...channel.listeners[event.type]);
|
|
9074
|
-
}
|
|
9376
|
+
const listeners = [
|
|
9377
|
+
...this.listeners.all ?? [],
|
|
9378
|
+
...this.listeners[event.type] ?? []
|
|
9379
|
+
];
|
|
9380
|
+
const { logger } = this._client;
|
|
9075
9381
|
for (const listener of listeners) {
|
|
9076
|
-
if (typeof listener
|
|
9077
|
-
|
|
9078
|
-
}
|
|
9382
|
+
if (typeof listener === "string") continue;
|
|
9383
|
+
invokeEventListener(listener, event, logger);
|
|
9079
9384
|
}
|
|
9080
9385
|
};
|
|
9081
9386
|
/**
|
|
@@ -9157,14 +9462,16 @@ var Channel = class {
|
|
|
9157
9462
|
*
|
|
9158
9463
|
* @return {Promise<SendMessageAPIResponse>} The Server Response
|
|
9159
9464
|
*/
|
|
9160
|
-
async _sendMessage(
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9465
|
+
async _sendMessage(rawMessage, options) {
|
|
9466
|
+
const client = this.getClient();
|
|
9467
|
+
const message = sanitizeOutgoingAttachments({
|
|
9468
|
+
client,
|
|
9469
|
+
message: rawMessage
|
|
9470
|
+
});
|
|
9471
|
+
return await client.post(this._channelURL() + "/message", {
|
|
9472
|
+
message,
|
|
9473
|
+
...options
|
|
9474
|
+
});
|
|
9168
9475
|
}
|
|
9169
9476
|
async sendMessage(message, options) {
|
|
9170
9477
|
try {
|
|
@@ -9987,14 +10294,15 @@ var Channel = class {
|
|
|
9987
10294
|
return await this.getClient().messageDeliveryReporter.markRead(this, data);
|
|
9988
10295
|
}
|
|
9989
10296
|
/**
|
|
9990
|
-
* markReadRequest - Send the mark read event for this user, only works if the `read_events` setting is
|
|
10297
|
+
* markReadRequest - Send the mark read event for this user, only works if the `read_events` setting is
|
|
10298
|
+
* enabled and the user holds the `read-events` capability on this channel
|
|
9991
10299
|
*
|
|
9992
10300
|
* @param {MarkReadOptions} data
|
|
9993
10301
|
* @return {Promise<EventAPIResponse | null>} Description
|
|
9994
10302
|
*/
|
|
9995
10303
|
async markAsReadRequest(data = {}) {
|
|
9996
10304
|
this._checkInitialized();
|
|
9997
|
-
if (!this.
|
|
10305
|
+
if (!this.getClient()._isUsingServerAuth() && (!this.getConfig()?.read_events || !channelHasReadEvents(this))) {
|
|
9998
10306
|
return null;
|
|
9999
10307
|
}
|
|
10000
10308
|
return await this.getClient().post(this._channelURL() + "/read", {
|
|
@@ -10002,14 +10310,15 @@ var Channel = class {
|
|
|
10002
10310
|
});
|
|
10003
10311
|
}
|
|
10004
10312
|
/**
|
|
10005
|
-
* markUnread - Mark the channel as unread from messageID, only works if the `read_events` setting is
|
|
10313
|
+
* markUnread - Mark the channel as unread from messageID, only works if the `read_events` setting is
|
|
10314
|
+
* enabled and the user holds the `read-events` capability on this channel
|
|
10006
10315
|
*
|
|
10007
10316
|
* @param {MarkUnreadOptions} data
|
|
10008
10317
|
* @return {APIResponse} An API response
|
|
10009
10318
|
*/
|
|
10010
10319
|
async markUnread(data) {
|
|
10011
10320
|
this._checkInitialized();
|
|
10012
|
-
if (!this.
|
|
10321
|
+
if (!this.getClient()._isUsingServerAuth() && (!this.getConfig()?.read_events || !channelHasReadEvents(this))) {
|
|
10013
10322
|
return Promise.resolve(null);
|
|
10014
10323
|
}
|
|
10015
10324
|
return await this.getClient().post(this._channelURL() + "/unread", {
|
|
@@ -10615,6 +10924,7 @@ var Channel = class {
|
|
|
10615
10924
|
// delivery-report network sync below is skipped for it.
|
|
10616
10925
|
case "message.read_locally":
|
|
10617
10926
|
case "message.read":
|
|
10927
|
+
if (event.thread) break;
|
|
10618
10928
|
if (event.user?.id && event.created_at) {
|
|
10619
10929
|
const previousReadState = channelState.read[event.user.id];
|
|
10620
10930
|
channelState.read[event.user.id] = {
|
|
@@ -11064,7 +11374,7 @@ var ClientState = class {
|
|
|
11064
11374
|
import WebSocket from "isomorphic-ws";
|
|
11065
11375
|
|
|
11066
11376
|
// src/insights.ts
|
|
11067
|
-
import
|
|
11377
|
+
import axios2 from "axios";
|
|
11068
11378
|
var InsightMetrics = class {
|
|
11069
11379
|
constructor() {
|
|
11070
11380
|
this.connectionStartTimestamp = null;
|
|
@@ -11077,7 +11387,7 @@ var postInsights = async (insightType, insights) => {
|
|
|
11077
11387
|
const maxAttempts = 3;
|
|
11078
11388
|
for (let i = 0; i < maxAttempts; i++) {
|
|
11079
11389
|
try {
|
|
11080
|
-
await
|
|
11390
|
+
await axios2.post(
|
|
11081
11391
|
`https://chat-insights.getstream.io/insights/${insightType}`,
|
|
11082
11392
|
insights
|
|
11083
11393
|
);
|
|
@@ -11585,131 +11895,6 @@ var StableWSConnection = class {
|
|
|
11585
11895
|
}
|
|
11586
11896
|
};
|
|
11587
11897
|
|
|
11588
|
-
// src/uploadManager.ts
|
|
11589
|
-
var initState7 = () => ({ uploads: {} });
|
|
11590
|
-
var upsertById = (uploads, record) => ({
|
|
11591
|
-
...uploads,
|
|
11592
|
-
[record.id]: { ...uploads[record.id], ...record }
|
|
11593
|
-
});
|
|
11594
|
-
var updateById = (uploads, record) => {
|
|
11595
|
-
if (!(record.id in uploads)) return null;
|
|
11596
|
-
const current = uploads[record.id];
|
|
11597
|
-
return { ...uploads, [record.id]: { ...current, ...record } };
|
|
11598
|
-
};
|
|
11599
|
-
var UploadManager = class {
|
|
11600
|
-
constructor(client) {
|
|
11601
|
-
this.client = client;
|
|
11602
|
-
this.inFlightUploads = /* @__PURE__ */ new Map();
|
|
11603
|
-
this.getUpload = (id) => this.uploads[id];
|
|
11604
|
-
/**
|
|
11605
|
-
* Clears all upload records.
|
|
11606
|
-
* Invoked when the user disconnects so a later session does not inherit stale upload state.
|
|
11607
|
-
* Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
|
|
11608
|
-
*/
|
|
11609
|
-
this.reset = () => {
|
|
11610
|
-
for (const { abortController } of this.inFlightUploads.values()) {
|
|
11611
|
-
abortController.abort();
|
|
11612
|
-
}
|
|
11613
|
-
this.inFlightUploads.clear();
|
|
11614
|
-
this.state.next(initState7());
|
|
11615
|
-
};
|
|
11616
|
-
/**
|
|
11617
|
-
* Removes the upload record for `id` if present.
|
|
11618
|
-
* If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
|
|
11619
|
-
*/
|
|
11620
|
-
this.deleteUploadRecord = (id) => {
|
|
11621
|
-
const flight = this.inFlightUploads.get(id);
|
|
11622
|
-
if (flight) {
|
|
11623
|
-
this.inFlightUploads.delete(id);
|
|
11624
|
-
flight.abortController.abort();
|
|
11625
|
-
}
|
|
11626
|
-
this.state.next((current) => {
|
|
11627
|
-
if (!(id in current.uploads)) return current;
|
|
11628
|
-
const uploads = { ...current.uploads };
|
|
11629
|
-
delete uploads[id];
|
|
11630
|
-
return { ...current, uploads };
|
|
11631
|
-
});
|
|
11632
|
-
};
|
|
11633
|
-
/**
|
|
11634
|
-
* Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
|
|
11635
|
-
* Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
|
|
11636
|
-
* Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
|
|
11637
|
-
*/
|
|
11638
|
-
this.upload = ({
|
|
11639
|
-
id,
|
|
11640
|
-
channelCid,
|
|
11641
|
-
file
|
|
11642
|
-
}) => {
|
|
11643
|
-
const existing = this.inFlightUploads.get(id);
|
|
11644
|
-
if (existing) return existing.promise;
|
|
11645
|
-
let resolvePromise;
|
|
11646
|
-
let rejectPromise;
|
|
11647
|
-
const promise = new Promise((resolve, reject) => {
|
|
11648
|
-
resolvePromise = resolve;
|
|
11649
|
-
rejectPromise = reject;
|
|
11650
|
-
});
|
|
11651
|
-
const abortController = new AbortController();
|
|
11652
|
-
this.inFlightUploads.set(id, { promise, abortController });
|
|
11653
|
-
void (async () => {
|
|
11654
|
-
const attachmentManager = this.resolveAttachmentManager(channelCid);
|
|
11655
|
-
const trackProgress = attachmentManager.config.trackUploadProgress;
|
|
11656
|
-
try {
|
|
11657
|
-
this.upsertUpload({
|
|
11658
|
-
id,
|
|
11659
|
-
uploadProgress: trackProgress ? 0 : void 0
|
|
11660
|
-
});
|
|
11661
|
-
const onProgress = trackProgress ? (progress) => {
|
|
11662
|
-
this.updateUpload({
|
|
11663
|
-
id,
|
|
11664
|
-
uploadProgress: progress
|
|
11665
|
-
});
|
|
11666
|
-
} : void 0;
|
|
11667
|
-
const uploadRequestOptions = {
|
|
11668
|
-
abortSignal: abortController.signal,
|
|
11669
|
-
...onProgress ? { onProgress } : {}
|
|
11670
|
-
};
|
|
11671
|
-
const response = await attachmentManager.doUploadRequest(
|
|
11672
|
-
file,
|
|
11673
|
-
uploadRequestOptions
|
|
11674
|
-
);
|
|
11675
|
-
resolvePromise(response);
|
|
11676
|
-
} catch (error) {
|
|
11677
|
-
rejectPromise(error);
|
|
11678
|
-
} finally {
|
|
11679
|
-
this.inFlightUploads.delete(id);
|
|
11680
|
-
this.deleteUploadRecord(id);
|
|
11681
|
-
}
|
|
11682
|
-
})();
|
|
11683
|
-
return promise;
|
|
11684
|
-
};
|
|
11685
|
-
this.upsertUpload = (record) => {
|
|
11686
|
-
this.state.partialNext({
|
|
11687
|
-
uploads: upsertById(this.uploads, record)
|
|
11688
|
-
});
|
|
11689
|
-
};
|
|
11690
|
-
this.updateUpload = (record) => {
|
|
11691
|
-
this.state.next((current) => {
|
|
11692
|
-
const nextUploads = updateById(current.uploads, record);
|
|
11693
|
-
if (!nextUploads) return current;
|
|
11694
|
-
return { ...current, uploads: nextUploads };
|
|
11695
|
-
});
|
|
11696
|
-
};
|
|
11697
|
-
this.state = new StateStore(initState7());
|
|
11698
|
-
}
|
|
11699
|
-
resolveAttachmentManager(channelCid) {
|
|
11700
|
-
const colon = channelCid.indexOf(":");
|
|
11701
|
-
if (colon <= 0 || colon === channelCid.length - 1) {
|
|
11702
|
-
throw new Error(`Invalid channelCid: ${channelCid}`);
|
|
11703
|
-
}
|
|
11704
|
-
const channelType = channelCid.slice(0, colon);
|
|
11705
|
-
const channelId = channelCid.slice(colon + 1);
|
|
11706
|
-
return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
|
|
11707
|
-
}
|
|
11708
|
-
get uploads() {
|
|
11709
|
-
return this.state.getLatestValue().uploads;
|
|
11710
|
-
}
|
|
11711
|
-
};
|
|
11712
|
-
|
|
11713
11898
|
// src/signing.ts
|
|
11714
11899
|
var import_jsonwebtoken = __toESM(require_jsonwebtoken());
|
|
11715
11900
|
var import_crypto = __toESM(require_crypto());
|
|
@@ -11968,7 +12153,7 @@ var TokenManager = class {
|
|
|
11968
12153
|
};
|
|
11969
12154
|
|
|
11970
12155
|
// src/connection_fallback.ts
|
|
11971
|
-
import
|
|
12156
|
+
import axios3 from "axios";
|
|
11972
12157
|
var WSConnectionFallback = class {
|
|
11973
12158
|
constructor({ client }) {
|
|
11974
12159
|
/** @private */
|
|
@@ -11987,7 +12172,7 @@ var WSConnectionFallback = class {
|
|
|
11987
12172
|
/** @private */
|
|
11988
12173
|
this._req = async (params, config, retry) => {
|
|
11989
12174
|
if (!this.cancelToken && !params.close) {
|
|
11990
|
-
this.cancelToken =
|
|
12175
|
+
this.cancelToken = axios3.CancelToken.source();
|
|
11991
12176
|
}
|
|
11992
12177
|
try {
|
|
11993
12178
|
const res = await this.client.doAxiosRequest(
|
|
@@ -12023,7 +12208,7 @@ var WSConnectionFallback = class {
|
|
|
12023
12208
|
}
|
|
12024
12209
|
}
|
|
12025
12210
|
} catch (error) {
|
|
12026
|
-
if (
|
|
12211
|
+
if (axios3.isCancel(error)) {
|
|
12027
12212
|
this._log(`_poll() - axios canceled request`);
|
|
12028
12213
|
return;
|
|
12029
12214
|
}
|
|
@@ -14446,7 +14631,7 @@ var StreamChat = class _StreamChat {
|
|
|
14446
14631
|
return this.handleResponse(response);
|
|
14447
14632
|
} catch (e) {
|
|
14448
14633
|
e.client_request_id = requestConfig.headers?.["x-client-request-id"];
|
|
14449
|
-
if (
|
|
14634
|
+
if (axios4.isCancel(e)) throw e;
|
|
14450
14635
|
this._logApiError(type, url, e);
|
|
14451
14636
|
this.consecutiveFailures += 1;
|
|
14452
14637
|
if (e.response) {
|
|
@@ -14595,16 +14780,12 @@ var StreamChat = class _StreamChat {
|
|
|
14595
14780
|
}
|
|
14596
14781
|
};
|
|
14597
14782
|
this._callClientListeners = (event) => {
|
|
14598
|
-
const
|
|
14599
|
-
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
}
|
|
14603
|
-
if (client.listeners[event.type]) {
|
|
14604
|
-
listeners.push(...client.listeners[event.type]);
|
|
14605
|
-
}
|
|
14783
|
+
const listeners = [
|
|
14784
|
+
...this.listeners.all ?? [],
|
|
14785
|
+
...this.listeners[event.type] ?? []
|
|
14786
|
+
];
|
|
14606
14787
|
for (const listener of listeners) {
|
|
14607
|
-
listener
|
|
14788
|
+
invokeEventListener(listener, event, this.logger);
|
|
14608
14789
|
}
|
|
14609
14790
|
};
|
|
14610
14791
|
this.recoverState = async () => {
|
|
@@ -14758,7 +14939,7 @@ var StreamChat = class _StreamChat {
|
|
|
14758
14939
|
return this.userAgent;
|
|
14759
14940
|
}
|
|
14760
14941
|
if (!this.cachedUserAgent) {
|
|
14761
|
-
const version = "9.
|
|
14942
|
+
const version = "9.52.1";
|
|
14762
14943
|
const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
|
|
14763
14944
|
const { name: appName, version: appVersion } = this.appIdentifier ?? {};
|
|
14764
14945
|
const { os, model: deviceModel } = this.deviceIdentifier ?? {};
|
|
@@ -14826,7 +15007,7 @@ var StreamChat = class _StreamChat {
|
|
|
14826
15007
|
keepAliveMsecs: 3e3
|
|
14827
15008
|
});
|
|
14828
15009
|
}
|
|
14829
|
-
this.axiosInstance =
|
|
15010
|
+
this.axiosInstance = axios4.create(this.options);
|
|
14830
15011
|
this.setBaseURL(this.options.baseURL || "https://chat.stream-io-api.com");
|
|
14831
15012
|
if (typeof process !== "undefined" && "env" in process && process.env.STREAM_LOCAL_TEST_RUN) {
|
|
14832
15013
|
this.setBaseURL("http://localhost:3030");
|
|
@@ -14847,7 +15028,7 @@ var StreamChat = class _StreamChat {
|
|
|
14847
15028
|
this.defaultWSTimeoutWithFallback = 6 * 1e3;
|
|
14848
15029
|
this.defaultWSTimeout = 15 * 1e3;
|
|
14849
15030
|
this.axiosInstance.defaults.paramsSerializer = axiosParamsSerializer;
|
|
14850
|
-
this.logger = isFunction(inputOptions.logger) ? inputOptions.logger :
|
|
15031
|
+
this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : noopLogger;
|
|
14851
15032
|
this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
|
|
14852
15033
|
this.threads = new ThreadManager({ client: this });
|
|
14853
15034
|
this.polls = new PollManager({ client: this });
|
|
@@ -16178,6 +16359,11 @@ var StreamChat = class _StreamChat {
|
|
|
16178
16359
|
}
|
|
16179
16360
|
/**
|
|
16180
16361
|
* unflagMessage - unflag a message
|
|
16362
|
+
*
|
|
16363
|
+
* @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
|
|
16364
|
+
* the backend - calling this resolves successfully but does not remove the flag.
|
|
16365
|
+
* Use `client.moderation.submitAction()` against the review queue item instead.
|
|
16366
|
+
*
|
|
16181
16367
|
* @param {string} targetMessageID
|
|
16182
16368
|
* @param {string} [options.user_id] currentUserID, only used with serverside auth
|
|
16183
16369
|
* @returns {Promise<APIResponse>}
|
|
@@ -16190,6 +16376,11 @@ var StreamChat = class _StreamChat {
|
|
|
16190
16376
|
}
|
|
16191
16377
|
/**
|
|
16192
16378
|
* unflagUser - unflag a user
|
|
16379
|
+
*
|
|
16380
|
+
* @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
|
|
16381
|
+
* the backend - calling this resolves successfully but does not remove the flag.
|
|
16382
|
+
* Use `client.moderation.submitAction()` against the review queue item instead.
|
|
16383
|
+
*
|
|
16193
16384
|
* @param {string} targetID
|
|
16194
16385
|
* @param {string} [options.user_id] currentUserID, only used with serverside auth
|
|
16195
16386
|
* @returns {Promise<APIResponse>}
|
|
@@ -16474,7 +16665,10 @@ var StreamChat = class _StreamChat {
|
|
|
16474
16665
|
if (!message.id) {
|
|
16475
16666
|
throw Error("Please specify the message.id when calling updateMessage");
|
|
16476
16667
|
}
|
|
16477
|
-
const payload =
|
|
16668
|
+
const payload = sanitizeOutgoingAttachments({
|
|
16669
|
+
client: this,
|
|
16670
|
+
message: toUpdatedMessagePayload(message)
|
|
16671
|
+
});
|
|
16478
16672
|
if (typeof partialUserOrUserId === "string") {
|
|
16479
16673
|
payload.user_id = partialUserOrUserId;
|
|
16480
16674
|
} else if (typeof partialUserOrUserId?.id === "string") {
|
|
@@ -18947,6 +19141,7 @@ var AbstractOfflineDB = class {
|
|
|
18947
19141
|
return this.handleMessageUpdatedEvent({ event, execute });
|
|
18948
19142
|
}
|
|
18949
19143
|
if (type === "message.read" || type === "notification.mark_read") {
|
|
19144
|
+
if (event.thread) return [];
|
|
18950
19145
|
return this.handleRead({ event, unreadMessages: 0, execute });
|
|
18951
19146
|
}
|
|
18952
19147
|
if (type === "message.read_locally") {
|
|
@@ -19513,6 +19708,7 @@ export {
|
|
|
19513
19708
|
createMessageComposerStateCompositionMiddleware,
|
|
19514
19709
|
createPollComposerStateMiddleware,
|
|
19515
19710
|
createPostUploadAttachmentEnrichmentMiddleware,
|
|
19711
|
+
createSendWithPendingUploadsAttachmentsMiddleware,
|
|
19516
19712
|
createSharedLocationCompositionMiddleware,
|
|
19517
19713
|
createTextComposerCompositionMiddleware,
|
|
19518
19714
|
createTextComposerPreValidationMiddleware,
|
|
@@ -19541,6 +19737,7 @@ export {
|
|
|
19541
19737
|
getTokenizedSuggestionDisplayName,
|
|
19542
19738
|
getTriggerCharWithToken,
|
|
19543
19739
|
gunzipPayload,
|
|
19740
|
+
hasPending,
|
|
19544
19741
|
insertItemWithTrigger,
|
|
19545
19742
|
isAudioAttachment,
|
|
19546
19743
|
isBlobButNotFile,
|
|
@@ -19548,6 +19745,7 @@ export {
|
|
|
19548
19745
|
isFileAttachment,
|
|
19549
19746
|
isFileList,
|
|
19550
19747
|
isFileReference,
|
|
19748
|
+
isFinishedUpload,
|
|
19551
19749
|
isGiphyAttachment,
|
|
19552
19750
|
isImageAttachment,
|
|
19553
19751
|
isImageFile,
|
|
@@ -19560,9 +19758,11 @@ export {
|
|
|
19560
19758
|
isLocalVoiceRecordingAttachment,
|
|
19561
19759
|
isOwnUser,
|
|
19562
19760
|
isPatch,
|
|
19761
|
+
isPendingUpload,
|
|
19563
19762
|
isScrapedContent,
|
|
19564
19763
|
isSharedLocationResponse,
|
|
19565
19764
|
isTargetedOptionTextUpdate,
|
|
19765
|
+
isUploadCancellation,
|
|
19566
19766
|
isUploadedAttachment,
|
|
19567
19767
|
isVideoAttachment,
|
|
19568
19768
|
isVoiceRecordingAttachment,
|
|
@@ -19582,11 +19782,14 @@ export {
|
|
|
19582
19782
|
readFileAsArrayBuffer,
|
|
19583
19783
|
removeDiacritics,
|
|
19584
19784
|
replaceWordWithEntity,
|
|
19785
|
+
settled,
|
|
19585
19786
|
stripCommandFromText,
|
|
19586
19787
|
stripMentionTokens,
|
|
19587
19788
|
textIsEmpty,
|
|
19588
19789
|
timeLeftMs,
|
|
19589
19790
|
verifyAndParseWebhook,
|
|
19590
|
-
verifySignature
|
|
19791
|
+
verifySignature,
|
|
19792
|
+
withCancellation,
|
|
19793
|
+
withoutConcurrency
|
|
19591
19794
|
};
|
|
19592
19795
|
//# sourceMappingURL=index.mjs.map
|