fix #LINQT-11899 text in messages disappearing (compute encoding to rich format only once for each message instead of doing it everytime something changes in the list)

This commit is contained in:
gaelle 2025-08-21 10:00:47 +02:00
parent 836d0b1da3
commit be531b5e9f
6 changed files with 120 additions and 8 deletions

View file

@ -63,6 +63,8 @@ ChatMessageContentCore::ChatMessageContentCore(const std::shared_ptr<linphone::C
mFileDuration = content->getFileDuration();
mFileOffset = 0;
mUtf8Text = Utils::coreStringToAppString(content->getUtf8Text());
auto chatRoom = chatMessageModel ? chatMessageModel->getMonitor()->getChatRoom() : nullptr;
mRichFormatText = ToolModel::encodeTextToQmlRichFormat(mUtf8Text, {}, chatRoom);
mWasDownloaded = !mFilePath.isEmpty() && QFileInfo(mFilePath).isFile();
mThumbnail = mFilePath.isEmpty()
? QString()

View file

@ -40,6 +40,7 @@ class ChatMessageContentCore : public QObject, public AbstractObject {
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)
Q_PROPERTY(QString richFormatText MEMBER mRichFormatText CONSTANT)
Q_PROPERTY(bool isFile READ isFile WRITE setIsFile NOTIFY isFileChanged)
Q_PROPERTY(bool isFileEncrypted READ isFileEncrypted WRITE setIsFileEncrypted NOTIFY isFileEncryptedChanged)
Q_PROPERTY(bool isFileTransfer READ isFileTransfer WRITE setIsFileTransfer NOTIFY isFileTransferChanged)
@ -122,6 +123,7 @@ private:
int mFileDuration;
QString mThumbnail;
QString mUtf8Text;
QString mRichFormatText;
QString mFilePath;
QString mName;
quint64 mFileSize;

View file

@ -24,6 +24,7 @@
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "model/friend/FriendsManager.hpp"
#include "tool/UriTools.hpp"
#include "tool/Utils.hpp"
#include <QDebug>
#include <QDirIterator>
@ -117,6 +118,108 @@ QString ToolModel::getDisplayName(QString address) {
return nameSplitted.join(" ");
}
QString ToolModel::encodeTextToQmlRichFormat(const QString &text,
const QVariantMap &options,
std::shared_ptr<linphone::ChatRoom> chatRoom) {
QStringList formattedText;
bool lastWasUrl = false;
auto primaryColor = QColor::fromString("#4AA8FF");
if (options.contains("noLink") && options["noLink"].toBool()) {
formattedText.append(Utils::encodeEmojiToQmlRichFormat(text));
} else {
auto iriParsed = UriTools::parseIri(text);
for (int i = 0; i < iriParsed.size(); ++i) {
QString iri = iriParsed[i]
.second.replace('&', "&amp;")
.replace('<', "\u2063&lt;")
.replace('>', "\u2063&gt;")
.replace('"', "&quot;")
.replace('\'', "&#039;");
if (!iriParsed[i].first) {
if (lastWasUrl) {
lastWasUrl = false;
if (iri.front() != ' ') iri.push_front(' ');
}
formattedText.append(Utils::encodeEmojiToQmlRichFormat(iri));
} else {
QString uri =
iriParsed[i].second.left(3) == "www" ? "http://" + iriParsed[i].second : iriParsed[i].second;
/* TODO : preview from link
int extIndex = iriParsed[i].second.lastIndexOf('.');
QString ext;
if( extIndex >= 0)
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display
(blank image if set without width) images += "<a href=\"" + uri + "\"><img" + (
options.contains("imagesWidth") ? QString(" width='") + options["imagesWidth"].toString() + "'" : ""
) + (
options.contains("imagesWidth")
? QString(" height='auto'")
: ""
) + " src=\"" + iriParsed[i].second + "\" />"+uri+"</a>";
}else{
*/
formattedText.append("<a style=\"color:" + primaryColor.name() + ";\" href=\"" + uri + "\">" + iri +
"</a>");
lastWasUrl = true;
/*}*/
}
}
}
if (lastWasUrl && formattedText.last().back() != ' ') {
formattedText.push_back(" ");
}
if (chatRoom) {
auto participants = chatRoom->getParticipants();
auto mentionsParsed = UriTools::parseMention(formattedText.join(""));
formattedText.clear();
for (int i = 0; i < mentionsParsed.size(); ++i) {
QString mention = mentionsParsed[i].second;
if (mentionsParsed[i].first) {
QString mentions = mentionsParsed[i].second;
QStringList finalMentions;
QStringList parts = mentions.split(" ");
for (auto part : parts) {
if (part.startsWith("@")) { // mention
QString username = part;
username.removeFirst();
auto it = std::find_if(participants.begin(), participants.end(),
[username](std::shared_ptr<linphone::Participant> p) {
return p->getAddress() && username == p->getAddress()->getUsername();
});
if (it != participants.end()) {
auto foundParticipant = *it;
// participants.at(std::distance(participants.begin(), it));
auto address = foundParticipant->getAddress()->clone();
auto isFriend = findFriendByAddress(address);
address->clean();
auto addressString = Utils::coreStringToAppString(address->asStringUriOnly());
if (isFriend)
part = "@" + Utils::coreStringToAppString(isFriend->getAddress()->getDisplayName());
QString participantLink = "<a style=\"color:" + primaryColor.name() +
";\" href=\"mention:" + addressString + "\">" + part + "</a>";
finalMentions.append(participantLink);
} else {
finalMentions.append(part);
}
} else {
finalMentions.append(part);
}
}
formattedText.push_back(finalMentions.join(" "));
} else {
formattedText.push_back(mentionsParsed[i].second);
}
}
}
return "<p style=\"white-space:pre-wrap;\">" + formattedText.join("");
}
std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(const QString &address) {
auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList();
if (!defaultFriendList) return nullptr;

View file

@ -50,6 +50,10 @@ public:
static QString getDisplayName(const std::shared_ptr<linphone::Address> &address);
static QString getDisplayName(QString address);
static QString encodeTextToQmlRichFormat(const QString &text,
const QVariantMap &options,
std::shared_ptr<linphone::ChatRoom> chatRoom);
static std::shared_ptr<linphone::Friend> findFriendByAddress(const QString &address);
static std::shared_ptr<linphone::Friend> findFriendByAddress(std::shared_ptr<linphone::Address> linphoneAddr);

View file

@ -88,7 +88,10 @@ ListView {
initialDisplayItems: 10
onEventInserted: (index, gui) => {
if (!mainItem.visible) return
if(mainItem.lastItemVisible) mainItem.positionViewAtIndex(index, ListView.Beginning)
if(mainItem.lastItemVisible) {
mainItem.positionViewAtIndex(index, ListView.Beginning)
markIndexAsRead(index)
}
}
Component.onCompleted: loading = true
onListAboutToBeReset: loading = true
@ -239,6 +242,7 @@ ListView {
Component.onCompleted: {
if (index === 0) mainItem.lastItemVisible = isFullyVisible
}
onYChanged: if (index === 0) mainItem.lastItemVisible = isFullyVisible
chat: mainItem.chat
searchedTextPart: mainItem.filterText
maxWidth: Math.round(mainItem.width * (3/4))

View file

@ -26,13 +26,10 @@ TextEdit {
readOnly: true
selectByMouse: true
property var encodeTextObj: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentGui.core.utf8Text, {}, mainItem.chatGui)
: ''
text: encodeTextObj
&& (searchedTextPart !== ""
? UtilsCpp.boldTextPart(encodeTextObj.value, searchedTextPart)
: encodeTextObj.value)
|| ""
text: searchedTextPart !== ""
? UtilsCpp.boldTextPart(contentGui.core.richFormatText, searchedTextPart)
: contentGui.core.richFormatText
textFormat: Text.RichText // To supports links and imgs.
wrapMode: TextEdit.Wrap