Skip to content

Commit d108d22

Browse files
accumulatorf321x
andcommitted
qml: initial support for bolt12 offers
Co-Authored-By: f321x <f@f321x.com>
1 parent d26c8e9 commit d108d22

6 files changed

Lines changed: 355 additions & 29 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import QtQuick
2+
import QtQuick.Layouts
3+
import QtQuick.Controls
4+
import QtQuick.Controls.Material
5+
6+
import org.electrum 1.0
7+
8+
import "controls"
9+
10+
ElDialog {
11+
id: dialog
12+
13+
title: qsTr('BOLT12 Offer')
14+
iconSource: '../../../icons/bolt12.png'
15+
16+
property InvoiceParser invoiceParser
17+
18+
padding: 0
19+
20+
property bool commentValid: true // TODO?
21+
property bool amountValid: amountBtc.textAsSats.satsInt > 0
22+
property bool valid: commentValid && amountValid
23+
24+
ColumnLayout {
25+
width: parent.width
26+
27+
spacing: 0
28+
29+
GridLayout {
30+
id: rootLayout
31+
columns: 2
32+
33+
Layout.fillWidth: true
34+
Layout.leftMargin: constants.paddingLarge
35+
Layout.rightMargin: constants.paddingLarge
36+
Layout.bottomMargin: constants.paddingLarge
37+
38+
// qml quirk; first cells cannot colspan without messing up the grid width
39+
Item { Layout.fillWidth: true; Layout.preferredWidth: 1; Layout.preferredHeight: 1 }
40+
Item { Layout.fillWidth: true; Layout.preferredWidth: 1; Layout.preferredHeight: 1 }
41+
42+
Label {
43+
Layout.columnSpan: 2
44+
text: qsTr('Issuer')
45+
color: Material.accentColor
46+
visible: 'issuer' in invoiceParser.offerData
47+
}
48+
TextHighlightPane {
49+
Layout.columnSpan: 2
50+
Layout.fillWidth: true
51+
visible: 'issuer' in invoiceParser.offerData
52+
Label {
53+
width: parent.width
54+
wrapMode: Text.Wrap
55+
text: invoiceParser.offerData['issuer']
56+
}
57+
}
58+
Label {
59+
Layout.columnSpan: 2
60+
Layout.fillWidth: true
61+
text: qsTr('Description')
62+
color: Material.accentColor
63+
}
64+
TextHighlightPane {
65+
Layout.columnSpan: 2
66+
Layout.fillWidth: true
67+
Label {
68+
width: parent.width
69+
text: invoiceParser.offerData['description']
70+
wrapMode: Text.Wrap
71+
}
72+
}
73+
Label {
74+
Layout.columnSpan: 2
75+
text: qsTr('Amount')
76+
color: Material.accentColor
77+
}
78+
79+
RowLayout {
80+
Layout.columnSpan: 2
81+
Layout.fillWidth: true
82+
BtcField {
83+
id: amountBtc
84+
Layout.preferredWidth: rootLayout.width /3
85+
text: 'amount' in invoiceParser.offerData
86+
? Config.formatSatsForEditing(invoiceParser.offerData['amount']/1000)
87+
: ''
88+
readOnly: 'amount' in invoiceParser.offerData
89+
color: Material.foreground // override gray-out on disabled
90+
fiatfield: amountFiat
91+
onTextAsSatsChanged: {
92+
invoiceParser.amountOverride = textAsSats
93+
}
94+
}
95+
Label {
96+
text: Config.baseUnit
97+
color: Material.accentColor
98+
}
99+
}
100+
101+
RowLayout {
102+
Layout.columnSpan: 2
103+
visible: Daemon.fx.enabled
104+
FiatField {
105+
id: amountFiat
106+
Layout.preferredWidth: rootLayout.width / 3
107+
btcfield: amountBtc
108+
readOnly: btcfield.readOnly
109+
}
110+
Label {
111+
text: Daemon.fx.fiatCurrency
112+
color: Material.accentColor
113+
}
114+
}
115+
116+
Label {
117+
Layout.columnSpan: 2
118+
text: qsTr('Note')
119+
color: Material.accentColor
120+
}
121+
ElTextArea {
122+
id: note
123+
Layout.columnSpan: 2
124+
Layout.fillWidth: true
125+
Layout.minimumHeight: 100
126+
wrapMode: TextEdit.Wrap
127+
placeholderText: qsTr('Enter an (optional) message for the receiver')
128+
// TODO: max 100 chars is arbitrary, not sure what the max size is
129+
color: text.length > 100 ? constants.colorError : Material.foreground
130+
}
131+
}
132+
133+
FlatButton {
134+
Layout.topMargin: constants.paddingLarge
135+
Layout.fillWidth: true
136+
text: qsTr('Pay')
137+
icon.source: '../../icons/confirmed.png'
138+
enabled: valid
139+
onClicked: {
140+
invoiceParser.requestInvoiceFromOffer(note.text)
141+
dialog.close()
142+
}
143+
}
144+
}
145+
146+
}

