From 5f67d10221da773861c5d281e75d89ada06ed47c Mon Sep 17 00:00:00 2001 From: Khushboo Mehta Date: Sat, 7 Mar 2026 16:17:57 +0000 Subject: [PATCH] fix: transfer_private_owned --- src/LEZWalletBackend.cpp | 14 ++++ src/LEZWalletBackend.h | 4 + src/qml/ExecutionZoneWalletView.qml | 50 +++++++++-- src/qml/controls/AccountComboBox.qml | 81 ++++++++++++++++++ src/qml/views/DashboardView.qml | 10 ++- src/qml/views/TransferPanel.qml | 119 ++++++++++----------------- src/wallet_resources.qrc | 1 + 7 files changed, 189 insertions(+), 90 deletions(-) create mode 100644 src/qml/controls/AccountComboBox.qml diff --git a/src/LEZWalletBackend.cpp b/src/LEZWalletBackend.cpp index a14e4b0..5dfaad7 100644 --- a/src/LEZWalletBackend.cpp +++ b/src/LEZWalletBackend.cpp @@ -146,6 +146,7 @@ void LEZWalletBackend::refreshBalances() const bool isPub = m_accountModel->data(idx, LEZWalletAccountModel::IsPublicRole).toBool(); m_accountModel->setBalanceByAddress(addr, getBalance(addr, isPub)); } + saveWallet(); } void LEZWalletBackend::fetchAndUpdateBlockHeights() @@ -272,6 +273,19 @@ QString LEZWalletBackend::transferPrivate( return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed."); } +QString LEZWalletBackend::transferPrivateOwned( + const QString& fromHex, + const QString& toHex, + const QString& amountLe16Hex) +{ + if (!m_walletClient) return QStringLiteral("Error: Module not initialized."); + const QString amountHex = amountToLe16Hex(amountLe16Hex); + if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount."); + QVariant result = m_walletClient->invokeRemoteMethod( + WALLET_MODULE_NAME, "transfer_private_owned", fromHex, toHex.trimmed(), amountHex); + return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed."); +} + bool LEZWalletBackend::createNew( const QString& configPath, const QString& storagePath, diff --git a/src/LEZWalletBackend.h b/src/LEZWalletBackend.h index d4e2cb5..82a880a 100644 --- a/src/LEZWalletBackend.h +++ b/src/LEZWalletBackend.h @@ -53,6 +53,10 @@ public: const QString& fromHex, const QString& toHex, const QString& amountLe16Hex); + Q_INVOKABLE QString transferPrivateOwned( + const QString& fromHex, + const QString& toHex, + const QString& amountLe16Hex); Q_INVOKABLE bool createNew( const QString& configPath, const QString& storagePath, diff --git a/src/qml/ExecutionZoneWalletView.qml b/src/qml/ExecutionZoneWalletView.qml index 4f90607..2808ee1 100644 --- a/src/qml/ExecutionZoneWalletView.qml +++ b/src/qml/ExecutionZoneWalletView.qml @@ -109,14 +109,9 @@ Rectangle { } backend.refreshBalances() } - onTransferRequested: function(isPublic, fromId, toAddress, amount) { - if (!backend) { - console.warning("backend is null") - return - } - var raw = isPublic - ? backend.transferPublic(fromId, toAddress, amount) - : backend.transferPrivate(fromId, toAddress, amount) + onTransferPublicRequested: (fromId, toAddress, amount) => { + if (!backend) return + var raw = backend.transferPublic(fromId, toAddress, amount) var msg = raw || "" var isError = false try { @@ -128,8 +123,45 @@ Rectangle { isError = true } } catch (e) { - if (msg.length > 0) + if (msg.length > 0) isError = true + } + dashboardView.transferResult = msg + dashboardView.transferResultIsError = isError + } + onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => { + if (!backend) return + var raw = backend.transferPrivate(fromId, toKeysJsonOrAddress, amount) + var msg = raw || "" + var isError = false + try { + var obj = JSON.parse(raw) + if (obj.success) { + msg = obj.tx_hash ? qsTr("Success. Tx: %1").arg(obj.tx_hash) : qsTr("Success.") + } else if (obj.error) { + msg = ffiErrors.format(obj.error) isError = true + } + } catch (e) { + if (msg.length > 0) isError = true + } + dashboardView.transferResult = msg + dashboardView.transferResultIsError = isError + } + onTransferPrivateOwnedRequested: (fromId, toAccountId, amount) => { + if (!backend) return + var raw = backend.transferPrivateOwned(fromId, toAccountId, amount) + var msg = raw || "" + var isError = false + try { + var obj = JSON.parse(raw) + if (obj.success) { + msg = obj.tx_hash ? qsTr("Success. Tx: %1").arg(obj.tx_hash) : qsTr("Success.") + } else if (obj.error) { + msg = ffiErrors.format(obj.error) + isError = true + } + } catch (e) { + if (msg.length > 0) isError = true } dashboardView.transferResult = msg dashboardView.transferResultIsError = isError diff --git a/src/qml/controls/AccountComboBox.qml b/src/qml/controls/AccountComboBox.qml new file mode 100644 index 0000000..932fa58 --- /dev/null +++ b/src/qml/controls/AccountComboBox.qml @@ -0,0 +1,81 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Logos.Theme +import Logos.Controls + +ComboBox { + id: root + + leftPadding: 12 + rightPadding: 12 + implicitHeight: 40 + textRole: "name" + valueRole: "address" + + background: Rectangle { + radius: Theme.spacing.radiusSmall + color: Theme.palette.backgroundSecondary + border.width: 1 + border.color: root.popup.visible ? Theme.palette.overlayOrange : Theme.palette.backgroundElevated + } + + indicator: LogosText { + id: indicatorText + text: "▼" + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + x: root.width - width - 12 + y: (root.height - height) / 2 + visible: root.count > 0 + } + + contentItem: Item { + implicitWidth: 120 + width: root.width - indicatorText.width - 12 + TextInput { + id: comboContentInput + anchors.fill: parent + readOnly: true + selectByMouse: true + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.text + text: root.displayText + verticalAlignment: Text.AlignVCenter + clip: true + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: root.popup.visible ? root.popup.close() : root.popup.open() + } + } + + delegate: AccountDelegate { + width: root.popup ? (root.popup.width - root.popup.leftPadding - root.popup.rightPadding) : 368 + highlighted: root.highlightedIndex === index + } + + popup: Popup { + y: root.height - 1 + width: 400 + height: Math.min(contentItem.implicitHeight + 8, 300) + padding: Theme.spacing.small + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: root.popup.visible ? root.delegateModel : null + ScrollIndicator.vertical: ScrollIndicator { } + highlightFollowsCurrentItem: false + } + + background: Rectangle { + color: Theme.palette.backgroundTertiary + border.width: 1 + border.color: Theme.palette.backgroundElevated + radius: Theme.spacing.radiusSmall + } + } +} diff --git a/src/qml/views/DashboardView.qml b/src/qml/views/DashboardView.qml index a7140ec..74e288b 100644 --- a/src/qml/views/DashboardView.qml +++ b/src/qml/views/DashboardView.qml @@ -17,7 +17,9 @@ Rectangle { signal createPublicAccountRequested() signal createPrivateAccountRequested() signal fetchBalancesRequested() - signal transferRequested(bool isPublic, string fromAccountId, string toAddress, string amount) + signal transferPublicRequested(string fromAccountId, string toAddress, string amount) + signal transferPrivateRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) + signal transferPrivateOwnedRequested(string fromAccountId, string toAccountId, string amount) signal copyRequested(string copyText) color: Theme.palette.background @@ -47,9 +49,9 @@ Rectangle { transferResult: root.transferResult transferResultIsError: root.transferResultIsError - onTransferRequested: function(isPublic, fromId, toAddress, amount) { - root.transferRequested(isPublic, fromId, toAddress, amount) - } + onTransferPublicRequested: (fromId, toAddress, amount) => root.transferPublicRequested(fromId, toAddress, amount) + onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => root.transferPrivateRequested(fromId, toKeysJsonOrAddress, amount) + onTransferPrivateOwnedRequested: (fromId, toAccountId, amount) => root.transferPrivateOwnedRequested(fromId, toAccountId, amount) onCopyRequested: (copyText) => root.copyRequested(copyText) } } diff --git a/src/qml/views/TransferPanel.qml b/src/qml/views/TransferPanel.qml index 6591d51..5f1561d 100644 --- a/src/qml/views/TransferPanel.qml +++ b/src/qml/views/TransferPanel.qml @@ -15,16 +15,23 @@ Rectangle { property string transferResult: "" property bool transferResultIsError: false - // --- Public API: signals out --- - signal transferRequested(bool isPublic, string fromAccountId, string toAddress, string amount) + // --- Public API: signals out (match backend: transfer_public, transfer_private, transfer_private_owned) --- + signal transferPublicRequested(string fromAccountId, string toAddress, string amount) + signal transferPrivateRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) + signal transferPrivateOwnedRequested(string fromAccountId, string toAccountId, string amount) signal copyRequested(string copyText) readonly property int fromFilterCount: fromAccountModel ? fromAccountModel.count : 0 QtObject { id: d - readonly property bool sendEnabled: toField && amountField && manualFromField - && toField.text.length > 0 && amountField.text.length > 0 + property bool useOwnedAccountForTo: false + readonly property bool isPrivateTab: transferTypeBar.currentIndex === 1 + readonly property bool toAddressValid: isPrivateTab && useOwnedAccountForTo + ? (fromFilterCount > 0 && toCombo.currentIndex >= 0) + : (toField && toField.text.trim().length > 0) + readonly property bool sendEnabled: amountField && manualFromField + && amountField.text.length > 0 && d.toAddressValid && ((fromFilterCount > 0 && fromCombo.currentIndex >= 0) || (fromFilterCount === 0 && manualFromField.text.trim().length > 0)) } @@ -82,81 +89,11 @@ Rectangle { visible: fromFilterCount === 0 } - ComboBox { + AccountComboBox { id: fromCombo Layout.fillWidth: true - leftPadding: 12 - rightPadding: 12 - implicitHeight: 40 model: fromAccountModel - textRole: "name" - valueRole: "address" visible: fromFilterCount > 0 - - background: Rectangle { - radius: Theme.spacing.radiusSmall - color: Theme.palette.backgroundSecondary - border.width: 1 - border.color: fromCombo.popup.visible ? Theme.palette.overlayOrange : Theme.palette.backgroundElevated - } - - indicator: LogosText { - id: indicatorText - text: "▼" - font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.textSecondary - x: fromCombo.width - width - 12 - y: (fromCombo.height - height) / 2 - visible: fromCombo.count > 0 - } - - contentItem: Item { - implicitWidth: 120 - width: fromCombo.width - indicatorText.width - 12 - TextInput { - id: fromComboContentInput - anchors.fill: parent - readOnly: true - selectByMouse: true - font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.text - text: fromCombo.displayText - verticalAlignment: Text.AlignVCenter - clip: true - } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: fromCombo.popup.visible ? fromCombo.popup.close() : fromCombo.popup.open() - } - } - - delegate: AccountDelegate { - width: fromCombo.popup.width - fromCombo.popup.leftPadding - fromCombo.popup.rightPadding - highlighted: fromCombo.highlightedIndex === index - } - - popup: Popup { - y: fromCombo.height - 1 - width: 400 - height: Math.min(contentItem.implicitHeight + 8, 300) - padding: Theme.spacing.small - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: fromCombo.popup.visible ? fromCombo.delegateModel : null - ScrollIndicator.vertical: ScrollIndicator { } - highlightFollowsCurrentItem: false - } - - background: Rectangle { - color: Theme.palette.backgroundTertiary - border.width: 1 - border.color: Theme.palette.backgroundElevated - radius: Theme.spacing.radiusSmall - } - } } } @@ -171,10 +108,28 @@ Rectangle { color: Theme.palette.textSecondary } + CheckBox { + id: useOwnedToCheck + visible: d.isPrivateTab + checked: d.useOwnedAccountForTo + onCheckedChanged: d.useOwnedAccountForTo = checked + text: qsTr("Use owned account") + font.pixelSize: Theme.typography.secondaryText + palette.text: Theme.palette.text + } + LogosTextField { id: toField Layout.fillWidth: true placeholderText: qsTr("Recipient public key") + visible: !d.isPrivateTab || !d.useOwnedAccountForTo + } + + AccountComboBox { + id: toCombo + Layout.fillWidth: true + model: fromAccountModel + visible: d.isPrivateTab && d.useOwnedAccountForTo && fromFilterCount > 0 } } @@ -206,8 +161,18 @@ Rectangle { var fromId = fromFilterCount > 0 && fromCombo.currentIndex >= 0 ? (fromCombo.currentValue ?? "") : manualFromField.text.trim() - if (fromId.length > 0) - root.transferRequested(transferTypeBar.currentIndex === 0, fromId, toField.text.trim(), amountField.text.trim()) + var toAddress = d.useOwnedAccountForTo && toCombo.currentIndex >= 0 + ? (toCombo.currentValue ?? "") + : toField.text.trim() + var amount = amountField.text.trim() + if (fromId.length > 0 && toAddress.length > 0 && amount.length > 0) { + if (transferTypeBar.currentIndex === 0) + root.transferPublicRequested(fromId, toAddress, amount) + else if (d.useOwnedAccountForTo) + root.transferPrivateOwnedRequested(fromId, toAddress, amount) + else + root.transferPrivateRequested(fromId, toAddress, amount) + } } } diff --git a/src/wallet_resources.qrc b/src/wallet_resources.qrc index 273a1de..e0e1086 100644 --- a/src/wallet_resources.qrc +++ b/src/wallet_resources.qrc @@ -1,6 +1,7 @@ qml/ExecutionZoneWalletView.qml + qml/controls/AccountComboBox.qml qml/controls/AccountDelegate.qml qml/popups/CreateAccountDialog.qml qml/views/qmldir