From 412a13d8a139a099510b43a2419492653423841e Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 19 Jan 2024 22:26:22 +0100 Subject: [PATCH 1/6] fix(upload): split out shareFiles action Signed-off-by: Maksim Sukharev --- src/store/fileUploadStore.js | 89 +++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js index 2c39ee3d738..c03f034d4bc 100644 --- a/src/store/fileUploadStore.js +++ b/src/store/fileUploadStore.js @@ -355,10 +355,8 @@ const actions = { }, contentLength: currentFile.size, }) - // Path for the sharing request - const sharePath = '/' + uniquePath // Mark the file as uploaded in the store - commit('markFileAsSuccessUpload', { uploadId, index, sharePath }) + commit('markFileAsSuccessUpload', { uploadId, index, sharePath: uniquePath }) } catch (exception) { let reason = 'failed-upload' if (exception.response) { @@ -381,38 +379,6 @@ const actions = { } } - const performShare = async ([index, shareableFile]) => { - const path = shareableFile.sharePath - const temporaryMessage = shareableFile.temporaryMessage - - const rawMetadata = { messageType: temporaryMessage.messageType } - if (caption && index === lastIndex) { - Object.assign(rawMetadata, { caption }) - } - if (options?.silent) { - Object.assign(rawMetadata, { silent: options.silent }) - } - if (temporaryMessage.parent) { - Object.assign(rawMetadata, { replyTo: temporaryMessage.parent.id }) - } - const metadata = JSON.stringify(rawMetadata) - - const { token, id, referenceId } = temporaryMessage - try { - dispatch('markFileAsSharing', { uploadId, index }) - await shareFile(path, token, referenceId, metadata) - dispatch('markFileAsShared', { uploadId, index }) - } catch (error) { - if (error?.response?.status === 403) { - showError(t('spreed', 'You are not allowed to share files')) - } else { - showError(t('spreed', 'An error happened when trying to share your file')) - } - dispatch('markTemporaryMessageAsFailed', { token, id, uploadId, reason: 'failed-share' }) - console.error('An error happened when trying to share your file: ', error) - } - } - const client = getDavClient() const userRoot = '/files/' + getters.getUserId() @@ -430,23 +396,64 @@ const actions = { await Promise.all(uploads.map(upload => performUpload(upload))) } - const shares = getters.getShareableFiles(uploadId) + await dispatch('shareFiles', { token, uploadId, lastIndex, caption, options }) + + EventBus.$emit('upload-finished') + }, + + /** + * Shares the files to the conversation + * + * @param {object} context the wrapping object + * @param {object} data the wrapping object + * @param {string} data.token The conversation token + * @param {string} data.uploadId The unique uploadId + * @param {string} data.lastIndex The index of last uploaded file + * @param {string|null} data.caption The text caption to the media + * @param {object|null} data.options The share options + */ + async shareFiles(context, { token, uploadId, lastIndex, caption, options }) { + const performShare = async ([index, shareableFile]) => { + const { id, messageType, parent, referenceId } = shareableFile.temporaryMessage || {} + + const metadata = JSON.stringify(Object.assign({ messageType }, + caption && index === lastIndex ? { caption } : {}, + options?.silent ? { silent: options.silent } : {}, + parent ? { replyTo: parent.id } : {}, + )) + + try { + context.dispatch('markFileAsSharing', { uploadId, index }) + await shareFile(shareableFile.sharePath, token, referenceId, metadata) + context.dispatch('markFileAsShared', { uploadId, index }) + } catch (error) { + if (error?.response?.status === 403) { + showError(t('spreed', 'You are not allowed to share files')) + } else { + showError(t('spreed', 'An error happened when trying to share your file')) + } + context.dispatch('markTemporaryMessageAsFailed', { token, id, uploadId, reason: 'failed-share' }) + console.error('An error happened when trying to share your file: ', error) + } + } + + const shares = context.getters.getShareableFiles(uploadId) + // Check if caption message for share was provided if (caption) { const captionShareIndex = shares.findIndex(([index]) => index === lastIndex) // Share all files in parallel, except for last one - const parallelShares = shares.slice(0, captionShareIndex).concat(shares.slice(captionShareIndex + 1)) - await Promise.all(parallelShares.map(share => performShare(share))) + await Promise.all(shares + .filter((_item, index) => index !== captionShareIndex) + .map(performShare)) // Share a last file, where caption is attached await performShare(shares.at(captionShareIndex)) } else { // Share all files in parallel - await Promise.all(shares.map(share => performShare(share))) + await Promise.all(shares.map(performShare)) } - - EventBus.$emit('upload-finished') }, /** From a4ba4add6abf17e068d917d4ac59685a057d1021 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 19 Jan 2024 23:01:56 +0100 Subject: [PATCH 2/6] fix(upload): split out prepareUploadPaths action Signed-off-by: Maksim Sukharev --- src/store/fileUploadStore.js | 105 +++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js index c03f034d4bc..6029ff2d229 100644 --- a/src/store/fileUploadStore.js +++ b/src/store/fileUploadStore.js @@ -67,6 +67,11 @@ const getters = { .filter(([_index, uploadedFile]) => uploadedFile.status === 'initialised') }, + getPendingUploads: (state, getters) => (uploadId) => { + return getters.getUploadsArray(uploadId) + .filter(([_index, uploadedFile]) => uploadedFile.status === 'pendingUpload') + }, + getFailedUploads: (state, getters) => (uploadId) => { return getters.getUploadsArray(uploadId) .filter(([_index, uploadedFile]) => uploadedFile.status === 'failedUpload') @@ -148,6 +153,12 @@ const mutations = { state.uploads[uploadId].files[index].status = 'initialised' }, + // Marks a given file as ready to be uploaded (after propfind) + markFileAsPendingUpload(state, { uploadId, index, sharePath }) { + state.uploads[uploadId].files[index].status = 'pendingUpload' + Vue.set(state.uploads[uploadId].files[index], 'sharePath', sharePath) + }, + // Marks a given file as failed upload markFileAsFailedUpload(state, { uploadId, index, status }) { state.uploads[uploadId].files[index].status = 'failedUpload' @@ -156,7 +167,6 @@ const mutations = { // Marks a given file as uploaded markFileAsSuccessUpload(state, { uploadId, index, sharePath }) { state.uploads[uploadId].files[index].status = 'successUpload' - Vue.set(state.uploads[uploadId].files[index], 'sharePath', sharePath) }, // Marks a given file as uploading @@ -314,8 +324,6 @@ const actions = { // If caption is provided, attach to the last temporary message const lastIndex = getters.getInitialisedUploads(uploadId).at(-1).at(0) for (const [index, uploadedFile] of getters.getInitialisedUploads(uploadId)) { - // mark all files as uploading - commit('markFileAsUploading', { uploadId, index }) // Store the previously created temporary message const message = { ...uploadedFile.temporaryMessage, @@ -327,36 +335,24 @@ const actions = { EventBus.$emit('scroll-chat-to-bottom', { force: true }) } - // Store propfind attempts within one action to reduce amount of requests for duplicates - const knownPaths = {} + const client = getDavClient() + const userRoot = '/files/' + getters.getUserId() const performUpload = async ([index, uploadedFile]) => { - // currentFile to be uploaded const currentFile = uploadedFile.file - // userRoot path const fileName = (currentFile.newName || currentFile.name) - // Candidate rest of the path - const path = getters.getAttachmentFolder() + '/' + fileName try { - // Check if previous propfind attempt was stored - const promptPath = getFileNamePrompt(path) - const knownSuffix = knownPaths[promptPath] - // Get a unique relative path based on the previous path variable - const { uniquePath, suffix } = await findUniquePath(client, userRoot, path, knownSuffix) - knownPaths[promptPath] = suffix - - // Upload the file + commit('markFileAsUploading', { uploadId, index }) const currentFileBuffer = await new Blob([currentFile]).arrayBuffer() - await client.putFileContents(userRoot + uniquePath, currentFileBuffer, { + await client.putFileContents(userRoot + uploadedFile.sharePath, currentFileBuffer, { onUploadProgress: progress => { const uploadedSize = progress.loaded commit('setUploadedSize', { state, uploadId, index, uploadedSize }) }, contentLength: currentFile.size, }) - // Mark the file as uploaded in the store - commit('markFileAsSuccessUpload', { uploadId, index, sharePath: uniquePath }) + commit('markFileAsSuccessUpload', { uploadId, index }) } catch (exception) { let reason = 'failed-upload' if (exception.response) { @@ -379,26 +375,67 @@ const actions = { } } + await dispatch('prepareUploadPaths', { token, uploadId }) + + const uploads = getters.getPendingUploads(uploadId) + await Promise.all(uploads.map(performUpload)) + + await dispatch('shareFiles', { token, uploadId, lastIndex, caption, options }) + + EventBus.$emit('upload-finished') + }, + + /** + * Prepare unique paths to upload for each file + * + * @param {object} context the wrapping object + * @param {object} data the wrapping object + * @param {string} data.token The conversation token + * @param {string} data.uploadId The unique uploadId + */ + async prepareUploadPaths(context, { token, uploadId }) { const client = getDavClient() - const userRoot = '/files/' + getters.getUserId() + const userRoot = '/files/' + context.getters.getUserId() + + // Store propfind attempts within one action to reduce amount of requests for duplicates + const knownPaths = {} + + const performPropFind = async ([index, uploadedFile]) => { + const fileName = (uploadedFile.file.newName || uploadedFile.file.name) + // Candidate rest of the path + const path = context.getters.getAttachmentFolder() + '/' + fileName - const uploads = getters.getUploadingFiles(uploadId) + try { + // Check if previous propfind attempt was stored + const promptPath = getFileNamePrompt(path) + const knownSuffix = knownPaths[promptPath] + // Get a unique relative path based on the previous path variable + const { uniquePath, suffix } = await findUniquePath(client, userRoot, path, knownSuffix) + knownPaths[promptPath] = suffix + context.commit('markFileAsPendingUpload', { uploadId, index, sharePath: uniquePath }) + } catch (exception) { + console.error(`Error while uploading file "${fileName}":` + exception.message, fileName) + showError(t('spreed', 'Error while uploading file "{fileName}"', { fileName })) + // Mark the upload as failed in the store + context.commit('markFileAsFailedUpload', { uploadId, index }) + const { id } = uploadedFile.temporaryMessage + context.dispatch('markTemporaryMessageAsFailed', { token, id, uploadId, reason: 'failed-upload' }) + } + } + + const initialisedUploads = context.getters.getInitialisedUploads(uploadId) // Check for duplicate names in the uploads array - if (hasDuplicateUploadNames(uploads)) { - const { uniques, duplicates } = separateDuplicateUploads(uploads) - await Promise.all(uniques.map(upload => performUpload(upload))) - // Search for uniquePath and upload files one by one to avoid 423 (Locked) - for (const upload of duplicates) { - await performUpload(upload) + if (hasDuplicateUploadNames(initialisedUploads)) { + const { uniques, duplicates } = separateDuplicateUploads(initialisedUploads) + await Promise.all(uniques.map(performPropFind)) + // Search for uniquePath one by one + for (const duplicate of duplicates) { + await performPropFind(duplicate) } } else { - // All original names are unique, upload files in parallel - await Promise.all(uploads.map(upload => performUpload(upload))) + // All original names are unique, prepare files in parallel + await Promise.all(initialisedUploads.map(performPropFind)) } - - await dispatch('shareFiles', { token, uploadId, lastIndex, caption, options }) - - EventBus.$emit('upload-finished') }, /** From 9ad1146eb0f01649ab6052ae12be842eab2838cd Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 19 Jan 2024 23:10:39 +0100 Subject: [PATCH 3/6] fix(upload): split out processUpload action Signed-off-by: Maksim Sukharev --- src/store/fileUploadStore.js | 96 ++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js index 6029ff2d229..b4ecec2f899 100644 --- a/src/store/fileUploadStore.js +++ b/src/store/fileUploadStore.js @@ -335,50 +335,9 @@ const actions = { EventBus.$emit('scroll-chat-to-bottom', { force: true }) } - const client = getDavClient() - const userRoot = '/files/' + getters.getUserId() - - const performUpload = async ([index, uploadedFile]) => { - const currentFile = uploadedFile.file - const fileName = (currentFile.newName || currentFile.name) - - try { - commit('markFileAsUploading', { uploadId, index }) - const currentFileBuffer = await new Blob([currentFile]).arrayBuffer() - await client.putFileContents(userRoot + uploadedFile.sharePath, currentFileBuffer, { - onUploadProgress: progress => { - const uploadedSize = progress.loaded - commit('setUploadedSize', { state, uploadId, index, uploadedSize }) - }, - contentLength: currentFile.size, - }) - commit('markFileAsSuccessUpload', { uploadId, index }) - } catch (exception) { - let reason = 'failed-upload' - if (exception.response) { - console.error(`Error while uploading file "${fileName}":` + exception, fileName, exception.response.status) - if (exception.response.status === 507) { - reason = 'quota' - showError(t('spreed', 'Not enough free space to upload file "{fileName}"', { fileName })) - } else { - showError(t('spreed', 'Error while uploading file "{fileName}"', { fileName })) - } - } else { - console.error(`Error while uploading file "${fileName}":` + exception.message, fileName) - showError(t('spreed', 'Error while uploading file "{fileName}"', { fileName })) - } - - // Mark the upload as failed in the store - commit('markFileAsFailedUpload', { uploadId, index }) - const { token, id } = uploadedFile.temporaryMessage - dispatch('markTemporaryMessageAsFailed', { token, id, uploadId, reason }) - } - } - await dispatch('prepareUploadPaths', { token, uploadId }) - const uploads = getters.getPendingUploads(uploadId) - await Promise.all(uploads.map(performUpload)) + await dispatch('processUpload', { token, uploadId }) await dispatch('shareFiles', { token, uploadId, lastIndex, caption, options }) @@ -438,6 +397,59 @@ const actions = { } }, + /** + * Upload all pending files to the server + * + * @param {object} context the wrapping object + * @param {object} data the wrapping object + * @param {string} data.token The conversation token + * @param {string} data.uploadId The unique uploadId + */ + async processUpload(context, { token, uploadId }) { + const client = getDavClient() + const userRoot = '/files/' + context.getters.getUserId() + + const performUpload = async ([index, uploadedFile]) => { + const currentFile = uploadedFile.file + const fileName = (currentFile.newName || currentFile.name) + + try { + context.commit('markFileAsUploading', { uploadId, index }) + const currentFileBuffer = await new Blob([currentFile]).arrayBuffer() + await client.putFileContents(userRoot + uploadedFile.sharePath, currentFileBuffer, { + onUploadProgress: progress => { + const uploadedSize = progress.loaded + context.commit('setUploadedSize', { state, uploadId, index, uploadedSize }) + }, + contentLength: currentFile.size, + }) + context.commit('markFileAsSuccessUpload', { uploadId, index }) + } catch (exception) { + let reason = 'failed-upload' + if (exception.response) { + console.error(`Error while uploading file "${fileName}":` + exception, fileName, exception.response.status) + if (exception.response.status === 507) { + reason = 'quota' + showError(t('spreed', 'Not enough free space to upload file "{fileName}"', { fileName })) + } else { + showError(t('spreed', 'Error while uploading file "{fileName}"', { fileName })) + } + } else { + console.error(`Error while uploading file "${fileName}":` + exception.message, fileName) + showError(t('spreed', 'Error while uploading file "{fileName}"', { fileName })) + } + + // Mark the upload as failed in the store + context.commit('markFileAsFailedUpload', { uploadId, index }) + const { id } = uploadedFile.temporaryMessage + context.dispatch('markTemporaryMessageAsFailed', { token, id, uploadId, reason }) + } + } + + const uploads = context.getters.getPendingUploads(uploadId) + await Promise.all(uploads.map(performUpload)) + }, + /** * Shares the files to the conversation * From 38a26e0a0bc520696cfe17f4872075178c713c9c Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Mon, 12 Feb 2024 12:50:28 +0100 Subject: [PATCH 4/6] chore(deps): install nextcloud/upload@1.0.4 Signed-off-by: Maksim Sukharev --- package-lock.json | 449 +++++++++++++++++++++++----------------------- package.json | 1 + 2 files changed, 226 insertions(+), 224 deletions(-) diff --git a/package-lock.json b/package-lock.json index c13a2d8cbaf..112612e2f95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@nextcloud/moment": "^1.3.1", "@nextcloud/paths": "^2.1.0", "@nextcloud/router": "^3.0.0", + "@nextcloud/upload": "^1.0.4", "@nextcloud/vue": "^8.6.2", "crypto-js": "^4.2.0", "debounce": "^2.0.0", @@ -3840,6 +3841,73 @@ "npm": "^7.0.0 || ^8.0.0" } }, + "node_modules/@nextcloud/upload": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@nextcloud/upload/-/upload-1.0.4.tgz", + "integrity": "sha512-ntyNCuhnCluied3CYAdMWjQTjbfUW/kJmyyhwX0jBjjM+6vadm4EDGFuDp14m0WL1I5IMawe3CztwdBRMWs7QQ==", + "dependencies": { + "@nextcloud/auth": "^2.2.1", + "@nextcloud/axios": "^2.4.0", + "@nextcloud/dialogs": "^5.0.0-beta.6", + "@nextcloud/files": "^3.0.0", + "@nextcloud/l10n": "^2.2.0", + "@nextcloud/logger": "^2.7.0", + "@nextcloud/paths": "^2.1.0", + "@nextcloud/router": "^2.2.0", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.0", + "p-cancelable": "^4.0.1", + "p-limit": "^5.0.0", + "p-queue": "^8.0.0", + "simple-eta": "^3.0.2", + "vue": "^2.7.15" + }, + "engines": { + "node": "^20.0.0", + "npm": "^9.0.0" + }, + "peerDependencies": { + "@nextcloud/vue": "^8.0.0-beta || ^8.0.0" + } + }, + "node_modules/@nextcloud/upload/node_modules/@nextcloud/router": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-2.2.1.tgz", + "integrity": "sha512-ZRc/WI0RaksEJMz08H/6LimIdP+1A1xTHThCYEghs7VgAKNp5917vT2OKSpG0cMRbIwk0ongFVt5FB5qjy/iFg==", + "dependencies": { + "@nextcloud/typings": "^1.7.0", + "core-js": "^3.6.4" + }, + "engines": { + "node": "^20.0.0", + "npm": "^10.0.0" + } + }, + "node_modules/@nextcloud/upload/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nextcloud/upload/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@nextcloud/vue": { "version": "8.6.2", "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.6.2.tgz", @@ -5849,8 +5917,6 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -5861,9 +5927,7 @@ "node_modules/asn1.js/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/assert": { "version": "2.1.0", @@ -6387,7 +6451,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -6401,8 +6464,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/batch": { "version": "0.6.1", @@ -6438,9 +6500,7 @@ "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true, - "peer": true + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "node_modules/body-parser": { "version": "1.20.1", @@ -6546,9 +6606,7 @@ "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true, - "peer": true + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" }, "node_modules/browser-process-hrtime": { "version": "1.0.0", @@ -6560,8 +6618,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "peer": true, "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -6575,8 +6631,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "peer": true, "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -6587,8 +6641,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "peer": true, "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -6600,8 +6652,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^5.0.0", "randombytes": "^2.0.1" @@ -6611,8 +6661,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", @@ -6632,7 +6680,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -6646,8 +6693,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/browserify-zlib": { "version": "0.2.0", @@ -6702,7 +6748,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "funding": [ { "type": "github", @@ -6717,7 +6762,6 @@ "url": "https://feross.org/support" } ], - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -6731,9 +6775,7 @@ "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true, - "peer": true + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" }, "node_modules/builtin-modules": { "version": "3.3.0", @@ -7001,8 +7043,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -7390,8 +7430,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -7400,16 +7438,12 @@ "node_modules/create-ecdh/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "peer": true, "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -7422,8 +7456,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "peer": true, "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -7561,8 +7593,6 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "peer": true, "dependencies": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -8009,8 +8039,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -8066,8 +8094,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -8077,9 +8103,7 @@ "node_modules/diffie-hellman/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/dir-glob": { "version": "3.0.1", @@ -8280,8 +8304,6 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -8295,9 +8317,7 @@ "node_modules/elliptic/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/emittery": { "version": "0.13.1", @@ -9568,8 +9588,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "peer": true, "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -10655,8 +10673,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "peer": true, "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -10670,7 +10686,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -10684,8 +10699,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/hash-sum": { "version": "1.0.2", @@ -10697,8 +10711,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -10762,8 +10774,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "peer": true, "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -11076,7 +11086,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -11090,8 +11099,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/ignore": { "version": "5.2.4", @@ -13925,8 +13933,6 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "peer": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -14893,8 +14899,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -14906,9 +14910,7 @@ "node_modules/miller-rabin/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/mime": { "version": "1.6.0", @@ -14963,16 +14965,12 @@ "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "peer": true + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true, - "peer": true + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" }, "node_modules/minimatch": { "version": "3.1.2", @@ -15611,6 +15609,14 @@ "os-tmpdir": "^1.0.0" } }, + "node_modules/p-cancelable": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", + "engines": { + "node": ">=14.16" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -15636,6 +15642,26 @@ "node": ">=8" } }, + "node_modules/p-queue": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz", + "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -15650,6 +15676,17 @@ "node": ">=8" } }, + "node_modules/p-timeout": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -15682,8 +15719,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "peer": true, "dependencies": { "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", @@ -15781,8 +15816,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "peer": true, "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -16185,8 +16218,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "peer": true, "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -16199,9 +16230,7 @@ "node_modules/public-encrypt/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/punycode": { "version": "2.3.1", @@ -16296,8 +16325,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -16306,8 +16333,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "peer": true, "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -16486,8 +16511,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -17302,8 +17325,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "peer": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -17387,8 +17408,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { "version": "1.66.1", @@ -17730,8 +17750,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -17819,6 +17837,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-eta": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/simple-eta/-/simple-eta-3.0.2.tgz", + "integrity": "sha512-+OmPgi01yHK/bRNQDoehUcV8fqs9nNJkG2DoWCnnLvj0lmowab7BH3v9776BG0y7dGEOLh0F7mfd37k+ht26Yw==" + }, "node_modules/sirv": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", @@ -18104,8 +18127,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -18113,9 +18134,7 @@ "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true, - "peer": true + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, "node_modules/string-length": { "version": "4.0.2", @@ -19537,8 +19556,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -23421,6 +23439,52 @@ "vue-router": "<4" } }, + "@nextcloud/upload": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@nextcloud/upload/-/upload-1.0.4.tgz", + "integrity": "sha512-ntyNCuhnCluied3CYAdMWjQTjbfUW/kJmyyhwX0jBjjM+6vadm4EDGFuDp14m0WL1I5IMawe3CztwdBRMWs7QQ==", + "requires": { + "@nextcloud/auth": "^2.2.1", + "@nextcloud/axios": "^2.4.0", + "@nextcloud/dialogs": "^5.0.0-beta.6", + "@nextcloud/files": "^3.0.0", + "@nextcloud/l10n": "^2.2.0", + "@nextcloud/logger": "^2.7.0", + "@nextcloud/paths": "^2.1.0", + "@nextcloud/router": "^2.2.0", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.0", + "p-cancelable": "^4.0.1", + "p-limit": "^5.0.0", + "p-queue": "^8.0.0", + "simple-eta": "^3.0.2", + "vue": "^2.7.15" + }, + "dependencies": { + "@nextcloud/router": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-2.2.1.tgz", + "integrity": "sha512-ZRc/WI0RaksEJMz08H/6LimIdP+1A1xTHThCYEghs7VgAKNp5917vT2OKSpG0cMRbIwk0ongFVt5FB5qjy/iFg==", + "requires": { + "@nextcloud/typings": "^1.7.0", + "core-js": "^3.6.4" + } + }, + "p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==" + } + } + }, "@nextcloud/vue": { "version": "8.6.2", "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.6.2.tgz", @@ -24988,8 +25052,6 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "peer": true, "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -25000,9 +25062,7 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -25437,9 +25497,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "peer": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch": { "version": "0.6.1", @@ -25469,9 +25527,7 @@ "bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true, - "peer": true + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "body-parser": { "version": "1.20.1", @@ -25571,9 +25627,7 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true, - "peer": true + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" }, "browser-process-hrtime": { "version": "1.0.0", @@ -25585,8 +25639,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "peer": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -25600,8 +25652,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "peer": true, "requires": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -25612,8 +25662,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "peer": true, "requires": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -25625,8 +25673,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "peer": true, "requires": { "bn.js": "^5.0.0", "randombytes": "^2.0.1" @@ -25636,8 +25682,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", - "dev": true, - "peer": true, "requires": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", @@ -25653,9 +25697,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "peer": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -25692,8 +25734,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "peer": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -25707,9 +25747,7 @@ "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true, - "peer": true + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" }, "builtin-modules": { "version": "3.3.0", @@ -25894,8 +25932,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "peer": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -26207,8 +26243,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "peer": true, "requires": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -26217,9 +26251,7 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -26227,8 +26259,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "peer": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -26241,8 +26271,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "peer": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -26345,8 +26373,6 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "peer": true, "requires": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -26671,8 +26697,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "peer": true, "requires": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -26714,8 +26738,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "peer": true, "requires": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -26725,9 +26747,7 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -26892,8 +26912,6 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "peer": true, "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -26907,9 +26925,7 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -27850,8 +27866,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "peer": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -28677,8 +28691,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "peer": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -28688,9 +28700,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "peer": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -28704,8 +28714,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "peer": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -28756,8 +28764,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "peer": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -29003,9 +29009,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "peer": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "5.2.4", @@ -31068,8 +31072,6 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "peer": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -31725,8 +31727,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "peer": true, "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -31735,9 +31735,7 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -31776,16 +31774,12 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "peer": true + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true, - "peer": true + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" }, "minimatch": { "version": "3.1.2", @@ -32266,6 +32260,11 @@ "os-tmpdir": "^1.0.0" } }, + "p-cancelable": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==" + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -32282,6 +32281,22 @@ "p-limit": "^2.2.0" } }, + "p-queue": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz", + "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==", + "requires": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "dependencies": { + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + } + } + }, "p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -32293,6 +32308,11 @@ "retry": "^0.13.1" } }, + "p-timeout": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==" + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -32319,8 +32339,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "peer": true, "requires": { "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", @@ -32397,8 +32415,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "peer": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -32661,8 +32677,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "peer": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -32675,9 +32689,7 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -32732,8 +32744,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, "requires": { "safe-buffer": "^5.1.0" } @@ -32742,8 +32752,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "peer": true, "requires": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -32871,8 +32879,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -33484,8 +33490,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "peer": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -33548,8 +33552,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { "version": "1.66.1", @@ -33821,8 +33824,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "peer": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -33892,6 +33893,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-eta": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/simple-eta/-/simple-eta-3.0.2.tgz", + "integrity": "sha512-+OmPgi01yHK/bRNQDoehUcV8fqs9nNJkG2DoWCnnLvj0lmowab7BH3v9776BG0y7dGEOLh0F7mfd37k+ht26Yw==" + }, "sirv": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", @@ -34140,8 +34146,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "peer": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -34149,9 +34153,7 @@ "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true, - "peer": true + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" } } }, @@ -35202,8 +35204,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", diff --git a/package.json b/package.json index b897d801086..aa3b1afd52c 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@nextcloud/moment": "^1.3.1", "@nextcloud/paths": "^2.1.0", "@nextcloud/router": "^3.0.0", + "@nextcloud/upload": "^1.0.4", "@nextcloud/vue": "^8.6.2", "crypto-js": "^4.2.0", "debounce": "^2.0.0", From 415a073db608fd93d89c5ea192083eda938f1024 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Sat, 20 Jan 2024 01:22:38 +0100 Subject: [PATCH 5/6] fix(upload): use nextcloud/upload for file uploads, adjust tests Signed-off-by: Maksim Sukharev --- .../Message/MessagePart/FilePreview.spec.js | 23 +++++++--- .../Message/MessagePart/FilePreview.vue | 45 ++++++++++++++++--- src/store/fileUploadStore.js | 28 +++--------- src/store/fileUploadStore.spec.js | 36 +++++++-------- src/test-setup.js | 4 ++ 5 files changed, 81 insertions(+), 55 deletions(-) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.spec.js b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.spec.js index 683f46071fa..5a5619b4dd7 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.spec.js +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.spec.js @@ -7,6 +7,7 @@ import PlayCircleOutline from 'vue-material-design-icons/PlayCircleOutline.vue' import { getCapabilities } from '@nextcloud/capabilities' import { imagePath, generateRemoteUrl } from '@nextcloud/router' +import { getUploader } from '@nextcloud/upload' import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' @@ -151,22 +152,32 @@ describe('FilePreview.vue', () => { }) describe('uploading', () => { - let uploadProgressMock + const path = '/Talk/path-to-file.png' + let getUploadFileMock beforeEach(() => { - uploadProgressMock = jest.fn() - testStoreConfig.modules.fileUploadStore.getters.uploadProgress = () => uploadProgressMock + getUploadFileMock = jest.fn(() => ({ + sharePath: path, + status: 'uploading', + })) + testStoreConfig.modules.fileUploadStore.getters.getUploadFile = () => getUploadFileMock store = new Vuex.Store(testStoreConfig) }) test('renders progress bar while uploading', async () => { + getUploader.mockImplementation(() => ({ + queue: [{ + _source: path, + _uploaded: 85, + _size: 100, + }], + })) + propsData.id = 'temp-123' propsData.index = 'index-1' propsData.uploadId = 1000 propsData.localUrl = 'blob:XYZ' - uploadProgressMock.mockReturnValue(85) - const wrapper = shallowMount(FilePreview, { localVue, store, @@ -182,7 +193,7 @@ describe('FilePreview.vue', () => { expect(progressEl.exists()).toBe(true) expect(progressEl.props('value')).toBe(85) - expect(uploadProgressMock).toHaveBeenCalledWith(1000, 'index-1') + expect(getUploadFileMock).toHaveBeenCalledWith(1000, 'index-1') }) }) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue index e9df7c0c180..3df75eb29d4 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue @@ -79,6 +79,7 @@ import PlayCircleOutline from 'vue-material-design-icons/PlayCircleOutline.vue' import { getCapabilities } from '@nextcloud/capabilities' import { encodePath } from '@nextcloud/paths' import { generateUrl, imagePath, generateRemoteUrl } from '@nextcloud/router' +import { getUploader } from '@nextcloud/upload' import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcProgressBar from '@nextcloud/vue/dist/Components/NcProgressBar.js' @@ -283,6 +284,7 @@ export default { return { isLoading: true, failed: false, + uploadManager: null, } }, computed: { @@ -511,14 +513,29 @@ export default { return this.id.startsWith('temp') && this.index && this.uploadId }, + uploadFile() { + return this.$store.getters.getUploadFile(this.uploadId, this.index) + }, + + upload() { + return this.uploadManager?.queue.find(item => item._source.includes(this.uploadFile.sharePath)) + }, + uploadProgress() { - if (this.isTemporaryUpload) { - if (this.$store.getters.uploadProgress(this.uploadId, this.index)) { - return this.$store.getters.uploadProgress(this.uploadId, this.index) - } + switch (this.uploadFile?.status) { + case 'shared': + case 'sharing': + case 'successUpload': + return 100 + case 'uploading': + return this.upload + ? this.upload._uploaded / this.upload._size * 100 + : 100 // file was removed from the upload queue, so considering done + case 'pendingUpload': + case 'initialised': + default: + return 0 } - // likely never reached - return 0 }, hasTemporaryImageUrl() { @@ -534,7 +551,19 @@ export default { }, }, + watch: { + uploadProgress(value) { + if (value === 100) { + this.uploadManager = null + } + }, + }, + mounted() { + if (this.isTemporaryUpload && !this.isUploadEditor) { + this.uploadManager = getUploader() + } + const img = new Image() img.onerror = () => { this.isLoading = false @@ -546,6 +575,10 @@ export default { img.src = this.previewUrl }, + beforeDestroy() { + this.uploadManager = null + }, + methods: { handleClick(event) { if (this.isUploadEditor) { diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js index b4ecec2f899..eac5f68c072 100644 --- a/src/store/fileUploadStore.js +++ b/src/store/fileUploadStore.js @@ -25,6 +25,7 @@ import Vue from 'vue' import { showError } from '@nextcloud/dialogs' import { loadState } from '@nextcloud/initial-state' import moment from '@nextcloud/moment' +import { getUploader } from '@nextcloud/upload' import { getDavClient } from '../services/DavClient.js' import { EventBus } from '../services/EventBus.js' @@ -104,12 +105,8 @@ const getters = { return state.localUrls[referenceId] }, - uploadProgress: (state) => (uploadId, index) => { - if (state.uploads[uploadId]?.files[index]) { - return state.uploads[uploadId].files[index].uploadedSize / state.uploads[uploadId].files[index].totalSize * 100 - } else { - return 0 - } + getUploadFile: (state) => (uploadId, index) => { + return state.uploads[uploadId]?.files[index] }, currentUploadId: (state) => { @@ -142,7 +139,6 @@ const mutations = { file, status: 'initialised', totalSize: file.size, - uploadedSize: 0, temporaryMessage, }) Vue.set(state.localUrls, temporaryMessage.referenceId, localUrl) @@ -194,11 +190,6 @@ const mutations = { state.attachmentFolder = attachmentFolder }, - // Sets uploaded amount of bytes - setUploadedSize(state, { uploadId, index, uploadedSize }) { - state.uploads[uploadId].files[index].uploadedSize = uploadedSize - }, - // Set temporary message for each file setTemporaryMessageForFile(state, { uploadId, index, temporaryMessage }) { console.debug('uploadId: ' + uploadId + ' index: ' + index) @@ -406,23 +397,14 @@ const actions = { * @param {string} data.uploadId The unique uploadId */ async processUpload(context, { token, uploadId }) { - const client = getDavClient() - const userRoot = '/files/' + context.getters.getUserId() - const performUpload = async ([index, uploadedFile]) => { const currentFile = uploadedFile.file const fileName = (currentFile.newName || currentFile.name) try { context.commit('markFileAsUploading', { uploadId, index }) - const currentFileBuffer = await new Blob([currentFile]).arrayBuffer() - await client.putFileContents(userRoot + uploadedFile.sharePath, currentFileBuffer, { - onUploadProgress: progress => { - const uploadedSize = progress.loaded - context.commit('setUploadedSize', { state, uploadId, index, uploadedSize }) - }, - contentLength: currentFile.size, - }) + const uploader = getUploader() + await uploader.upload(uploadedFile.sharePath, currentFile) context.commit('markFileAsSuccessUpload', { uploadId, index }) } catch (exception) { let reason = 'failed-upload' diff --git a/src/store/fileUploadStore.spec.js b/src/store/fileUploadStore.spec.js index 37b6c3d5cbb..53f4360d89e 100644 --- a/src/store/fileUploadStore.spec.js +++ b/src/store/fileUploadStore.spec.js @@ -5,6 +5,7 @@ import { createPinia, setActivePinia } from 'pinia' import Vuex from 'vuex' import { showError } from '@nextcloud/dialogs' +import { getUploader } from '@nextcloud/upload' // eslint-disable-next-line no-unused-vars -- required for testing import storeConfig from './storeConfig.js' @@ -84,8 +85,9 @@ describe('fileUploadStore', () => { describe('uploading', () => { let restoreConsole + const uploadMock = jest.fn() const client = { - putFileContents: jest.fn(), + exists: jest.fn(), } beforeEach(() => { @@ -93,6 +95,7 @@ describe('fileUploadStore', () => { store = new Vuex.Store(storeConfig) restoreConsole = mockConsole(['error', 'debug']) getDavClient.mockReturnValue(client) + getUploader.mockReturnValue({ upload: uploadMock }) }) afterEach(() => { @@ -150,7 +153,6 @@ describe('fileUploadStore', () => { size: 123, lastModified: Date.UTC(2021, 3, 27, 15, 30, 0), } - const fileBuffer = await new Blob([file]).arrayBuffer() await store.dispatch('initialiseUpload', { uploadId: 'upload-id1', @@ -162,6 +164,7 @@ describe('fileUploadStore', () => { const uniqueFileName = '/Talk/' + file.name + 'uniq' findUniquePath.mockResolvedValueOnce({ uniquePath: uniqueFileName, suffix: 1 }) + uploadMock.mockResolvedValue() shareFile.mockResolvedValue() await store.dispatch('uploadFiles', { token: 'XXTOKENXX', uploadId: 'upload-id1', caption: 'text-caption', options: { silent: true } }) @@ -169,11 +172,11 @@ describe('fileUploadStore', () => { expect(findUniquePath).toHaveBeenCalledTimes(1) expect(findUniquePath).toHaveBeenCalledWith(client, '/files/current-user', '/Talk/' + file.name, undefined) - expect(client.putFileContents).toHaveBeenCalledTimes(1) - expect(client.putFileContents).toHaveBeenCalledWith(`/files/current-user${uniqueFileName}`, fileBuffer, expect.anything()) + expect(uploadMock).toHaveBeenCalledTimes(1) + expect(uploadMock).toHaveBeenCalledWith(uniqueFileName, file) expect(shareFile).toHaveBeenCalledTimes(1) - expect(shareFile).toHaveBeenCalledWith(`/${uniqueFileName}`, 'XXTOKENXX', 'reference-id-1', '{"caption":"text-caption","silent":true}') + expect(shareFile).toHaveBeenCalledWith(uniqueFileName, 'XXTOKENXX', 'reference-id-1', '{"caption":"text-caption","silent":true}') expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(1) expect(store.getters.currentUploadId).not.toBeDefined() @@ -193,10 +196,6 @@ describe('fileUploadStore', () => { lastModified: Date.UTC(2021, 3, 25, 15, 30, 0), } const files = [file1, file2] - const fileBuffers = [ - await new Blob([file1]).arrayBuffer(), - await new Blob([file2]).arrayBuffer(), - ] await store.dispatch('initialiseUpload', { uploadId: 'upload-id1', @@ -216,17 +215,15 @@ describe('fileUploadStore', () => { await store.dispatch('uploadFiles', { token: 'XXTOKENXX', uploadId: 'upload-id1', caption: 'text-caption', options: { silent: false } }) expect(findUniquePath).toHaveBeenCalledTimes(2) - expect(client.putFileContents).toHaveBeenCalledTimes(2) - expect(shareFile).toHaveBeenCalledTimes(2) - + expect(uploadMock).toHaveBeenCalledTimes(2) for (const index in files) { - expect(findUniquePath).toHaveBeenCalledWith(client, '/files/current-user', '/Talk/' + files[index].name, undefined) - expect(client.putFileContents).toHaveBeenCalledWith(`/files/current-user/Talk/${files[index].name}uniq`, fileBuffers[index], expect.anything()) + expect(findUniquePath).toHaveBeenNthCalledWith(+index + 1, client, '/files/current-user', '/Talk/' + files[index].name, undefined) + expect(uploadMock).toHaveBeenNthCalledWith(+index + 1, `/Talk/${files[index].name}uniq`, files[index]) } expect(shareFile).toHaveBeenCalledTimes(2) - expect(shareFile).toHaveBeenNthCalledWith(1, '//Talk/' + files[0].name + 'uniq', 'XXTOKENXX', 'reference-id-1', '{}') - expect(shareFile).toHaveBeenNthCalledWith(2, '//Talk/' + files[1].name + 'uniq', 'XXTOKENXX', 'reference-id-2', '{"caption":"text-caption"}') + expect(shareFile).toHaveBeenNthCalledWith(1, '/Talk/' + files[0].name + 'uniq', 'XXTOKENXX', 'reference-id-1', '{}') + expect(shareFile).toHaveBeenNthCalledWith(2, '/Talk/' + files[1].name + 'uniq', 'XXTOKENXX', 'reference-id-2', '{"caption":"text-caption"}') expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(2) expect(store.getters.currentUploadId).not.toBeDefined() @@ -250,15 +247,14 @@ describe('fileUploadStore', () => { findUniquePath .mockResolvedValueOnce({ uniquePath: '/Talk/' + files[0].name + 'uniq', suffix: 1 }) - client.putFileContents.mockRejectedValueOnce({ + uploadMock.mockRejectedValueOnce({ response: { status: 403, }, }) - await store.dispatch('uploadFiles', { token: 'XXTOKENXX', uploadId: 'upload-id1', options: { silent: false } }) - expect(client.putFileContents).toHaveBeenCalledTimes(1) + expect(uploadMock).toHaveBeenCalledTimes(1) expect(shareFile).not.toHaveBeenCalled() expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(1) @@ -299,7 +295,7 @@ describe('fileUploadStore', () => { await store.dispatch('uploadFiles', { token: 'XXTOKENXX', uploadId: 'upload-id1', options: { silent: false } }) - expect(client.putFileContents).toHaveBeenCalledTimes(1) + expect(uploadMock).toHaveBeenCalledTimes(1) expect(shareFile).toHaveBeenCalledTimes(1) expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(1) diff --git a/src/test-setup.js b/src/test-setup.js index d6ef6aca0d0..79ac166d39a 100644 --- a/src/test-setup.js +++ b/src/test-setup.js @@ -39,6 +39,10 @@ jest.mock('@nextcloud/initial-state', () => ({ }), })) +jest.mock('@nextcloud/upload', () => ({ + getUploader: jest.fn(), +})) + window.IntersectionObserver = jest.fn(() => ({ observe: jest.fn(), unobserve: jest.fn(), From 467dc403a81e06d3967004b3aff1744f052a43ac Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 19 Jan 2024 23:55:11 +0100 Subject: [PATCH 6/6] fix(upload): update NcProgressBar styles Signed-off-by: Maksim Sukharev --- .../Message/MessagePart/FilePreview.vue | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue index 3df75eb29d4..46338e0e4d1 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue @@ -50,6 +50,10 @@ :class="previewImageClass" alt="" :src="defaultIconUrl"> + -
{{ fileDetail }}
@@ -538,6 +541,11 @@ export default { } }, + showUploadProgress() { + return this.isTemporaryUpload && !this.isUploadEditor + && ['shared', 'sharing', 'successUpload', 'uploading'].includes(this.uploadFile?.status) + }, + hasTemporaryImageUrl() { return this.mimetype.startsWith('image/') && this.localUrl }, @@ -628,6 +636,7 @@ export default { border-radius: 16px; box-sizing: content-box !important; + &:hover, &:focus, &:focus-visible { @@ -648,6 +657,13 @@ export default { transition: outline 0.1s ease-in-out; } + &__progress { + position: absolute; + top: 50%; + right: 0; + transform: translate(100%, -50%); + } + .loading { display: inline-block; min-width: 32px;