diff --git a/client/ui/controllers/transferController.cpp b/client/ui/controllers/transferController.cpp index 427cc290d..91b8dc48c 100644 --- a/client/ui/controllers/transferController.cpp +++ b/client/ui/controllers/transferController.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "core/api/apiUtils.h" #include "amnezia_application.h" @@ -13,11 +16,27 @@ #include "ui/controllers/importController.h" #include "core/api/apiDefs.h" #include "core/controllers/gatewayController.h" +#include "core/errorstrings.h" -static ErrorCode postPlainJson(const QString& url, - const QJsonObject& payload, - int timeoutMs, - QByteArray& responseBody); +namespace { + void logSystemProxiesForUrl(const QString &urlStr) + { + const QUrl url(urlStr); + const QList proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(url)); + QStringList proxyDesc; + proxyDesc.reserve(proxies.size()); + for (const auto &p : proxies) { + proxyDesc << QStringLiteral("%1 %2:%3") + .arg(p.type() == QNetworkProxy::NoProxy ? QStringLiteral("NoProxy") + : p.type() == QNetworkProxy::HttpProxy ? QStringLiteral("HttpProxy") + : p.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral("Socks5Proxy") + : QStringLiteral("Proxy")) + .arg(p.hostName()) + .arg(p.port()); + } + qDebug() << "TransferController: system proxies for" << urlStr << ":" << proxyDesc; + } +} TransferController::TransferController(const std::shared_ptr &settings, const QSharedPointer &serversModel, @@ -37,21 +56,20 @@ void TransferController::handleImportControllerDestroyed() TransferController::~TransferController() { } -QString TransferController::buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid, int version) const +QString TransferController::buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid) const { QJsonObject obj; obj["gw"] = gatewayUrl; - obj["u"] = uuid; - obj["v"] = version; + obj["uuid"] = uuid; qDebug() << "built QrPayload with GW = " << gatewayUrl - << " uuid = " << uuid - << " version = " << version; + << " uuid = " << uuid; return QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); } void TransferController::generateNewQrCode() { - qDebug() << "TransferController::generateNewQrCode: generating QR code"; + // Debug mode: keep UUID/payload generation, but disable actual QR rendering (temporary). + qDebug() << "TransferController::generateNewQrCode: generating transfer payload (QR rendering disabled)"; QString gw = m_settings->getGatewayEndpoint(); if (!gw.endsWith('/')) { @@ -61,13 +79,20 @@ void TransferController::generateNewQrCode() m_currentUuid = QUuid::createUuid().toString(QUuid::WithoutBraces); qDebug() << "uuid:" << m_currentUuid; - const QString payload = buildQrPayloadJson(gw, m_currentUuid, 1); + m_currentPayload = buildQrPayloadJson(gw, m_currentUuid); - auto qr = qrCodeUtils::generateQrCode(payload.toUtf8()); - const QString svg = QString::fromStdString(toSvgString(qr, 1)); - m_qrCodeUrl = qrCodeUtils::svgToBase64(svg); + // QR generation disabled for debugging/CLI-style copy-paste flow. + // If/when QR is re-enabled, restore this block: + // + // auto qr = qrCodeUtils::generateQrCode(m_currentPayload.toUtf8()); + // const QString svg = QString::fromStdString(toSvgString(qr, 1)); + // m_qrCodeUrl = qrCodeUtils::svgToBase64(svg); + // emit qrCodeUpdated(); + m_qrCodeUrl.clear(); emit qrCodeUpdated(); + emit currentUuidChanged(); + emit currentPayloadChanged(); } void TransferController::stopScanner() @@ -76,14 +101,7 @@ void TransferController::stopScanner() emit scannerShouldStop(); } -QString TransferController::getPremiumConfigToSend() const -{ - qDebug() << "TransferController:getPremiumConfigToSend() called with apiKey " << apiDefs::key::apiKey; - //Q_UNUSED(apiDefs::key::apiKey) - return m_exportController ? m_exportController->getConfig() : QString(); -} - -QString TransferController::getCurrentApiKey() const +QString TransferController::getCurrentApiKey(QString *vpnKeyOut) const { const int idx = m_serversModel ? m_serversModel->getProcessedServerIndex() : -1; if (idx < 0 || !m_serversModel) { @@ -95,23 +113,25 @@ QString TransferController::getCurrentApiKey() const qDebug() << "server:" << server; const QJsonObject apiConfig = server.value(apiDefs::key::apiConfig).toObject(); - QJsonObject authData = server.value(QStringLiteral("auth_data")).toObject(); - //QString key = authData.value(apiDefs::key::apiKey).toString(); - QString key = authData.value(QStringLiteral("api_key")).toString(); + const QJsonObject authData = server.value(QStringLiteral("auth_data")).toObject(); - /*if (key.isEmpty()) { - const QJsonObject nestedAuth = apiConfig.value(QStringLiteral("auth_data")).toObject(); - if (!nestedAuth.isEmpty()) { - key = nestedAuth.value(apiDefs::key::apiKey).toString(); + const QString apiKey = authData.value(QStringLiteral("api_key")).toString(); + + if (vpnKeyOut) { + QString vpnKey = apiConfig.value(apiDefs::key::vpnKey).toString(); + if (vpnKey.isEmpty()) { + // Fallback for older Premium V1 configs where vpn_key may be derived. + vpnKey = apiUtils::getPremiumV1VpnKey(server); } - }*/ + *vpnKeyOut = vpnKey; + } - return key; + return apiKey; } void TransferController::onTransferQrScanned(const QString &code) { - qDebug() << "TransferController has scanned the Qr"; + qDebug() << "TransferController has scanned the Qr"; QJsonParseError err; const QJsonDocument doc = QJsonDocument::fromJson(code.toUtf8(), &err); @@ -123,7 +143,7 @@ void TransferController::onTransferQrScanned(const QString &code) const QJsonObject obj = doc.object(); QString gw = obj.value("gw").toString(); - const QString uuid = obj.value("u").toString(); + const QString uuid = obj.value("uuid").toString(); if (gw.isEmpty() || uuid.isEmpty()) { qWarning() << "TransferController::onTransferQrScanned: QR missing gw or uuid"; @@ -134,17 +154,15 @@ void TransferController::onTransferQrScanned(const QString &code) gw.append('/'); } - const QString apiKey = getCurrentApiKey(); + QString vpnKey; + const QString apiKey = getCurrentApiKey(&vpnKey); qDebug() << "scanned apiKey:" << apiKey; - const QString config = getPremiumConfigToSend(); - qDebug() << "config:" << config; - if (apiKey.isEmpty() || config.isEmpty()) { + if (apiKey.isEmpty()) { qWarning() << "TransferController::onTransferQrScanned: no subscription key or config to send"; emit postFailed(QStringLiteral("No subscription key or config to send")); return; } - // Allow only premium (subscription) configs bool isPremium = m_serversModel && m_serversModel->processedServerIsPremium(); qDebug() << "isPremium: " << isPremium; bool isFromGatewayApi = m_serversModel && m_serversModel->getProcessedServerData("isServerFromGatewayApi").toBool(); @@ -157,85 +175,52 @@ void TransferController::onTransferQrScanned(const QString &code) emit postStarted(); - qDebug() << "entered POST section"; - qDebug() << "gw:" << gw; - qDebug() << "uuid:" << uuid; - qDebug() << "apiKey:" << apiKey; - qDebug() << "config:" << config; + if (vpnKey.isEmpty()) { + qWarning() << "TransferController::onTransferQrScanned: missing vpn_key"; + emit postFailed(QStringLiteral("Missing vpn_key to send")); + return; + } - /*GatewayController gatewayController(gw, //m_settings->getGatewayEndpoint(), + // sendConfig can take longer on slower networks / debug setups, so use a bigger timeout than generic API calls. + const int sendTimeoutMs = 120000; + GatewayController gatewayController(gw, m_settings->isDevGatewayEnv(), - apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled());*/ - QByteArray responseBody; - + sendTimeoutMs, + m_settings->isStrictKillSwitchEnabled()); QJsonObject payload; - payload.insert(QStringLiteral("config"), config); payload.insert(QStringLiteral("uuid"), uuid); payload.insert(QStringLiteral("api_key"), apiKey); + payload.insert(QStringLiteral("config"), vpnKey); - const QString url = gw + "sendConfig"; - qDebug() << "TransferController::onTransferQrScanned: sending POST to" << url - << "with payload:" << QJsonDocument(payload).toJson(QJsonDocument::Compact); + const QString endpoint = QStringLiteral("%1v1/sendConfig"); + QByteArray responseBody; + const QString fullUrl = endpoint.arg(gw); + qDebug() << "TransferController::onTransferQrScanned: POST" << fullUrl + << "uuid:" << uuid; + logSystemProxiesForUrl(fullUrl); + const auto errorCode = gatewayController.post(endpoint, payload, responseBody); + qDebug() << "TransferController::onTransferQrScanned: sendConfig finished with code" + << static_cast(errorCode) + << "response size:" << responseBody.size(); - auto ec = postPlainJson(url, payload, apiDefs::requestTimeoutMsecs, responseBody); - qDebug() << "TransferController::onTransferQrScanned: POST finished with code" - << static_cast(ec); - - if (ec != ErrorCode::NoError) { - qWarning() << "TransferController::onTransferQrScanned: network error during POST" - << "body:" << responseBody; - emit postFailed(QStringLiteral("Network error")); + if (errorCode != ErrorCode::NoError) { + qWarning() << "TransferController::onTransferQrScanned: sendConfig failed with code" << static_cast(errorCode); + emit postFailed(QStringLiteral("sendConfig failed: %1").arg(errorString(errorCode))); return; } - // Parse response and handle success/failure - { - QJsonParseError parseErr; - const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr); - if (parseErr.error == QJsonParseError::NoError && respDoc.isObject()) { - const QJsonObject respObj = respDoc.object(); - const QString status = respObj.value(QStringLiteral("status")).toString(); - if (status == QStringLiteral("success")) { - qDebug() << "TransferController::onTransferQrScanned: gateway returned success"; - emit postSucceeded(); - stopScanner(); - return; - } - } - qWarning() << "TransferController::onTransferQrScanned: gateway response error" << responseBody; - emit postFailed(QStringLiteral("Gateway response error")); + QJsonParseError parseErr; + const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr); + if (parseErr.error == QJsonParseError::NoError && respDoc.isObject() + && respDoc.object().value(QStringLiteral("status")).toString() == QStringLiteral("success")) { + emit postSucceeded(); + stopScanner(); return; } - //const QString endpoint = QStringLiteral("%1sendConfig").arg(gw); - //const QString endpoint = QStringLiteral("sendConfig"); - /*qDebug() << "TransferController::onTransferQrScanned: sending POST to " << endpoint - << "with payload: " << QJsonDocument(payload).toJson(QJsonDocument::Compact); - auto errorCode = gatewayController.post(endpoint, payload, responseBody); - qDebug() << "TransferController::onTransferQrScanned: POST finished with code" - << static_cast(errorCode); - - if (errorCode == ErrorCode::NoError) { - QJsonParseError err; - const QJsonDocument doc = QJsonDocument::fromJson(responseBody, &err); - if (err.error == QJsonParseError::NoError && doc.isObject()) { - const QJsonObject obj = doc.object(); - if (obj.value(QStringLiteral("status")).toString() == QStringLiteral("success")) { - qDebug() << "TransferController::onTransferQrScanned: gateway returned success"; - emit postSucceeded(); - stopScanner(); - return; - } - } - - qWarning() << "TransferController::onTransferQrScanned: gateway response error"; - emit postFailed(QStringLiteral("Gateway response error")); - } else { - qWarning() << "TransferController::onTransferQrScanned: network error during POST"; - emit postFailed(QStringLiteral("Network error")); - }*/ + qWarning() << "TransferController::onTransferQrScanned: unexpected gateway response:" << responseBody; + emit postFailed(QStringLiteral("Gateway response error")); } QString TransferController::qrCodeUrl() const @@ -261,58 +246,33 @@ void TransferController::startWaitForConfig(ImportController *importController) m_importController = importController; if (m_importController) { - connect(m_importController, &QObject::destroyed, + connect(m_importController, &ImportController::destroyed, this, &TransferController::handleImportControllerDestroyed, Qt::UniqueConnection); } - // Blocking request to /waitConfig with a timeout. - const int waitTimeoutMs = 30000; - - /*GatewayController gatewayController(gw, //m_settings->getGatewayEndpoint(), - m_settings->isDevGatewayEnv(), - waitTimeoutMs, - m_settings->isStrictKillSwitchEnabled()); - const QString endpoint = QStringLiteral("%1waitConfig"); - //const QString endpoint = QStringLiteral("waitConfig"); - - qDebug() << "waitConfig endpoint:" << QString(endpoint).arg(gw);*/ + const int waitTimeoutMs = 300000; QJsonObject payload; payload.insert(QStringLiteral("uuid"), uuid); + + GatewayController gatewayController(gw, + m_settings->isDevGatewayEnv(), + waitTimeoutMs, + m_settings->isStrictKillSwitchEnabled()); + + const QString endpoint = QStringLiteral("%1v1/waitConfig"); QByteArray responseBody; + const QString fullUrl = endpoint.arg(gw); + qDebug() << "TransferController::startWaitForConfig: POST" << fullUrl + << "uuid:" << uuid; + logSystemProxiesForUrl(fullUrl); + const auto errorCode = gatewayController.post(endpoint, payload, responseBody); - const QString url = gw + "waitConfig"; - qDebug() << "waitConfig endpoint: " << url; - auto ec = postPlainJson(url, payload, waitTimeoutMs, responseBody); - if (ec != ErrorCode::NoError) { - qWarning() << "waitConfig failed, code:" << (int)ec << "body:" << responseBody; - emit waitError(QStringLiteral("Network error")); - return; - } - - /*auto errorCode = gatewayController.post(endpoint, payload, responseBody); if (errorCode != ErrorCode::NoError) { - qWarning() << "TransferController::startWaitForConfig: network error during waitConfig"; - emit waitError(QStringLiteral("Network error")); - return; - }*/ - - QJsonParseError err; - const QJsonDocument doc = QJsonDocument::fromJson(responseBody, &err); - if (err.error != QJsonParseError::NoError ||!doc.isObject()) { - qWarning() << "TransferController::startWaitForConfig: invalid gateway JSON"; - emit waitError(QStringLiteral("Gateway Response Error")); - return; - } - - const QJsonObject obj = doc.object(); - QString cfg = obj.value(QStringLiteral("config")).toString(); - - if (cfg.isEmpty() || cfg == QStringLiteral("timeout")) { - qWarning() << "TransferController::startWaitForConfig: timeout or empty config"; - emit waitError(QStringLiteral("Gateway response error (timeout)")); + qWarning() << "TransferController::startWaitForConfig: waitConfig failed with code" << static_cast(errorCode); + emit waitError(QStringLiteral("waitConfig failed (%1)").arg(static_cast(errorCode))); return; } @@ -322,40 +282,41 @@ void TransferController::startWaitForConfig(ImportController *importController) return; } - if (!m_importController->extractConfigFromData(cfg)) { - qWarning() << "TransferController::startWaitForConfig: invalid config received from gateway"; - emit waitError(QStringLiteral("Invalid config received from gateway")); + QJsonParseError parseErr; + const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr); + if (parseErr.error != QJsonParseError::NoError || !respDoc.isObject()) { + qWarning() << "TransferController::startWaitForConfig: invalid JSON response:" << responseBody; + emit waitError(QStringLiteral("Invalid gateway response")); + return; + } + const QJsonObject respObj = respDoc.object(); + const QString status = respObj.value(QStringLiteral("status")).toString(); + const QString configStr = respObj.value(QStringLiteral("config")).toString(); + if (status != QStringLiteral("success")) { + qWarning() << "TransferController::startWaitForConfig: gateway status not success:" << status; + emit waitError(QStringLiteral("Gateway error")); + return; + } + if (configStr.isEmpty()) { + emit waitError(QStringLiteral("Empty config")); + return; + } + if (configStr == QStringLiteral("timeout")) { + emit waitError(QStringLiteral("Timeout")); + return; + } + + if (!m_importController->extractConfigFromData(configStr)) { + qWarning() << "TransferController::startWaitForConfig: failed to parse config string"; + emit waitError(QStringLiteral("Invalid config payload")); return; } m_importController->importConfig(); emit configApplied(); } - void TransferController::stopWaitForConfig() { qDebug() << "TransferController::stopWaitForConfig: stop flag set"; } -static ErrorCode postPlainJson(const QString& url, const QJsonObject& payload, int timeoutMs, QByteArray& responseBody) -{ - QNetworkRequest request; - request.setTransferTimeout(timeoutMs); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setUrl(QUrl(url)); - - QNetworkReply* reply = amnApp->networkManager()->post(request, QJsonDocument(payload).toJson(QJsonDocument::Compact)); - - QEventLoop wait; - QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); - - QList sslErrors; - QObject::connect(reply, &QNetworkReply::sslErrors, [&sslErrors](const QList& errors){ sslErrors = errors; }); - - wait.exec(); - responseBody = reply->readAll(); - - auto ec = apiUtils::checkNetworkReplyErrors(sslErrors, reply); - reply->deleteLater(); - return ec; -} diff --git a/client/ui/controllers/transferController.h b/client/ui/controllers/transferController.h index 30c2ca51c..d96373d03 100644 --- a/client/ui/controllers/transferController.h +++ b/client/ui/controllers/transferController.h @@ -7,8 +7,6 @@ #include #include -#include "core/qrCodeUtils.h" - class Settings; class ServersModel; class ExportController; @@ -20,6 +18,9 @@ class TransferController : public QObject Q_PROPERTY(QString qrCodeUrl READ qrCodeUrl NOTIFY qrCodeUpdated) Q_PROPERTY(QString pendingQrCode READ pendingQrCode WRITE setPendingQrCode NOTIFY pendingQrCodeChanged) + // Debug / manual transfer support (temporary): expose UUID and payload JSON used to generate the QR code. + Q_PROPERTY(QString currentUuid READ currentUuid NOTIFY currentUuidChanged) + Q_PROPERTY(QString currentPayload READ currentPayload NOTIFY currentPayloadChanged) public: explicit TransferController(const std::shared_ptr &settings, @@ -41,11 +42,15 @@ public: Q_INVOKABLE void stopWaitForConfig(); QString qrCodeUrl() const; + QString currentUuid() const { return m_currentUuid; } + QString currentPayload() const { return m_currentPayload; } signals: void qrCodeUpdated(); void scannerShouldStop(); void pendingQrCodeChanged(); + void currentUuidChanged(); + void currentPayloadChanged(); void waitError(const QString &message); void configApplied(); @@ -58,10 +63,10 @@ private slots: void handleImportControllerDestroyed(); private: - QString buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid, int version) const; - QString getPremiumConfigToSend() const; + QString buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid) const; + //QString getPremiumConfigToSend() const; QString m_pendingQrCode; - QString getCurrentApiKey() const; + QString getCurrentApiKey(QString *vpnKeyOut = nullptr) const; std::shared_ptr m_settings; QSharedPointer m_serversModel; ExportController *m_exportController { nullptr }; @@ -69,6 +74,7 @@ private: QString m_qrCodeUrl; QString m_currentUuid; + QString m_currentPayload; }; #endif // TRANSFERCONTROLLER_H diff --git a/client/ui/qml/Pages2/PageSettingsApiAddDeviceConfirm.qml b/client/ui/qml/Pages2/PageSettingsApiAddDeviceConfirm.qml index 7512fb465..aa74def00 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAddDeviceConfirm.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAddDeviceConfirm.qml @@ -14,6 +14,9 @@ import "../Config" PageType { id: root + // Debug-friendly: don't auto-close on send; keep the page responsive and show status. + property bool isSending: false + function getAvailableCount() { var max = ApiAccountInfoModel.data("maxDeviceCount") var active = ApiAccountInfoModel.data("activeDeviceCount") @@ -32,7 +35,16 @@ PageType { header: ColumnLayout { width: listView.width - BackButtonType {} + BackButtonType { + backButtonFunction: function() { + if (root.isSending) { + // We cannot truly abort GatewayController::post() without changing it, + // but we can at least stop waiting on the other side and let the user navigate. + TransferController.stopWaitForConfig() + } + PageController.closePage() + } + } BaseHeaderType { Layout.fillWidth: true @@ -50,13 +62,13 @@ PageType { Layout.topMargin: 16 text: qsTr("Yes, share") - enabled: root.getAvailableCount() > 0 && TransferController.pendingQrCode !== "" + enabled: !root.isSending && root.getAvailableCount() > 0 && TransferController.pendingQrCode !== "" clickedFunc: function() { if (TransferController.pendingQrCode !== "") { + root.isSending = true TransferController.onTransferQrScanned(TransferController.pendingQrCode) } - PageController.closePage() } } @@ -74,6 +86,7 @@ PageType { borderWidth: 1 text: qsTr("Cancel") + enabled: !root.isSending clickedFunc: function() { PageController.closePage() @@ -86,14 +99,17 @@ PageType { target: TransferController function onPostStarted() { - PageController.showInfoMessage(qsTr("Sending configuration...")) + PageController.showNotificationMessage(qsTr("Sending configuration...")) } function onPostSucceeded() { - PageController.showInfoMessage(qsTr("Configuration sent successfully")) + root.isSending = false + PageController.showNotificationMessage(qsTr("Configuration sent successfully")) + PageController.closePage() } function onPostFailed(message) { + root.isSending = false PageController.showErrorMessage(message) } } diff --git a/client/ui/qml/Pages2/PageSettingsApiAddDeviceScan.qml b/client/ui/qml/Pages2/PageSettingsApiAddDeviceScan.qml index 4705b7437..5df420465 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAddDeviceScan.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAddDeviceScan.qml @@ -4,7 +4,6 @@ import QtQuick.Layouts import QtQuick.Dialogs import PageEnum 1.0 -import QRCodeReader 1.0 import Style 1.0 import "./" @@ -35,7 +34,7 @@ PageType { anchors.leftMargin: 16 anchors.rightMargin: 16 - text: qsTr("Point the camera at the QR code and hold it for a couple of seconds. ") + progressString + text: qsTr("Debug mode: paste payload JSON from the other device (or enter UUID). ") + progressString } ProgressBarType { @@ -49,7 +48,7 @@ PageType { anchors.rightMargin: 16 } - // Manual UUID input (temporary instead of camera scanning) + // Debug: manual payload/UUID input (temporary instead of camera scanning) ColumnLayout { id: manualInput anchors.left: parent.left @@ -63,6 +62,14 @@ PageType { anchors.bottomMargin: 24 spacing: 12 + TextFieldWithHeaderType { + id: payloadField + Layout.fillWidth: true + + headerText: qsTr("Payload JSON") + textField.placeholderText: qsTr("Paste JSON like {\"gw\":\"...\",\"uuid\":\"...\"}") + } + TextFieldWithHeaderType { id: uuidField Layout.fillWidth: true @@ -72,9 +79,28 @@ PageType { } BasicButtonType { - id: sendButton + id: usePayloadButton Layout.fillWidth: true - text: qsTr("Send") + text: qsTr("Next (use payload)") + enabled: payloadField.textField.text.length > 0 + + clickedFunc: function() { + TransferController.setPendingQrCode(payloadField.textField.text) + PageController.goToPage(PageEnum.PageSettingsApiAddDeviceConfirm) + } + } + + BasicButtonType { + id: buildFromUuidButton + Layout.fillWidth: true + defaultColor: AmneziaStyle.color.transparent + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + textColor: AmneziaStyle.color.paleGray + borderColor: AmneziaStyle.color.paleGray + borderWidth: 1 + + text: qsTr("Next (build from UUID)") enabled: uuidField.textField.text.length > 0 clickedFunc: function() { @@ -94,7 +120,7 @@ PageType { gw = gw + "/" } - var payload = JSON.stringify({ gw: gw, u: uuidField.textField.text, v: 1 }) + var payload = JSON.stringify({ gw: gw, uuid: uuidField.textField.text }) TransferController.setPendingQrCode(payload) PageController.goToPage(PageEnum.PageSettingsApiAddDeviceConfirm) } diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index 746180ab0..012ed4503 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -60,7 +60,7 @@ PageType { Layout.rightMargin: 16 Layout.topMargin: 16 - visible: GC.isMobile() + //visible: GC.isMobile() defaultColor: AmneziaStyle.color.transparent hoveredColor: AmneziaStyle.color.translucentWhite diff --git a/client/ui/qml/Pages2/PageTransferConfigViaQR.qml b/client/ui/qml/Pages2/PageTransferConfigViaQR.qml index 5de1dbfe9..1e0720cf5 100644 --- a/client/ui/qml/Pages2/PageTransferConfigViaQR.qml +++ b/client/ui/qml/Pages2/PageTransferConfigViaQR.qml @@ -7,7 +7,6 @@ import PageEnum 1.0 import Style 1.0 import "../Controls2" import "../Components" -import QRCodeReader 1.0 import "../Controls2/TextTypes" PageType { @@ -58,31 +57,56 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - text: qsTr("Scan the QR code with the phone where Amnezia Premium is active") + text: qsTr("Debug mode: copy the payload to the other device and send it there") } - Rectangle { - id: qrContainer + ColumnLayout { + id: debugPayload anchors.left: parent.left anchors.right: parent.right anchors.top: qrHeader.bottom - anchors.topMargin: 8 - // anchors.horizontalCenter: parent.horizontalCenter // УБРАТЬ + anchors.topMargin: 12 anchors.bottom: bottomHint.top - anchors.bottomMargin: 8 + anchors.bottomMargin: 12 + spacing: 8 - color: AmneziaStyle.color.transparent - border.color: AmneziaStyle.color.paleGray - border.width: 1 + TextArea { + id: payloadArea + Layout.fillWidth: true + Layout.fillHeight: true + readOnly: true + wrapMode: TextArea.WrapAnywhere + selectByMouse: true + text: TransferController.currentPayload + placeholderText: qsTr("Payload will appear here after generating") + } - Image { - id: qrImage - anchors.centerIn: parent - width: Math.min(parent.width, parent.height) - height: width - source: TransferController.qrCodeUrl - fillMode: Image.Stretch - smooth: false + RowLayout { + Layout.fillWidth: true + spacing: 8 + + BasicButtonType { + Layout.fillWidth: true + text: qsTr("Regenerate") + clickedFunc: function() { + TransferController.generateNewQrCode() + TransferController.startWaitForConfig(ImportController) + } + } + + BasicButtonType { + Layout.fillWidth: true + defaultColor: AmneziaStyle.color.transparent + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + textColor: AmneziaStyle.color.paleGray + borderColor: AmneziaStyle.color.paleGray + borderWidth: 1 + text: qsTr("Restart wait") + clickedFunc: function() { + TransferController.startWaitForConfig(ImportController) + } + } } } @@ -92,7 +116,7 @@ PageType { anchors.right: parent.right anchors.bottom: parent.bottom anchors.bottomMargin: 8 - text: qsTr("In the Amnezia app on your phone, tap «+» at the bottom → «QR code»") + text: qsTr("Copy the JSON payload above, paste it on the other device, and confirm sending there") horizontalAlignment: Text.AlignHCenter } } @@ -100,7 +124,6 @@ PageType { Connections { target: TransferController - function onQrCodeUpdated() { qrImage.source = TransferController.qrCodeUrl } function onConfigApplied() { PageController.showNotificationMessage(qsTr("Configuration received and applied")) }