logos-execution-zone-wallet-ui/src/qml/ExecutionZoneWalletView.qml

192 lines
7.5 KiB
QML
Raw Normal View History

2026-02-20 14:20:39 +01:00
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Logos.Theme
import Logos.Controls
2026-02-20 18:10:59 +01:00
import "views"
2026-02-20 14:20:39 +01:00
Rectangle {
id: root
2026-04-20 15:17:40 +02:00
readonly property var backend: logos.module("lez_wallet_ui")
readonly property var accountModel: logos.model("lez_wallet_ui", "accountModel")
property bool ready: false
Connections {
target: logos
function onViewModuleReadyChanged(moduleName, isReady) {
2026-04-20 15:17:40 +02:00
if (moduleName === "lez_wallet_ui")
root.ready = isReady && root.backend !== null
}
}
// Map wallet FFI error codes to user-facing strings. Matches lssa/wallet-ffi WalletFfiError enum.
QtObject {
id: ffiErrors
readonly property var codeToMessage: ({
0: qsTr("Success"),
1: qsTr("Invalid argument (null pointer)"),
2: qsTr("Invalid UTF-8 string"),
3: qsTr("Wallet not initialized"),
4: qsTr("Configuration error"),
5: qsTr("Storage or persistence error"),
6: qsTr("Network or RPC error"),
7: qsTr("Account not found"),
8: qsTr("Key not found for account"),
9: qsTr("Insufficient funds"),
10: qsTr("Invalid account ID format"),
11: qsTr("Runtime error"),
12: qsTr("Password required but not provided"),
13: qsTr("Block synchronization error"),
14: qsTr("Serialization error"),
15: qsTr("Invalid type conversion"),
16: qsTr("Invalid key value"),
99: qsTr("Internal error")
})
function format(errorMessage) {
if (!errorMessage || typeof errorMessage !== "string")
return errorMessage || ""
var match = errorMessage.match(/wallet FFI error (\d+)/)
if (match) {
var code = match[1]
var msg = codeToMessage[code]
if (msg)
return msg
return qsTr("Wallet error (code %1)").arg(code)
}
return errorMessage
}
// 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
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
}
}
QtObject {
id: d
readonly property bool isWalletOpen: backend && backend.isWalletOpen
onIsWalletOpenChanged: if (root.ready) updateStack(isWalletOpen)
2026-02-26 22:23:19 +01:00
function updateStack(walletOpen) {
stackView.replace(walletOpen ? mainView : onboardingView)
}
}
onReadyChanged: if (ready) d.updateStack(d.isWalletOpen)
Component.onCompleted: {
root.ready = root.backend !== null
2026-04-20 15:17:40 +02:00
&& logos.isViewModuleReady("lez_wallet_ui")
if (root.ready) d.updateStack(d.isWalletOpen)
}
2026-02-26 22:23:19 +01:00
2026-02-20 14:20:39 +01:00
color: Theme.palette.background
// Used as a clipboard helper — TextEdit.copy() works in the GUI process.
TextEdit {
id: clipHelper
visible: false
}
2026-02-20 18:10:59 +01:00
StackView {
id: stackView
2026-02-20 14:20:39 +01:00
anchors.fill: parent
2026-02-20 18:10:59 +01:00
Component {
id: onboardingView
OnboardingView {
storePath: backend ? backend.storagePath : ""
configPath: backend ? backend.configPath : ""
2026-02-20 18:10:59 +01:00
onCreateWallet: function(configPath, storagePath, password) {
if (!backend) return
logos.watch(backend.createNew(configPath, storagePath, password),
function(ok) {
if (!ok)
createError = qsTr("Failed to create wallet. Check paths and try again.")
},
function(error) {
createError = qsTr("Error creating wallet: %1").arg(error)
}
)
2026-02-20 14:20:39 +01:00
}
}
}
2026-02-20 18:10:59 +01:00
Component {
id: mainView
2026-02-22 02:03:13 +05:30
DashboardView {
id: dashboardView
accountModel: root.accountModel
2026-02-22 02:03:13 +05:30
onCreatePublicAccountRequested: {
if (!backend) { console.warn("backend is null"); return }
// Result not consumed here — accountModel updates via NOTIFY when
// the backend's refreshAccounts() runs after creation.
logos.watch(backend.createAccountPublic(),
function(_id) { /* ignored */ },
function(error) { console.warn("createAccountPublic failed:", error) })
2026-02-22 02:03:13 +05:30
}
onCreatePrivateAccountRequested: {
if (!backend) { console.warn("backend is null"); return }
logos.watch(backend.createAccountPrivate(),
function(_id) { /* ignored */ },
function(error) { console.warn("createAccountPrivate failed:", error) })
2026-02-22 02:03:13 +05:30
}
onFetchBalancesRequested: {
if (!backend) { console.warn("backend is null"); return }
backend.refreshBalances() // void slot, fire-and-forget
2026-02-22 02:03:13 +05:30
}
2026-03-07 16:17:57 +00:00
onTransferPublicRequested: (fromId, toAddress, amount) => {
if (!backend) return
logos.watch(backend.transferPublic(fromId, toAddress, amount),
function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) },
function(error) {
dashboardView.transferResult = qsTr("Error: %1").arg(error)
dashboardView.transferResultIsError = true
})
2026-03-07 16:17:57 +00:00
}
onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => {
if (!backend) return
logos.watch(backend.transferPrivate(fromId, toKeysJsonOrAddress, amount),
function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) },
function(error) {
dashboardView.transferResult = qsTr("Error: %1").arg(error)
dashboardView.transferResultIsError = true
})
2026-03-07 16:17:57 +00:00
}
onTransferPrivateOwnedRequested: (fromId, toAccountId, amount) => {
if (!backend) return
logos.watch(backend.transferPrivateOwned(fromId, toAccountId, amount),
function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) },
function(error) {
dashboardView.transferResult = qsTr("Error: %1").arg(error)
dashboardView.transferResultIsError = true
})
}
onCopyRequested: (copyText) => {
clipHelper.text = copyText
clipHelper.selectAll()
clipHelper.copy()
2026-02-20 14:20:39 +01:00
}
}
}
}
}