2026-06-20 15:25:14 +02:00
import QtQuick
import QtQuick . Controls
import QtQuick . Layouts
import Logos . Theme
import Logos . Controls
import "../controls"
2026-06-22 17:52:14 +02:00
// 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.
2026-06-20 15:25:14 +02:00
ColumnLayout {
id: root
2026-06-22 17:52:14 +02:00
// JSON from wallet_get_claimable_vouchers:
// { "tip": "<hex>", "vouchers": [ {commitment, nullifier}, ... ] }
property string vouchersJson: ""
2026-06-20 15:25:14 +02:00
signal claimLeaderRewardsRequested ( )
signal copyToClipboard ( string text )
function setLeaderClaimResult ( text ) {
leaderClaimResultText . text = text
}
2026-06-22 17:52:14 +02:00
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 }
}
2026-06-20 15:25:14 +02:00
spacing: Theme . spacing . large
2026-06-22 17:52:14 +02:00
// ---- Claimable vouchers card ----
2026-06-20 15:25:14 +02:00
Rectangle {
Layout.fillWidth: true
2026-06-22 17:52:14 +02:00
Layout.preferredHeight: vouchersCol . implicitHeight + 2 * Theme . spacing . large
2026-06-20 15:25:14 +02:00
color: Theme . palette . backgroundTertiary
radius: Theme . spacing . radiusLarge
border.color: Theme . palette . border
border.width: 1
ColumnLayout {
2026-06-22 17:52:14 +02:00
id: vouchersCol
2026-06-20 15:25:14 +02:00
anchors.top: parent . top
anchors.left: parent . left
anchors.right: parent . right
anchors.margins: Theme . spacing . large
spacing: Theme . spacing . small
2026-06-22 17:52:14 +02:00
RowLayout {
Layout.fillWidth: true
spacing: Theme . spacing . small
LogosText {
text: qsTr ( "Claimable vouchers" )
font.pixelSize: Theme . typography . secondaryText
font.bold: true
}
// 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
}
2026-06-22 19:04:33 +02:00
InfoButton {
Layout.alignment: Qt . AlignVCenter
text: qsTr ( "Claim block-leader rewards. The list shows your pending claimable vouchers (refreshed each block). Claim submits a claim transaction; the protocol selects which voucher it consumes." )
}
2026-06-20 15:25:14 +02:00
}
2026-06-22 17:52:14 +02:00
// Horizontally-sliding list of voucher cards.
ListView {
id: vouchersList
2026-06-20 15:25:14 +02:00
Layout.fillWidth: true
2026-06-22 17:52:14 +02:00
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 }
2026-06-20 15:25:14 +02:00
2026-06-22 17:52:14 +02:00
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
2026-06-20 15:25:14 +02:00
LogosText {
2026-06-22 17:52:14 +02:00
text: qsTr ( "Voucher %1" ) . arg ( index + 1 )
2026-06-20 15:25:14 +02:00
font.pixelSize: Theme . typography . secondaryText
2026-06-22 17:52:14 +02:00
font.bold: true
color: Theme . palette . textSecondary
}
VoucherField {
label: qsTr ( "cm" )
value: modelData && modelData . commitment ? String ( modelData . commitment ) : ""
2026-06-20 15:25:14 +02:00
}
2026-06-22 17:52:14 +02:00
VoucherField {
label: qsTr ( "nf" )
value: modelData && modelData . nullifier ? String ( modelData . nullifier ) : ""
2026-06-20 15:25:14 +02:00
}
}
}
}
2026-06-22 17:52:14 +02:00
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
}
}
}
2026-06-20 15:25:14 +02:00
}
}
Item { Layout.fillHeight: true }
2026-06-22 17:52:14 +02:00
// 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 )
}
}
2026-06-20 15:25:14 +02:00
}