[network/ruqola] src: Add pending feature

Laurent Montel <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 0e5fff9efa17b0e4f360326e8d5104ccaff84ed3 by Laurent Montel.
Committed on 03/08/2026 at 06:14.
Pushed by mlaurent into branch 'master'.

Add pending feature

M  +3    -0    src/core/autotests/e2ekeymanagertest.cpp
M  +10   -1    src/core/encryption/e2ekeymanager.cpp
M  +2    -0    src/core/encryption/e2ekeymanager.h
M  +13   -0    src/core/rocketchataccount.cpp
M  +2    -0    src/core/rocketchataccount.h
M  +3    -3    src/widgets/room/roomwidget.cpp

https://invent.kde.org/network/ruqola/-/commit/0e5fff9efa17b0e4f360326e8d5104ccaff84ed3

diff --git a/src/core/autotests/e2ekeymanagertest.cpp b/src/core/autotests/e2ekeymanagertest.cpp
index e4649899b7..4e44ff5f5b 100644
--- a/src/core/autotests/e2ekeymanagertest.cpp
+++ b/src/core/autotests/e2ekeymanagertest.cpp
@@ -32,6 +32,7 @@ void E2eKeyManagerTest::shouldHaveDefaultValues()
     E2eKeyManager m(nullptr);
     QCOMPARE(m.status(), E2eKeyManager::Status::Unknown);
     QVERIFY(!m.keySaved());
+    QVERIFY(!m.hasPendingUploadFailure());
 }
 
 void E2eKeyManagerTest::shouldEmitDecodeSignalOnlyWhenNeeded()
@@ -171,11 +172,13 @@ void E2eKeyManagerTest::shouldKeepGenerationStateAndAllowRetryWhenUploadFails()
     manager.verifyExistingKeyForTest(QJsonObject{});
     QCOMPARE(manager.status(), E2eKeyManager::Status::NeedToGenerateKey);
     QCOMPARE(uploadFailedSpy.count(), 1);
+    QVERIFY(manager.hasPendingUploadFailure());
 
     // Retry should attempt another upload with the same pending generated key data.
     QVERIFY(!manager.retryUploadGeneratedKey());
     QCOMPARE(manager.status(), E2eKeyManager::Status::NeedToGenerateKey);
     QCOMPARE(uploadFailedSpy.count(), 2);
+    QVERIFY(manager.hasPendingUploadFailure());
 
     QVERIFY(account.localDatabaseManager()->e2EDatabase()->deleteKey(u"test-e2e-user-upload-retry"_s));
 #endif
