From c327d3e3c8928780f4274e047a09b06934a7f0fd Mon Sep 17 00:00:00 2001 From: dranik Date: Wed, 20 May 2026 11:46:21 +0300 Subject: [PATCH] add error 1123 & fix update qr code & fix hide qr code --- .../controllers/api/pairingController.cpp | 5 +++++ client/core/utils/api/apiUtils.cpp | 15 +++++++++++++++ client/core/utils/errorCodes.h | 1 + client/core/utils/errorStrings.cpp | 1 + client/tests/testPairingParsers.cpp | 9 +++++++++ .../controllers/api/pairingUiController.cpp | 19 ++++++++++++++++++- .../ui/controllers/api/pairingUiController.h | 3 +++ .../ui/qml/Pages2/PageSettingsApiDevices.qml | 10 +++++----- .../PageSetupWizardApiQrPairingReceive.qml | 8 ++++++-- 9 files changed, 63 insertions(+), 8 deletions(-) diff --git a/client/core/controllers/api/pairingController.cpp b/client/core/controllers/api/pairingController.cpp index 6f2291462..9843b1d3d 100644 --- a/client/core/controllers/api/pairingController.cpp +++ b/client/core/controllers/api/pairingController.cpp @@ -78,6 +78,11 @@ ErrorCode applyGatewayOrOpenApiScanError(const QJsonObject &obj) } const QString msg = obj.value(QStringLiteral("message")).toString(); + if (msg.contains(QStringLiteral("QR session"), Qt::CaseInsensitive) + && (msg.contains(QStringLiteral("not found"), Qt::CaseInsensitive) + || msg.contains(QStringLiteral("expired"), Qt::CaseInsensitive))) { + return ErrorCode::ApiPairingSessionExpiredError; + } if (msg.contains(QStringLiteral("not found"), Qt::CaseInsensitive) || msg.contains(QStringLiteral("expired"), Qt::CaseInsensitive)) { return ErrorCode::ApiNotFoundError; } diff --git a/client/core/utils/api/apiUtils.cpp b/client/core/utils/api/apiUtils.cpp index 0b23a59f2..7882f9491 100644 --- a/client/core/utils/api/apiUtils.cpp +++ b/client/core/utils/api/apiUtils.cpp @@ -153,6 +153,21 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl if (httpStatusFromBody == httpStatusCodePaymentRequired) { return amnezia::ErrorCode::ApiSubscriptionNotActiveError; } + + const QString msg = apiErrorMessageFromJson(jsonObj); + if (msg.contains(QStringLiteral("QR session"), Qt::CaseInsensitive) + && (msg.contains(QStringLiteral("not found"), Qt::CaseInsensitive) + || msg.contains(QStringLiteral("expired"), Qt::CaseInsensitive))) { + return amnezia::ErrorCode::ApiPairingSessionExpiredError; + } + if (msg.contains(QStringLiteral("not found"), Qt::CaseInsensitive) + || msg.contains(QStringLiteral("expired"), Qt::CaseInsensitive)) { + return amnezia::ErrorCode::ApiNotFoundError; + } + if (httpStatusCode == httpStatusCodeNotFound) { + return amnezia::ErrorCode::ApiNotFoundError; + } + return amnezia::ErrorCode::ApiConfigDownloadError; } diff --git a/client/core/utils/errorCodes.h b/client/core/utils/errorCodes.h index dee0024f3..408bed091 100644 --- a/client/core/utils/errorCodes.h +++ b/client/core/utils/errorCodes.h @@ -106,6 +106,7 @@ namespace amnezia ApiPairingServiceUnavailableError = 1120, ApiPairingPayloadTooLargeError = 1121, ApiPairingMissingMetadataError = 1122, + ApiPairingSessionExpiredError = 1123, // QFile errors OpenError = 1200, diff --git a/client/core/utils/errorStrings.cpp b/client/core/utils/errorStrings.cpp index 03529e7d3..77f934fa6 100644 --- a/client/core/utils/errorStrings.cpp +++ b/client/core/utils/errorStrings.cpp @@ -90,6 +90,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiPairingServiceUnavailableError): errorMessage = QObject::tr("Service temporarily unavailable. Please try again later"); break; case (ErrorCode::ApiPairingPayloadTooLargeError): errorMessage = QObject::tr("QR pairing data is too large to send"); break; case (ErrorCode::ApiPairingMissingMetadataError): errorMessage = QObject::tr("This subscription is missing data required to transfer via QR (service type or country). Refresh the subscription or pick another server."); break; + case (ErrorCode::ApiPairingSessionExpiredError): errorMessage = QObject::tr("The QR code session has ended. Show a new QR code on the other device and scan again."); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/tests/testPairingParsers.cpp b/client/tests/testPairingParsers.cpp index c525dc7da..183d9d213 100644 --- a/client/tests/testPairingParsers.cpp +++ b/client/tests/testPairingParsers.cpp @@ -108,6 +108,15 @@ private slots: QCOMPARE(PairingController::parseScanQrResponseBody(body), ErrorCode::ApiNotFoundError); } + void scanQr_qrSessionExpiredMessage() + { + QJsonObject o; + o[QStringLiteral("message")] = QStringLiteral("QR session not found or expired"); + const QByteArray body = QJsonDocument(o).toJson(); + + QCOMPARE(PairingController::parseScanQrResponseBody(body), ErrorCode::ApiPairingSessionExpiredError); + } + void validateScanFields_oversizedVpnKey() { QString vpnKey; diff --git a/client/ui/controllers/api/pairingUiController.cpp b/client/ui/controllers/api/pairingUiController.cpp index 059b5e782..6e5df0ba4 100644 --- a/client/ui/controllers/api/pairingUiController.cpp +++ b/client/ui/controllers/api/pairingUiController.cpp @@ -435,12 +435,24 @@ void PairingUiController::startTvQrSession() if (m_tvPairingBusy) { return; } + rotateTvQrSession(); +} + +void PairingUiController::rotateTvQrSession() +{ + if (!m_pairingController || !m_appSettingsRepository) { + return; + } if (m_tvWatcher) { m_tvWatcher->disconnect(); m_tvWatcher->deleteLater(); m_tvWatcher.clear(); } + if (m_tvNetworkReply) { + m_tvNetworkReply->abort(); + m_tvNetworkReply.clear(); + } ++m_tvSessionGeneration; const quint64 generation = m_tvSessionGeneration; @@ -505,6 +517,11 @@ void PairingUiController::dispatchTvGenerateQrAttempt(quint64 generation, int re setTvBusy(false); if (impErr != ErrorCode::NoError) { emit errorOccurred(impErr); + if (impErr == ErrorCode::ApiConfigAlreadyAdded) { + emit tvPairingConfigAlreadyAdded(); + QTimer::singleShot(0, this, [this]() { rotateTvQrSession(); }); + return; + } resetTvQrDisplay(); return; } @@ -526,7 +543,7 @@ void PairingUiController::dispatchTvGenerateQrAttempt(quint64 generation, int re if (logicalErr == ErrorCode::ApiConfigTimeoutError) { setTvBusy(false); - QTimer::singleShot(0, this, [this]() { startTvQrSession(); }); + QTimer::singleShot(0, this, [this]() { rotateTvQrSession(); }); return; } diff --git a/client/ui/controllers/api/pairingUiController.h b/client/ui/controllers/api/pairingUiController.h index 71e7ab8d2..38e2adefd 100644 --- a/client/ui/controllers/api/pairingUiController.h +++ b/client/ui/controllers/api/pairingUiController.h @@ -62,6 +62,8 @@ public: public slots: bool canOpenTvQrPairingPage(); void startTvQrSession(); + /** New UUID + QR image; aborts in-flight generate_qr without clearing the code first. */ + void rotateTvQrSession(); void cancelTvQrSession(); void cancelAllPairingActivity(); @@ -84,6 +86,7 @@ signals: void lastSuccessfulPhonePairingDisplayNameChanged(); void tvPairingConfigReceived(); + void tvPairingConfigAlreadyAdded(); void phonePairingSucceeded(); void pairingUuidFromScan(const QString &uuid); diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index 6066f89c3..b94f22528 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -109,16 +109,16 @@ PageType { } function onPhonePairingSucceeded() { - if (!root.visible) { - return - } SubscriptionUiController.updateApiDevicesModel() const label = PairingUiController.lastSuccessfulPhonePairingDisplayName if (label.length > 0) { PageController.showNotificationMessage( - qsTr("%1 has been added to your subscription").arg(label)) + qsTr("Configuration was sent (%1). Finish setup on the device that displayed the QR code — " + + "if it already has this config, that device will show a message.").arg(label)) } else { - PageController.showNotificationMessage(qsTr("New device has been added to your subscription")) + PageController.showNotificationMessage( + qsTr("Configuration was sent to the device that displayed the QR code. " + + "If it already has this config, that device will show a message.")) } } diff --git a/client/ui/qml/Pages2/PageSetupWizardApiQrPairingReceive.qml b/client/ui/qml/Pages2/PageSetupWizardApiQrPairingReceive.qml index 4fde1559c..6a1bb04a9 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiQrPairingReceive.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiQrPairingReceive.qml @@ -48,8 +48,7 @@ PageType { repeat: true running: root.visible onTriggered: { - PairingUiController.cancelTvQrSession() - PairingUiController.startTvQrSession() + PairingUiController.rotateTvQrSession() } } @@ -184,5 +183,10 @@ PageType { PageController.closePage() }) } + + function onTvPairingConfigAlreadyAdded() { + scrollToBottomRetryTimer.stop() + qrRotationTimer.restart() + } } }