mirror of
https://github.com/logos-blockchain/logos-execution-zone-wallet-ui.git
synced 2026-02-27 19:53:10 +00:00
feat: update some flows and fix UI issues + polish
This commit is contained in:
parent
c24cca889f
commit
0a82f61a93
33
flake.lock
generated
33
flake.lock
generated
@ -528,11 +528,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1771838299,
|
||||
"narHash": "sha256-Uf45wbh2q5ewoiw4u04YImc2Gij3OXIfbB5NYpUm5dw=",
|
||||
"lastModified": 1771862755,
|
||||
"narHash": "sha256-uRQztNMMLhHnQw0P2ZisB1LZ1V7pNqkZRI3pe0wUIts=",
|
||||
"owner": "logos-co",
|
||||
"repo": "logos-design-system",
|
||||
"rev": "fc6f52d85a008aa1bb513f6b42648df4bcf0713d",
|
||||
"rev": "4d55203607759c9ef042be4505bbc6efffbbd444",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -548,11 +548,11 @@
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1771420202,
|
||||
"narHash": "sha256-r3lO12wvYPm6Xe7YFGK40iYe+1OID5YlGlup6RGgvs8=",
|
||||
"lastModified": 1771604479,
|
||||
"narHash": "sha256-DxgL4uT8+F0ALuEpENkbJ0OcyNhdFgX8Mr8tezMnZ5E=",
|
||||
"owner": "logos-blockchain",
|
||||
"repo": "lssa",
|
||||
"rev": "89ce9f322a1fc4214ec818e094e6efc97be02d9c",
|
||||
"rev": "bc84d5cf31b3f6967060c35a5b98618d99780ade",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -574,14 +574,17 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1771839856,
|
||||
"narHash": "sha256-MtgK/B6mx9xyojbRc6RY8vHuMkMrS4XMV7BKcO6zK/I=",
|
||||
"path": "/Users/khushboomehta/Documents/logos/logos-execution-zone-module",
|
||||
"type": "path"
|
||||
"lastModified": 1771922567,
|
||||
"narHash": "sha256-ObbXljdS/hR0PCgBosZgzwMHWRxvSGWmaxgFEy3OvOE=",
|
||||
"owner": "logos-blockchain",
|
||||
"repo": "logos-execution-zone-module",
|
||||
"rev": "8474602359b69f56cfad232b052b52ae2701f74c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"path": "/Users/khushboomehta/Documents/logos/logos-execution-zone-module",
|
||||
"type": "path"
|
||||
"owner": "logos-blockchain",
|
||||
"repo": "logos-execution-zone-module",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"logos-liblogos": {
|
||||
@ -778,11 +781,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770837874,
|
||||
"narHash": "sha256-wr75lv1q4U1FS5+l/6ypwzJFJe06l2RyUvx1npoRS88=",
|
||||
"lastModified": 1771871578,
|
||||
"narHash": "sha256-6Mu3cmdhd8e7i+n8OWcaIBye+i12gwlwt1fhd9QCbCI=",
|
||||
"owner": "logos-co",
|
||||
"repo": "logos-liblogos",
|
||||
"rev": "e3741c01fd3abf6b7bd9ff2fa8edf89c41fc0cea",
|
||||
"rev": "19d29d4ef99292d9285b3a561cb7ea8029be3b74",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include "LEZWalletBackend.h"
|
||||
#include <QAbstractItemModel>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QJsonArray>
|
||||
#include <QSettings>
|
||||
#include <QUrl>
|
||||
@ -12,6 +14,19 @@ namespace {
|
||||
const char STORAGE_PATH_KEY[] = "storagePath";
|
||||
const QString WALLET_MODULE_NAME = QStringLiteral("liblogos_execution_zone_wallet_module");
|
||||
const int WALLET_FFI_SUCCESS = 0;
|
||||
|
||||
// Convert decimal amount string to 32-char hex (16 bytes little-endian) for transfer_public/transfer_private.
|
||||
QString amountToLe16Hex(const QString& amountStr) {
|
||||
const QString trimmed = amountStr.trimmed();
|
||||
if (trimmed.isEmpty()) return QString();
|
||||
bool parseOk = false;
|
||||
const quint64 value = trimmed.toULongLong(&parseOk);
|
||||
if (!parseOk) return QString();
|
||||
uint8_t bytes[16] = {0};
|
||||
for (int i = 0; i < 8; ++i)
|
||||
bytes[i] = static_cast<uint8_t>((value >> (i * 8)) & 0xff);
|
||||
return QByteArray(reinterpret_cast<const char*>(bytes), 16).toHex();
|
||||
}
|
||||
}
|
||||
|
||||
LEZWalletBackend::LEZWalletBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
@ -42,16 +57,21 @@ LEZWalletBackend::LEZWalletBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
}
|
||||
|
||||
if (!m_configPath.isEmpty() && !m_storagePath.isEmpty()) {
|
||||
qDebug() << "LEZWalletBackend: opening wallet with config path" << m_configPath << "and storage path" << m_storagePath;
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "open", m_configPath, m_storagePath);
|
||||
int err = result.isValid() ? result.toInt() : -1;
|
||||
if (err == WALLET_FFI_SUCCESS) {
|
||||
qWarning() << "LEZWalletBackend: wallet opened successfully";
|
||||
setWalletOpen(true);
|
||||
refreshAccounts();
|
||||
refreshBlockHeights();
|
||||
refreshSequencerAddr();
|
||||
}
|
||||
}
|
||||
|
||||
// Save wallet when app quits; host may not call destroyWidget() so destructor might not run.
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, this, [this]() { saveWallet(); }, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
LEZWalletBackend::~LEZWalletBackend()
|
||||
@ -112,6 +132,7 @@ void LEZWalletBackend::refreshAccounts()
|
||||
}
|
||||
m_accountModel->replaceFromJsonArray(arr);
|
||||
emit accountModelChanged();
|
||||
refreshBalances();
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshBalances()
|
||||
@ -125,13 +146,11 @@ void LEZWalletBackend::refreshBalances()
|
||||
}
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshBlockHeights()
|
||||
void LEZWalletBackend::fetchAndUpdateBlockHeights()
|
||||
{
|
||||
if (!m_walletClient) return;
|
||||
QVariant last = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_last_synced_block");
|
||||
QVariant current = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_current_block_height");
|
||||
quint64 lastVal = last.isValid() ? last.toULongLong() : 0;
|
||||
quint64 currentVal = current.isValid() ? current.toULongLong() : 0;
|
||||
const quint64 lastVal = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_last_synced_block").toULongLong();
|
||||
const quint64 currentVal = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_current_block_height").toULongLong();
|
||||
if (m_lastSyncedBlock != lastVal) {
|
||||
m_lastSyncedBlock = lastVal;
|
||||
emit lastSyncedBlockChanged();
|
||||
@ -142,6 +161,13 @@ void LEZWalletBackend::refreshBlockHeights()
|
||||
}
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshBlockHeights()
|
||||
{
|
||||
fetchAndUpdateBlockHeights();
|
||||
if (m_currentBlockHeight > 0 && m_lastSyncedBlock < m_currentBlockHeight && syncToBlock(m_currentBlockHeight))
|
||||
fetchAndUpdateBlockHeights();
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshSequencerAddr()
|
||||
{
|
||||
if (!m_walletClient) return;
|
||||
@ -205,11 +231,7 @@ bool LEZWalletBackend::syncToBlock(quint64 blockId)
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "sync_to_block", blockId);
|
||||
int err = result.isValid() ? result.toInt() : -1;
|
||||
if (err == WALLET_FFI_SUCCESS) {
|
||||
refreshBlockHeights();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return err == WALLET_FFI_SUCCESS;
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::transferPublic(
|
||||
@ -218,19 +240,33 @@ QString LEZWalletBackend::transferPublic(
|
||||
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_public", fromHex, toHex, amountLe16Hex);
|
||||
WALLET_MODULE_NAME, "transfer_public", fromHex, toHex, amountHex);
|
||||
return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed.");
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::transferPrivate(
|
||||
const QString& fromHex,
|
||||
const QString& toKeysJson,
|
||||
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.");
|
||||
|
||||
QString keysPayload = toHex.trimmed();
|
||||
// If "To" is not JSON (e.g. user pasted account id hex), resolve to keys via get_private_account_keys.
|
||||
if (!keysPayload.startsWith(QLatin1Char('{'))) {
|
||||
qDebug() << "LEZWalletBackend::transferPrivate: keysPayload is not JSON, resolving to keys via get_private_account_keys";
|
||||
const QString resolved = getPrivateAccountKeys(keysPayload);
|
||||
if (!resolved.isEmpty())
|
||||
keysPayload = resolved;
|
||||
}
|
||||
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "transfer_private", fromHex, toKeysJson, amountLe16Hex);
|
||||
WALLET_MODULE_NAME, "transfer_private", fromHex, keysPayload, amountHex);
|
||||
return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed.");
|
||||
}
|
||||
|
||||
@ -269,3 +305,9 @@ int LEZWalletBackend::indexOfAddressInModel(QObject* model, const QString& addre
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void LEZWalletBackend::copyToClipboard(const QString& text)
|
||||
{
|
||||
if (QGuiApplication::clipboard())
|
||||
QGuiApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
@ -51,13 +51,14 @@ public:
|
||||
const QString& amountLe16Hex);
|
||||
Q_INVOKABLE QString transferPrivate(
|
||||
const QString& fromHex,
|
||||
const QString& toKeysJson,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex);
|
||||
Q_INVOKABLE bool createNew(
|
||||
const QString& configPath,
|
||||
const QString& storagePath,
|
||||
const QString& password);
|
||||
Q_INVOKABLE int indexOfAddressInModel(QObject* model, const QString& address) const;
|
||||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||
|
||||
signals:
|
||||
void isWalletOpenChanged();
|
||||
@ -74,6 +75,7 @@ private:
|
||||
void refreshBlockHeights();
|
||||
void refreshSequencerAddr();
|
||||
void saveWallet();
|
||||
void fetchAndUpdateBlockHeights();
|
||||
|
||||
bool m_isWalletOpen;
|
||||
QString m_configPath;
|
||||
|
||||
@ -22,8 +22,8 @@ QWidget* LEZWalletPlugin::createWidget(LogosAPI* logosAPI) {
|
||||
LEZWalletBackend* backend = new LEZWalletBackend(logosAPI, quickWidget);
|
||||
quickWidget->rootContext()->setContextProperty("backend", backend);
|
||||
|
||||
QString qmlSource = "qrc:/qml/ExecutionZoneWalletView.qml";
|
||||
QString importPath = "qrc:/qml";
|
||||
QString qmlSource = "qrc:/lezwallet/qml/ExecutionZoneWalletView.qml";
|
||||
QString importPath = "qrc:/lezwallet/qml";
|
||||
|
||||
QString envPath = QString::fromUtf8(qgetenv("DEV_QML_PATH")).trimmed();
|
||||
if (!envPath.isEmpty()) {
|
||||
|
||||
@ -10,15 +10,67 @@ import "views"
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property bool isWalletOpen: backend && backend.isWalletOpen
|
||||
onIsWalletOpenChanged: {
|
||||
if(isWalletOpen) {
|
||||
stackView.push(mainView)
|
||||
} else {
|
||||
stackView.push(onboardingView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: Theme.palette.background
|
||||
|
||||
StackView {
|
||||
id: stackView
|
||||
anchors.fill: parent
|
||||
initialItem: backend && backend.isWalletOpen ? mainView: onboardingView
|
||||
|
||||
Component {
|
||||
id: onboardingView
|
||||
OnboardingView {
|
||||
storePath: backend.storagePath
|
||||
configPath: backend.configPath
|
||||
onCreateWallet: function(configPath, storagePath, password) {
|
||||
if (!backend || !backend.createNew(configPath, storagePath, password))
|
||||
createError = qsTr("Failed to create wallet. Check paths and try again.")
|
||||
@ -31,7 +83,6 @@ Rectangle {
|
||||
DashboardView {
|
||||
id: dashboardView
|
||||
accountModel: backend ? backend.accountModel : null
|
||||
filteredAccountModel: backend ? backend.filteredAccountModel : null
|
||||
|
||||
onCreatePublicAccountRequested: {
|
||||
if (!backend) {
|
||||
@ -59,9 +110,25 @@ Rectangle {
|
||||
console.warning("backend is null")
|
||||
return
|
||||
}
|
||||
dashboardView.transferResult = isPublic
|
||||
var raw = isPublic
|
||||
? backend.transferPublic(fromId, toAddress, amount)
|
||||
: backend.transferPrivate(fromId, toAddress, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,47 +8,69 @@ import Logos.Controls
|
||||
ItemDelegate {
|
||||
id: root
|
||||
|
||||
implicitHeight: 80
|
||||
leftPadding: Theme.spacing.medium
|
||||
rightPadding: Theme.spacing.medium
|
||||
topPadding: Theme.spacing.medium
|
||||
bottomPadding: Theme.spacing.medium
|
||||
|
||||
background: Rectangle {
|
||||
color: root.highlighted ? Theme.palette.backgroundMuted : "transparent"
|
||||
radius: Theme.spacing.radiusSmall
|
||||
color: root.highlighted || root.hovered ?
|
||||
Theme.palette.backgroundMuted :
|
||||
Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
contentItem: ColumnLayout {
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: model.name
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: tagLabel.implicitWidth + Theme.spacing.small * 2
|
||||
Layout.preferredHeight: tagLabel.implicitHeight + 4
|
||||
radius: 2
|
||||
color: model.isPublic ? Theme.palette.backgroundElevated : Theme.palette.backgroundSecondary
|
||||
RowLayout {
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
id: tagLabel
|
||||
anchors.centerIn: parent
|
||||
text: model.isPublic ? qsTr("Public") : qsTr("Private")
|
||||
font.pixelSize: Theme.typography.captionText
|
||||
color: Theme.palette.textSecondary
|
||||
text: model.name
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: tagLabel.implicitWidth + Theme.spacing.small * 2
|
||||
Layout.preferredHeight: tagLabel.implicitHeight + 4
|
||||
radius: 4
|
||||
color: Theme.palette.backgroundSecondary
|
||||
|
||||
LogosText {
|
||||
id: tagLabel
|
||||
anchors.centerIn: parent
|
||||
text: model.isPublic ? qsTr("Public") : qsTr("Private")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
LogosText {
|
||||
text: model.balance && model.balance.length > 0 ? model.balance : "—"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
LogosText {
|
||||
text: model.balance && model.balance.length > 0 ? model.balance : "—"
|
||||
id: addressLabel
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: model.address && model.address.length > 9
|
||||
? model.address.slice(0, 4) + "…" + model.address.slice(-5)
|
||||
: (model.address || "")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
color: Theme.palette.textMuted
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onDoubleClicked: {
|
||||
if (model.address && typeof backend !== "undefined")
|
||||
backend.copyToClipboard(model.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,11 +16,7 @@ Popup {
|
||||
padding: Theme.spacing.large
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
// Center in overlay (main window when modal)
|
||||
parent: Overlay.overlay
|
||||
anchors.centerIn: parent
|
||||
// width: contentWrapper.width + leftPadding + rightPadding
|
||||
// height: contentWrapper.height + topPadding + bottomPadding
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
|
||||
@ -8,12 +8,10 @@ import Logos.Controls
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: Theme.palette.background
|
||||
|
||||
// --- Public API: input properties (set by parent / MainView) ---
|
||||
property var accountModel: null
|
||||
property var filteredAccountModel: null
|
||||
property string transferResult: ""
|
||||
property bool transferResultIsError: false
|
||||
|
||||
// --- Public API: output signals (parent connects and calls backend) ---
|
||||
signal createPublicAccountRequested()
|
||||
@ -21,6 +19,8 @@ Rectangle {
|
||||
signal fetchBalancesRequested()
|
||||
signal transferRequested(bool isPublic, string fromAccountId, string toAddress, string amount)
|
||||
|
||||
color: Theme.palette.background
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.xlarge
|
||||
@ -42,9 +42,9 @@ Rectangle {
|
||||
id: transferPanel
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
fromAccountModel: root.filteredAccountModel
|
||||
fromAccountModel: root.accountModel
|
||||
transferResult: root.transferResult
|
||||
transferResultIsError: root.transferResultIsError
|
||||
|
||||
onTransferRequested: function(isPublic, fromId, toAddress, amount) {
|
||||
root.transferRequested(isPublic, fromId, toAddress, amount)
|
||||
|
||||
@ -9,11 +9,25 @@ import Logos.Controls
|
||||
Control {
|
||||
id: root
|
||||
|
||||
property string configPath: ""
|
||||
property string storePath: ""
|
||||
property string createError: ""
|
||||
|
||||
signal createWallet(string configPath, string storagePath, string password)
|
||||
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
function configParentFolderUrl(path) {
|
||||
if (!path || path.length === 0) return ""
|
||||
var p = path
|
||||
var i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"))
|
||||
if (i <= 0) return ""
|
||||
var dir = p.substring(0, i)
|
||||
return dir.indexOf("file://") === 0 ? dir : "file://" + dir
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: cardColumn
|
||||
|
||||
@ -46,7 +60,8 @@ Control {
|
||||
LogosTextField {
|
||||
id: storagePathField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("/Users/you/.lez-wallet/")
|
||||
placeholderText: qsTr("Add store path")
|
||||
text: root.storePath
|
||||
}
|
||||
LogosButton {
|
||||
text: qsTr("Browse")
|
||||
@ -66,6 +81,7 @@ Control {
|
||||
id: configPathField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Add path to config")
|
||||
text: root.configPath
|
||||
}
|
||||
LogosButton {
|
||||
Layout.preferredHeight: configPathField.height
|
||||
@ -121,16 +137,19 @@ Control {
|
||||
}
|
||||
}
|
||||
|
||||
FolderDialog {
|
||||
FileDialog {
|
||||
id: storageFolderDialog
|
||||
modality: Qt.NonModal
|
||||
onAccepted: storagePathField.text = selectedFolder.toString().replace(/^file:\/\//, "")
|
||||
nameFilters: ["JSON files (*.json)"]
|
||||
currentFolder: root.storePath ? d.configParentFolderUrl(root.storePath) : ""
|
||||
onAccepted: storagePathField.text = selectedFile.toString().replace(/^file:\/\//, "")
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: configFileDialog
|
||||
modality: Qt.NonModal
|
||||
nameFilters: ["YAML files (*.yaml)"]
|
||||
nameFilters: ["JSON files (*.json)"]
|
||||
currentFolder: root.configPath ? d.configParentFolderUrl(oot.configPath) : ""
|
||||
onAccepted: {
|
||||
if (selectedFile) configPathField.text = selectedFile.toString().replace(/^file:\/\//, "")
|
||||
}
|
||||
|
||||
@ -4,13 +4,15 @@ import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
import "../controls"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// --- Public API: data in ---
|
||||
property var fromAccountModel: null // LEZAccountFilterModel from backend (filtered by public/private)
|
||||
property var fromAccountModel: null
|
||||
property string transferResult: ""
|
||||
property bool transferResultIsError: false
|
||||
|
||||
// --- Public API: signals out ---
|
||||
signal transferRequested(bool isPublic, string fromAccountId, string toAddress, string amount)
|
||||
@ -25,13 +27,6 @@ Rectangle {
|
||||
|| (fromFilterCount === 0 && manualFromField.text.trim().length > 0))
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: fromAccountModel
|
||||
property: "filterByPublic"
|
||||
value: transferTypeBar.currentIndex === 0
|
||||
when: fromAccountModel != null
|
||||
}
|
||||
|
||||
radius: Theme.spacing.radiusXlarge
|
||||
color: Theme.palette.backgroundSecondary
|
||||
|
||||
@ -113,44 +108,36 @@ Rectangle {
|
||||
visible: fromCombo.count > 0
|
||||
}
|
||||
|
||||
contentItem: TextInput {
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
width: fromCombo.width - indicatorText.width - 12
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.text
|
||||
text: fromCombo.currentValue ?? ""
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
clip: true
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: delegate
|
||||
width: fromCombo.width
|
||||
leftPadding: 12
|
||||
rightPadding: 12
|
||||
contentItem: LogosText {
|
||||
width: parent.width - parent.leftPadding - parent.rightPadding
|
||||
contentItem: Item {
|
||||
implicitWidth: 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: model.name
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: fromCombo.displayText
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
clip: true
|
||||
}
|
||||
background: Rectangle {
|
||||
color: delegate.highlighted
|
||||
? Theme.palette.backgroundElevated
|
||||
: Theme.palette.backgroundSecondary
|
||||
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: fromCombo.width
|
||||
width: 400
|
||||
height: Math.min(contentItem.implicitHeight + 8, 300)
|
||||
padding: 0
|
||||
padding: Theme.spacing.small
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
@ -161,7 +148,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
color: Theme.palette.backgroundTertiary
|
||||
border.width: 1
|
||||
border.color: Theme.palette.backgroundElevated
|
||||
radius: Theme.spacing.radiusSmall
|
||||
@ -226,7 +213,9 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
text: root.transferResult
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: root.transferResult.length > 0 ? Theme.palette.textSecondary : "transparent"
|
||||
color: root.transferResult.length > 0
|
||||
? (root.transferResultIsError ? Theme.palette.error : Theme.palette.textSecondary)
|
||||
: "transparent"
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<qresource prefix="/lezwallet">
|
||||
<file>qml/ExecutionZoneWalletView.qml</file>
|
||||
<file>qml/controls/AccountDelegate.qml</file>
|
||||
<file>qml/popups/CreateAccountDialog.qml</file>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user