diff --git a/src/commoncomponents/DataTransferMessageDelegate.qml b/src/commoncomponents/DataTransferMessageDelegate.qml index 7875e01dc7ba4b11ff790d4254998d7ffef88f20..2e7dcc0af1eaad231718c6394faba81bfcecd7fb 100644 --- a/src/commoncomponents/DataTransferMessageDelegate.qml +++ b/src/commoncomponents/DataTransferMessageDelegate.qml @@ -252,10 +252,11 @@ Loader { Loader { id: localMediaCompLoader anchors.right: isOutgoing ? parent.right : undefined + asynchronous: true width: sourceComponent.width height: sourceComponent.height sourceComponent: mediaInfo.isImage !== undefined ? - imageComp : + imageComp : mediaInfo.isAnimatedImage !== undefined ? animatedImageComp : avComp Component { id: avComp @@ -302,9 +303,9 @@ Loader { } } Component { - id: imageComp + id: animatedImageComp AnimatedImage { - id: img + id: animatedImg anchors.right: isOutgoing ? parent.right : undefined property real minSize: 192 property real maxSize: 256 @@ -327,6 +328,51 @@ Loader { anchors.fill: parent } layer.enabled: true + layer.effect: OpacityMask { + maskSource: MessageBubble { + out: isOutgoing + type: seq + width: animatedImg.width + height: animatedImg.height + radius: msgRadius + } + } + HoverHandler { + target : parent + onHoveredChanged: { + localMediaMsgItem.hoveredLink = hovered ? animatedImg.source : "" + } + cursorShape: Qt.PointingHandCursor + } + } + } + + Component { + id: imageComp + Image { + id: img + anchors.right: isOutgoing ? parent.right : undefined + property real minSize: 192 + property real maxSize: 256 + cache: true + fillMode: Image.PreserveAspectCrop + mipmap: true + antialiasing: true + autoTransform: false + asynchronous: true + source: "file:///" + Body + property real aspectRatio: implicitWidth / implicitHeight + property real adjustedWidth: Math.min(maxSize, + Math.max(minSize, + innerContent.width - senderMargin)) + width: adjustedWidth + height: Math.ceil(adjustedWidth / aspectRatio) + Rectangle { + color: JamiTheme.previewImageBackgroundColor + z: -1 + anchors.fill: parent + } + layer.enabled: true layer.effect: OpacityMask { maskSource: MessageBubble { out: isOutgoing diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp index 91f8eed02458a63056b1a1f8adbc9418a8095b91..ba38e533ad12a74d91b1372704df06a3cc350960 100644 --- a/src/messagesadapter.cpp +++ b/src/messagesadapter.cpp @@ -458,13 +458,24 @@ MessagesAdapter::conversationTypersUrlToName(const QSet<QString>& typersSet) return nameList; } -bool +QVariantMap MessagesAdapter::isLocalImage(const QString& msg) { QImageReader reader; reader.setDecideFormatFromContent(true); reader.setFileName(msg); - return !reader.read().isNull(); + QByteArray fileFormat = reader.format(); + if (fileFormat == "gif") { + return {{"isAnimatedImage", true}}; + } + QList<QByteArray> supportedFormats = reader.supportedImageFormats(); + auto iterator = std::find_if(supportedFormats.begin(), + supportedFormats.end(), + [fileFormat](QByteArray format) { return format == fileFormat; }); + if (iterator != supportedFormats.end()) { + return {{"isImage", true}}; + } + return {{"isImage", false}}; } QVariantMap @@ -476,8 +487,9 @@ MessagesAdapter::getMediaInfo(const QString& msg) "<%1 style='width:100%;height:%2;outline:none;background-color:#f1f3f4;" "object-fit:cover;' " "controls controlsList='nodownload' src='file://%3' type='%4'/></body>"; - if (isLocalImage(msg)) { - return {{"isImage", true}}; + QVariantMap fileInfo = isLocalImage(msg); + if (fileInfo["isImage"].toBool() || fileInfo["isAnimatedImage"].toBool()) { + return fileInfo; } QRegularExpression vPattern("[^\\s]+(.*?)\\.(avi|mov|webm|webp|rmvb)$", QRegularExpression::CaseInsensitiveOption); diff --git a/src/messagesadapter.h b/src/messagesadapter.h index bfa4e6279d1f5cd6292991065966e7b2bad08215..1965c5eb2f50ef1c75dd1baf595562b24e9a0e56 100644 --- a/src/messagesadapter.h +++ b/src/messagesadapter.h @@ -101,7 +101,7 @@ protected: Q_INVOKABLE void deleteInteraction(const QString& interactionId); Q_INVOKABLE void copyToDownloads(const QString& interactionId, const QString& displayName); Q_INVOKABLE void userIsComposing(bool isComposing); - Q_INVOKABLE bool isLocalImage(const QString& msg); + Q_INVOKABLE QVariantMap isLocalImage(const QString& msg); Q_INVOKABLE QVariantMap getMediaInfo(const QString& msg); Q_INVOKABLE bool isRemoteImage(const QString& msg); Q_INVOKABLE QString getFormattedTime(const quint64 timestamp);