fix thumbnail path initialized with \\ when sent from Windows device

This commit is contained in:
gaelle 2025-11-20 15:21:04 +01:00
parent d0cf951fe4
commit db5f6dc2af
4 changed files with 92 additions and 88 deletions

View file

@ -112,88 +112,91 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
// lDebug() << "[ChatMessageCore] new" << this; // lDebug() << "[ChatMessageCore] new" << this;
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage); if (chatmessage) {
mChatMessageModel->setSelf(mChatMessageModel); mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage);
mText = ToolModel::getMessageFromContent(chatmessage->getContents()); mChatMessageModel->setSelf(mChatMessageModel);
mUtf8Text = mChatMessageModel->getUtf8Text(); mText = ToolModel::getMessageFromContent(chatmessage->getContents());
mHasTextContent = mChatMessageModel->getHasTextContent(); mUtf8Text = mChatMessageModel->getUtf8Text();
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime()); mHasTextContent = mChatMessageModel->getHasTextContent();
mIsOutgoing = chatmessage->isOutgoing(); mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
mIsRemoteMessage = !chatmessage->isOutgoing(); mIsOutgoing = chatmessage->isOutgoing();
mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly()); mIsRemoteMessage = !chatmessage->isOutgoing();
mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()); mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly());
auto fromAddress = chatmessage->getFromAddress(); mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress());
// fromAddress->clean(); auto fromAddress = chatmessage->getFromAddress();
mFromAddress = Utils::coreStringToAppString(fromAddress->asStringUriOnly()); // fromAddress->clean();
mFromName = ToolModel::getDisplayName(chatmessage->getFromAddress()); mFromAddress = Utils::coreStringToAppString(fromAddress->asStringUriOnly());
mToName = ToolModel::getDisplayName(chatmessage->getToAddress()); 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(); if (mIsEphemeral) {
mIsFromChatGroup = chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference) && auto now = QDateTime::currentDateTime();
!chatroom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne); mEphemeralDuration = chatmessage->getEphemeralExpireTime() == 0
mIsRead = chatmessage->isRead(); ? chatmessage->getEphemeralLifetime()
mMessageState = LinphoneEnums::fromLinphone(chatmessage->getState()); : now.secsTo(QDateTime::fromSecsSinceEpoch(chatmessage->getEphemeralExpireTime()));
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;
} }
} mMessageId = Utils::coreStringToAppString(chatmessage->getMessageId());
//: "Reactions": all reactions for one message label for (auto content : chatmessage->getContents()) {
mTotalReactionsLabel = tr("all_reactions_label"); auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel);
auto reac = chatmessage->getOwnReaction(); mChatMessageContentList.push_back(contentCore);
mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString(); if ((content->isFile() || content->isFileTransfer()) && !content->isVoiceRecording())
for (auto &reaction : chatmessage->getReactions()) { mHasFileContent = true;
if (reaction) { if (content->isIcalendar()) mIsCalendarInvite = true;
auto fromAddr = reaction->getFromAddress()->clone(); if (content->isVoiceRecording()) {
fromAddr->clean(); mIsVoiceRecording = true;
auto reac = mVoiceRecordingContent = contentCore;
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);
} }
} }
} //: "Reactions": all reactions for one message label
connect(this, &ChatMessageCore::messageReactionChanged, this, &ChatMessageCore::resetReactionsSingleton); 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(); auto it = std::find_if(mReactionsSingletonMap.begin(), mReactionsSingletonMap.end(),
mIsReply = chatmessage->isReply(); [body = reac.mBody](QVariant data) {
if (mIsReply) { auto dataBody = data.toMap()["body"].toString();
auto replymessage = chatmessage->getReplyMessage(); return body == dataBody;
if (replymessage) { });
mReplyText = ToolModel::getMessageFromContent(replymessage->getContents()); if (it == mReactionsSingletonMap.end())
if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress()); 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() { ChatMessageCore::~ChatMessageCore() {

View file

@ -46,7 +46,7 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mName = QFileInfo(fileName).baseName(); mName = QFileInfo(fileName).baseName();
} }
} }
mFilePath = Utils::coreStringToAppString(content->getFilePath()); mFilePath = QDir::fromNativeSeparators(Utils::coreStringToAppString(content->getFilePath()));
mIsFile = content->isFile(); mIsFile = content->isFile();
mIsFileEncrypted = content->isFileEncrypted(); mIsFileEncrypted = content->isFileEncrypted();
mIsFileTransfer = content->isFileTransfer(); mIsFileTransfer = content->isFileTransfer();
@ -67,8 +67,8 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom); mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom);
mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile(); mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile();
mThumbnail = mFilePath.isEmpty() mThumbnail = mFilePath.isEmpty()
? QString() ? QUrl()
: QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath); : QUrl(QString("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(mFilePath));
mChatMessageContentModel = Utils::makeQObject_ptr<ChatMessageContentModel>(content, chatMessageModel); mChatMessageContentModel = Utils::makeQObject_ptr<ChatMessageContentModel>(content, chatMessageModel);
} }
} }
@ -92,7 +92,7 @@ void ChatMessageContentCore::setSelf(QSharedPointer<ChatMessageContentCore> me)
}); });
mChatMessageContentModelConnection->makeConnectToModel( mChatMessageContentModelConnection->makeConnectToModel(
&ChatMessageContentModel::thumbnailChanged, [this, updateThumbnailType](QString thumbnail) { &ChatMessageContentModel::thumbnailChanged, [this, updateThumbnailType](QString thumbnail) {
mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(thumbnail); }); mChatMessageContentModelConnection->invokeToCore([this, thumbnail] { setThumbnail(QUrl(thumbnail)); });
}); });
mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() { mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() {
@ -250,11 +250,11 @@ bool ChatMessageContentCore::wasDownloaded() const {
return mWasDownloaded; return mWasDownloaded;
} }
QString ChatMessageContentCore::getThumbnail() const { QUrl ChatMessageContentCore::getThumbnail() const {
return mThumbnail; return mThumbnail;
} }
void ChatMessageContentCore::setThumbnail(const QString &data) { void ChatMessageContentCore::setThumbnail(const QUrl &data) {
if (mThumbnail != data) { if (mThumbnail != data) {
mThumbnail = data; mThumbnail = data;
emit thumbnailChanged(); emit thumbnailChanged();

View file

@ -36,7 +36,7 @@ class ChatMessageContentCore : public QObject, public AbstractObject {
Q_PROPERTY(QString name READ getName CONSTANT) Q_PROPERTY(QString name READ getName CONSTANT)
Q_PROPERTY(quint64 fileOffset READ getFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged) 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(bool wasDownloaded READ wasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged)
Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged) Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged)
Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT) Q_PROPERTY(QString utf8Text READ getUtf8Text CONSTANT)
@ -84,8 +84,8 @@ public:
int getFileDuration() const; int getFileDuration() const;
ConferenceInfoGui *getConferenceInfoGui() const; ConferenceInfoGui *getConferenceInfoGui() const;
void setThumbnail(const QString &data); void setThumbnail(const QUrl &data);
QString getThumbnail() const; QUrl getThumbnail() const;
bool wasDownloaded() const; bool wasDownloaded() const;
void setWasDownloaded(bool downloaded); void setWasDownloaded(bool downloaded);
@ -121,7 +121,7 @@ private:
bool mIsText; bool mIsText;
bool mIsVoiceRecording; bool mIsVoiceRecording;
int mFileDuration; int mFileDuration;
QString mThumbnail; QUrl mThumbnail;
QString mUtf8Text; QString mUtf8Text;
QString mRichFormatText; QString mRichFormatText;
QString mFilePath; QString mFilePath;

View file

@ -19,7 +19,7 @@ Item {
property string filePath: contentGui && contentGui.core.filePath property string filePath: contentGui && contentGui.core.filePath
property bool wasDownloaded: contentGui && contentGui.core.wasDownloaded property bool wasDownloaded: contentGui && contentGui.core.wasDownloaded
property bool isAnimatedImage : contentGui && contentGui.core.wasDownloaded && UtilsCpp.isAnimatedImage(filePath) 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 int fileSize: contentGui ? contentGui.core.fileSize : 0
property bool isTransferring property bool isTransferring
property bool isVideo: UtilsCpp.isVideo(filePath) property bool isVideo: UtilsCpp.isVideo(filePath)
@ -68,9 +68,10 @@ Item {
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
} }
Image { Image {
id: errorImage
anchors.fill: parent anchors.fill: parent
z: image.z + 1 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 source: AppIcons.fileImage
sourceSize.width: mainItem.width sourceSize.width: mainItem.width
sourceSize.height: mainItem.height sourceSize.height: mainItem.height
@ -79,7 +80,7 @@ Item {
Item { Item {
id: loadingImageItem id: loadingImageItem
anchors.fill: parent anchors.fill: parent
visible: mainItem.isImage && image.status === Image.Loading visible: image.status === Image.Loading && !image.visible && !errorImage.visilbe
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: DefaultStyle.main1_200 color: DefaultStyle.main1_200