From edf27b82aad5f42570d915e79b3ae6f935ba723e Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 5 Jun 2026 02:06:25 -0300 Subject: [PATCH] add proog generation message and spinner --- src/LEZWalletBackend.cpp | 32 +++++++++++++++++++++++------ src/qml/ExecutionZoneWalletView.qml | 21 +++++++++++++------ src/qml/views/DashboardView.qml | 2 ++ src/qml/views/TransferPanel.qml | 26 ++++++++++++++++++++++- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/LEZWalletBackend.cpp b/src/LEZWalletBackend.cpp index f2f9f46..12308bc 100644 --- a/src/LEZWalletBackend.cpp +++ b/src/LEZWalletBackend.cpp @@ -11,6 +11,7 @@ #include #include "logos_api.h" +#include "logos_api_client.h" #include "logos_sdk.h" namespace { @@ -18,7 +19,12 @@ namespace { const char SETTINGS_APP[] = "ExecutionZoneWalletUI"; const char CONFIG_PATH_KEY[] = "configPath"; const char STORAGE_PATH_KEY[] = "storagePath"; + const char LEZ_MODULE[] = "logos_execution_zone"; const int WALLET_FFI_SUCCESS = 0; + // Proof generation time is unbounded on commodity hardware. + // Timeout(-1) means "wait indefinitely", matching Qt's own convention + // for infinite waits (e.g. QRemoteObjectPendingCall::waitForFinished(-1)). + const Timeout NO_TIMEOUT{-1}; // Convert a decimal amount string to 32-char hex (16 bytes little-endian) // for transfer_public/transfer_private/transfer_private_owned. @@ -219,7 +225,6 @@ QString LEZWalletBackend::transferPrivate(QString fromHex, QString toHex, QStrin if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount."); QString keysPayload = toHex.trimmed(); - // If "To" is not JSON (e.g. user pasted account id hex), resolve to keys. if (!keysPayload.startsWith(QLatin1Char('{'))) { qDebug() << "LEZWalletBackend::transferPrivate: resolving keys via get_private_account_keys"; const QString resolved = getPrivateAccountKeys(keysPayload); @@ -227,14 +232,20 @@ QString LEZWalletBackend::transferPrivate(QString fromHex, QString toHex, QStrin keysPayload = resolved; } - return m_logos->logos_execution_zone.transfer_private(fromHex, keysPayload, amountHex); + return m_logosAPI->getClient(LEZ_MODULE)->invokeRemoteMethod( + LEZ_MODULE, "transfer_private", + QVariantList{fromHex.trimmed(), keysPayload, amountHex}, + NO_TIMEOUT).toString(); } QString LEZWalletBackend::transferPrivateOwned(QString fromHex, QString toHex, QString amountStr) { const QString amountHex = amountToLe16Hex(amountStr); if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount."); - return m_logos->logos_execution_zone.transfer_private_owned(fromHex, toHex.trimmed(), amountHex); + return m_logosAPI->getClient(LEZ_MODULE)->invokeRemoteMethod( + LEZ_MODULE, "transfer_private_owned", + QVariantList{fromHex.trimmed(), toHex.trimmed(), amountHex}, + NO_TIMEOUT).toString(); } QString LEZWalletBackend::transferShielded(QString fromHex, QString toKeysJson, QString amountStr) @@ -250,21 +261,30 @@ QString LEZWalletBackend::transferShielded(QString fromHex, QString toKeysJson, keysPayload = resolved; } - return m_logos->logos_execution_zone.transfer_shielded(fromHex, keysPayload, amountHex); + return m_logosAPI->getClient(LEZ_MODULE)->invokeRemoteMethod( + LEZ_MODULE, "transfer_shielded", + QVariantList{fromHex.trimmed(), keysPayload, amountHex}, + NO_TIMEOUT).toString(); } QString LEZWalletBackend::transferShieldedOwned(QString fromHex, QString toHex, QString amountStr) { const QString amountHex = amountToLe16Hex(amountStr); if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount."); - return m_logos->logos_execution_zone.transfer_shielded_owned(fromHex, toHex.trimmed(), amountHex); + return m_logosAPI->getClient(LEZ_MODULE)->invokeRemoteMethod( + LEZ_MODULE, "transfer_shielded_owned", + QVariantList{fromHex.trimmed(), toHex.trimmed(), amountHex}, + NO_TIMEOUT).toString(); } QString LEZWalletBackend::transferDeshielded(QString fromHex, QString toHex, QString amountStr) { const QString amountHex = amountToLe16Hex(amountStr); if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount."); - return m_logos->logos_execution_zone.transfer_deshielded(fromHex.trimmed(), toHex.trimmed(), amountHex); + return m_logosAPI->getClient(LEZ_MODULE)->invokeRemoteMethod( + LEZ_MODULE, "transfer_deshielded", + QVariantList{fromHex.trimmed(), toHex.trimmed(), amountHex}, + NO_TIMEOUT).toString(); } bool LEZWalletBackend::createNew(QString configPath, QString storagePath, QString password) diff --git a/src/qml/ExecutionZoneWalletView.qml b/src/qml/ExecutionZoneWalletView.qml index 38fb435..de4f165 100644 --- a/src/qml/ExecutionZoneWalletView.qml +++ b/src/qml/ExecutionZoneWalletView.qml @@ -61,7 +61,6 @@ Rectangle { } // Parse a transfer result JSON string and write to dashboardView. - // Used by all three transfer handlers below. function applyTransferResult(dashboardView, raw) { var msg = raw || "" var isError = false @@ -168,45 +167,55 @@ Rectangle { } onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => { if (!backend) return + dashboardView.transferPending = true logos.watch(backend.transferPrivate(fromId, toKeysJsonOrAddress, amount), - function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) }, + function(raw) { dashboardView.transferPending = false; ffiErrors.applyTransferResult(dashboardView, raw) }, function(error) { + dashboardView.transferPending = false dashboardView.transferResult = qsTr("Error: %1").arg(error) dashboardView.transferResultIsError = true }) } onTransferPrivateOwnedRequested: (fromId, toAccountId, amount) => { if (!backend) return + dashboardView.transferPending = true logos.watch(backend.transferPrivateOwned(fromId, toAccountId, amount), - function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) }, + function(raw) { dashboardView.transferPending = false; ffiErrors.applyTransferResult(dashboardView, raw) }, function(error) { + dashboardView.transferPending = false dashboardView.transferResult = qsTr("Error: %1").arg(error) dashboardView.transferResultIsError = true }) } onTransferShieldedRequested: (fromId, toKeysJsonOrAddress, amount) => { if (!backend) return + dashboardView.transferPending = true logos.watch(backend.transferShielded(fromId, toKeysJsonOrAddress, amount), - function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) }, + function(raw) { dashboardView.transferPending = false; ffiErrors.applyTransferResult(dashboardView, raw) }, function(error) { + dashboardView.transferPending = false dashboardView.transferResult = qsTr("Error: %1").arg(error) dashboardView.transferResultIsError = true }) } onTransferShieldedOwnedRequested: (fromId, toAccountId, amount) => { if (!backend) return + dashboardView.transferPending = true logos.watch(backend.transferShieldedOwned(fromId, toAccountId, amount), - function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) }, + function(raw) { dashboardView.transferPending = false; ffiErrors.applyTransferResult(dashboardView, raw) }, function(error) { + dashboardView.transferPending = false dashboardView.transferResult = qsTr("Error: %1").arg(error) dashboardView.transferResultIsError = true }) } onTransferDeshieldedRequested: (fromId, toAccountId, amount) => { if (!backend) return + dashboardView.transferPending = true logos.watch(backend.transferDeshielded(fromId, toAccountId, amount), - function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) }, + function(raw) { dashboardView.transferPending = false; ffiErrors.applyTransferResult(dashboardView, raw) }, function(error) { + dashboardView.transferPending = false dashboardView.transferResult = qsTr("Error: %1").arg(error) dashboardView.transferResultIsError = true }) diff --git a/src/qml/views/DashboardView.qml b/src/qml/views/DashboardView.qml index 429ceb9..84ebb12 100644 --- a/src/qml/views/DashboardView.qml +++ b/src/qml/views/DashboardView.qml @@ -14,6 +14,7 @@ Rectangle { property var privateAccountModel: null property string transferResult: "" property bool transferResultIsError: false + property bool transferPending: false // --- Public API: output signals (parent connects and calls backend) --- signal createPublicAccountRequested() @@ -55,6 +56,7 @@ Rectangle { privateAccountModel: root.privateAccountModel transferResult: root.transferResult transferResultIsError: root.transferResultIsError + transferPending: root.transferPending onTransferPublicRequested: (fromId, toAddress, amount) => root.transferPublicRequested(fromId, toAddress, amount) onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => root.transferPrivateRequested(fromId, toKeysJsonOrAddress, amount) diff --git a/src/qml/views/TransferPanel.qml b/src/qml/views/TransferPanel.qml index 0c67eb9..77474da 100644 --- a/src/qml/views/TransferPanel.qml +++ b/src/qml/views/TransferPanel.qml @@ -15,6 +15,7 @@ Rectangle { property var privateAccountModel: null property string transferResult: "" property bool transferResultIsError: false + property bool transferPending: false // --- Public API: signals out (match backend: transfer_public, transfer_private, transfer_private_owned, transfer_shielded, transfer_shielded_owned) --- signal transferPublicRequested(string fromAccountId, string toAddress, string amount) @@ -40,7 +41,9 @@ Rectangle { readonly property bool toAddressValid: (toComboPermanent || (showOwnedOption && useOwnedAccountForTo)) ? (toFilterCount > 0 && toCombo.currentIndex >= 0) : (toField && toField.text.trim().length > 0) - readonly property bool sendEnabled: amountField && manualFromField + readonly property bool needsProof: isPrivateTab || isShieldedTab || isDeshieldedTab + readonly property bool sendEnabled: !root.transferPending + && amountField && manualFromField && amountField.text.length > 0 && d.toAddressValid && ((fromFilterCount > 0 && fromCombo.currentIndex >= 0) || (fromFilterCount === 0 && manualFromField.text.trim().length > 0)) @@ -203,9 +206,30 @@ Rectangle { } } + // Proof pending indicator + RowLayout { + Layout.fillWidth: true + visible: root.transferPending + spacing: Theme.spacing.small + + LogosSpinner { + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + running: root.transferPending + } + + LogosText { + Layout.fillWidth: true + text: qsTr("Generating proof, please wait…") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + } + // Result label RowLayout { Layout.fillWidth: true + visible: !root.transferPending LogosText { id: resultText Layout.fillWidth: true