From e5ff0b4a6a21886ff84862889795d59b2a0fc73a Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Mon, 27 Mar 2023 13:53:17 -0400 Subject: [PATCH] fix(images): fix second pasted image replaces the first one Fixes #9966 Uses the validate function to paste images so that it concatenates the previous images. FIxes the validate function to also accept data images. Moves the size validation function to Utils to reuse the data path prefix constant and fix the possible crash when we try to get the size of a data image. --- ui/app/mainui/panels/DropAreaPanel.qml | 2 +- .../StatusChatImageExtensionValidator.qml | 2 +- .../status/StatusChatImageSizeValidator.qml | 4 +-- ui/imports/shared/status/StatusChatInput.qml | 29 ++++++++++--------- ui/imports/utils/Constants.qml | 1 + ui/imports/utils/Utils.qml | 16 ++++++++-- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/ui/app/mainui/panels/DropAreaPanel.qml b/ui/app/mainui/panels/DropAreaPanel.qml index 5fb1ab420f..51e8c33d35 100644 --- a/ui/app/mainui/panels/DropAreaPanel.qml +++ b/ui/app/mainui/panels/DropAreaPanel.qml @@ -32,7 +32,7 @@ DropArea { } // needed because drag.urls is not a normal js array - rptDraggedPreviews.model = drag.urls.filter(img => Utils.hasDragNDropImageExtension(img)) + rptDraggedPreviews.model = drag.urls.filter(img => Utils.isValidDragNDropImage(img)) } onPositionChanged: { rptDraggedPreviews.x = drag.x diff --git a/ui/imports/shared/status/StatusChatImageExtensionValidator.qml b/ui/imports/shared/status/StatusChatImageExtensionValidator.qml index a4780574d9..2b04f3ca60 100644 --- a/ui/imports/shared/status/StatusChatImageExtensionValidator.qml +++ b/ui/imports/shared/status/StatusChatImageExtensionValidator.qml @@ -15,7 +15,7 @@ StatusChatImageValidator { onImagesChanged: { let isValid = true root.validImages = images.filter(img => { - const isImage = Utils.hasDragNDropImageExtension(img) + const isImage = Utils.isValidDragNDropImage(img) isValid = isValid && isImage return isImage }) diff --git a/ui/imports/shared/status/StatusChatImageSizeValidator.qml b/ui/imports/shared/status/StatusChatImageSizeValidator.qml index bb4fb0430c..b6c3af81ba 100644 --- a/ui/imports/shared/status/StatusChatImageSizeValidator.qml +++ b/ui/imports/shared/status/StatusChatImageSizeValidator.qml @@ -4,13 +4,11 @@ import utils 1.0 StatusChatImageValidator { id: root - readonly property int maxImgSizeBytes: Constants.maxUploadFilesizeMB * 1048576 /* 1 MB in bytes */ onImagesChanged: { let isValid = true root.validImages = images.filter(img => { - let size = parseInt(globalUtils.getFileSize(img)) - const isSmallEnough = size <= maxImgSizeBytes + const isSmallEnough = Utils.isFilesizeValid(img) isValid = isValid && isSmallEnough return isSmallEnough }) diff --git a/ui/imports/shared/status/StatusChatInput.qml b/ui/imports/shared/status/StatusChatInput.qml index 6d9a6b99ab..3b5b0c7990 100644 --- a/ui/imports/shared/status/StatusChatInput.qml +++ b/ui/imports/shared/status/StatusChatInput.qml @@ -436,7 +436,7 @@ Rectangle { if (event.matches(StandardKey.Paste)) { if (QClipboardProxy.hasImage) { const clipboardImage = QClipboardProxy.imageBase64 - showImageArea([clipboardImage]) + validateImagesAndShowImageArea([clipboardImage]) event.accepted = true } else if (QClipboardProxy.hasText) { messageInputField.remove(messageInputField.selectionStart, messageInputField.selectionEnd) @@ -883,11 +883,22 @@ Rectangle { function showImageArea(imagePathsOrData) { isImage = true; - imageArea.imageSource = imagePathsOrData control.fileUrlsAndSources = imageArea.imageSource } + // Use this to validate and show the images. The concatanation of previous selected images is done automatically + // Returns true if the images were valid and added + function validateImagesAndShowImageArea(imagePaths) { + const validImages = validateImages(imagePaths) + + if (validImages.length > 0) { + showImageArea(validImages) + return true + } + return false + } + function showReplyArea(messageId, userName, message, contentType, image, sticker) { isReply = true replyArea.userName = userName @@ -908,9 +919,7 @@ Rectangle { target: Global.dragArea ignoreUnknownSignals: true function onDroppedOnValidScreen(drop) { - let validImages = validateImages(drop.urls) - if (validImages.length > 0) { - showImageArea(validImages) + if (validateImagesAndShowImageArea(drop.urls)) { drop.acceptProposedAction() } } @@ -918,10 +927,7 @@ Rectangle { // This is used by Squish tests to not have to access the file dialog function selectImageString(filePath) { - let validImages = validateImages([filePath]) - if (validImages.length > 0) { - control.showImageArea(validImages) - } + validateImagesAndShowImageArea([filePath]) messageInputField.forceActiveFocus(); } @@ -935,10 +941,7 @@ Rectangle { ] onAccepted: { imageBtn.highlighted = false - let validImages = validateImages(imageDialog.fileUrls) - if (validImages.length > 0) { - control.showImageArea(validImages) - } + validateImagesAndShowImageArea(imageDialog.fileUrls) messageInputField.forceActiveFocus(); } onRejected: { diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index f761579988..fac7e52e82 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -797,6 +797,7 @@ QtObject { readonly property int maxNumberOfPins: 3 + readonly property string dataImagePrefix: "data:image" readonly property var acceptedImageExtensions: [".png", ".jpg", ".jpeg", ".svg", ".gif"] readonly property var acceptedDragNDropImageExtensions: [".png", ".jpg", ".jpeg", ".heif", ".tif", ".tiff"] diff --git a/ui/imports/utils/Utils.qml b/ui/imports/utils/Utils.qml index d670b3563c..b6bcd3495f 100644 --- a/ui/imports/utils/Utils.qml +++ b/ui/imports/utils/Utils.qml @@ -10,6 +10,8 @@ QtObject { property var mainModuleInst: typeof mainModule !== "undefined" ? mainModule : null property var globalUtilsInst: typeof globalUtils !== "undefined" ? globalUtils : null + readonly property int maxImgSizeBytes: Constants.maxUploadFilesizeMB * 1048576 /* 1 MB in bytes */ + function isDigit(value) { return /^\d$/.test(value); } @@ -320,8 +322,18 @@ QtObject { return message.replace(/(?:https?|ftp):\/\/[\n\S]*(\.gif)+/gm, ''); } - function hasDragNDropImageExtension(url) { - return Constants.acceptedDragNDropImageExtensions.some(ext => url.toLowerCase().includes(ext)) + function isValidDragNDropImage(url) { + let lowerCaseUrl = url.toLowerCase() + return Constants.acceptedDragNDropImageExtensions.some(ext => lowerCaseUrl.endsWith(ext)) || + lowerCaseUrl.startsWith(Constants.dataImagePrefix); + } + + function isFilesizeValid(img) { + if (img.startsWith(Constants.dataImagePrefix)) { + return img.length < maxImgSizeBytes + } + let size = parseInt(globalUtils.getFileSize(img)) + return size <= maxImgSizeBytes } function deduplicate(array) {