Browse Source

allow zero amount invoices, add edit amount option for invoices

patch-4
Sander van Grieken 2 years ago
parent
commit
fb68931a8d
  1. 216
      electrum/gui/qml/components/InvoiceDialog.qml
  2. 6
      electrum/gui/qml/components/ReceiveDialog.qml
  3. 5
      electrum/gui/qml/components/SendDialog.qml
  4. 8
      electrum/gui/qml/components/WalletMainView.qml
  5. 6
      electrum/gui/qml/components/controls/QRScan.qml
  6. 4
      electrum/gui/qml/qeconfig.py
  7. 23
      electrum/gui/qml/qeinvoice.py
  8. 12
      electrum/gui/qml/qewallet.py

216
electrum/gui/qml/components/InvoiceDialog.qml

@ -15,9 +15,6 @@ ElDialog {
signal doPay
width: parent.width
height: parent.height
title: qsTr('Invoice')
standardButtons: invoice_key != '' ? Dialog.Close : Dialog.Cancel
@ -40,8 +37,151 @@ ElDialog {
color: Material.accentColor
}
Label {
text: qsTr('Amount to send')
color: Material.accentColor
Layout.columnSpan: 2
}
TextHighlightPane {
id: amountContainer
Layout.columnSpan: 2
Layout.preferredWidth: parent.width //* 0.75
Layout.alignment: Qt.AlignHCenter
padding: 0
leftPadding: constants.paddingXXLarge
property bool editmode: false
RowLayout {
id: amountLayout
width: parent.width
GridLayout {
visible: !amountContainer.editmode
columns: 2
Label {
font.pixelSize: constants.fontSizeXLarge
font.family: FixedFont
font.bold: true
text: Config.formatSats(invoice.amount, false)
}
Label {
Layout.fillWidth: true
text: Config.baseUnit
color: Material.accentColor
font.pixelSize: constants.fontSizeXLarge
}
Label {
id: fiatValue
visible: Daemon.fx.enabled
text: Daemon.fx.fiatValue(invoice.amount, false)
font.pixelSize: constants.fontSizeMedium
color: constants.mutedForeground
}
Label {
visible: Daemon.fx.enabled
Layout.fillWidth: true
text: Daemon.fx.fiatCurrency
font.pixelSize: constants.fontSizeMedium
color: constants.mutedForeground
}
}
ToolButton {
visible: !amountContainer.editmode
icon.source: '../../icons/pen.png'
icon.color: 'transparent'
onClicked: {
amountBtc.text = invoice.amount.satsInt == 0 ? '' : Config.formatSats(invoice.amount)
amountContainer.editmode = true
amountBtc.focus = true
}
}
GridLayout {
visible: amountContainer.editmode
Layout.fillWidth: true
columns: 2
BtcField {
id: amountBtc
fiatfield: amountFiat
}
Label {
text: Config.baseUnit
color: Material.accentColor
Layout.fillWidth: true
}
FiatField {
id: amountFiat
btcfield: amountBtc
visible: Daemon.fx.enabled
}
Label {
visible: Daemon.fx.enabled
text: Daemon.fx.fiatCurrency
color: Material.accentColor
}
}
ToolButton {
visible: amountContainer.editmode
Layout.fillWidth: false
icon.source: '../../icons/confirmed.png'
icon.color: 'transparent'
onClicked: {
amountContainer.editmode = false
invoice.amount = Config.unitsToSats(amountBtc.text)
}
}
ToolButton {
visible: amountContainer.editmode
Layout.fillWidth: false
icon.source: '../../icons/closebutton.png'
icon.color: 'transparent'
onClicked: amountContainer.editmode = false
}
}
}
Label {
text: qsTr('Description')
visible: invoice.message
Layout.columnSpan: 2
color: Material.accentColor
}
TextHighlightPane {
visible: invoice.message
Layout.columnSpan: 2
Layout.preferredWidth: parent.width
Layout.alignment: Qt.AlignHCenter
padding: 0
leftPadding: constants.paddingMedium
Label {
text: invoice.message
Layout.fillWidth: true
font.pixelSize: constants.fontSizeXLarge
wrapMode: Text.Wrap
elide: Text.ElideRight
}
}
Label {
text: qsTr('Type')
color: Material.accentColor
}
RowLayout {
@ -64,48 +204,10 @@ ElDialog {
}
}
Label {
text: qsTr('Amount to send')
}
RowLayout {
Layout.fillWidth: true
Label {
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
font.bold: true
text: Config.formatSats(invoice.amount, false)
}
Label {
text: Config.baseUnit
color: Material.accentColor
}
Label {
id: fiatValue
Layout.fillWidth: true
text: Daemon.fx.enabled
? '(' + Daemon.fx.fiatValue(invoice.amount, false) + ' ' + Daemon.fx.fiatCurrency + ')'
: ''
font.pixelSize: constants.fontSizeMedium
}
}
Label {
text: qsTr('Description')
}
Label {
text: invoice.message
Layout.fillWidth: true
wrapMode: Text.Wrap
elide: Text.ElideRight
}
Label {
visible: invoice.invoiceType == Invoice.OnchainInvoice
text: qsTr('Address')
color: Material.accentColor
}
Label {
@ -119,6 +221,7 @@ ElDialog {
Label {
visible: invoice.invoiceType == Invoice.LightningInvoice
text: qsTr('Remote Pubkey')
color: Material.accentColor
}
Label {
@ -132,6 +235,7 @@ ElDialog {
Label {
visible: invoice.invoiceType == Invoice.LightningInvoice
text: qsTr('Route via (t)')
color: Material.accentColor
}
Label {
@ -145,6 +249,7 @@ ElDialog {
Label {
visible: invoice.invoiceType == Invoice.LightningInvoice
text: qsTr('Route via (r)')
color: Material.accentColor
}
Label {
@ -157,6 +262,7 @@ ElDialog {
Label {
text: qsTr('Status')
color: Material.accentColor
}
Label {
@ -194,30 +300,20 @@ ElDialog {
}
}
Button {
text: qsTr('Save')
icon.source: '../../icons/save.png'
visible: invoice_key == ''
enabled: invoice.canSave
onClicked: {
invoice.save_invoice()
dialog.close()
}
}
Button {
text: qsTr('Pay now')
FlatButton {
text: qsTr('Pay')
icon.source: '../../icons/confirmed.png'
enabled: invoice.invoiceType != Invoice.Invalid && invoice.canPay
onClicked: {
if (invoice_key == '') // save invoice if not retrieved from key
invoice.save_invoice()
dialog.close()
if (invoice.invoiceType == Invoice.OnchainInvoice) {
doPay() // only signal here
} else if (invoice.invoiceType == Invoice.LightningInvoice) {
doPay() // only signal here
}
doPay() // only signal here
// if (invoice.invoiceType == Invoice.OnchainInvoice) {
// doPay() // only signal here
// } else if (invoice.invoiceType == Invoice.LightningInvoice) {
// doPay() // only signal here
// }
}
}
}

6
electrum/gui/qml/components/ReceiveDialog.qml

@ -212,16 +212,16 @@ ElDialog {
var qamt = Config.unitsToSats(receiveDetailsDialog.amount)
if (qamt.satsInt > Daemon.currentWallet.lightningCanReceive.satsInt) {
console.log('Creating OnChain request')
Daemon.currentWallet.create_request(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, false, ignoreGaplimit)
Daemon.currentWallet.createRequest(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, false, ignoreGaplimit)
} else {
console.log('Creating Lightning request')
Daemon.currentWallet.create_request(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, true)
Daemon.currentWallet.createRequest(qamt, receiveDetailsDialog.description, receiveDetailsDialog.expiry, true)
}
}
function createDefaultRequest(ignoreGaplimit = false) {
console.log('Creating default request')
Daemon.currentWallet.create_default_request(ignoreGaplimit)
Daemon.currentWallet.createDefaultRequest(ignoreGaplimit)
}
Connections {

5
electrum/gui/qml/components/SendDialog.qml

@ -22,10 +22,15 @@ ElDialog {
padding: 0
function restart() {
qrscan.restart()
}
ColumnLayout {
anchors.fill: parent
QRScan {
id: qrscan
Layout.preferredWidth: parent.width
Layout.fillHeight: true

8
electrum/gui/qml/components/WalletMainView.qml

@ -143,6 +143,9 @@ Item {
wallet: Daemon.currentWallet
onValidationError: {
var dialog = app.messageDialog.createObject(app, {'text': message })
dialog.closed.connect(function() {
_sendDialog.restart()
})
dialog.open()
}
onValidationWarning: {
@ -176,6 +179,9 @@ Item {
Component {
id: invoiceDialog
InvoiceDialog {
width: parent.width
height: parent.height
onDoPay: {
if (invoice.invoiceType == Invoice.OnchainInvoice) {
var dialog = confirmPaymentDialog.createObject(mainView, {
@ -206,7 +212,7 @@ Item {
}
close()
}
// onClosed: destroy()
onClosed: destroy()
}
}

6
electrum/gui/qml/components/controls/QRScan.qml

@ -15,6 +15,12 @@ Item {
signal found
function restart() {
still.source = ''
_pointsVisible = false
active = true
}
VideoOutput {
id: vo
anchors.fill: parent

4
electrum/gui/qml/qeconfig.py

@ -85,9 +85,7 @@ class QEConfig(AuthMixin, QObject):
requestExpiryChanged = pyqtSignal()
@pyqtProperty(int, notify=requestExpiryChanged)
def requestExpiry(self):
a = self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
self._logger.debug(f'request expiry {a}')
return a
return self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
@requestExpiry.setter
def requestExpiry(self, expiry):

23
electrum/gui/qml/qeinvoice.py

@ -165,6 +165,16 @@ class QEInvoiceParser(QEInvoice):
self._amount = QEAmount(from_invoice=self._effectiveInvoice)
return self._amount
@amount.setter
def amount(self, new_amount):
self._logger.debug('set amount')
if self._effectiveInvoice:
self._effectiveInvoice.amount_msat = int(new_amount.satsInt * 1000)
# TODO: side effects?
# TODO: recalc outputs for onchain
self.determine_can_pay()
self.invoiceChanged.emit()
@pyqtProperty('quint64', notify=invoiceChanged)
def expiration(self):
return self._effectiveInvoice.exp if self._effectiveInvoice else 0
@ -242,6 +252,10 @@ class QEInvoiceParser(QEInvoice):
self.statusChanged.emit()
def determine_can_pay(self):
if self.amount.satsInt == 0:
self.canPay = False
return
if self.invoiceType == QEInvoice.Type.LightningInvoice:
if self.status in [PR_UNPAID, PR_FAILED]:
if self.get_max_spendable_lightning() >= self.amount.satsInt:
@ -374,9 +388,12 @@ class QEInvoiceParser(QEInvoice):
else:
self._logger.debug('flow without LN but having bip21 uri')
if 'amount' not in self._bip21: #TODO can we have amount-less invoices?
self.validationError.emit('no_amount', 'no amount in uri')
return
outputs = [PartialTxOutput.from_address_and_value(self._bip21['address'], self._bip21['amount'])]
# self.validationError.emit('no_amount', 'no amount in uri')
# return
amount = 0
else:
amount = self._bip21['amount']
outputs = [PartialTxOutput.from_address_and_value(self._bip21['address'], amount)]
self._logger.debug(outputs)
message = self._bip21['message'] if 'message' in self._bip21 else ''
invoice = self.create_onchain_invoice(outputs, message, None, self._bip21)

12
electrum/gui/qml/qewallet.py

@ -8,7 +8,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer
from electrum import bitcoin
from electrum.i18n import _
from electrum.invoices import (InvoiceError)
from electrum.invoices import InvoiceError, PR_DEFAULT_EXPIRATION_WHEN_CREATING
from electrum.logging import get_logger
from electrum.network import TxBroadcastError, BestEffortRequestFailed
from electrum.transaction import PartialTxOutput
@ -505,7 +505,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
@pyqtSlot(QEAmount, str, int)
@pyqtSlot(QEAmount, str, int, bool)
@pyqtSlot(QEAmount, str, int, bool, bool)
def create_request(self, amount: QEAmount, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False):
def createRequest(self, amount: QEAmount, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False):
# TODO: unify this method and create_bitcoin_request
try:
if is_lightning:
@ -531,15 +531,17 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
@pyqtSlot()
@pyqtSlot(bool)
def create_default_request(self, ignore_gap: bool = False):
def createDefaultRequest(self, ignore_gap: bool = False):
try:
default_expiry = self.wallet.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING)
if self.wallet.lnworker.channels:
addr = None
if self.wallet.config.get('bolt11_fallback', True):
addr = self.wallet.get_unused_address()
# if addr is None, we ran out of addresses. for lightning enabled wallets, ignore for now
key = self.wallet.create_request(None, None, 3600, addr) # TODO : expiration from config
key = self.wallet.create_request(None, None, default_expiry, addr)
else:
key, addr = self.create_bitcoin_request(None, None, 3600, ignore_gap)
key, addr = self.create_bitcoin_request(None, None, default_expiry, ignore_gap)
if not key:
return
# self.addressModel.init_model()

Loading…
Cancel
Save