electrum/gui/qml/components/InvoiceDialog.qml

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ ElDialog {
1717
signal doPay
1818
signal invoiceAmountChanged
1919

20-
title: invoice.invoiceType == Invoice.OnchainInvoice ? qsTr('On-chain Invoice') : qsTr('Lightning Invoice')
20+
title: invoice.invoiceType == Invoice.OnchainInvoice
21+
? qsTr('On-chain Invoice')
22+
: invoice.lnprops.is_bolt12
23+
? qsTr('BOLT12 Invoice')
24+
: qsTr('Lightning Invoice')
2125
iconSource: Qt.resolvedUrl('../../icons/tab_send.png')
2226

2327
padding: 0
@@ -107,6 +111,30 @@ ElDialog {
107111
}
108112
}
109113

114+
Label {
115+
Layout.columnSpan: 2
116+
Layout.topMargin: constants.paddingSmall
117+
text: qsTr('Issuer')
118+
visible: 'issuer' in invoice.lnprops && invoice.lnprops.issuer != ''
119+
color: Material.accentColor
120+
}
121+
122+
TextHighlightPane {
123+
Layout.columnSpan: 2
124+
Layout.fillWidth: true
125+
126+
visible: 'issuer' in invoice.lnprops && invoice.lnprops.issuer != ''
127+
leftPadding: constants.paddingMedium
128+
129+
Label {
130+
text: 'issuer' in invoice.lnprops ? invoice.lnprops.issuer : ''
131+
width: parent.width
132+
font.pixelSize: constants.fontSizeXLarge
133+
wrapMode: Text.Wrap
134+
elide: Text.ElideRight
135+
}
136+
}
137+
110138
Label {
111139
Layout.columnSpan: 2
112140
Layout.topMargin: constants.paddingSmall
@@ -423,6 +451,36 @@ ElDialog {
423451
}
424452
}
425453

454+
Label {
455+
Layout.columnSpan: 2
456+
Layout.topMargin: constants.paddingSmall
457+
visible: 'blinded_paths' in invoice.lnprops && invoice.lnprops.blinded_paths.length
458+
text: qsTr('Blinded paths')
459+
color: Material.accentColor
460+
}
461+
462+
Repeater {
463+
visible: 'blinded_paths' in invoice.lnprops && invoice.lnprops.blinded_paths.length
464+
model: invoice.lnprops.blinded_paths
465+
466+
TextHighlightPane {
467+
Layout.columnSpan: 2
468+
Layout.fillWidth: true
469+
470+
RowLayout {
471+
width: parent.width
472+
473+
Label {
474+
Layout.fillWidth: true
475+
text: qsTr('via %1 (%2 hops)')
476+
.arg(modelData.first_node)
477+
.arg(modelData.path_length)
478+
wrapMode: Text.Wrap
479+
}
480+
}
481+
}
482+
}
483+
426484
Label {
427485
Layout.columnSpan: 2
428486
Layout.topMargin: constants.paddingSmall

electrum/gui/qml/components/WalletMainView.qml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Item {
4444
// Android based send dialog if on android
4545
var scanner = app.scanDialog.createObject(mainView, {
4646
hint: Daemon.currentWallet.isLightning
47-
? qsTr('Scan an Invoice, an Address, an LNURL, a PSBT or a Channel Backup')
47+
? qsTr('Scan an Invoice, an Address, an Offer, a LNURL, a PSBT or a Channel Backup')
4848
: qsTr('Scan an Invoice, an Address, an LNURL or a PSBT')
4949
})
5050
scanner.onFoundText.connect(function(data) {
@@ -438,6 +438,20 @@ Item {
438438
})
439439
dialog.open()
440440
}
441+
onBolt12Offer: {
442+
closeSendDialog()
443+
var dialog = bolt12OfferDialog.createObject(app, {
444+
invoiceParser: invoiceParser
445+
})
446+
dialog.open()
447+
}
448+
onBolt12Invoice: {
449+
closeSendDialog()
450+
var dialog = invoiceDialog.createObject(app, {
451+
invoice: invoiceParser
452+
})
453+
dialog.open()
454+
}
441455
}
442456

443457
Bitcoin {
@@ -748,6 +762,16 @@ Item {
748762
}
749763
}
750764

765+
Component {
766+
id: bolt12OfferDialog
767+
Bolt12OfferDialog {
768+
width: parent.width * 0.9
769+
anchors.centerIn: parent
770+
771+
onClosed: destroy()
772+
}
773+
}
774+
751775
Component {
752776
id: otpDialog
753777
OtpDialog {

electrum/gui/qml/components/controls/InvoiceDelegate.qml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ ItemDelegate {
4040
Layout.preferredWidth: constants.iconSizeLarge
4141
Layout.preferredHeight: constants.iconSizeLarge
4242
source: model.is_lightning
43-
? "../../../icons/lightning.png"
43+
? model.is_bolt12
44+
? "../../../icons/bolt12.png"
45+
: "../../../icons/lightning.png"
4446
: "../../../icons/bitcoin.png"
4547

4648
Image {

0 commit comments

Comments
 (0)