[network/ruqola] src/core/model: Allow to decrypt message when we load from Database

Laurent Montel <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 3dc01346f297000d393aac2913450d311e74b4d7 by Laurent Montel.
Committed on 13/08/2026 at 18:31.
Pushed by mlaurent into branch 'master'.

Allow to decrypt message when we load from Database

M  +44   -7    src/core/model/messagesmodel.cpp
M  +8    -0    src/core/model/messagesmodel.h

https://invent.kde.org/network/ruqola/-/commit/3dc01346f297000d393aac2913450d311e74b4d7

diff --git a/src/core/model/messagesmodel.cpp b/src/core/model/messagesmodel.cpp
index b1ea93f42f..f46548f6ad 100644
--- a/src/core/model/messagesmodel.cpp
+++ b/src/core/model/messagesmodel.cpp
@@ -173,14 +173,15 @@ void MessagesModel::addMessage(const Message &message)
         const QModelIndex index = createIndex(rowNumber, 0);
         Q_EMIT dataChanged(index, index, roles);
     };
-    if (mRoom && !mRoom->sessionKey().isEmpty()) {
-        if (auto f = message.messageEncrypted()) {
-            f->decryptContent(mRoom->sessionKey());
-        }
-    }
+    decryptMessage(message);
 
     // When we have 1 element.
     if (mAllMessages.count() == 1 && (*mAllMessages.begin()).messageId() == message.messageId()) {
+        if (message.pendingMessage()) {
+            // If we already have a message and we must add pending message it's that server
+            // send quickly new message => replace not it by a pending message
+            return;
+        }
         (*mAllMessages.begin()) = message;
         qCDebug(RUQOLA_MESSAGEMODELS_LOG) << "Update first message";
         emitChanged(0, {OriginalMessageOrAttachmentDescription});
@@ -208,6 +209,7 @@ void MessagesModel::addMessagesSyncAfterLoadingFromDatabase(QList<Message> messa
         beginResetModel();
         std::sort(messages.begin(), messages.end(), compareTimeStamps);
         const QList<Message> reducedMessageList = messages.mid(messages.count() - 50);
+        decryptMessageList(reducedMessageList);
         mAllMessages = reducedMessageList;
         endResetModel();
     } else {
@@ -224,11 +226,13 @@ void MessagesModel::addMessages(const QList<Message> &messages, bool insertListM
         return;
     }
     if (mAllMessages.isEmpty()) {
+        decryptMessageList(messages);
         beginInsertRows(QModelIndex(), 0, messages.count() - 1);
         mAllMessages = messages;
         std::sort(mAllMessages.begin(), mAllMessages.end(), compareTimeStamps);
         endInsertRows();
     } else if (insertListMessages) {
+        decryptMessageList(messages);
         beginResetModel();
         mAllMessages += messages;
         std::sort(mAllMessages.begin(), mAllMessages.end(), compareTimeStamps);
@@ -437,11 +441,44 @@ void MessagesModel::generateText(const Message &message, const QString &searchTe
     // mNumberOfTextSearched = numberOfTextSearched;
 }
 
+void MessagesModel::decryptMessage(const Message &message) const
+{
+    if (!mRoom) {
+        return;
+    }
+    if (const auto sessionKey = mRoom->sessionKey(); !sessionKey.isEmpty()) {
+        if (auto f = message.messageEncrypted()) {
+            f->decryptContent(sessionKey);
+        }
+    }
+}
+
+void MessagesModel::decryptMessageList(const QList<Message> &messages) const
+{
+    if (!mRoom) {
+        return;
+    }
+    if (const auto sessionKey = mRoom->sessionKey(); !sessionKey.isEmpty()) {
+        for (const Message &message : messages) {
+            if (auto f = message.messageEncrypted()) {
+                f->decryptContent(sessionKey);
+            }
+        }
+    }
+}
+
 void MessagesModel::decryptMessages(const QByteArray &sessionKey)
 {
-    for (auto &msg : std::as_const(mAllMessages)) {
-        if (auto f = msg.messageEncrypted()) {
+    // The session key usually arrives after the messages have been loaded and painted, so the rows
+    // that just became readable must be announced, otherwise they keep showing the encrypted text.
+    for (int row = 0, total = mAllMessages.count(); row < total; ++row) {
+        // QList::at() hands out a const reference on purpose: decryptContent() only fills the
+        // mutable decrypted members of the shared MessageEncrypted, no detach needed here.
+        if (auto f = mAllMessages.at(row).messageEncrypted()) {
             f->decryptContent(sessionKey);
+            // One signal per row: the view drops its text/size-hint cache for topLeft only.
+            const QModelIndex idx = createIndex(row, 0);
+            Q_EMIT dataChanged(idx, idx, {OriginalMessageOrAttachmentDescription});
         }
     }
 }
diff --git a/src/core/model/messagesmodel.h b/src/core/model/messagesmodel.h
index a4329525ac..b90463d60d 100644
--- a/src/core/model/messagesmodel.h
+++ b/src/core/model/messagesmodel.h
@@ -193,6 +193,14 @@ private:
     LIBRUQOLACORE_NO_EXPORT void addMessage(const Message &message);
 
     LIBRUQOLACORE_NO_EXPORT void refresh();
+    /**
+     * @brief Decrypts @p message in place with the room session key, if we already have one.
+     *
+     * Must be called on every path that puts messages into the model, otherwise an encrypted
+     * message stays displayed as such.
+     */
+    LIBRUQOLACORE_NO_EXPORT void decryptMessage(const Message &message) const;
+    LIBRUQOLACORE_NO_EXPORT void decryptMessageList(const QList<Message> &messages) const;
     [[nodiscard]] LIBRUQOLACORE_NO_EXPORT bool threadMessageFollowed(const QByteArray &threadMessageId) const;
     [[nodiscard]] LIBRUQOLACORE_NO_EXPORT QStringList roomRoles(const QByteArray &userId) const;
     [[nodiscard]] LIBRUQOLACORE_NO_EXPORT QString convertMessageText(const Message &message,
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.