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
|
@@ -189,6 +189,7 @@ __export(index_exports, {
|
|
|
189
189
|
createMessageComposerStateCompositionMiddleware: () => createMessageComposerStateCompositionMiddleware,
|
|
190
190
|
createPollComposerStateMiddleware: () => createPollComposerStateMiddleware,
|
|
191
191
|
createPostUploadAttachmentEnrichmentMiddleware: () => createPostUploadAttachmentEnrichmentMiddleware,
|
|
192
|
+
createSendWithPendingUploadsAttachmentsMiddleware: () => createSendWithPendingUploadsAttachmentsMiddleware,
|
|
192
193
|
createSharedLocationCompositionMiddleware: () => createSharedLocationCompositionMiddleware,
|
|
193
194
|
createTextComposerCompositionMiddleware: () => createTextComposerCompositionMiddleware,
|
|
194
195
|
createTextComposerPreValidationMiddleware: () => createTextComposerPreValidationMiddleware,
|
|
@@ -217,6 +218,7 @@ __export(index_exports, {
|
|
|
217
218
|
getTokenizedSuggestionDisplayName: () => getTokenizedSuggestionDisplayName,
|
|
218
219
|
getTriggerCharWithToken: () => getTriggerCharWithToken,
|
|
219
220
|
gunzipPayload: () => gunzipPayload,
|
|
221
|
+
hasPending: () => hasPending,
|
|
220
222
|
insertItemWithTrigger: () => insertItemWithTrigger,
|
|
221
223
|
isAudioAttachment: () => isAudioAttachment,
|
|
222
224
|
isBlobButNotFile: () => isBlobButNotFile,
|
|
@@ -224,6 +226,7 @@ __export(index_exports, {
|
|
|
224
226
|
isFileAttachment: () => isFileAttachment,
|
|
225
227
|
isFileList: () => isFileList,
|
|
226
228
|
isFileReference: () => isFileReference,
|
|
229
|
+
isFinishedUpload: () => isFinishedUpload,
|
|
227
230
|
isGiphyAttachment: () => isGiphyAttachment,
|
|
228
231
|
isImageAttachment: () => isImageAttachment,
|
|
229
232
|
isImageFile: () => isImageFile,
|
|
@@ -236,9 +239,11 @@ __export(index_exports, {
|
|
|
236
239
|
isLocalVoiceRecordingAttachment: () => isLocalVoiceRecordingAttachment,
|
|
237
240
|
isOwnUser: () => isOwnUser,
|
|
238
241
|
isPatch: () => isPatch,
|
|
242
|
+
isPendingUpload: () => isPendingUpload,
|
|
239
243
|
isScrapedContent: () => isScrapedContent,
|
|
240
244
|
isSharedLocationResponse: () => isSharedLocationResponse,
|
|
241
245
|
isTargetedOptionTextUpdate: () => isTargetedOptionTextUpdate,
|
|
246
|
+
isUploadCancellation: () => isUploadCancellation,
|
|
242
247
|
isUploadedAttachment: () => isUploadedAttachment,
|
|
243
248
|
isVideoAttachment: () => isVideoAttachment,
|
|
244
249
|
isVoiceRecordingAttachment: () => isVoiceRecordingAttachment,
|
|
@@ -258,12 +263,15 @@ __export(index_exports, {
|
|
|
258
263
|
readFileAsArrayBuffer: () => readFileAsArrayBuffer,
|
|
259
264
|
removeDiacritics: () => removeDiacritics,
|
|
260
265
|
replaceWordWithEntity: () => replaceWordWithEntity,
|
|
266
|
+
settled: () => settled,
|
|
261
267
|
stripCommandFromText: () => stripCommandFromText,
|
|
262
268
|
stripMentionTokens: () => stripMentionTokens,
|
|
263
269
|
textIsEmpty: () => textIsEmpty,
|
|
264
270
|
timeLeftMs: () => timeLeftMs,
|
|
265
271
|
verifyAndParseWebhook: () => verifyAndParseWebhook,
|
|
266
|
-
verifySignature: () => verifySignature
|
|
272
|
+
verifySignature: () => verifySignature,
|
|
273
|
+
withCancellation: () => withCancellation,
|
|
274
|
+
withoutConcurrency: () => withoutConcurrency
|
|
267
275
|
});
|
|
268
276
|
module.exports = __toCommonJS(index_exports);
|
|
269
277
|
|
|
@@ -530,7 +538,7 @@ var ChannelBatchUpdater = class {
|
|
|
530
538
|
};
|
|
531
539
|
|
|
532
540
|
// src/client.ts
|
|
533
|
-
var
|
|
541
|
+
var import_axios4 = __toESM(require("axios"));
|
|
534
542
|
var import_https = __toESM(require_https());
|
|
535
543
|
|
|
536
544
|
// src/utils.ts
|
|
@@ -579,6 +587,22 @@ function logChatPromiseExecution(promise, name) {
|
|
|
579
587
|
console.warn(`failed to do ${name}, ran into error: `, error);
|
|
580
588
|
});
|
|
581
589
|
}
|
|
590
|
+
var noopLogger = () => null;
|
|
591
|
+
var logError = (logger, error, event) => {
|
|
592
|
+
const message = "Unhandled error in event listener";
|
|
593
|
+
logger("error", message, { error, event });
|
|
594
|
+
if (logger === noopLogger) console.error(message, error, event);
|
|
595
|
+
};
|
|
596
|
+
var invokeEventListener = (listener, event, logger) => {
|
|
597
|
+
try {
|
|
598
|
+
const result = listener(event);
|
|
599
|
+
if (result && typeof result.then === "function") {
|
|
600
|
+
Promise.resolve(result).catch((error) => logError(logger, error, event));
|
|
601
|
+
}
|
|
602
|
+
} catch (error) {
|
|
603
|
+
logError(logger, error, event);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
582
606
|
var sleep = (m) => new Promise((r) => setTimeout(r, m));
|
|
583
607
|
function isFunction(value) {
|
|
584
608
|
return typeof value === "function" || value instanceof Function || Object.prototype.toString.call(value) === "[object Function]";
|
|
@@ -626,7 +650,6 @@ var channelHasReadEvents = (channel) => {
|
|
|
626
650
|
return !(Array.isArray(ownCapabilities) && !ownCapabilities.includes("read-events"));
|
|
627
651
|
};
|
|
628
652
|
var channelTracksReadLocally = (channel) => !channelHasReadEvents(channel) && !!channel?.getClient().options.isLocalUnreadCountEnabled;
|
|
629
|
-
var userHasReadReceipts = (client) => client.user?.privacy_settings?.read_receipts?.enabled ?? true;
|
|
630
653
|
function addFileToFormData(uri, name, contentType) {
|
|
631
654
|
const data = new import_form_data.default();
|
|
632
655
|
if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri) || isBlobWebAPI(uri)) {
|
|
@@ -790,6 +813,38 @@ function unformatMessage(message) {
|
|
|
790
813
|
quoted_message: toMessageResponseBase(message.quoted_message)
|
|
791
814
|
};
|
|
792
815
|
}
|
|
816
|
+
var isRemoteUrl = (url) => !!url && /^https?:\/\//i.test(url.trim());
|
|
817
|
+
var sanitizeOutgoingAttachments = ({
|
|
818
|
+
client,
|
|
819
|
+
message
|
|
820
|
+
}) => {
|
|
821
|
+
const attachments = message.attachments;
|
|
822
|
+
if (!attachments?.length) return message;
|
|
823
|
+
const unresolved = [];
|
|
824
|
+
let changed = false;
|
|
825
|
+
const sanitized = attachments.reduce((acc, attachment) => {
|
|
826
|
+
const { localMetadata, ...rest } = attachment;
|
|
827
|
+
if (!localMetadata) {
|
|
828
|
+
acc.push(attachment);
|
|
829
|
+
return acc;
|
|
830
|
+
}
|
|
831
|
+
changed = true;
|
|
832
|
+
const hasRemoteSource = isRemoteUrl(rest.asset_url) || isRemoteUrl(rest.image_url) || isRemoteUrl(rest.og_scrape_url) || isRemoteUrl(rest.title_link);
|
|
833
|
+
if (hasRemoteSource) {
|
|
834
|
+
acc.push(rest);
|
|
835
|
+
} else {
|
|
836
|
+
unresolved.push(rest);
|
|
837
|
+
}
|
|
838
|
+
return acc;
|
|
839
|
+
}, []);
|
|
840
|
+
if (unresolved.length) {
|
|
841
|
+
const text = `Dropped ${unresolved.length} attachment(s) whose upload never completed. Composing with pending uploads requires awaiting them before sending.`;
|
|
842
|
+
const extraData = { attachments: unresolved, message, tags: ["attachments"] };
|
|
843
|
+
client.logger("warn", text, extraData);
|
|
844
|
+
console.warn(text, extraData);
|
|
845
|
+
}
|
|
846
|
+
return changed ? { ...message, attachments: sanitized } : message;
|
|
847
|
+
};
|
|
793
848
|
var localMessageToNewMessagePayload = (localMessage) => {
|
|
794
849
|
const {
|
|
795
850
|
// Remove all timestamp fields and client-specific fields.
|
|
@@ -2562,6 +2617,12 @@ var CooldownTimer = class extends WithSubscriptions {
|
|
|
2562
2617
|
var isScrapedContent = (attachment) => !!attachment?.og_scrape_url || !!attachment?.title_link;
|
|
2563
2618
|
var isLocalAttachment = (attachment) => !!attachment?.localMetadata?.id;
|
|
2564
2619
|
var isLocalUploadAttachment = (attachment) => !!attachment?.localMetadata?.uploadState;
|
|
2620
|
+
var PENDING_UPLOAD_STATES = [
|
|
2621
|
+
"pending",
|
|
2622
|
+
"uploading"
|
|
2623
|
+
];
|
|
2624
|
+
var isPendingUpload = (attachment) => isLocalUploadAttachment(attachment) && PENDING_UPLOAD_STATES.includes(attachment.localMetadata.uploadState);
|
|
2625
|
+
var isFinishedUpload = (attachment) => isLocalUploadAttachment(attachment) && attachment.localMetadata.uploadState === "finished";
|
|
2565
2626
|
var isFileAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "file" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) === -1 && attachment.type !== "video");
|
|
2566
2627
|
var isLocalFileAttachment = (attachment) => isFileAttachment(attachment) && isLocalAttachment(attachment);
|
|
2567
2628
|
var isImageAttachment = (attachment) => attachment.type === "image" && !isScrapedContent(attachment);
|
|
@@ -2637,7 +2698,7 @@ var ensureIsLocalAttachment = (attachment) => {
|
|
|
2637
2698
|
};
|
|
2638
2699
|
|
|
2639
2700
|
// src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts
|
|
2640
|
-
var createPostUploadAttachmentEnrichmentMiddleware = () => ({
|
|
2701
|
+
var createPostUploadAttachmentEnrichmentMiddleware = (composer) => ({
|
|
2641
2702
|
id: "stream-io/attachment-manager-middleware/post-upload-enrichment",
|
|
2642
2703
|
handlers: {
|
|
2643
2704
|
postProcess: ({
|
|
@@ -2650,9 +2711,10 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
|
|
|
2650
2711
|
if (error) return forward();
|
|
2651
2712
|
if (!attachment || !response) return discard();
|
|
2652
2713
|
const enrichedAttachment = { ...attachment };
|
|
2653
|
-
const previewUri = attachment.localMetadata
|
|
2654
|
-
|
|
2655
|
-
|
|
2714
|
+
const { id, previewUri } = attachment.localMetadata;
|
|
2715
|
+
const attachmentIsStillInComposer = !composer || !!composer.attachmentManager.attachmentsById[id];
|
|
2716
|
+
if (previewUri && attachmentIsStillInComposer) {
|
|
2717
|
+
if (previewUri.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
|
|
2656
2718
|
delete enrichedAttachment.localMetadata.previewUri;
|
|
2657
2719
|
}
|
|
2658
2720
|
if (isLocalImageAttachment(attachment)) {
|
|
@@ -2675,6 +2737,12 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
|
|
|
2675
2737
|
var withoutConcurrency = createRunner(wrapWithContinuationTracking);
|
|
2676
2738
|
var withCancellation = createRunner(wrapWithCancellation);
|
|
2677
2739
|
var pendingPromises = /* @__PURE__ */ new Map();
|
|
2740
|
+
function hasPending(tag) {
|
|
2741
|
+
return pendingPromises.has(tag);
|
|
2742
|
+
}
|
|
2743
|
+
async function settled(tag) {
|
|
2744
|
+
await pendingPromises.get(tag)?.promise;
|
|
2745
|
+
}
|
|
2678
2746
|
function createRunner(wrapper) {
|
|
2679
2747
|
return function run(tag, cb) {
|
|
2680
2748
|
const { cb: wrapped, onContinued } = wrapper(tag, cb);
|
|
@@ -2717,6 +2785,16 @@ var MiddlewareExecutor = class {
|
|
|
2717
2785
|
this.middleware = [];
|
|
2718
2786
|
this.id = generateUUIDv4();
|
|
2719
2787
|
}
|
|
2788
|
+
/**
|
|
2789
|
+
* The middleware currently installed, in execution order.
|
|
2790
|
+
*
|
|
2791
|
+
* Read-only: use `use` / `insert` / `replace` / `setOrder` / `remove` to change the chain. It
|
|
2792
|
+
* exists so components can ask what the chain is capable of - a middleware may declare
|
|
2793
|
+
* behaviour its host has to account for, and the host has no other way to find out.
|
|
2794
|
+
*/
|
|
2795
|
+
get installedMiddleware() {
|
|
2796
|
+
return this.middleware;
|
|
2797
|
+
}
|
|
2720
2798
|
use(middleware) {
|
|
2721
2799
|
this.middleware = this.middleware.concat(middleware);
|
|
2722
2800
|
return this;
|
|
@@ -2820,6 +2898,142 @@ var MiddlewareExecutor = class {
|
|
|
2820
2898
|
}
|
|
2821
2899
|
};
|
|
2822
2900
|
|
|
2901
|
+
// src/uploadManager.ts
|
|
2902
|
+
var import_axios = __toESM(require("axios"));
|
|
2903
|
+
var isUploadCancellation = (error) => import_axios.default.isCancel(error) || error instanceof Error && error.name === "AbortError";
|
|
2904
|
+
var initState = () => ({ uploads: {} });
|
|
2905
|
+
var upsertById = (uploads, record) => ({
|
|
2906
|
+
...uploads,
|
|
2907
|
+
[record.id]: { ...uploads[record.id], ...record }
|
|
2908
|
+
});
|
|
2909
|
+
var updateById = (uploads, record) => {
|
|
2910
|
+
if (!(record.id in uploads)) return null;
|
|
2911
|
+
const current = uploads[record.id];
|
|
2912
|
+
return { ...uploads, [record.id]: { ...current, ...record } };
|
|
2913
|
+
};
|
|
2914
|
+
var UploadManager = class {
|
|
2915
|
+
constructor(client) {
|
|
2916
|
+
this.client = client;
|
|
2917
|
+
this.inFlightUploads = /* @__PURE__ */ new Map();
|
|
2918
|
+
this.getUpload = (id) => this.uploads[id];
|
|
2919
|
+
/**
|
|
2920
|
+
* Clears all upload records.
|
|
2921
|
+
* Invoked when the user disconnects so a later session does not inherit stale upload state.
|
|
2922
|
+
* Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
|
|
2923
|
+
*/
|
|
2924
|
+
this.reset = () => {
|
|
2925
|
+
for (const { abortController } of this.inFlightUploads.values()) {
|
|
2926
|
+
abortController.abort();
|
|
2927
|
+
}
|
|
2928
|
+
this.inFlightUploads.clear();
|
|
2929
|
+
this.state.next(initState());
|
|
2930
|
+
};
|
|
2931
|
+
/**
|
|
2932
|
+
* Removes the upload record for `id` if present.
|
|
2933
|
+
* If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
|
|
2934
|
+
*/
|
|
2935
|
+
this.deleteUploadRecord = (id) => {
|
|
2936
|
+
const flight = this.inFlightUploads.get(id);
|
|
2937
|
+
if (flight) {
|
|
2938
|
+
this.inFlightUploads.delete(id);
|
|
2939
|
+
flight.abortController.abort();
|
|
2940
|
+
}
|
|
2941
|
+
this.state.next((current) => {
|
|
2942
|
+
if (!(id in current.uploads)) return current;
|
|
2943
|
+
const uploads = { ...current.uploads };
|
|
2944
|
+
delete uploads[id];
|
|
2945
|
+
return { ...current, uploads };
|
|
2946
|
+
});
|
|
2947
|
+
};
|
|
2948
|
+
/**
|
|
2949
|
+
* Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
|
|
2950
|
+
* Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
|
|
2951
|
+
* Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
|
|
2952
|
+
*/
|
|
2953
|
+
this.upload = ({
|
|
2954
|
+
id,
|
|
2955
|
+
channelCid,
|
|
2956
|
+
file
|
|
2957
|
+
}) => {
|
|
2958
|
+
const existing = this.inFlightUploads.get(id);
|
|
2959
|
+
if (existing) return existing.promise;
|
|
2960
|
+
let resolvePromise;
|
|
2961
|
+
let rejectPromise;
|
|
2962
|
+
const promise = new Promise((resolve, reject) => {
|
|
2963
|
+
resolvePromise = resolve;
|
|
2964
|
+
rejectPromise = reject;
|
|
2965
|
+
});
|
|
2966
|
+
const abortController = new AbortController();
|
|
2967
|
+
this.inFlightUploads.set(id, { promise, abortController });
|
|
2968
|
+
void (async () => {
|
|
2969
|
+
const attachmentManager = this.resolveAttachmentManager(channelCid);
|
|
2970
|
+
const trackProgress = attachmentManager.config.trackUploadProgress;
|
|
2971
|
+
try {
|
|
2972
|
+
this.upsertUpload({
|
|
2973
|
+
uploadConfirmationPending: false,
|
|
2974
|
+
id,
|
|
2975
|
+
uploadProgress: trackProgress ? 0 : void 0
|
|
2976
|
+
});
|
|
2977
|
+
const onProgress = trackProgress ? (progress) => {
|
|
2978
|
+
this.updateUpload({
|
|
2979
|
+
id,
|
|
2980
|
+
uploadProgress: progress,
|
|
2981
|
+
// Only a number says anything about the flush, so a report without one leaves
|
|
2982
|
+
// the flag alone: `undefined` means the transport cannot measure this upload,
|
|
2983
|
+
// not that bytes were un-sent. Lowering it there would drop a UI that had
|
|
2984
|
+
// already gone indeterminate back to a determinate one with nothing to show.
|
|
2985
|
+
//
|
|
2986
|
+
// The record is removed in the `finally` below, so a record that still exists
|
|
2987
|
+
// while reporting 100% can only mean "flushed, awaiting the response".
|
|
2988
|
+
...typeof progress === "number" ? { uploadConfirmationPending: progress >= 100 } : {}
|
|
2989
|
+
});
|
|
2990
|
+
} : void 0;
|
|
2991
|
+
const uploadRequestOptions = {
|
|
2992
|
+
abortSignal: abortController.signal,
|
|
2993
|
+
...onProgress ? { onProgress } : {}
|
|
2994
|
+
};
|
|
2995
|
+
const response = await attachmentManager.doUploadRequest(
|
|
2996
|
+
file,
|
|
2997
|
+
uploadRequestOptions
|
|
2998
|
+
);
|
|
2999
|
+
resolvePromise(response);
|
|
3000
|
+
} catch (error) {
|
|
3001
|
+
rejectPromise(error);
|
|
3002
|
+
} finally {
|
|
3003
|
+
this.inFlightUploads.delete(id);
|
|
3004
|
+
this.deleteUploadRecord(id);
|
|
3005
|
+
}
|
|
3006
|
+
})();
|
|
3007
|
+
return promise;
|
|
3008
|
+
};
|
|
3009
|
+
this.upsertUpload = (record) => {
|
|
3010
|
+
this.state.partialNext({
|
|
3011
|
+
uploads: upsertById(this.uploads, record)
|
|
3012
|
+
});
|
|
3013
|
+
};
|
|
3014
|
+
this.updateUpload = (record) => {
|
|
3015
|
+
this.state.next((current) => {
|
|
3016
|
+
const nextUploads = updateById(current.uploads, record);
|
|
3017
|
+
if (!nextUploads) return current;
|
|
3018
|
+
return { ...current, uploads: nextUploads };
|
|
3019
|
+
});
|
|
3020
|
+
};
|
|
3021
|
+
this.state = new StateStore(initState());
|
|
3022
|
+
}
|
|
3023
|
+
resolveAttachmentManager(channelCid) {
|
|
3024
|
+
const colon = channelCid.indexOf(":");
|
|
3025
|
+
if (colon <= 0 || colon === channelCid.length - 1) {
|
|
3026
|
+
throw new Error(`Invalid channelCid: ${channelCid}`);
|
|
3027
|
+
}
|
|
3028
|
+
const channelType = channelCid.slice(0, colon);
|
|
3029
|
+
const channelId = channelCid.slice(colon + 1);
|
|
3030
|
+
return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
|
|
3031
|
+
}
|
|
3032
|
+
get uploads() {
|
|
3033
|
+
return this.state.getLatestValue().uploads;
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
3036
|
+
|
|
2823
3037
|
// src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts
|
|
2824
3038
|
var createUploadErrorHandlerMiddleware = (composer) => ({
|
|
2825
3039
|
id: "stream-io/attachment-manager-middleware/upload-error",
|
|
@@ -2832,6 +3046,7 @@ var createUploadErrorHandlerMiddleware = (composer) => ({
|
|
|
2832
3046
|
const { attachment, error } = state;
|
|
2833
3047
|
if (!error) return forward();
|
|
2834
3048
|
if (!attachment) return discard();
|
|
3049
|
+
if (isUploadCancellation(error)) return forward();
|
|
2835
3050
|
const reason = error instanceof Error ? error.message : "unknown error";
|
|
2836
3051
|
composer.client.notifications.addError({
|
|
2837
3052
|
message: "Error uploading attachment",
|
|
@@ -2856,7 +3071,7 @@ var AttachmentPostUploadMiddlewareExecutor = class extends MiddlewareExecutor {
|
|
|
2856
3071
|
super();
|
|
2857
3072
|
this.use([
|
|
2858
3073
|
createUploadErrorHandlerMiddleware(composer),
|
|
2859
|
-
createPostUploadAttachmentEnrichmentMiddleware()
|
|
3074
|
+
createPostUploadAttachmentEnrichmentMiddleware(composer)
|
|
2860
3075
|
]);
|
|
2861
3076
|
}
|
|
2862
3077
|
};
|
|
@@ -3304,7 +3519,7 @@ function mergeWithDiff(target, source, customizer) {
|
|
|
3304
3519
|
}
|
|
3305
3520
|
|
|
3306
3521
|
// src/messageComposer/attachmentManager.ts
|
|
3307
|
-
var
|
|
3522
|
+
var initState2 = ({
|
|
3308
3523
|
message
|
|
3309
3524
|
}) => ({
|
|
3310
3525
|
attachments: (message?.attachments ?? [])?.filter(({ og_scrape_url }) => !og_scrape_url).map((att) => {
|
|
@@ -3332,13 +3547,14 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3332
3547
|
...attachment,
|
|
3333
3548
|
localMetadata: {
|
|
3334
3549
|
...attachment.localMetadata,
|
|
3550
|
+
uploadConfirmationPending: false,
|
|
3335
3551
|
uploadProgress: void 0,
|
|
3336
3552
|
uploadState: "failed"
|
|
3337
3553
|
}
|
|
3338
3554
|
};
|
|
3339
3555
|
};
|
|
3340
3556
|
this.initState = ({ message } = {}) => {
|
|
3341
|
-
this.state.next(
|
|
3557
|
+
this.state.next(initState2({ message }));
|
|
3342
3558
|
};
|
|
3343
3559
|
this.getSnapshot = () => {
|
|
3344
3560
|
const state = this.state.getLatestValue();
|
|
@@ -3415,8 +3631,22 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3415
3631
|
this.state.partialNext({ attachments });
|
|
3416
3632
|
}
|
|
3417
3633
|
};
|
|
3634
|
+
/**
|
|
3635
|
+
* Releases a local preview URL the SDK minted, if it minted one.
|
|
3636
|
+
*
|
|
3637
|
+
* Only `blob:` URLs are ours to revoke: `toLocalUploadAttachment` puts a
|
|
3638
|
+
* `URL.createObjectURL` result in `previewUri` for a `File`, but for a React Native
|
|
3639
|
+
* `FileReference` it stores the caller's own native file URI, which must be left alone.
|
|
3640
|
+
*/
|
|
3641
|
+
this.revokePreviewUri = (attachment) => {
|
|
3642
|
+
const previewUri = attachment.localMetadata?.previewUri;
|
|
3643
|
+
if (previewUri?.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
|
|
3644
|
+
};
|
|
3418
3645
|
this.removeAttachments = (localAttachmentIds) => {
|
|
3419
3646
|
if (!localAttachmentIds.length) return;
|
|
3647
|
+
const removedAttachments = this.attachments.filter(
|
|
3648
|
+
(attachment) => localAttachmentIds.includes(attachment.localMetadata?.id)
|
|
3649
|
+
);
|
|
3420
3650
|
this.state.partialNext({
|
|
3421
3651
|
attachments: this.attachments.filter(
|
|
3422
3652
|
(attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
|
|
@@ -3425,6 +3655,7 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3425
3655
|
for (const id of localAttachmentIds) {
|
|
3426
3656
|
this.client.uploadManager.deleteUploadRecord(id);
|
|
3427
3657
|
}
|
|
3658
|
+
removedAttachments.forEach(this.revokePreviewUri);
|
|
3428
3659
|
};
|
|
3429
3660
|
this.getUploadConfigCheck = async (fileLike) => {
|
|
3430
3661
|
const client = this.channel.getClient();
|
|
@@ -3576,21 +3807,27 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3576
3807
|
localMetadata: {
|
|
3577
3808
|
...attachment.localMetadata,
|
|
3578
3809
|
uploadState: "failed",
|
|
3579
|
-
uploadProgress: void 0
|
|
3810
|
+
uploadProgress: void 0,
|
|
3811
|
+
// `false`, not `undefined`: these updates go through `updateAttachment`, which merges
|
|
3812
|
+
// via `mergeWith` — and that skips `undefined` source values, so `undefined` would
|
|
3813
|
+
// leave a stale `true` in place.
|
|
3814
|
+
uploadConfirmationPending: false
|
|
3580
3815
|
}
|
|
3581
3816
|
};
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3817
|
+
if (!isUploadCancellation(error)) {
|
|
3818
|
+
this.client.notifications.addError({
|
|
3819
|
+
message: "Error uploading attachment",
|
|
3820
|
+
origin: {
|
|
3821
|
+
emitter: "AttachmentManager",
|
|
3822
|
+
context: { attachment, failedAttachment }
|
|
3823
|
+
},
|
|
3824
|
+
options: {
|
|
3825
|
+
type: "api:attachment:upload:failed",
|
|
3826
|
+
metadata: { reason },
|
|
3827
|
+
originalError: error instanceof Error ? error : void 0
|
|
3828
|
+
}
|
|
3829
|
+
});
|
|
3830
|
+
}
|
|
3594
3831
|
this.updateAttachment(failedAttachment);
|
|
3595
3832
|
return failedAttachment;
|
|
3596
3833
|
}
|
|
@@ -3603,7 +3840,9 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3603
3840
|
localMetadata: {
|
|
3604
3841
|
...attachment.localMetadata,
|
|
3605
3842
|
uploadState: "finished",
|
|
3606
|
-
uploadProgress: void 0
|
|
3843
|
+
uploadProgress: void 0,
|
|
3844
|
+
// See the note in the failure branch: `undefined` cannot clear through `mergeWith`.
|
|
3845
|
+
uploadConfirmationPending: false
|
|
3607
3846
|
}
|
|
3608
3847
|
};
|
|
3609
3848
|
const previewUri = uploadedAttachment.localMetadata.previewUri;
|
|
@@ -3652,7 +3891,9 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3652
3891
|
localMetadata: {
|
|
3653
3892
|
...attachment.localMetadata,
|
|
3654
3893
|
uploadState: error ? "failed" : "finished",
|
|
3655
|
-
uploadProgress: void 0
|
|
3894
|
+
uploadProgress: void 0,
|
|
3895
|
+
// See the note in `uploadAttachment`: `undefined` cannot clear through `mergeWith`.
|
|
3896
|
+
uploadConfirmationPending: false
|
|
3656
3897
|
}
|
|
3657
3898
|
},
|
|
3658
3899
|
error,
|
|
@@ -3677,7 +3918,7 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3677
3918
|
);
|
|
3678
3919
|
};
|
|
3679
3920
|
this.composer = composer;
|
|
3680
|
-
this.state = new StateStore(
|
|
3921
|
+
this.state = new StateStore(initState2({ message }));
|
|
3681
3922
|
this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
|
|
3682
3923
|
this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
|
|
3683
3924
|
composer
|
|
@@ -3782,7 +4023,8 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3782
4023
|
localMetadata: {
|
|
3783
4024
|
...attachment.localMetadata,
|
|
3784
4025
|
uploadState: "uploading",
|
|
3785
|
-
uploadProgress: this.config.trackUploadProgress ? 0 : void 0
|
|
4026
|
+
uploadProgress: this.config.trackUploadProgress ? 0 : void 0,
|
|
4027
|
+
uploadConfirmationPending: false
|
|
3786
4028
|
}
|
|
3787
4029
|
}
|
|
3788
4030
|
]);
|
|
@@ -3795,7 +4037,8 @@ var _AttachmentManager = class _AttachmentManager {
|
|
|
3795
4037
|
localMetadata: {
|
|
3796
4038
|
...attachment.localMetadata,
|
|
3797
4039
|
uploadState: "uploading",
|
|
3798
|
-
uploadProgress: nextUpload.uploadProgress
|
|
4040
|
+
uploadProgress: nextUpload.uploadProgress,
|
|
4041
|
+
uploadConfirmationPending: nextUpload.uploadConfirmationPending
|
|
3799
4042
|
}
|
|
3800
4043
|
});
|
|
3801
4044
|
}
|
|
@@ -3849,6 +4092,50 @@ var localAttachmentToAttachment = (localAttachment) => {
|
|
|
3849
4092
|
const { localMetadata, ...attachment } = localAttachment;
|
|
3850
4093
|
return attachment;
|
|
3851
4094
|
};
|
|
4095
|
+
var composeWithPendingUploads = ({
|
|
4096
|
+
composer,
|
|
4097
|
+
state
|
|
4098
|
+
}) => {
|
|
4099
|
+
const composerIsKeptAsDraft = !!composer.pollId;
|
|
4100
|
+
const relevantAttachments = composer.attachmentManager.attachments.filter(
|
|
4101
|
+
(attachment) => isFinishedUpload(attachment) || !composerIsKeptAsDraft && isPendingUpload(attachment)
|
|
4102
|
+
);
|
|
4103
|
+
const localAttachments = (state.localMessage.attachments ?? []).concat(
|
|
4104
|
+
relevantAttachments.map(
|
|
4105
|
+
(attachment) => isPendingUpload(attachment) ? attachment : localAttachmentToAttachment(attachment)
|
|
4106
|
+
)
|
|
4107
|
+
);
|
|
4108
|
+
const messageAttachments = (state.message.attachments ?? []).concat(
|
|
4109
|
+
relevantAttachments.filter(isFinishedUpload).map(localAttachmentToAttachment)
|
|
4110
|
+
);
|
|
4111
|
+
if (!localAttachments.length && !messageAttachments.length) return state;
|
|
4112
|
+
return {
|
|
4113
|
+
...state,
|
|
4114
|
+
localMessage: localAttachments.length ? {
|
|
4115
|
+
...state.localMessage,
|
|
4116
|
+
attachments: localAttachments
|
|
4117
|
+
} : state.localMessage,
|
|
4118
|
+
message: messageAttachments.length ? {
|
|
4119
|
+
...state.message,
|
|
4120
|
+
attachments: messageAttachments
|
|
4121
|
+
} : state.message
|
|
4122
|
+
};
|
|
4123
|
+
};
|
|
4124
|
+
var createSendWithPendingUploadsAttachmentsMiddleware = (composer) => ({
|
|
4125
|
+
allowsPendingUploads: true,
|
|
4126
|
+
id: "stream-io/message-composer-middleware/attachments",
|
|
4127
|
+
handlers: {
|
|
4128
|
+
compose: ({
|
|
4129
|
+
state,
|
|
4130
|
+
next,
|
|
4131
|
+
forward
|
|
4132
|
+
}) => {
|
|
4133
|
+
const { attachmentManager } = composer;
|
|
4134
|
+
if (!attachmentManager) return forward();
|
|
4135
|
+
return next(composeWithPendingUploads({ composer, state }));
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
3852
4139
|
var createAttachmentsCompositionMiddleware = (composer) => ({
|
|
3853
4140
|
id: "stream-io/message-composer-middleware/attachments",
|
|
3854
4141
|
handlers: {
|
|
@@ -4065,7 +4352,7 @@ var getInitialMentions = (message) => {
|
|
|
4065
4352
|
};
|
|
4066
4353
|
var isSameMentionEntity = (first, second) => first.id === second.id && first.mentionType === second.mentionType;
|
|
4067
4354
|
var getMentionsFromState = (state) => state.mentions ?? state.mentionedUsers.map(userResponseToMentionEntity);
|
|
4068
|
-
var
|
|
4355
|
+
var initState3 = ({
|
|
4069
4356
|
composer,
|
|
4070
4357
|
message
|
|
4071
4358
|
}) => {
|
|
@@ -4094,7 +4381,7 @@ var initState2 = ({
|
|
|
4094
4381
|
var TextComposer = class {
|
|
4095
4382
|
constructor({ composer, message }) {
|
|
4096
4383
|
this.initState = ({ message } = {}) => {
|
|
4097
|
-
this.state.next(
|
|
4384
|
+
this.state.next(initState3({ composer: this.composer, message }));
|
|
4098
4385
|
};
|
|
4099
4386
|
this.getSnapshot = (state = this.state.getLatestValue()) => state;
|
|
4100
4387
|
this.restoreSnapshot = (snapshot) => {
|
|
@@ -4287,7 +4574,7 @@ var TextComposer = class {
|
|
|
4287
4574
|
this.commitState(output.state);
|
|
4288
4575
|
};
|
|
4289
4576
|
this.composer = composer;
|
|
4290
|
-
this.state = new StateStore(
|
|
4577
|
+
this.state = new StateStore(initState3({ composer, message }));
|
|
4291
4578
|
this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
|
|
4292
4579
|
}
|
|
4293
4580
|
get channel() {
|
|
@@ -5989,6 +6276,13 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
|
|
|
5989
6276
|
createCompositionDataCleanupMiddleware(composer)
|
|
5990
6277
|
]);
|
|
5991
6278
|
}
|
|
6279
|
+
/**
|
|
6280
|
+
* Narrows the base accessor to the composition middleware type, so callers can read the
|
|
6281
|
+
* declarations it carries (`allowsPendingUploads`) without casting.
|
|
6282
|
+
*/
|
|
6283
|
+
get installedMiddleware() {
|
|
6284
|
+
return super.installedMiddleware;
|
|
6285
|
+
}
|
|
5992
6286
|
};
|
|
5993
6287
|
var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
|
|
5994
6288
|
constructor({ composer }) {
|
|
@@ -7103,7 +7397,7 @@ var DEFAULT_COMPOSER_CONFIG = {
|
|
|
7103
7397
|
};
|
|
7104
7398
|
|
|
7105
7399
|
// src/messageComposer/CustomDataManager.ts
|
|
7106
|
-
var
|
|
7400
|
+
var initState4 = (options) => {
|
|
7107
7401
|
if (!options)
|
|
7108
7402
|
return { message: {}, custom: {} };
|
|
7109
7403
|
return { message: {}, custom: {} };
|
|
@@ -7112,14 +7406,14 @@ var CustomDataManager = class {
|
|
|
7112
7406
|
constructor({ composer, message }) {
|
|
7113
7407
|
this.isMessageDataEqual = (nextState, previousState) => JSON.stringify(nextState.message) === JSON.stringify(previousState?.message);
|
|
7114
7408
|
this.initState = ({ message } = {}) => {
|
|
7115
|
-
this.state.next(
|
|
7409
|
+
this.state.next(initState4({ composer: this.composer, message }));
|
|
7116
7410
|
};
|
|
7117
7411
|
this.getSnapshot = () => this.state.getLatestValue();
|
|
7118
7412
|
this.restoreSnapshot = (snapshot) => {
|
|
7119
7413
|
this.state.next(snapshot);
|
|
7120
7414
|
};
|
|
7121
7415
|
this.composer = composer;
|
|
7122
|
-
this.state = new StateStore(
|
|
7416
|
+
this.state = new StateStore(initState4({ composer, message }));
|
|
7123
7417
|
}
|
|
7124
7418
|
get customMessageData() {
|
|
7125
7419
|
return this.state.getLatestValue().message;
|
|
@@ -7155,7 +7449,7 @@ var LinkPreviewStatus = /* @__PURE__ */ ((LinkPreviewStatus2) => {
|
|
|
7155
7449
|
return LinkPreviewStatus2;
|
|
7156
7450
|
})(LinkPreviewStatus || {});
|
|
7157
7451
|
var linkPreviewArrayToMap = (linkPreviews) => new Map(linkPreviews.map((linkPreview) => [linkPreview.og_scrape_url, linkPreview]));
|
|
7158
|
-
var
|
|
7452
|
+
var initState5 = ({
|
|
7159
7453
|
message
|
|
7160
7454
|
}) => message ? {
|
|
7161
7455
|
previews: message.attachments?.reduce((acc, attachment) => {
|
|
@@ -7174,7 +7468,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
|
|
|
7174
7468
|
this.shouldDiscardEnrichQueries = false;
|
|
7175
7469
|
this.initState = ({ message } = {}) => {
|
|
7176
7470
|
this.cancelURLEnrichment();
|
|
7177
|
-
this.state.next(
|
|
7471
|
+
this.state.next(initState5({ message: this.enabled ? message : void 0 }));
|
|
7178
7472
|
};
|
|
7179
7473
|
this.getSnapshot = () => this.state.getLatestValue();
|
|
7180
7474
|
this.restoreSnapshot = (snapshot) => {
|
|
@@ -7277,7 +7571,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
|
|
|
7277
7571
|
};
|
|
7278
7572
|
this.composer = composer;
|
|
7279
7573
|
this.state = new StateStore(
|
|
7280
|
-
|
|
7574
|
+
initState5({ message: this.enabled ? message : void 0 })
|
|
7281
7575
|
);
|
|
7282
7576
|
this.findAndEnrichUrls = debounce(
|
|
7283
7577
|
this._findAndEnrichUrls.bind(this),
|
|
@@ -7365,7 +7659,7 @@ var LinkPreviewsManager = _LinkPreviewsManager;
|
|
|
7365
7659
|
|
|
7366
7660
|
// src/messageComposer/LocationComposer.ts
|
|
7367
7661
|
var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
|
|
7368
|
-
var
|
|
7662
|
+
var initState6 = ({
|
|
7369
7663
|
message
|
|
7370
7664
|
}) => ({
|
|
7371
7665
|
location: message?.shared_location ?? null
|
|
@@ -7373,7 +7667,7 @@ var initState5 = ({
|
|
|
7373
7667
|
var LocationComposer = class {
|
|
7374
7668
|
constructor({ composer, message }) {
|
|
7375
7669
|
this.initState = ({ message } = {}) => {
|
|
7376
|
-
this.state.next(
|
|
7670
|
+
this.state.next(initState6({ message }));
|
|
7377
7671
|
};
|
|
7378
7672
|
this.getSnapshot = () => this.state.getLatestValue();
|
|
7379
7673
|
this.restoreSnapshot = (snapshot) => {
|
|
@@ -7391,7 +7685,7 @@ var LocationComposer = class {
|
|
|
7391
7685
|
});
|
|
7392
7686
|
};
|
|
7393
7687
|
this.composer = composer;
|
|
7394
|
-
this.state = new StateStore(
|
|
7688
|
+
this.state = new StateStore(initState6({ message }));
|
|
7395
7689
|
this._deviceId = this.config.getDeviceId();
|
|
7396
7690
|
}
|
|
7397
7691
|
get config() {
|
|
@@ -8112,7 +8406,7 @@ var initEditingAuditState = (composition) => {
|
|
|
8112
8406
|
}
|
|
8113
8407
|
};
|
|
8114
8408
|
};
|
|
8115
|
-
var
|
|
8409
|
+
var initState7 = (composition) => {
|
|
8116
8410
|
if (!composition) {
|
|
8117
8411
|
return {
|
|
8118
8412
|
draftId: null,
|
|
@@ -8199,7 +8493,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
8199
8493
|
this.textComposer.initState({ message });
|
|
8200
8494
|
this.pollComposer.initState();
|
|
8201
8495
|
this.customDataManager.initState({ message });
|
|
8202
|
-
this.state.next(
|
|
8496
|
+
this.state.next(initState7(composition));
|
|
8203
8497
|
};
|
|
8204
8498
|
this.initStateFromChannelResponse = (channelApiResponse) => {
|
|
8205
8499
|
if (this.channel.cid !== channelApiResponse.channel.cid) {
|
|
@@ -8665,7 +8959,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
8665
8959
|
this.editingAuditState = new StateStore(
|
|
8666
8960
|
this.initEditingAuditState(composition)
|
|
8667
8961
|
);
|
|
8668
|
-
this.state = new StateStore(
|
|
8962
|
+
this.state = new StateStore(initState7(composition));
|
|
8669
8963
|
this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
|
|
8670
8964
|
composer: this
|
|
8671
8965
|
});
|
|
@@ -8744,8 +9038,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
8744
9038
|
const currentCommand = this.textComposer.command;
|
|
8745
9039
|
return !currentCommand || this.validateCommandSendability(currentCommand).ready;
|
|
8746
9040
|
}
|
|
9041
|
+
/**
|
|
9042
|
+
* Whether the installed composition middleware composes messages whose uploads are still in
|
|
9043
|
+
* flight — see `MessageCompositionMiddleware.allowsPendingUploads`.
|
|
9044
|
+
*
|
|
9045
|
+
* Derived from the chain rather than from config, so there is exactly one switch: the UI SDK
|
|
9046
|
+
* that implements the rest of the flow installs the middleware, and everything that has to
|
|
9047
|
+
* follow from that (starting with {@link hasSendableData}) follows on its own.
|
|
9048
|
+
*
|
|
9049
|
+
* **Temporary API.** In v10 sending with pending uploads becomes part of the composer
|
|
9050
|
+
* configuration (`MessageComposerConfig`, set through `updateConfig`), and this getter,
|
|
9051
|
+
* together with the middleware declaration it reads, is replaced by that config field.
|
|
9052
|
+
*/
|
|
9053
|
+
get allowsPendingUploads() {
|
|
9054
|
+
return this.compositionMiddlewareExecutor.installedMiddleware.some(
|
|
9055
|
+
(middleware) => middleware.allowsPendingUploads
|
|
9056
|
+
);
|
|
9057
|
+
}
|
|
8747
9058
|
get hasSendableData() {
|
|
8748
|
-
|
|
9059
|
+
if (!this.isCommandSendable) return false;
|
|
9060
|
+
if (this.allowsPendingUploads) {
|
|
9061
|
+
const hasSendableAttachment = this.attachmentManager.attachments.some(
|
|
9062
|
+
(attachment) => isFinishedUpload(attachment) || isPendingUpload(attachment)
|
|
9063
|
+
);
|
|
9064
|
+
return !!(!this.textComposer.textIsEmpty || hasSendableAttachment || this.pollId || this.locationComposer.validLocation);
|
|
9065
|
+
}
|
|
9066
|
+
return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
|
|
8749
9067
|
}
|
|
8750
9068
|
get compositionIsEmpty() {
|
|
8751
9069
|
return !this.quotedMessage && this.contentIsEmpty;
|
|
@@ -8884,7 +9202,6 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
|
|
|
8884
9202
|
* @param options
|
|
8885
9203
|
*/
|
|
8886
9204
|
this.markRead = async (collection, options) => {
|
|
8887
|
-
if (!userHasReadReceipts(this.client)) return null;
|
|
8888
9205
|
let result = null;
|
|
8889
9206
|
if (isChannel(collection)) {
|
|
8890
9207
|
result = await collection.markAsReadRequest(options);
|
|
@@ -8894,7 +9211,7 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
|
|
|
8894
9211
|
thread_id: collection.id
|
|
8895
9212
|
});
|
|
8896
9213
|
}
|
|
8897
|
-
this.removeCandidateFor(collection);
|
|
9214
|
+
if (result) this.removeCandidateFor(collection);
|
|
8898
9215
|
return result;
|
|
8899
9216
|
};
|
|
8900
9217
|
/**
|
|
@@ -9279,18 +9596,14 @@ var Channel = class {
|
|
|
9279
9596
|
return await this.query(defaultOptions, "latest");
|
|
9280
9597
|
};
|
|
9281
9598
|
this._callChannelListeners = (event) => {
|
|
9282
|
-
const
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
}
|
|
9287
|
-
if (channel.listeners[event.type]) {
|
|
9288
|
-
listeners.push(...channel.listeners[event.type]);
|
|
9289
|
-
}
|
|
9599
|
+
const listeners = [
|
|
9600
|
+
...this.listeners.all ?? [],
|
|
9601
|
+
...this.listeners[event.type] ?? []
|
|
9602
|
+
];
|
|
9603
|
+
const { logger } = this._client;
|
|
9290
9604
|
for (const listener of listeners) {
|
|
9291
|
-
if (typeof listener
|
|
9292
|
-
|
|
9293
|
-
}
|
|
9605
|
+
if (typeof listener === "string") continue;
|
|
9606
|
+
invokeEventListener(listener, event, logger);
|
|
9294
9607
|
}
|
|
9295
9608
|
};
|
|
9296
9609
|
/**
|
|
@@ -9372,14 +9685,16 @@ var Channel = class {
|
|
|
9372
9685
|
*
|
|
9373
9686
|
* @return {Promise<SendMessageAPIResponse>} The Server Response
|
|
9374
9687
|
*/
|
|
9375
|
-
async _sendMessage(
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9688
|
+
async _sendMessage(rawMessage, options) {
|
|
9689
|
+
const client = this.getClient();
|
|
9690
|
+
const message = sanitizeOutgoingAttachments({
|
|
9691
|
+
client,
|
|
9692
|
+
message: rawMessage
|
|
9693
|
+
});
|
|
9694
|
+
return await client.post(this._channelURL() + "/message", {
|
|
9695
|
+
message,
|
|
9696
|
+
...options
|
|
9697
|
+
});
|
|
9383
9698
|
}
|
|
9384
9699
|
async sendMessage(message, options) {
|
|
9385
9700
|
try {
|
|
@@ -10202,14 +10517,15 @@ var Channel = class {
|
|
|
10202
10517
|
return await this.getClient().messageDeliveryReporter.markRead(this, data);
|
|
10203
10518
|
}
|
|
10204
10519
|
/**
|
|
10205
|
-
* markReadRequest - Send the mark read event for this user, only works if the `read_events` setting is
|
|
10520
|
+
* markReadRequest - Send the mark read event for this user, only works if the `read_events` setting is
|
|
10521
|
+
* enabled and the user holds the `read-events` capability on this channel
|
|
10206
10522
|
*
|
|
10207
10523
|
* @param {MarkReadOptions} data
|
|
10208
10524
|
* @return {Promise<EventAPIResponse | null>} Description
|
|
10209
10525
|
*/
|
|
10210
10526
|
async markAsReadRequest(data = {}) {
|
|
10211
10527
|
this._checkInitialized();
|
|
10212
|
-
if (!this.
|
|
10528
|
+
if (!this.getClient()._isUsingServerAuth() && (!this.getConfig()?.read_events || !channelHasReadEvents(this))) {
|
|
10213
10529
|
return null;
|
|
10214
10530
|
}
|
|
10215
10531
|
return await this.getClient().post(this._channelURL() + "/read", {
|
|
@@ -10217,14 +10533,15 @@ var Channel = class {
|
|
|
10217
10533
|
});
|
|
10218
10534
|
}
|
|
10219
10535
|
/**
|
|
10220
|
-
* markUnread - Mark the channel as unread from messageID, only works if the `read_events` setting is
|
|
10536
|
+
* markUnread - Mark the channel as unread from messageID, only works if the `read_events` setting is
|
|
10537
|
+
* enabled and the user holds the `read-events` capability on this channel
|
|
10221
10538
|
*
|
|
10222
10539
|
* @param {MarkUnreadOptions} data
|
|
10223
10540
|
* @return {APIResponse} An API response
|
|
10224
10541
|
*/
|
|
10225
10542
|
async markUnread(data) {
|
|
10226
10543
|
this._checkInitialized();
|
|
10227
|
-
if (!this.
|
|
10544
|
+
if (!this.getClient()._isUsingServerAuth() && (!this.getConfig()?.read_events || !channelHasReadEvents(this))) {
|
|
10228
10545
|
return Promise.resolve(null);
|
|
10229
10546
|
}
|
|
10230
10547
|
return await this.getClient().post(this._channelURL() + "/unread", {
|
|
@@ -10830,6 +11147,7 @@ var Channel = class {
|
|
|
10830
11147
|
// delivery-report network sync below is skipped for it.
|
|
10831
11148
|
case "message.read_locally":
|
|
10832
11149
|
case "message.read":
|
|
11150
|
+
if (event.thread) break;
|
|
10833
11151
|
if (event.user?.id && event.created_at) {
|
|
10834
11152
|
const previousReadState = channelState.read[event.user.id];
|
|
10835
11153
|
channelState.read[event.user.id] = {
|
|
@@ -11279,7 +11597,7 @@ var ClientState = class {
|
|
|
11279
11597
|
var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
|
|
11280
11598
|
|
|
11281
11599
|
// src/insights.ts
|
|
11282
|
-
var
|
|
11600
|
+
var import_axios2 = __toESM(require("axios"));
|
|
11283
11601
|
var InsightMetrics = class {
|
|
11284
11602
|
constructor() {
|
|
11285
11603
|
this.connectionStartTimestamp = null;
|
|
@@ -11292,7 +11610,7 @@ var postInsights = async (insightType, insights) => {
|
|
|
11292
11610
|
const maxAttempts = 3;
|
|
11293
11611
|
for (let i = 0; i < maxAttempts; i++) {
|
|
11294
11612
|
try {
|
|
11295
|
-
await
|
|
11613
|
+
await import_axios2.default.post(
|
|
11296
11614
|
`https://chat-insights.getstream.io/insights/${insightType}`,
|
|
11297
11615
|
insights
|
|
11298
11616
|
);
|
|
@@ -11800,131 +12118,6 @@ var StableWSConnection = class {
|
|
|
11800
12118
|
}
|
|
11801
12119
|
};
|
|
11802
12120
|
|
|
11803
|
-
// src/uploadManager.ts
|
|
11804
|
-
var initState7 = () => ({ uploads: {} });
|
|
11805
|
-
var upsertById = (uploads, record) => ({
|
|
11806
|
-
...uploads,
|
|
11807
|
-
[record.id]: { ...uploads[record.id], ...record }
|
|
11808
|
-
});
|
|
11809
|
-
var updateById = (uploads, record) => {
|
|
11810
|
-
if (!(record.id in uploads)) return null;
|
|
11811
|
-
const current = uploads[record.id];
|
|
11812
|
-
return { ...uploads, [record.id]: { ...current, ...record } };
|
|
11813
|
-
};
|
|
11814
|
-
var UploadManager = class {
|
|
11815
|
-
constructor(client) {
|
|
11816
|
-
this.client = client;
|
|
11817
|
-
this.inFlightUploads = /* @__PURE__ */ new Map();
|
|
11818
|
-
this.getUpload = (id) => this.uploads[id];
|
|
11819
|
-
/**
|
|
11820
|
-
* Clears all upload records.
|
|
11821
|
-
* Invoked when the user disconnects so a later session does not inherit stale upload state.
|
|
11822
|
-
* Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
|
|
11823
|
-
*/
|
|
11824
|
-
this.reset = () => {
|
|
11825
|
-
for (const { abortController } of this.inFlightUploads.values()) {
|
|
11826
|
-
abortController.abort();
|
|
11827
|
-
}
|
|
11828
|
-
this.inFlightUploads.clear();
|
|
11829
|
-
this.state.next(initState7());
|
|
11830
|
-
};
|
|
11831
|
-
/**
|
|
11832
|
-
* Removes the upload record for `id` if present.
|
|
11833
|
-
* If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
|
|
11834
|
-
*/
|
|
11835
|
-
this.deleteUploadRecord = (id) => {
|
|
11836
|
-
const flight = this.inFlightUploads.get(id);
|
|
11837
|
-
if (flight) {
|
|
11838
|
-
this.inFlightUploads.delete(id);
|
|
11839
|
-
flight.abortController.abort();
|
|
11840
|
-
}
|
|
11841
|
-
this.state.next((current) => {
|
|
11842
|
-
if (!(id in current.uploads)) return current;
|
|
11843
|
-
const uploads = { ...current.uploads };
|
|
11844
|
-
delete uploads[id];
|
|
11845
|
-
return { ...current, uploads };
|
|
11846
|
-
});
|
|
11847
|
-
};
|
|
11848
|
-
/**
|
|
11849
|
-
* Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
|
|
11850
|
-
* Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
|
|
11851
|
-
* Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
|
|
11852
|
-
*/
|
|
11853
|
-
this.upload = ({
|
|
11854
|
-
id,
|
|
11855
|
-
channelCid,
|
|
11856
|
-
file
|
|
11857
|
-
}) => {
|
|
11858
|
-
const existing = this.inFlightUploads.get(id);
|
|
11859
|
-
if (existing) return existing.promise;
|
|
11860
|
-
let resolvePromise;
|
|
11861
|
-
let rejectPromise;
|
|
11862
|
-
const promise = new Promise((resolve, reject) => {
|
|
11863
|
-
resolvePromise = resolve;
|
|
11864
|
-
rejectPromise = reject;
|
|
11865
|
-
});
|
|
11866
|
-
const abortController = new AbortController();
|
|
11867
|
-
this.inFlightUploads.set(id, { promise, abortController });
|
|
11868
|
-
void (async () => {
|
|
11869
|
-
const attachmentManager = this.resolveAttachmentManager(channelCid);
|
|
11870
|
-
const trackProgress = attachmentManager.config.trackUploadProgress;
|
|
11871
|
-
try {
|
|
11872
|
-
this.upsertUpload({
|
|
11873
|
-
id,
|
|
11874
|
-
uploadProgress: trackProgress ? 0 : void 0
|
|
11875
|
-
});
|
|
11876
|
-
const onProgress = trackProgress ? (progress) => {
|
|
11877
|
-
this.updateUpload({
|
|
11878
|
-
id,
|
|
11879
|
-
uploadProgress: progress
|
|
11880
|
-
});
|
|
11881
|
-
} : void 0;
|
|
11882
|
-
const uploadRequestOptions = {
|
|
11883
|
-
abortSignal: abortController.signal,
|
|
11884
|
-
...onProgress ? { onProgress } : {}
|
|
11885
|
-
};
|
|
11886
|
-
const response = await attachmentManager.doUploadRequest(
|
|
11887
|
-
file,
|
|
11888
|
-
uploadRequestOptions
|
|
11889
|
-
);
|
|
11890
|
-
resolvePromise(response);
|
|
11891
|
-
} catch (error) {
|
|
11892
|
-
rejectPromise(error);
|
|
11893
|
-
} finally {
|
|
11894
|
-
this.inFlightUploads.delete(id);
|
|
11895
|
-
this.deleteUploadRecord(id);
|
|
11896
|
-
}
|
|
11897
|
-
})();
|
|
11898
|
-
return promise;
|
|
11899
|
-
};
|
|
11900
|
-
this.upsertUpload = (record) => {
|
|
11901
|
-
this.state.partialNext({
|
|
11902
|
-
uploads: upsertById(this.uploads, record)
|
|
11903
|
-
});
|
|
11904
|
-
};
|
|
11905
|
-
this.updateUpload = (record) => {
|
|
11906
|
-
this.state.next((current) => {
|
|
11907
|
-
const nextUploads = updateById(current.uploads, record);
|
|
11908
|
-
if (!nextUploads) return current;
|
|
11909
|
-
return { ...current, uploads: nextUploads };
|
|
11910
|
-
});
|
|
11911
|
-
};
|
|
11912
|
-
this.state = new StateStore(initState7());
|
|
11913
|
-
}
|
|
11914
|
-
resolveAttachmentManager(channelCid) {
|
|
11915
|
-
const colon = channelCid.indexOf(":");
|
|
11916
|
-
if (colon <= 0 || colon === channelCid.length - 1) {
|
|
11917
|
-
throw new Error(`Invalid channelCid: ${channelCid}`);
|
|
11918
|
-
}
|
|
11919
|
-
const channelType = channelCid.slice(0, colon);
|
|
11920
|
-
const channelId = channelCid.slice(colon + 1);
|
|
11921
|
-
return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
|
|
11922
|
-
}
|
|
11923
|
-
get uploads() {
|
|
11924
|
-
return this.state.getLatestValue().uploads;
|
|
11925
|
-
}
|
|
11926
|
-
};
|
|
11927
|
-
|
|
11928
12121
|
// src/signing.ts
|
|
11929
12122
|
var import_jsonwebtoken = __toESM(require_jsonwebtoken());
|
|
11930
12123
|
var import_crypto = __toESM(require_crypto());
|
|
@@ -12183,7 +12376,7 @@ var TokenManager = class {
|
|
|
12183
12376
|
};
|
|
12184
12377
|
|
|
12185
12378
|
// src/connection_fallback.ts
|
|
12186
|
-
var
|
|
12379
|
+
var import_axios3 = __toESM(require("axios"));
|
|
12187
12380
|
var WSConnectionFallback = class {
|
|
12188
12381
|
constructor({ client }) {
|
|
12189
12382
|
/** @private */
|
|
@@ -12202,7 +12395,7 @@ var WSConnectionFallback = class {
|
|
|
12202
12395
|
/** @private */
|
|
12203
12396
|
this._req = async (params, config, retry) => {
|
|
12204
12397
|
if (!this.cancelToken && !params.close) {
|
|
12205
|
-
this.cancelToken =
|
|
12398
|
+
this.cancelToken = import_axios3.default.CancelToken.source();
|
|
12206
12399
|
}
|
|
12207
12400
|
try {
|
|
12208
12401
|
const res = await this.client.doAxiosRequest(
|
|
@@ -12238,7 +12431,7 @@ var WSConnectionFallback = class {
|
|
|
12238
12431
|
}
|
|
12239
12432
|
}
|
|
12240
12433
|
} catch (error) {
|
|
12241
|
-
if (
|
|
12434
|
+
if (import_axios3.default.isCancel(error)) {
|
|
12242
12435
|
this._log(`_poll() - axios canceled request`);
|
|
12243
12436
|
return;
|
|
12244
12437
|
}
|
|
@@ -14661,7 +14854,7 @@ var StreamChat = class _StreamChat {
|
|
|
14661
14854
|
return this.handleResponse(response);
|
|
14662
14855
|
} catch (e) {
|
|
14663
14856
|
e.client_request_id = requestConfig.headers?.["x-client-request-id"];
|
|
14664
|
-
if (
|
|
14857
|
+
if (import_axios4.default.isCancel(e)) throw e;
|
|
14665
14858
|
this._logApiError(type, url, e);
|
|
14666
14859
|
this.consecutiveFailures += 1;
|
|
14667
14860
|
if (e.response) {
|
|
@@ -14810,16 +15003,12 @@ var StreamChat = class _StreamChat {
|
|
|
14810
15003
|
}
|
|
14811
15004
|
};
|
|
14812
15005
|
this._callClientListeners = (event) => {
|
|
14813
|
-
const
|
|
14814
|
-
|
|
14815
|
-
|
|
14816
|
-
|
|
14817
|
-
}
|
|
14818
|
-
if (client.listeners[event.type]) {
|
|
14819
|
-
listeners.push(...client.listeners[event.type]);
|
|
14820
|
-
}
|
|
15006
|
+
const listeners = [
|
|
15007
|
+
...this.listeners.all ?? [],
|
|
15008
|
+
...this.listeners[event.type] ?? []
|
|
15009
|
+
];
|
|
14821
15010
|
for (const listener of listeners) {
|
|
14822
|
-
listener
|
|
15011
|
+
invokeEventListener(listener, event, this.logger);
|
|
14823
15012
|
}
|
|
14824
15013
|
};
|
|
14825
15014
|
this.recoverState = async () => {
|
|
@@ -14973,7 +15162,7 @@ var StreamChat = class _StreamChat {
|
|
|
14973
15162
|
return this.userAgent;
|
|
14974
15163
|
}
|
|
14975
15164
|
if (!this.cachedUserAgent) {
|
|
14976
|
-
const version = "9.
|
|
15165
|
+
const version = "9.52.1";
|
|
14977
15166
|
const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
|
|
14978
15167
|
const { name: appName, version: appVersion } = this.appIdentifier ?? {};
|
|
14979
15168
|
const { os, model: deviceModel } = this.deviceIdentifier ?? {};
|
|
@@ -15041,7 +15230,7 @@ var StreamChat = class _StreamChat {
|
|
|
15041
15230
|
keepAliveMsecs: 3e3
|
|
15042
15231
|
});
|
|
15043
15232
|
}
|
|
15044
|
-
this.axiosInstance =
|
|
15233
|
+
this.axiosInstance = import_axios4.default.create(this.options);
|
|
15045
15234
|
this.setBaseURL(this.options.baseURL || "https://chat.stream-io-api.com");
|
|
15046
15235
|
if (typeof process !== "undefined" && "env" in process && process.env.STREAM_LOCAL_TEST_RUN) {
|
|
15047
15236
|
this.setBaseURL("http://localhost:3030");
|
|
@@ -15062,7 +15251,7 @@ var StreamChat = class _StreamChat {
|
|
|
15062
15251
|
this.defaultWSTimeoutWithFallback = 6 * 1e3;
|
|
15063
15252
|
this.defaultWSTimeout = 15 * 1e3;
|
|
15064
15253
|
this.axiosInstance.defaults.paramsSerializer = axiosParamsSerializer;
|
|
15065
|
-
this.logger = isFunction(inputOptions.logger) ? inputOptions.logger :
|
|
15254
|
+
this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : noopLogger;
|
|
15066
15255
|
this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
|
|
15067
15256
|
this.threads = new ThreadManager({ client: this });
|
|
15068
15257
|
this.polls = new PollManager({ client: this });
|
|
@@ -16393,6 +16582,11 @@ var StreamChat = class _StreamChat {
|
|
|
16393
16582
|
}
|
|
16394
16583
|
/**
|
|
16395
16584
|
* unflagMessage - unflag a message
|
|
16585
|
+
*
|
|
16586
|
+
* @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
|
|
16587
|
+
* the backend - calling this resolves successfully but does not remove the flag.
|
|
16588
|
+
* Use `client.moderation.submitAction()` against the review queue item instead.
|
|
16589
|
+
*
|
|
16396
16590
|
* @param {string} targetMessageID
|
|
16397
16591
|
* @param {string} [options.user_id] currentUserID, only used with serverside auth
|
|
16398
16592
|
* @returns {Promise<APIResponse>}
|
|
@@ -16405,6 +16599,11 @@ var StreamChat = class _StreamChat {
|
|
|
16405
16599
|
}
|
|
16406
16600
|
/**
|
|
16407
16601
|
* unflagUser - unflag a user
|
|
16602
|
+
*
|
|
16603
|
+
* @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
|
|
16604
|
+
* the backend - calling this resolves successfully but does not remove the flag.
|
|
16605
|
+
* Use `client.moderation.submitAction()` against the review queue item instead.
|
|
16606
|
+
*
|
|
16408
16607
|
* @param {string} targetID
|
|
16409
16608
|
* @param {string} [options.user_id] currentUserID, only used with serverside auth
|
|
16410
16609
|
* @returns {Promise<APIResponse>}
|
|
@@ -16689,7 +16888,10 @@ var StreamChat = class _StreamChat {
|
|
|
16689
16888
|
if (!message.id) {
|
|
16690
16889
|
throw Error("Please specify the message.id when calling updateMessage");
|
|
16691
16890
|
}
|
|
16692
|
-
const payload =
|
|
16891
|
+
const payload = sanitizeOutgoingAttachments({
|
|
16892
|
+
client: this,
|
|
16893
|
+
message: toUpdatedMessagePayload(message)
|
|
16894
|
+
});
|
|
16693
16895
|
if (typeof partialUserOrUserId === "string") {
|
|
16694
16896
|
payload.user_id = partialUserOrUserId;
|
|
16695
16897
|
} else if (typeof partialUserOrUserId?.id === "string") {
|
|
@@ -18505,7 +18707,7 @@ var OfflineError = class extends Error {
|
|
|
18505
18707
|
};
|
|
18506
18708
|
|
|
18507
18709
|
// src/offline-support/offline_sync_manager.ts
|
|
18508
|
-
var
|
|
18710
|
+
var import_axios5 = require("axios");
|
|
18509
18711
|
var OfflineDBSyncManager = class {
|
|
18510
18712
|
constructor({
|
|
18511
18713
|
client,
|
|
@@ -18653,7 +18855,7 @@ var OfflineDBSyncManager = class {
|
|
|
18653
18855
|
});
|
|
18654
18856
|
} catch (e) {
|
|
18655
18857
|
console.log("An error has occurred while syncing the DB.", e);
|
|
18656
|
-
if ((0,
|
|
18858
|
+
if ((0, import_axios5.isAxiosError)(e) && e.code === "ECONNABORTED") {
|
|
18657
18859
|
return;
|
|
18658
18860
|
}
|
|
18659
18861
|
const error = e;
|
|
@@ -19162,6 +19364,7 @@ var AbstractOfflineDB = class {
|
|
|
19162
19364
|
return this.handleMessageUpdatedEvent({ event, execute });
|
|
19163
19365
|
}
|
|
19164
19366
|
if (type === "message.read" || type === "notification.mark_read") {
|
|
19367
|
+
if (event.thread) return [];
|
|
19165
19368
|
return this.handleRead({ event, unreadMessages: 0, execute });
|
|
19166
19369
|
}
|
|
19167
19370
|
if (type === "message.read_locally") {
|