diff --git a/src/core/encryption/e2ekeymanager.cpp b/src/core/encryption/e2ekeymanager.cpp
index 33059b2615..f40138257f 100644
--- a/src/core/encryption/e2ekeymanager.cpp
+++ b/src/core/encryption/e2ekeymanager.cpp
@@ -106,7 +106,7 @@ void E2eKeyManager::postponeDecryption()
 bool E2eKeyManager::retryUploadGeneratedKey()
 {
 #if USE_E2E_SUPPORT
-    if (!mAccount || mPendingUploadPublicKey.isEmpty() || mPendingUploadPrivateKey.isEmpty()) {
+    if (!mAccount || !mPendingUploadFailed || mPendingUploadPublicKey.isEmpty() || mPendingUploadPrivateKey.isEmpty()) {
         return false;
     }
 
@@ -117,6 +117,11 @@ bool E2eKeyManager::retryUploadGeneratedKey()
 #endif
 }
 
+bool E2eKeyManager::hasPendingUploadFailure() const
+{
+    return mPendingUploadFailed;
+}
+
 QString E2eKeyManager::generateRandomPassword() const
 {
 #if USE_E2E_SUPPORT
@@ -236,6 +241,7 @@ bool E2eKeyManager::startUploadGeneratedKey(const QByteArray &publicKey, const Q
 
     mPendingUploadPublicKey = publicKey;
     mPendingUploadPrivateKey = encryptedPrivateKey;
+    mPendingUploadFailed = false;
 
     auto setJob = new RocketChatRestApi::SetUserPublicAndPrivateKeysJob(this);
     mAccount->restApi()->initializeRestApiJob(setJob);
@@ -246,15 +252,18 @@ bool E2eKeyManager::startUploadGeneratedKey(const QByteArray &publicKey, const Q
     setJob->setSetUserPublicAndPrivateKeysInfo(info);
 
     connect(setJob, &RocketChatRestApi::SetUserPublicAndPrivateKeysJob::setUserPublicAndPrivateKeysDone, this, [this]() {
+        mPendingUploadFailed = false;
         Q_EMIT uploadEncryptionKeyDone();
     });
     connect(setJob, &RocketChatRestApi::RestApiAbstractJob::failed, this, [this](const QString &, const QString &) {
+        mPendingUploadFailed = true;
         setStatus(Status::NeedToGenerateKey);
         Q_EMIT uploadEncryptionKeyFailed();
     });
 
     if (!setJob->start()) {
         qCWarning(RUQOLA_ENCRYPTION_LOG) << "Unable to upload generated E2E keypair";
+        mPendingUploadFailed = true;
         setStatus(Status::NeedToGenerateKey);
         Q_EMIT uploadEncryptionKeyFailed();
         return false;
diff --git a/src/core/encryption/e2ekeymanager.h b/src/core/encryption/e2ekeymanager.h
index 674c6e16bd..bc3bc965bb 100644
--- a/src/core/encryption/e2ekeymanager.h
+++ b/src/core/encryption/e2ekeymanager.h
@@ -28,6 +28,7 @@ public:
     [[nodiscard]] bool decodeEncryptionKey(const QString &password);
     void postponeDecryption();
     [[nodiscard]] bool retryUploadGeneratedKey();
+    [[nodiscard]] bool hasPendingUploadFailure() const;
 
     void fetchMyKeys();
 
@@ -60,5 +61,6 @@ private:
     QByteArray mDecodedPrivateKey;
     QByteArray mPendingUploadPublicKey;
     QByteArray mPendingUploadPrivateKey;
+    bool mPendingUploadFailed = false;
     RocketChatAccount *const mAccount;
 };
diff --git a/src/core/rocketchataccount.cpp b/src/core/rocketchataccount.cpp
index d035639bd9..9f2c43f7d1 100644
--- a/src/core/rocketchataccount.cpp
+++ b/src/core/rocketchataccount.cpp
@@ -305,6 +305,8 @@ RocketChatAccount::RocketChatAccount(const QString &accountFileName, QObject *pa
     connect(mE2eKeyManager, &E2eKeyManager::decodeEncryptionKeyDone, this, &RocketChatAccount::slotE2eDecodeKeyDone);
     connect(mE2eKeyManager, &E2eKeyManager::failedDecodeEncryptionKey, this, &RocketChatAccount::slotE2eDecodeKeyFailed);
     connect(mE2eKeyManager, &E2eKeyManager::decodeEncryptionKeyPostponed, this, &RocketChatAccount::slotE2eDecodeKeyPostponed);
+    connect(mE2eKeyManager, &E2eKeyManager::uploadEncryptionKeyDone, this, &RocketChatAccount::slotE2eUploadKeyDone);
+    connect(mE2eKeyManager, &E2eKeyManager::uploadEncryptionKeyFailed, this, &RocketChatAccount::slotE2eUploadKeyFailed);
     connect(mMemoryManager, &MemoryManager::clearApplicationSettingsModelRequested, mAppsMarketPlaceModel, &AppsMarketPlaceModel::clear);
     connect(mMemoryManager, &MemoryManager::cleanRoomHistoryRequested, mRoomModel, &RoomModel::cleanRoomHistory);
 
@@ -3358,6 +3360,17 @@ void RocketChatAccount::slotE2eDecodeKeyPostponed()
     setE2EPasswordMustBeDecrypt(true);
 }
 
+void RocketChatAccount::slotE2eUploadKeyDone()
+{
+    setE2EPasswordMustBeSave(false);
+}
+
+void RocketChatAccount::slotE2eUploadKeyFailed()
+{
+    setE2EPasswordMustBeSave(true);
+    Q_EMIT needToSaveE2EPassword();
+}
+
 MemoryManager *RocketChatAccount::memoryManager() const
 {
     return mMemoryManager;
diff --git a/src/core/rocketchataccount.h b/src/core/rocketchataccount.h
index 529d25f0e8..bc1628846b 100644
--- a/src/core/rocketchataccount.h
+++ b/src/core/rocketchataccount.h
@@ -598,6 +598,8 @@ private:
     LIBRUQOLACORE_NO_EXPORT void slotE2eDecodeKeyDone();
     LIBRUQOLACORE_NO_EXPORT void slotE2eDecodeKeyFailed();
     LIBRUQOLACORE_NO_EXPORT void slotE2eDecodeKeyPostponed();
+    LIBRUQOLACORE_NO_EXPORT void slotE2eUploadKeyDone();
+    LIBRUQOLACORE_NO_EXPORT void slotE2eUploadKeyFailed();
     LIBRUQOLACORE_NO_EXPORT void slotDDpLoginStatusChanged();
     LIBRUQOLACORE_NO_EXPORT void slotRESTLoginStatusChanged();
     LIBRUQOLACORE_NO_EXPORT void slotRoomOpenChanged(const QByteArray &rid);
diff --git a/src/widgets/room/roomwidget.cpp b/src/widgets/room/roomwidget.cpp
index 2e1f3421cc..9786133fbe 100644
--- a/src/widgets/room/roomwidget.cpp
+++ b/src/widgets/room/roomwidget.cpp
@@ -294,9 +294,9 @@ void RoomWidget::slotGenerateNewPassword()
     QPointer<E2eCopyPasswordDialog> dlg = new E2eCopyPasswordDialog(mCurrentRocketChatAccount, this);
     if (dlg->exec()) {
         mCurrentRocketChatAccount->settings()->setKeySaved(true);
-        // Retry upload in case the initial key publication failed (e.g. temporary network/server issue).
-        if (!mCurrentRocketChatAccount->e2eKeyManager()->retryUploadGeneratedKey()) {
-            qCWarning(RUQOLAWIDGETS_LOG) << "Unable to retry uploading generated E2E key";
+        // Retry only after an actual upload failure, otherwise this is a no-op that creates noise.
+        if (mCurrentRocketChatAccount->e2eKeyManager()->hasPendingUploadFailure() && !mCurrentRocketChatAccount->e2eKeyManager()->retryUploadGeneratedKey()) {
+            qCWarning(RUQOLAWIDGETS_LOG) << "Unable to retry failed upload of generated E2E key";
         }
         // TODO save it in kwalletmanagers ?
     }
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.