initial create invoice from user input
This commit is contained in:
@@ -84,7 +84,7 @@ Pane {
|
|||||||
BtcField {
|
BtcField {
|
||||||
id: amount
|
id: amount
|
||||||
fiatfield: amountFiat
|
fiatfield: amountFiat
|
||||||
Layout.preferredWidth: parent.width /2
|
Layout.preferredWidth: parent.width /3
|
||||||
onTextChanged: channelopener.amount = Config.unitsToSats(amount.text)
|
onTextChanged: channelopener.amount = Config.unitsToSats(amount.text)
|
||||||
enabled: !is_max.checked
|
enabled: !is_max.checked
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ Pane {
|
|||||||
id: amountFiat
|
id: amountFiat
|
||||||
btcfield: amount
|
btcfield: amount
|
||||||
visible: Daemon.fx.enabled
|
visible: Daemon.fx.enabled
|
||||||
Layout.preferredWidth: parent.width /2
|
Layout.preferredWidth: parent.width /3
|
||||||
enabled: !is_max.checked
|
enabled: !is_max.checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Pane {
|
|||||||
function clear() {
|
function clear() {
|
||||||
recipient.text = ''
|
recipient.text = ''
|
||||||
amount.text = ''
|
amount.text = ''
|
||||||
|
message.text = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
@@ -21,10 +22,10 @@ Pane {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
rowSpacing: constants.paddingSmall
|
rowSpacing: constants.paddingSmall
|
||||||
columnSpacing: constants.paddingSmall
|
columnSpacing: constants.paddingSmall
|
||||||
columns: 4
|
columns: 3
|
||||||
|
|
||||||
BalanceSummary {
|
BalanceSummary {
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 3
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,20 +33,22 @@ Pane {
|
|||||||
text: qsTr('Recipient')
|
text: qsTr('Recipient')
|
||||||
}
|
}
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: recipient
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
Layout.fillWidth: true
|
|
||||||
font.family: FixedFont
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
placeholderText: qsTr('Paste address or invoice')
|
|
||||||
onTextChanged: {
|
|
||||||
if (activeFocus)
|
|
||||||
invoice.recipient = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
|
||||||
|
TextArea {
|
||||||
|
id: recipient
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.family: FixedFont
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
placeholderText: qsTr('Paste address or invoice')
|
||||||
|
onTextChanged: {
|
||||||
|
if (activeFocus)
|
||||||
|
invoice.recipient = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
ToolButton {
|
ToolButton {
|
||||||
icon.source: '../../icons/paste.png'
|
icon.source: '../../icons/paste.png'
|
||||||
@@ -75,15 +78,26 @@ Pane {
|
|||||||
id: amount
|
id: amount
|
||||||
fiatfield: amountFiat
|
fiatfield: amountFiat
|
||||||
Layout.preferredWidth: parent.width /3
|
Layout.preferredWidth: parent.width /3
|
||||||
|
onTextChanged: {
|
||||||
|
invoice.create_invoice(recipient.text, is_max.checked ? MAX : Config.unitsToSats(amount.text), message.text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
RowLayout {
|
||||||
text: Config.baseUnit
|
|
||||||
color: Material.accentColor
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
|
||||||
|
|
||||||
Item { width: 1; height: 1 }
|
Label {
|
||||||
|
text: Config.baseUnit
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
Switch {
|
||||||
|
id: is_max
|
||||||
|
text: qsTr('Max')
|
||||||
|
onCheckedChanged: {
|
||||||
|
invoice.create_invoice(recipient.text, is_max.checked ? MAX : Config.unitsToSats(amount.text), message.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item { width: 1; height: 1; visible: Daemon.fx.enabled }
|
Item { width: 1; height: 1; visible: Daemon.fx.enabled }
|
||||||
|
|
||||||
@@ -95,54 +109,56 @@ Pane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
visible: Daemon.fx.enabled
|
visible: Daemon.fx.enabled
|
||||||
text: Daemon.fx.fiatCurrency
|
text: Daemon.fx.fiatCurrency
|
||||||
color: Material.accentColor
|
color: Material.accentColor
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { visible: Daemon.fx.enabled ; height: 1; width: 1 }
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr('Description')
|
text: qsTr('Description')
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: message
|
id: message
|
||||||
font.family: FixedFont
|
|
||||||
placeholderText: qsTr('Message')
|
placeholderText: qsTr('Message')
|
||||||
Layout.columnSpan: 3
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
onTextChanged: {
|
||||||
|
invoice.create_invoice(recipient.text, is_max.checked ? MAX : Config.unitsToSats(amount.text), message.text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 3
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
spacing: constants.paddingMedium
|
spacing: constants.paddingMedium
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr('Save')
|
text: qsTr('Save')
|
||||||
enabled: invoice.invoiceType != Invoice.Invalid
|
enabled: invoice.canSave
|
||||||
icon.source: '../../icons/save.png'
|
icon.source: '../../icons/save.png'
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Daemon.currentWallet.create_invoice(recipient.text, amount.text, message.text)
|
invoice.save_invoice()
|
||||||
|
invoice.clear()
|
||||||
|
rootItem.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr('Pay now')
|
text: qsTr('Pay now')
|
||||||
enabled: invoice.invoiceType != Invoice.Invalid // TODO && has funds
|
enabled: invoice.canPay
|
||||||
icon.source: '../../icons/confirmed.png'
|
icon.source: '../../icons/confirmed.png'
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var f_amount = parseFloat(amount.text)
|
invoice.save_invoice()
|
||||||
if (isNaN(f_amount))
|
|
||||||
return
|
|
||||||
var dialog = confirmPaymentDialog.createObject(app, {
|
var dialog = confirmPaymentDialog.createObject(app, {
|
||||||
'address': recipient.text,
|
'address': recipient.text,
|
||||||
'satoshis': Config.unitsToSats(amount.text),
|
'satoshis': Config.unitsToSats(amount.text),
|
||||||
'message': message.text
|
'message': message.text
|
||||||
})
|
})
|
||||||
dialog.open()
|
dialog.open()
|
||||||
|
invoice.clear()
|
||||||
|
rootItem.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,6 +307,8 @@ Pane {
|
|||||||
dialog.open()
|
dialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onInvoiceCreateError: console.log(code + ' ' + message)
|
||||||
|
|
||||||
onInvoiceSaved: {
|
onInvoiceSaved: {
|
||||||
console.log('invoice got saved')
|
console.log('invoice got saved')
|
||||||
Daemon.currentWallet.invoiceModel.init_model()
|
Daemon.currentWallet.invoiceModel.init_model()
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS
|
|||||||
|
|
||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.keystore import bip39_is_checksum_valid
|
|
||||||
from electrum.util import (parse_URI, create_bip21_uri, InvalidBitcoinURI, InvoiceError,
|
from electrum.util import (parse_URI, create_bip21_uri, InvalidBitcoinURI, InvoiceError,
|
||||||
maybe_extract_bolt11_invoice)
|
maybe_extract_bolt11_invoice)
|
||||||
from electrum.invoices import Invoice
|
from electrum.invoices import Invoice
|
||||||
from electrum.invoices import (PR_UNPAID,PR_EXPIRED,PR_UNKNOWN,PR_PAID,PR_INFLIGHT,
|
from electrum.invoices import (PR_UNPAID,PR_EXPIRED,PR_UNKNOWN,PR_PAID,PR_INFLIGHT,
|
||||||
PR_FAILED,PR_ROUTING,PR_UNCONFIRMED)
|
PR_FAILED,PR_ROUTING,PR_UNCONFIRMED)
|
||||||
from electrum.transaction import PartialTxOutput
|
from electrum.transaction import PartialTxOutput
|
||||||
|
from electrum import bitcoin
|
||||||
|
|
||||||
from .qewallet import QEWallet
|
from .qewallet import QEWallet
|
||||||
from .qetypes import QEAmount
|
from .qetypes import QEAmount
|
||||||
@@ -44,6 +44,8 @@ class QEInvoice(QObject):
|
|||||||
_invoiceType = Type.Invalid
|
_invoiceType = Type.Invalid
|
||||||
_recipient = ''
|
_recipient = ''
|
||||||
_effectiveInvoice = None
|
_effectiveInvoice = None
|
||||||
|
_canSave = False
|
||||||
|
_canPay = False
|
||||||
|
|
||||||
invoiceChanged = pyqtSignal()
|
invoiceChanged = pyqtSignal()
|
||||||
invoiceSaved = pyqtSignal()
|
invoiceSaved = pyqtSignal()
|
||||||
@@ -52,6 +54,8 @@ class QEInvoice(QObject):
|
|||||||
validationWarning = pyqtSignal([str,str], arguments=['code', 'message'])
|
validationWarning = pyqtSignal([str,str], arguments=['code', 'message'])
|
||||||
validationError = pyqtSignal([str,str], arguments=['code', 'message'])
|
validationError = pyqtSignal([str,str], arguments=['code', 'message'])
|
||||||
|
|
||||||
|
invoiceCreateError = pyqtSignal([str,str], arguments=['code', 'message'])
|
||||||
|
|
||||||
def __init__(self, config, parent=None):
|
def __init__(self, config, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.config = config
|
self.config = config
|
||||||
@@ -99,10 +103,7 @@ class QEInvoice(QObject):
|
|||||||
self._amount = QEAmount()
|
self._amount = QEAmount()
|
||||||
if not self._effectiveInvoice:
|
if not self._effectiveInvoice:
|
||||||
return self._amount
|
return self._amount
|
||||||
sats = self._effectiveInvoice.get_amount_sat()
|
self._amount = QEAmount(from_invoice=self._effectiveInvoice)
|
||||||
if not sats:
|
|
||||||
return self._amount
|
|
||||||
self._amount = QEAmount(amount_sat=sats)
|
|
||||||
return self._amount
|
return self._amount
|
||||||
|
|
||||||
@pyqtProperty('quint64', notify=invoiceChanged)
|
@pyqtProperty('quint64', notify=invoiceChanged)
|
||||||
@@ -132,12 +133,33 @@ class QEInvoice(QObject):
|
|||||||
def address(self):
|
def address(self):
|
||||||
return self._effectiveInvoice.get_address() if self._effectiveInvoice else ''
|
return self._effectiveInvoice.get_address() if self._effectiveInvoice else ''
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=invoiceChanged)
|
||||||
|
def canSave(self):
|
||||||
|
return self._canSave
|
||||||
|
|
||||||
|
@canSave.setter
|
||||||
|
def canSave(self, _canSave):
|
||||||
|
if self._canSave != _canSave:
|
||||||
|
self._canSave = _canSave
|
||||||
|
self.invoiceChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=invoiceChanged)
|
||||||
|
def canPay(self):
|
||||||
|
return self._canPay
|
||||||
|
|
||||||
|
@canPay.setter
|
||||||
|
def canPay(self, _canPay):
|
||||||
|
if self._canPay != _canPay:
|
||||||
|
self._canPay = _canPay
|
||||||
|
self.invoiceChanged.emit()
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.recipient = ''
|
self.recipient = ''
|
||||||
self.invoiceSetsAmount = False
|
|
||||||
self.setInvoiceType(QEInvoice.Type.Invalid)
|
self.setInvoiceType(QEInvoice.Type.Invalid)
|
||||||
self._bip21 = None
|
self._bip21 = None
|
||||||
|
self._canSave = False
|
||||||
|
self._canPay = False
|
||||||
self.invoiceChanged.emit()
|
self.invoiceChanged.emit()
|
||||||
|
|
||||||
# don't parse the recipient string, but init qeinvoice from an invoice key
|
# don't parse the recipient string, but init qeinvoice from an invoice key
|
||||||
@@ -155,6 +177,9 @@ class QEInvoice(QObject):
|
|||||||
self.setInvoiceType(QEInvoice.Type.LightningInvoice)
|
self.setInvoiceType(QEInvoice.Type.LightningInvoice)
|
||||||
else:
|
else:
|
||||||
self.setInvoiceType(QEInvoice.Type.OnchainInvoice)
|
self.setInvoiceType(QEInvoice.Type.OnchainInvoice)
|
||||||
|
# TODO check if exists?
|
||||||
|
self.canSave = True
|
||||||
|
self.canPay = True # TODO
|
||||||
self.invoiceChanged.emit()
|
self.invoiceChanged.emit()
|
||||||
self.statusChanged.emit()
|
self.statusChanged.emit()
|
||||||
|
|
||||||
@@ -257,6 +282,7 @@ class QEInvoice(QObject):
|
|||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def save_invoice(self):
|
def save_invoice(self):
|
||||||
|
self.canSave = False
|
||||||
if not self._effectiveInvoice:
|
if not self._effectiveInvoice:
|
||||||
return
|
return
|
||||||
# TODO detect duplicate?
|
# TODO detect duplicate?
|
||||||
@@ -265,9 +291,12 @@ class QEInvoice(QObject):
|
|||||||
|
|
||||||
@pyqtSlot(str, QEAmount, str)
|
@pyqtSlot(str, QEAmount, str)
|
||||||
def create_invoice(self, address: str, amount: QEAmount, message: str):
|
def create_invoice(self, address: str, amount: QEAmount, message: str):
|
||||||
# create onchain invoice from user entered fields
|
# create invoice from user entered fields
|
||||||
# (any other type of invoice is created from parsing recipient)
|
# (any other type of invoice is created from parsing recipient)
|
||||||
self._logger.debug('saving invoice to %s' % address)
|
self._logger.debug('creating invoice to %s, amount=%s, message=%s' % (address, repr(amount), message))
|
||||||
|
|
||||||
|
self.clear()
|
||||||
|
|
||||||
if not address:
|
if not address:
|
||||||
self.invoiceCreateError.emit('fatal', _('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request'))
|
self.invoiceCreateError.emit('fatal', _('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request'))
|
||||||
return
|
return
|
||||||
@@ -276,19 +305,17 @@ class QEInvoice(QObject):
|
|||||||
self.invoiceCreateError.emit('fatal', _('Invalid Bitcoin address'))
|
self.invoiceCreateError.emit('fatal', _('Invalid Bitcoin address'))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.amount:
|
if amount.isEmpty:
|
||||||
self.invoiceCreateError.emit('fatal', _('Invalid amount'))
|
self.invoiceCreateError.emit('fatal', _('Invalid amount'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
inv_amt = '!' if amount.isMax else (amount.satsInt * 1000) # FIXME msat precision from UI?
|
||||||
|
|
||||||
|
try:
|
||||||
|
outputs = [PartialTxOutput.from_address_and_value(address, inv_amt)]
|
||||||
|
invoice = self._wallet.wallet.create_invoice(outputs=outputs, message=message, pr=None, URI=None)
|
||||||
|
except InvoiceError as e:
|
||||||
|
self.invoiceCreateError.emit('fatal', _('Error creating payment') + ':\n' + str(e))
|
||||||
|
return
|
||||||
|
|
||||||
#
|
self.set_effective_invoice(invoice)
|
||||||
if self.is_max:
|
|
||||||
amount = '!'
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
amount = self.app.get_amount(self.amount)
|
|
||||||
except:
|
|
||||||
self.app.show_error(_('Invalid amount') + ':\n' + self.amount)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user