mirror of
https://github.com/logos-blockchain/logos-blockchain-ui.git
synced 2026-07-08 00:39:28 +00:00
Add vouchers view to leader claim with pending counter
This commit is contained in:
parent
c47fc8ddea
commit
8ae4c07b4b
@ -230,6 +230,15 @@ QVariantMap BlockchainBackend::getPeerId()
|
||||
BLOCKCHAIN_MODULE_NAME, QStringLiteral("get_peer_id"), userConfig())));
|
||||
}
|
||||
|
||||
QVariantMap BlockchainBackend::getClaimableVouchers()
|
||||
{
|
||||
if (!m_blockchainClient)
|
||||
return result::toVariantMap(result::err(QStringLiteral("Module not initialized.")));
|
||||
|
||||
return result::toVariantMap(result::toLogosResult(m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, QStringLiteral("wallet_get_claimable_vouchers"))));
|
||||
}
|
||||
|
||||
void BlockchainBackend::startBlockchain()
|
||||
{
|
||||
if (!m_blockchainClient) {
|
||||
|
||||
@ -47,6 +47,7 @@ public slots:
|
||||
QVariantMap claimLeaderRewards() override;
|
||||
QVariantMap getCryptarchiaInfo() override;
|
||||
QVariantMap getPeerId() override;
|
||||
QVariantMap getClaimableVouchers() override;
|
||||
QVariantMap generateConfig(QString outputPath, QStringList initialPeers, int netPort,
|
||||
int blendPort, QString httpAddr, QString externalAddress,
|
||||
bool noPublicIpCheck, int deploymentMode,
|
||||
|
||||
@ -17,6 +17,7 @@ class BlockchainBackend
|
||||
SLOT(QVariantMap claimLeaderRewards())
|
||||
SLOT(QVariantMap getCryptarchiaInfo())
|
||||
SLOT(QVariantMap getPeerId())
|
||||
SLOT(QVariantMap getClaimableVouchers())
|
||||
SLOT(QVariantMap generateConfig(QString outputPath, QStringList initialPeers, int netPort, int blendPort, QString httpAddr, QString externalAddress, bool noPublicIpCheck, int deploymentMode, QString deploymentConfigPath, QString statePath))
|
||||
SLOT(QVariantMap getNotes(QString walletAddressHex, QString optionalTipHex))
|
||||
SLOT(QVariantMap channelDepositWithNotes(QString channelIdHex, QStringList inputNoteIdHexes, QString metadataBase58, QString changePublicKeyHex, QStringList fundingPublicKeyHexes, QString maxTxFee, QString optionalTipHex))
|
||||
|
||||
@ -147,6 +147,39 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Wallet's claimable ("pending") vouchers. Auto-refreshed on every incoming
|
||||
// block, and once when the node starts running.
|
||||
property string claimableVouchersJson: ""
|
||||
|
||||
function refreshClaimableVouchers() {
|
||||
if (!root.backend || root.backend.status !== BlockchainBackend.Running)
|
||||
return
|
||||
logos.watch(
|
||||
root.backend.getClaimableVouchers(),
|
||||
function(result) { if (result.success) root.claimableVouchersJson = result.value },
|
||||
function(error) { /* keep last known list on transient errors */ }
|
||||
)
|
||||
}
|
||||
|
||||
// Incoming blocks arrive as row insertions on the remoted block model.
|
||||
Connections {
|
||||
target: root.blockModel
|
||||
enabled: root.blockModel !== null
|
||||
ignoreUnknownSignals: true
|
||||
function onRowsInserted() { root.refreshClaimableVouchers() }
|
||||
}
|
||||
|
||||
// Initial load when the node reaches Running (before the next block).
|
||||
Connections {
|
||||
target: root.backend
|
||||
enabled: root.backend !== null
|
||||
ignoreUnknownSignals: true
|
||||
function onStatusChanged() {
|
||||
if (root.backend.status === BlockchainBackend.Running)
|
||||
root.refreshClaimableVouchers()
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _d
|
||||
function errorText(message) {
|
||||
@ -478,6 +511,7 @@ Rectangle {
|
||||
|
||||
LeaderRewardsView {
|
||||
id: leaderRewardsView
|
||||
vouchersJson: root.claimableVouchersJson
|
||||
|
||||
onClaimLeaderRewardsRequested: function() {
|
||||
if (!root.backend) return
|
||||
@ -489,6 +523,8 @@ Rectangle {
|
||||
} else {
|
||||
leaderRewardsView.setLeaderClaimResult(_d.errorText(result.error))
|
||||
}
|
||||
// Reflect the claim in the pending list.
|
||||
root.refreshClaimableVouchers()
|
||||
},
|
||||
function(error) { leaderRewardsView.setLeaderClaimResult(_d.errorText(error)) }
|
||||
)
|
||||
|
||||
@ -7,10 +7,16 @@ import Logos.Controls
|
||||
|
||||
import "../controls"
|
||||
|
||||
// Leader rewards panel. Extracted from the former WalletView.
|
||||
// Leader rewards panel: claim action plus a read-only, horizontally-sliding
|
||||
// list of the wallet's claimable ("pending") vouchers with a count. The
|
||||
// protocol picks which voucher a claim consumes — the list is informational.
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
// JSON from wallet_get_claimable_vouchers:
|
||||
// { "tip": "<hex>", "vouchers": [ {commitment, nullifier}, ... ] }
|
||||
property string vouchersJson: ""
|
||||
|
||||
signal claimLeaderRewardsRequested()
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
@ -18,65 +24,168 @@ ColumnLayout {
|
||||
leaderClaimResultText.text = text
|
||||
}
|
||||
|
||||
readonly property var _parsed: safeParse(vouchersJson)
|
||||
readonly property var vouchers: (_parsed && _parsed.vouchers)
|
||||
? _parsed.vouchers
|
||||
: (Array.isArray(_parsed) ? _parsed : [])
|
||||
readonly property string tip: (_parsed && _parsed.tip) ? String(_parsed.tip) : ""
|
||||
|
||||
function safeParse(s) {
|
||||
try { return s && s.length > 0 ? JSON.parse(s) : null } catch (e) { return null }
|
||||
}
|
||||
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
// ---- Claimable vouchers card ----
|
||||
Rectangle {
|
||||
id: leaderRewardsRect
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: leaderRewardsCol.height + 2 * Theme.spacing.large
|
||||
Layout.preferredHeight: vouchersCol.implicitHeight + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: leaderRewardsCol
|
||||
id: vouchersCol
|
||||
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()
|
||||
spacing: Theme.spacing.small
|
||||
LogosText {
|
||||
text: qsTr("Claimable vouchers")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
// Pending counter badge
|
||||
Rectangle {
|
||||
radius: Theme.spacing.radiusSmall
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
implicitWidth: pendingText.implicitWidth + 2 * Theme.spacing.small
|
||||
implicitHeight: pendingText.implicitHeight + Theme.spacing.tiny
|
||||
LogosText {
|
||||
id: pendingText
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("%1 pending").arg(root.vouchers.length)
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
LogosText {
|
||||
visible: root.tip.length > 0
|
||||
text: qsTr("tip %1").arg(root.tip)
|
||||
elide: Text.ElideMiddle
|
||||
Layout.maximumWidth: 140
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontally-sliding list of voucher cards.
|
||||
ListView {
|
||||
id: vouchersList
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 78
|
||||
visible: root.vouchers.length > 0
|
||||
orientation: ListView.Horizontal
|
||||
clip: true
|
||||
spacing: Theme.spacing.small
|
||||
model: root.vouchers
|
||||
snapMode: ListView.SnapToItem
|
||||
ScrollBar.horizontal: ScrollBar { policy: ScrollBar.AsNeeded }
|
||||
|
||||
delegate: Rectangle {
|
||||
width: 260
|
||||
height: ListView.view.height
|
||||
radius: Theme.spacing.radiusSmall
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.small
|
||||
spacing: Theme.spacing.tiny
|
||||
|
||||
LogosText {
|
||||
id: leaderClaimResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
text: qsTr("Voucher %1").arg(index + 1)
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
font.bold: true
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(leaderClaimResultText.text)
|
||||
visible: leaderClaimResultText.text
|
||||
VoucherField {
|
||||
label: qsTr("cm")
|
||||
value: modelData && modelData.commitment ? String(modelData.commitment) : ""
|
||||
}
|
||||
VoucherField {
|
||||
label: qsTr("nf")
|
||||
value: modelData && modelData.nullifier ? String(modelData.nullifier) : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogosText {
|
||||
visible: root.vouchers.length === 0
|
||||
text: qsTr("No claimable vouchers.")
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Claim action ----
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: claimRow.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
id: claimRow
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,4 +193,32 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
// A labelled, elided, copyable hash inside a voucher card.
|
||||
component VoucherField: RowLayout {
|
||||
id: vf
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.tiny
|
||||
LogosText {
|
||||
text: vf.label
|
||||
Layout.preferredWidth: 20
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
}
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
text: vf.value || "—"
|
||||
elide: Text.ElideMiddle
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.family: "monospace"
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 24
|
||||
visible: vf.value.length > 0
|
||||
onCopyText: root.copyToClipboard(vf.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user