mirror of
https://github.com/logos-blockchain/logos-blockchain-ui.git
synced 2026-07-13 11:20:02 +00:00
Merge remote-tracking branch 'origin/master' into deposit
# Conflicts: # src/qml/BlockchainView.qml
This commit is contained in:
commit
7d849653fa
28
.clang-format
Normal file
28
.clang-format
Normal file
@ -0,0 +1,28 @@
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
|
||||
IndentAccessModifiers: false
|
||||
AccessModifierOffset: -4
|
||||
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
BinPackParameters: false
|
||||
BinPackArguments: false
|
||||
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
|
||||
BreakAfterReturnType: None
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
|
||||
BreakTemplateDeclarations: Yes
|
||||
|
||||
NamespaceIndentation: All
|
||||
2
flake.lock
generated
2
flake.lock
generated
@ -31,6 +31,7 @@
|
||||
"original": {
|
||||
"owner": "logos-blockchain",
|
||||
"repo": "logos-blockchain-module",
|
||||
"rev": "ab733aa7074cf1992f605c203c3d6a7923602705",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@ -1771,6 +1772,7 @@
|
||||
"original": {
|
||||
"owner": "logos-co",
|
||||
"repo": "logos-module-builder",
|
||||
"rev": "38ddf92c1f240f4e420d300a1fbabb1609d5db01",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
description = "Blockchain UI plugin for the Logos application";
|
||||
|
||||
inputs = {
|
||||
logos-module-builder.url = "github:logos-co/logos-module-builder";
|
||||
logos-module-builder.url = "github:logos-co/logos-module-builder/38ddf92c1f240f4e420d300a1fbabb1609d5db01";
|
||||
nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx";
|
||||
liblogos_blockchain_module.url = "github:logos-blockchain/logos-blockchain-module";
|
||||
liblogos_blockchain_module.url = "github:logos-blockchain/logos-blockchain-module/ab733aa7074cf1992f605c203c3d6a7923602705";
|
||||
};
|
||||
|
||||
outputs = inputs@{ logos-module-builder, ... }:
|
||||
|
||||
24
justfile
24
justfile
@ -28,8 +28,12 @@ clean *args:
|
||||
fi
|
||||
}
|
||||
|
||||
if pgrep -x logos > /dev/null; then
|
||||
confirm "Kill 'logos' process?" && pkill logos
|
||||
logos_procs="$(pgrep -ax logos_host_qt || true)"
|
||||
if [ -n "$logos_procs" ]; then
|
||||
echo "Found matching processes:"
|
||||
echo "$logos_procs"
|
||||
echo "This will kill the process(es) above."
|
||||
confirm "Proceed?" && { pkill -x logos_host_qt; echo "Killed."; }
|
||||
else
|
||||
echo "Already killed"
|
||||
fi
|
||||
@ -50,3 +54,19 @@ clean *args:
|
||||
|
||||
run: (clean "-y")
|
||||
nix run
|
||||
|
||||
# Launch CLion inside the Nix dev shell so it inherits required variables and fully integrates with Nix.
|
||||
clion:
|
||||
nix develop -c "$HOME/.local/share/JetBrains/Toolbox/apps/clion/bin/clion.sh" . >/dev/null 2>&1 &
|
||||
|
||||
prettify:
|
||||
nix shell nixpkgs#clang-tools -c clang-format -i src/**.cpp src/**.h
|
||||
|
||||
# Reuses cmake-build-debug's compile_commands.json (generated by CLion) instead
|
||||
# of a `configure` step — this repo has no standalone `build` recipe.
|
||||
lint:
|
||||
nix shell nixpkgs#clang-tools --command clang-tidy \
|
||||
-p cmake-build-debug \
|
||||
--header-filter='src/.*' \
|
||||
--extra-arg-before=--driver-mode=g++ \
|
||||
src/BlockchainPlugin.cpp src/BlockchainBackend.cpp src/AccountsModel.cpp src/LogModel.cpp
|
||||
|
||||
@ -19,6 +19,12 @@
|
||||
const QString BlockchainBackend::BLOCKCHAIN_MODULE_NAME =
|
||||
QStringLiteral("liblogos_blockchain_module");
|
||||
|
||||
void BlockchainBackend::setError(const QString& message)
|
||||
{
|
||||
setLastErrorMessage(message);
|
||||
setStatus(Error);
|
||||
}
|
||||
|
||||
static QString toLocalPath(const QString& pathInput)
|
||||
{
|
||||
if (pathInput.trimmed().isEmpty())
|
||||
@ -26,6 +32,30 @@ static QString toLocalPath(const QString& pathInput)
|
||||
return QUrl::fromUserInput(pathInput).toLocalFile();
|
||||
}
|
||||
|
||||
// invokeRemoteMethod() returns an invalid QVariant (not a LogosResult) when
|
||||
// the call itself fails to get a reply (timeout, disconnected module, etc.),
|
||||
// as opposed to the remote method running and reporting failure normally.
|
||||
static LogosResult toLogosResult(const QVariant& reply)
|
||||
{
|
||||
if (!reply.isValid())
|
||||
return LogosResult{false, QVariant(), QStringLiteral("Call failed.")};
|
||||
return reply.value<LogosResult>();
|
||||
}
|
||||
|
||||
static QString toErrorMessage(const LogosResult& result)
|
||||
{
|
||||
return QStringLiteral("Error: %1").arg(result.error.toString());
|
||||
}
|
||||
|
||||
// QML callers distinguish success from failure by sniffing for an "Error"
|
||||
// prefix (see BlockchainView.qml), so both branches must share this type.
|
||||
// TODO: Currently functions are returning a string (for ok and ko cases), which is later re-parsed to know if the call
|
||||
// succeeded or failed. We can simplify this by returning the result itself upward.
|
||||
static QString toDisplayMessage(const LogosResult& result)
|
||||
{
|
||||
return result.success ? result.value.toString() : toErrorMessage(result);
|
||||
}
|
||||
|
||||
BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
: BlockchainBackendSimpleSource(parent)
|
||||
, m_logosAPI(logosAPI)
|
||||
@ -83,7 +113,7 @@ BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
|
||||
m_blockchainClient = m_logosAPI->getClient(BLOCKCHAIN_MODULE_NAME);
|
||||
if (!m_blockchainClient) {
|
||||
setStatus(ErrorNotInitialized);
|
||||
setError(QStringLiteral("Module not initialized"));
|
||||
qWarning() << "BlockchainBackend: failed to get blockchain module client";
|
||||
return;
|
||||
}
|
||||
@ -105,7 +135,7 @@ BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
m_logModel->append(line);
|
||||
});
|
||||
} else {
|
||||
setStatus(ErrorSubscribeFailed);
|
||||
setError(QStringLiteral("Failed to subscribe to events"));
|
||||
}
|
||||
|
||||
qDebug() << "BlockchainBackend: initialized";
|
||||
@ -117,26 +147,32 @@ BlockchainBackend::~BlockchainBackend()
|
||||
stopBlockchain();
|
||||
}
|
||||
|
||||
QString BlockchainBackend::claimLeaderRewards()
|
||||
{
|
||||
if (!m_blockchainClient)
|
||||
return QStringLiteral("Error: Module not initialized.");
|
||||
|
||||
return toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "leader_claim")));
|
||||
}
|
||||
|
||||
void BlockchainBackend::startBlockchain()
|
||||
{
|
||||
if (!m_blockchainClient) {
|
||||
setStatus(ErrorNotInitialized);
|
||||
setError(QStringLiteral("Module not initialized"));
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(Starting);
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "start", userConfig(), deploymentConfig());
|
||||
int resultCode = result.isValid() ? result.toInt() : -1;
|
||||
const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "start", userConfig(), deploymentConfig()));
|
||||
|
||||
if (resultCode == 0 || resultCode == 1) {
|
||||
if (result.success) {
|
||||
setStatus(Running);
|
||||
QTimer::singleShot(500, this, [this]() { refreshAccounts(); });
|
||||
} else if (resultCode == 2) {
|
||||
setStatus(ErrorConfigMissing);
|
||||
} else {
|
||||
setStatus(ErrorStartFailed);
|
||||
setError(result.error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,32 +182,32 @@ void BlockchainBackend::stopBlockchain()
|
||||
return;
|
||||
|
||||
if (!m_blockchainClient) {
|
||||
setStatus(ErrorNotInitialized);
|
||||
setError(QStringLiteral("Module not initialized"));
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(Stopping);
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "stop");
|
||||
int resultCode = result.isValid() ? result.toInt() : -1;
|
||||
const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "stop"));
|
||||
|
||||
if (resultCode == 0 || resultCode == 1)
|
||||
if (result.success) {
|
||||
setStatus(Stopped);
|
||||
else
|
||||
setStatus(ErrorStopFailed);
|
||||
} else {
|
||||
setError(result.error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainBackend::refreshAccounts()
|
||||
{
|
||||
if (!m_blockchainClient) return;
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "wallet_get_known_addresses");
|
||||
QStringList list =
|
||||
result.isValid() && result.canConvert<QStringList>()
|
||||
? result.toStringList()
|
||||
: QStringList();
|
||||
const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "wallet_get_known_addresses"));
|
||||
|
||||
QStringList list;
|
||||
if (result.success && result.value.canConvert<QStringList>())
|
||||
list = result.value.toStringList();
|
||||
|
||||
m_accountsModel->setAddresses(list);
|
||||
|
||||
@ -194,10 +230,8 @@ QString BlockchainBackend::getBalance(QString addressHex)
|
||||
if (!m_blockchainClient) {
|
||||
result = QStringLiteral("Error: Module not initialized.");
|
||||
} else {
|
||||
QVariant v = m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "wallet_get_balance", addressHex);
|
||||
result = v.isValid() ? v.toString()
|
||||
: QStringLiteral("Error: Call failed.");
|
||||
result = toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "wallet_get_balance", addressHex)));
|
||||
}
|
||||
|
||||
m_accountsModel->setBalanceForAddress(addressHex, result);
|
||||
@ -211,11 +245,9 @@ QString BlockchainBackend::transferFunds(
|
||||
return QStringLiteral("Error: Module not initialized.");
|
||||
|
||||
QStringList senders{fromKeyHex};
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
return toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "wallet_transfer_funds",
|
||||
fromKeyHex, senders, toKeyHex, amountStr, QString());
|
||||
return result.isValid() ? result.toString()
|
||||
: QStringLiteral("Error: Call failed.");
|
||||
fromKeyHex, senders, toKeyHex, amountStr, QString())));
|
||||
}
|
||||
|
||||
int BlockchainBackend::generateConfig(
|
||||
@ -272,9 +304,10 @@ int BlockchainBackend::generateConfig(
|
||||
const QString jsonToSend =
|
||||
QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "generate_user_config", jsonToSend);
|
||||
return result.isValid() ? result.toInt() : -1;
|
||||
const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "generate_user_config", jsonToSend));
|
||||
|
||||
return result.success ? 0 : -1;
|
||||
}
|
||||
|
||||
QString BlockchainBackend::getNotes(QString walletAddressHex, QString optionalTipHex)
|
||||
@ -282,11 +315,9 @@ QString BlockchainBackend::getNotes(QString walletAddressHex, QString optionalTi
|
||||
if (!m_blockchainClient)
|
||||
return QStringLiteral("Error: Module not initialized.");
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
return toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "wallet_get_notes",
|
||||
walletAddressHex, optionalTipHex);
|
||||
return result.isValid() ? result.toString()
|
||||
: QStringLiteral("Error: Call failed.");
|
||||
walletAddressHex, optionalTipHex)));
|
||||
}
|
||||
|
||||
QString BlockchainBackend::channelDepositWithNotes(
|
||||
@ -303,11 +334,9 @@ QString BlockchainBackend::channelDepositWithNotes(
|
||||
args << channelIdHex << inputNoteIdHexes << metadataHex << changePublicKeyHex
|
||||
<< fundingPublicKeyHexes << maxTxFee << optionalTipHex;
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
return toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, QStringLiteral("channel_deposit_with_notes"),
|
||||
args);
|
||||
return result.isValid() ? result.toString()
|
||||
: QStringLiteral("Error: Call failed.");
|
||||
args)));
|
||||
}
|
||||
|
||||
void BlockchainBackend::clearLogs()
|
||||
|
||||
@ -43,6 +43,7 @@ public slots:
|
||||
void refreshAccounts() override;
|
||||
QString getBalance(QString addressHex) override;
|
||||
QString transferFunds(QString fromKeyHex, QString toKeyHex, QString amountStr) override;
|
||||
QString claimLeaderRewards() override;
|
||||
int generateConfig(QString outputPath, QStringList initialPeers, int netPort,
|
||||
int blendPort, QString httpAddr, QString externalAddress,
|
||||
bool noPublicIpCheck, int deploymentMode,
|
||||
@ -60,6 +61,7 @@ public slots:
|
||||
|
||||
private:
|
||||
void fetchBalancesForAccounts(const QStringList& list);
|
||||
void setError(const QString& message);
|
||||
|
||||
LogosAPI* m_logosAPI = nullptr;
|
||||
LogosAPIClient* m_blockchainClient = nullptr;
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
class BlockchainBackend
|
||||
{
|
||||
ENUM BlockchainStatus { NotStarted=0, Starting=1, Running=2, Stopping=3, Stopped=4, Error=5, ErrorNotInitialized=6, ErrorConfigMissing=7, ErrorStartFailed=8, ErrorStopFailed=9, ErrorSubscribeFailed=10 }
|
||||
ENUM BlockchainStatus { NotStarted=0, Starting=1, Running=2, Stopping=3, Stopped=4, Error=5 }
|
||||
|
||||
PROP(BlockchainStatus status READONLY)
|
||||
PROP(QString userConfig READWRITE)
|
||||
PROP(QString deploymentConfig READWRITE)
|
||||
PROP(bool useGeneratedConfig READWRITE)
|
||||
PROP(QString generatedUserConfigPath READONLY)
|
||||
PROP(QString lastErrorMessage READONLY)
|
||||
|
||||
SLOT(void startBlockchain())
|
||||
SLOT(void stopBlockchain())
|
||||
SLOT(void refreshAccounts())
|
||||
SLOT(QString getBalance(QString addressHex))
|
||||
SLOT(QString transferFunds(QString fromKeyHex, QString toKeyHex, QString amountStr))
|
||||
SLOT(QString claimLeaderRewards())
|
||||
SLOT(int generateConfig(QString outputPath, QStringList initialPeers, int netPort, int blendPort, QString httpAddr, QString externalAddress, bool noPublicIpCheck, int deploymentMode, QString deploymentConfigPath, QString statePath))
|
||||
SLOT(QString getNotes(QString walletAddressHex, QString optionalTipHex))
|
||||
SLOT(QString channelDepositWithNotes(QString channelIdHex, QStringList inputNoteIdHexes, QString metadataHex, QString changePublicKeyHex, QStringList fundingPublicKeyHexes, QString maxTxFee, QString optionalTipHex))
|
||||
|
||||
@ -4,7 +4,7 @@ import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
// BlockchainStatus enum (NotStarted/Starting/Running/.../ErrorSubscribeFailed)
|
||||
// BlockchainStatus enum (NotStarted/Starting/Running/Stopping/Stopped/Error)
|
||||
// declared in BlockchainBackend.rep — registered with QML by the replica
|
||||
// factory plugin.
|
||||
import Logos.BlockchainBackend 1.0
|
||||
@ -49,12 +49,7 @@ Rectangle {
|
||||
case BlockchainBackend.Running: return qsTr("Running")
|
||||
case BlockchainBackend.Stopping: return qsTr("Stopping...")
|
||||
case BlockchainBackend.Stopped: return qsTr("Stopped")
|
||||
case BlockchainBackend.Error: return qsTr("Error")
|
||||
case BlockchainBackend.ErrorNotInitialized: return qsTr("Error: Module not initialized")
|
||||
case BlockchainBackend.ErrorConfigMissing: return qsTr("Error: Config path missing")
|
||||
case BlockchainBackend.ErrorStartFailed: return qsTr("Error: Failed to start node")
|
||||
case BlockchainBackend.ErrorStopFailed: return qsTr("Error: Failed to stop node")
|
||||
case BlockchainBackend.ErrorSubscribeFailed: return qsTr("Error: Failed to subscribe to events")
|
||||
case BlockchainBackend.Error: return qsTr("Error: %1").arg(root.backend.lastErrorMessage)
|
||||
default: return qsTr("Unknown")
|
||||
}
|
||||
}
|
||||
@ -243,6 +238,14 @@ Rectangle {
|
||||
function(error) { walletView.setTransferResult("Error: " + error) }
|
||||
)
|
||||
}
|
||||
onClaimLeaderRewardsRequested: function() {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.claimLeaderRewards(),
|
||||
function(result) { walletView.setLeaderClaimResult(result) },
|
||||
function(error) { walletView.setLeaderClaimResult("Error: " + error) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
@ -17,18 +17,23 @@ RowLayout {
|
||||
|
||||
signal getBalanceRequested(string addressHex)
|
||||
signal transferRequested(string fromKeyHex, string toKeyHex, string amount)
|
||||
signal claimLeaderRewardsRequested()
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
function setTransferResult(text) {
|
||||
transferResultText.text = text
|
||||
}
|
||||
|
||||
function setLeaderClaimResult(text) {
|
||||
leaderClaimResultText.text = text
|
||||
}
|
||||
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
// Get balance card
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: transferRect.height
|
||||
implicitHeight: actionsCol.height
|
||||
Layout.preferredHeight: Math.min(implicitHeight, 400)
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
@ -75,84 +80,156 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer funds card
|
||||
Rectangle {
|
||||
id: transferRect
|
||||
ColumnLayout {
|
||||
id: actionsCol
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
ColumnLayout {
|
||||
id: transferCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
// Transfer funds card
|
||||
Rectangle {
|
||||
id: transferRect
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Transfer funds")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
StyledAddressComboBox {
|
||||
id: transferFromCombo
|
||||
model: root.accountsModel
|
||||
textRole: "address"
|
||||
}
|
||||
ColumnLayout {
|
||||
id: transferCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosTextField {
|
||||
id: transferToField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("To key (64 hex chars)")
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferAmountField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("Amount")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferButton.implicitHeight
|
||||
|
||||
LogosButton {
|
||||
id: transferButton
|
||||
Layout.preferredWidth: 60
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Send")
|
||||
onClicked: root.transferRequested(transferFromCombo.currentText.trim(), transferToField.text.trim(), transferAmountField.text)
|
||||
LogosText {
|
||||
text: qsTr("Transfer funds")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
StyledAddressComboBox {
|
||||
id: transferFromCombo
|
||||
model: root.accountsModel
|
||||
textRole: "address"
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferToField
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: transferResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("To key (64 hex chars)")
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferAmountField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("Amount")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferButton.implicitHeight
|
||||
|
||||
LogosButton {
|
||||
id: transferButton
|
||||
Layout.preferredWidth: 60
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Send")
|
||||
onClicked: root.transferRequested(transferFromCombo.currentText.trim(), transferToField.text.trim(), transferAmountField.text)
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: transferResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(transferResultText.text)
|
||||
visible: transferResultText.text
|
||||
}
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyRequested(transferResultText.text)
|
||||
visible: transferResultText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leader rewards card
|
||||
Rectangle {
|
||||
id: leaderRewardsRect
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: leaderRewardsCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: leaderRewardsCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Leader rewards")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
LogosButton {
|
||||
id: leaderClaimButton
|
||||
Layout.preferredWidth: 140
|
||||
text: qsTr("Claim")
|
||||
onClicked: root.claimLeaderRewardsRequested()
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: leaderClaimResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(leaderClaimResultText.text)
|
||||
visible: leaderClaimResultText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user