From db5f6dc2af4fb616dd8d9aec3fd78c2c862edd7d Mon Sep 17 00:00:00 2001 From: gaelle Date: Thu, 20 Nov 2025 15:21:04 +0100 Subject: [PATCH] fix thumbnail path initialized with \\ when sent from Windows device --- .../core/chat/message/ChatMessageCore.cpp | 153 +++++++++--------- .../content/ChatMessageContentCore.cpp | 12 +- .../content/ChatMessageContentCore.hpp | 8 +- .../view/Control/Display/Chat/FileView.qml | 7 +- 4 files changed, 92 insertions(+), 88 deletions(-) diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index 8cbd82dbd..ff1dbe9fc 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -112,88 +112,91 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c // lDebug() << "[ChatMessageCore] new" << this; mustBeInLinphoneThread(getClassName()); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); - mChatMessageModel = Utils::makeQObject_ptr(chatmessage); - mChatMessageModel->setSelf(mChatMessageModel); - mText = ToolModel::getMessageFromContent(chatmessage->getContents()); - mUtf8Text = mChatMessageModel->getUtf8Text(); - mHasTextContent = mChatMessageModel->getHasTextContent(); - mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime()); - mIsOutgoing = chatmessage->isOutgoing(); - mIsRemoteMessage = !chatmessage->isOutgoing(); - mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly()); - mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()); - auto fromAddress = chatmessage->getFromAddress(); - // fromAddress->clean(); - mFromAddress = Utils::coreStringToAppString(fromAddress->asStringUriOnly()); - mFromName = ToolModel::getDisplayName(chatmessage->getFromAddress()); - mToName = ToolModel::getDisplayName(chatmessage->getToAddress()); + if (chatmessage) { + mChatMessageModel = Utils::makeQObject_ptr(chatmessage); + mChatMessageModel->setSelf(mChatMessageModel); + mText = ToolModel::getMessageFromContent(chatmessage->getContents()); + mUtf8Text = mChatMessageModel->getUtf8Text(); + mHasTextContent = mChatMessageModel->getHasTextContent(); + mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime()); + mIsOutgoing = chatmessage->isOutgoing(); + mIsRemoteMessage = !chatmessage->isOutgoing(); + mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly()); + mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()); + auto fromAddress = chatmessage->getFromAddress(); + // fromAddress->clean(); + mFromAddress = Utils::coreStringToAppString(fromAddress->asStringUriOnly()); + mFromName = ToolModel::getDisplayName(chatmessage->getFromAddress()); + mToName = ToolModel::getDisplayName(chatmessage->getToAddress()); + auto chatroom = chatmessage->getChatRoom(); + mIsFromChatGroup = chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference) && + !chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne); + mIsRead = chatmessage->isRead(); + mMessageState = LinphoneEnums::fromLinphone(chatmessage->getState()); + mIsEphemeral = chatmessage->isEphemeral(); - auto chatroom = chatmessage->getChatRoom(); - mIsFromChatGroup = chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference) && - !chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne); - mIsRead = chatmessage->isRead(); - mMessageState = LinphoneEnums::fromLinphone(chatmessage->getState()); - mIsEphemeral = chatmessage->isEphemeral(); - if (mIsEphemeral) { - auto now = QDateTime::currentDateTime(); - mEphemeralDuration = chatmessage->getEphemeralExpireTime() == 0 - ? chatmessage->getEphemeralLifetime() - : now.secsTo(QDateTime::fromSecsSinceEpoch(chatmessage->getEphemeralExpireTime())); - } - mMessageId = Utils::coreStringToAppString(chatmessage->getMessageId()); - for (auto content : chatmessage->getContents()) { - auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel); - mChatMessageContentList.push_back(contentCore); - if ((content->isFile() || content->isFileTransfer()) && !content->isVoiceRecording()) mHasFileContent = true; - if (content->isIcalendar()) mIsCalendarInvite = true; - if (content->isVoiceRecording()) { - mIsVoiceRecording = true; - mVoiceRecordingContent = contentCore; + if (mIsEphemeral) { + auto now = QDateTime::currentDateTime(); + mEphemeralDuration = chatmessage->getEphemeralExpireTime() == 0 + ? chatmessage->getEphemeralLifetime() + : now.secsTo(QDateTime::fromSecsSinceEpoch(chatmessage->getEphemeralExpireTime())); } - } - //: "Reactions": all reactions for one message label - mTotalReactionsLabel = tr("all_reactions_label"); - auto reac = chatmessage->getOwnReaction(); - mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString(); - for (auto &reaction : chatmessage->getReactions()) { - if (reaction) { - auto fromAddr = reaction->getFromAddress()->clone(); - fromAddr->clean(); - auto reac = - Reaction::createMessageReactionVariant(Utils::coreStringToAppString(reaction->getBody()), - Utils::coreStringToAppString(fromAddr->asStringUriOnly())); - mReactions.append(reac); - - auto it = std::find_if(mReactionsSingletonMap.begin(), mReactionsSingletonMap.end(), - [body = reac.mBody](QVariant data) { - auto dataBody = data.toMap()["body"].toString(); - return body == dataBody; - }); - if (it == mReactionsSingletonMap.end()) - mReactionsSingletonMap.push_back(createReactionSingletonVariant(reac.mBody, 1)); - else { - auto map = it->toMap(); - auto count = map["count"].toInt(); - ++count; - map.remove("count"); - map.insert("count", count); - mReactionsSingletonMap.erase(it); - mReactionsSingletonMap.push_back(map); + mMessageId = Utils::coreStringToAppString(chatmessage->getMessageId()); + for (auto content : chatmessage->getContents()) { + auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel); + mChatMessageContentList.push_back(contentCore); + if ((content->isFile() || content->isFileTransfer()) && !content->isVoiceRecording()) + mHasFileContent = true; + if (content->isIcalendar()) mIsCalendarInvite = true; + if (content->isVoiceRecording()) { + mIsVoiceRecording = true; + mVoiceRecordingContent = contentCore; } } - } - connect(this, &ChatMessageCore::messageReactionChanged, this, &ChatMessageCore::resetReactionsSingleton); + //: "Reactions": all reactions for one message label + mTotalReactionsLabel = tr("all_reactions_label"); + auto reac = chatmessage->getOwnReaction(); + mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString(); + for (auto &reaction : chatmessage->getReactions()) { + if (reaction) { + auto fromAddr = reaction->getFromAddress()->clone(); + fromAddr->clean(); + auto reac = + Reaction::createMessageReactionVariant(Utils::coreStringToAppString(reaction->getBody()), + Utils::coreStringToAppString(fromAddr->asStringUriOnly())); + mReactions.append(reac); - mIsForward = chatmessage->isForward(); - mIsReply = chatmessage->isReply(); - if (mIsReply) { - auto replymessage = chatmessage->getReplyMessage(); - if (replymessage) { - mReplyText = ToolModel::getMessageFromContent(replymessage->getContents()); - if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress()); + auto it = std::find_if(mReactionsSingletonMap.begin(), mReactionsSingletonMap.end(), + [body = reac.mBody](QVariant data) { + auto dataBody = data.toMap()["body"].toString(); + return body == dataBody; + }); + if (it == mReactionsSingletonMap.end()) + mReactionsSingletonMap.push_back(createReactionSingletonVariant(reac.mBody, 1)); + else { + auto map = it->toMap(); + auto count = map["count"].toInt(); + ++count; + map.remove("count"); + map.insert("count", count); + mReactionsSingletonMap.erase(it); + mReactionsSingletonMap.push_back(map); + } + } } + connect(this, &ChatMessageCore::messageReactionChanged, this, &ChatMessageCore::resetReactionsSingleton); + + mIsForward = chatmessage->isForward(); + mIsReply = chatmessage->isReply(); + if (mIsReply) { + auto replymessage = chatmessage->getReplyMessage(); + if (replymessage) { + mReplyText = ToolModel::getMessageFromContent(replymessage->getContents()); + if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress()); + } + } + mImdnStatusList = computeDeliveryStatus(chatmessage); } - mImdnStatusList = computeDeliveryStatus(chatmessage); } ChatMessageCore::~ChatMessageCore() { diff --git a/Linphone/core/chat/message/content/ChatMessageContentCore.cpp b/Linphone/core/chat/message/content/ChatMessageContentCore.cpp index e37abf6f6..c68fd10e6 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentCore.cpp +++ b/Linphone/core/chat/message/content/ChatMessageContentCore.cpp @@ -46,7 +46,7 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptrgetFilePath()); + mFilePath = QDir::fromNativeSeparators(Utils::coreStringToAppString(content->getFilePath())); mIsFile = content->isFile(); mIsFileEncrypted = content->isFileEncrypted(); mIsFileTransfer = content->isFileTransfer(); @@ -67,8 +67,8 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr(content, chatMessageModel); } } @@ -92,7 +92,7 @@ void ChatMessageContentCore::setSelf(QSharedPointer me) }); mChatMessageContentModelConnection->makeConnectToModel( &ChatMessageContentModel::thumbnailChanged, [this, updateThumbnailType](QString thumbnail) { - mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(thumbnail); }); + mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(QUrl(thumbnail)); }); }); mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() { @@ -250,11 +250,11 @@ bool ChatMessageContentCore::wasDownloaded() const { return mWasDownloaded; } -QString ChatMessageContentCore::getThumbnail() const { +QUrl ChatMessageContentCore::getThumbnail() const { return mThumbnail; } -void ChatMessageContentCore::setThumbnail(const QString &data) { +void ChatMessageContentCore::setThumbnail(const QUrl &data) { if (mThumbnail != data) { mThumbnail = data; emit thumbnailChanged(); diff --git a/Linphone/core/chat/message/content/ChatMessageContentCore.hpp b/Linphone/core/chat/message/content/ChatMessageContentCore.hpp index 0c4652a05..b0f33c9d2 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentCore.hpp +++ b/Linphone/core/chat/message/content/ChatMessageContentCore.hpp @@ -36,7 +36,7 @@ class ChatMessageContentCore : public QObject, public AbstractObject { Q_PROPERTY(QString name READ getName CONSTANT) Q_PROPERTY(quint64 fileOffset READ getFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged) - Q_PROPERTY(QString thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged) + Q_PROPERTY(QUrl thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged) Q_PROPERTY(bool wasDownloaded READ wasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged) Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged) Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT) @@ -84,8 +84,8 @@ public: int getFileDuration() const; ConferenceInfoGui *getConferenceInfoGui() const; - void setThumbnail(const QString &data); - QString getThumbnail() const; + void setThumbnail(const QUrl &data); + QUrl getThumbnail() const; bool wasDownloaded() const; void setWasDownloaded(bool downloaded); @@ -121,7 +121,7 @@ private: bool mIsText; bool mIsVoiceRecording; int mFileDuration; - QString mThumbnail; + QUrl mThumbnail; QString mUtf8Text; QString mRichFormatText; QString mFilePath; diff --git a/Linphone/view/Control/Display/Chat/FileView.qml b/Linphone/view/Control/Display/Chat/FileView.qml index 9cd3e7860..f9725f992 100644 --- a/Linphone/view/Control/Display/Chat/FileView.qml +++ b/Linphone/view/Control/Display/Chat/FileView.qml @@ -19,7 +19,7 @@ Item { property string filePath: contentGui && contentGui.core.filePath property bool wasDownloaded: contentGui && contentGui.core.wasDownloaded property bool isAnimatedImage : contentGui && contentGui.core.wasDownloaded && UtilsCpp.isAnimatedImage(filePath) - property bool haveThumbnail: contentGui && UtilsCpp.canHaveThumbnail(filePath) + property bool haveThumbnail: contentGui && UtilsCpp.canHaveThumbnail(filePath) && UtilsCpp.fileExists(filePath) property int fileSize: contentGui ? contentGui.core.fileSize : 0 property bool isTransferring property bool isVideo: UtilsCpp.isVideo(filePath) @@ -68,9 +68,10 @@ Item { fillMode: Image.PreserveAspectFit } Image { + id: errorImage anchors.fill: parent z: image.z + 1 - visible: image.status == Image.Error || image.status == Image.Null || !UtilsCpp.fileExists(mainItem.filePath) + visible: image.status == Image.Error || image.status == Image.Null source: AppIcons.fileImage sourceSize.width: mainItem.width sourceSize.height: mainItem.height @@ -79,7 +80,7 @@ Item { Item { id: loadingImageItem anchors.fill: parent - visible: mainItem.isImage && image.status === Image.Loading + visible: image.status === Image.Loading && !image.visible && !errorImage.visilbe Rectangle { anchors.fill: parent color: DefaultStyle.main1_200