Browse Source

frontend improvements, refactor qeinvoicelistmodel

patch-4
Sander van Grieken 3 years ago
parent
commit
3fd33169f5
  1. 4
      electrum/gui/qml/components/Receive.qml
  2. 7
      electrum/gui/qml/components/RequestDialog.qml
  3. 12
      electrum/gui/qml/components/Send.qml
  4. 4
      electrum/gui/qml/components/controls/FiatField.qml
  5. 26
      electrum/gui/qml/components/controls/InvoiceDelegate.qml
  6. 2
      electrum/gui/qml/qebitcoin.py
  7. 51
      electrum/gui/qml/qeinvoicelistmodel.py
  8. 15
      electrum/gui/qml/qetypes.py

4
electrum/gui/qml/components/Receive.qml

@ -39,7 +39,7 @@ Pane {
BtcField { BtcField {
id: amount id: amount
fiatfield: amountFiat fiatfield: amountFiat
Layout.preferredWidth: parent.width /2 Layout.preferredWidth: parent.width /3
} }
Label { Label {
@ -55,7 +55,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
} }
Label { Label {

7
electrum/gui/qml/components/RequestDialog.qml

@ -164,10 +164,12 @@ Dialog {
Label { Label {
text: qsTr('Address') text: qsTr('Address')
visible: !modelItem.is_lightning
} }
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
Layout.columnSpan: 3 Layout.columnSpan: 3
visible: !modelItem.is_lightning
font.family: FixedFont font.family: FixedFont
font.pixelSize: constants.fontSizeLarge font.pixelSize: constants.fontSizeLarge
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
@ -175,6 +177,7 @@ Dialog {
} }
ToolButton { ToolButton {
icon.source: '../../icons/copy_bw.png' icon.source: '../../icons/copy_bw.png'
visible: !modelItem.is_lightning
onClicked: { onClicked: {
AppController.textToClipboard(modelItem.address) AppController.textToClipboard(modelItem.address)
} }
@ -203,9 +206,11 @@ Dialog {
} }
Component.onCompleted: { Component.onCompleted: {
_bip21uri = bitcoin.create_uri(modelItem.address, modelItem.amount, modelItem.message, modelItem.timestamp, modelItem.expiration - modelItem.timestamp) if (!modelItem.is_lightning) {
_bip21uri = bitcoin.create_bip21_uri(modelItem.address, modelItem.amount, modelItem.message, modelItem.timestamp, modelItem.expiration - modelItem.timestamp)
qr.source = 'image://qrgen/' + _bip21uri qr.source = 'image://qrgen/' + _bip21uri
} }
}
Bitcoin { Bitcoin {
id: bitcoin id: bitcoin

12
electrum/gui/qml/components/Send.qml

@ -74,7 +74,7 @@ Pane {
BtcField { BtcField {
id: amount id: amount
fiatfield: amountFiat fiatfield: amountFiat
Layout.preferredWidth: parent.width /2 Layout.preferredWidth: parent.width /3
} }
Label { Label {
@ -91,7 +91,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
} }
Label { Label {
@ -123,6 +123,7 @@ Pane {
Button { Button {
text: qsTr('Save') text: qsTr('Save')
enabled: invoice.invoiceType != Invoice.Invalid enabled: invoice.invoiceType != Invoice.Invalid
icon.source: '../../icons/save.png'
onClicked: { onClicked: {
Daemon.currentWallet.create_invoice(recipient.text, amount.text, message.text) Daemon.currentWallet.create_invoice(recipient.text, amount.text, message.text)
} }
@ -131,6 +132,7 @@ Pane {
Button { Button {
text: qsTr('Pay now') text: qsTr('Pay now')
enabled: invoice.invoiceType != Invoice.Invalid // TODO && has funds enabled: invoice.invoiceType != Invoice.Invalid // TODO && has funds
icon.source: '../../icons/confirmed.png'
onClicked: { onClicked: {
var f_amount = parseFloat(amount.text) var f_amount = parseFloat(amount.text)
if (isNaN(f_amount)) if (isNaN(f_amount))
@ -193,7 +195,7 @@ Pane {
model: Daemon.currentWallet.invoiceModel model: Daemon.currentWallet.invoiceModel
delegate: InvoiceDelegate { delegate: InvoiceDelegate {
onClicked: { onClicked: {
var dialog = confirmInvoiceDialog.createObject(app, {'invoice' : invoice, 'invoice_key': model.key}) var dialog = invoiceDialog.createObject(app, {'invoice' : invoice, 'invoice_key': model.key})
dialog.open() dialog.open()
} }
} }
@ -227,7 +229,7 @@ Pane {
} }
Component { Component {
id: confirmInvoiceDialog id: invoiceDialog
InvoiceDialog { InvoiceDialog {
onDoPay: { onDoPay: {
if (invoice.invoiceType == Invoice.OnchainInvoice) { if (invoice.invoiceType == Invoice.OnchainInvoice) {
@ -285,7 +287,7 @@ Pane {
if (invoiceType == Invoice.OnchainOnlyAddress) if (invoiceType == Invoice.OnchainOnlyAddress)
recipient.text = invoice.recipient recipient.text = invoice.recipient
else { else {
var dialog = confirmInvoiceDialog.createObject(rootItem, {'invoice': invoice}) var dialog = invoiceDialog.createObject(rootItem, {'invoice': invoice})
dialog.open() dialog.open()
} }
} }

4
electrum/gui/qml/components/controls/FiatField.qml

@ -13,7 +13,9 @@ TextField {
inputMethodHints: Qt.ImhPreferNumbers inputMethodHints: Qt.ImhPreferNumbers
onTextChanged: { onTextChanged: {
if (amountFiat.activeFocus) if (amountFiat.activeFocus)
btcfield.text = text == '' ? '' : Config.satsToUnits(Daemon.fx.satoshiValue(amountFiat.text)) btcfield.text = text == ''
? ''
: Config.satsToUnits(Daemon.fx.satoshiValue(amountFiat.text))
} }
Connections { Connections {

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

@ -36,6 +36,18 @@ ItemDelegate {
source: model.is_lightning source: model.is_lightning
? "../../../icons/lightning.png" ? "../../../icons/lightning.png"
: "../../../icons/bitcoin.png" : "../../../icons/bitcoin.png"
Image {
visible: model.onchain_fallback
z: -1
source: "../../../icons/bitcoin.png"
anchors {
right: parent.right
bottom: parent.bottom
}
width: parent.width /2
height: parent.height /2
}
} }
RowLayout { RowLayout {
@ -55,13 +67,13 @@ ItemDelegate {
Label { Label {
id: amount id: amount
text: model.amount == 0 ? '' : Config.formatSats(model.amount) text: model.amount.isEmpty ? '' : Config.formatSats(model.amount)
font.pixelSize: constants.fontSizeMedium font.pixelSize: constants.fontSizeMedium
font.family: FixedFont font.family: FixedFont
} }
Label { Label {
text: model.amount == 0 ? '' : Config.baseUnit text: model.amount.isEmpty ? '' : Config.baseUnit
font.pixelSize: constants.fontSizeMedium font.pixelSize: constants.fontSizeMedium
color: Material.accentColor color: Material.accentColor
} }
@ -95,14 +107,14 @@ ItemDelegate {
id: fiatValue id: fiatValue
visible: Daemon.fx.enabled visible: Daemon.fx.enabled
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: model.amount == 0 ? '' : Daemon.fx.fiatValue(model.amount, false) text: model.amount.isEmpty ? '' : Daemon.fx.fiatValue(model.amount, false)
font.family: FixedFont font.family: FixedFont
font.pixelSize: constants.fontSizeSmall font.pixelSize: constants.fontSizeSmall
} }
Label { Label {
visible: Daemon.fx.enabled visible: Daemon.fx.enabled
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: model.amount == 0 ? '' : Daemon.fx.fiatCurrency text: model.amount.isEmpty ? '' : Daemon.fx.fiatCurrency
font.pixelSize: constants.fontSizeSmall font.pixelSize: constants.fontSizeSmall
color: Material.accentColor color: Material.accentColor
} }
@ -119,16 +131,16 @@ ItemDelegate {
Connections { Connections {
target: Config target: Config
function onBaseUnitChanged() { function onBaseUnitChanged() {
amount.text = model.amount == 0 ? '' : Config.formatSats(model.amount) amount.text = model.amount.isEmpty ? '' : Config.formatSats(model.amount)
} }
function onThousandsSeparatorChanged() { function onThousandsSeparatorChanged() {
amount.text = model.amount == 0 ? '' : Config.formatSats(model.amount) amount.text = model.amount.isEmpty ? '' : Config.formatSats(model.amount)
} }
} }
Connections { Connections {
target: Daemon.fx target: Daemon.fx
function onQuotesUpdated() { function onQuotesUpdated() {
fiatValue.text = model.amount == 0 ? '' : Daemon.fx.fiatValue(model.amount, false) fiatValue.text = model.amount.isEmpty ? '' : Daemon.fx.fiatValue(model.amount, false)
} }
} }

2
electrum/gui/qml/qebitcoin.py

@ -123,7 +123,7 @@ class QEBitcoin(QObject):
return { 'error': str(e) } return { 'error': str(e) }
@pyqtSlot(str, QEAmount, str, int, int, result=str) @pyqtSlot(str, QEAmount, str, int, int, result=str)
def create_uri(self, address, satoshis, message, timestamp, expiry): def create_bip21_uri(self, address, satoshis, message, timestamp, expiry):
extra_params = {} extra_params = {}
if expiry: if expiry:
extra_params['time'] = str(timestamp) extra_params['time'] = str(timestamp)

51
electrum/gui/qml/qeinvoicelistmodel.py

@ -18,7 +18,8 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
self.init_model() self.init_model()
# define listmodel rolemap # define listmodel rolemap
_ROLE_NAMES=('key','is_lightning','timestamp','date','message','amount','status','status_str','address','expiration','type') _ROLE_NAMES=('key', 'is_lightning', 'timestamp', 'date', 'message', 'amount',
'status', 'status_str', 'address', 'expiration', 'type', 'onchain_fallback')
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES)) _ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES])) _ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES]))
_ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS)) _ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS))
@ -96,6 +97,19 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
return return
i = i + 1 i = i + 1
def invoice_to_model(self, invoice: Invoice):
item = self.get_invoice_as_dict(invoice)
item['key'] = invoice.get_id()
item['is_lightning'] = invoice.is_lightning()
if invoice.is_lightning() and 'address' not in item:
item['address'] = ''
item['date'] = format_time(item['timestamp'])
item['amount'] = QEAmount(from_invoice=invoice)
item['onchain_fallback'] = invoice.is_lightning() and invoice._lnaddr.get_fallback_address()
item['type'] = 'invoice'
return item
@abstractmethod @abstractmethod
def get_invoice_for_key(self, key: str): def get_invoice_for_key(self, key: str):
raise Exception('provide impl') raise Exception('provide impl')
@ -105,55 +119,52 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
raise Exception('provide impl') raise Exception('provide impl')
@abstractmethod @abstractmethod
def invoice_to_model(self, invoice: Invoice): def get_invoice_as_dict(self, invoice: Invoice):
raise Exception('provide impl') raise Exception('provide impl')
class QEInvoiceListModel(QEAbstractInvoiceListModel): class QEInvoiceListModel(QEAbstractInvoiceListModel):
def __init__(self, wallet, parent=None): def __init__(self, wallet, parent=None):
super().__init__(wallet, parent) super().__init__(wallet, parent)
_logger = get_logger(__name__) _logger = get_logger(__name__)
def get_invoice_list(self):
return self.wallet.get_unpaid_invoices()
def invoice_to_model(self, invoice: Invoice): def invoice_to_model(self, invoice: Invoice):
item = self.wallet.export_invoice(invoice) item = super().invoice_to_model(invoice)
item['is_lightning'] = invoice.is_lightning()
item['date'] = format_time(item['timestamp'])
item['amount'] = QEAmount(amount_sat=invoice.get_amount_sat())
item['key'] = invoice.get_id()
item['type'] = 'invoice' item['type'] = 'invoice'
return item return item
def get_invoice_list(self):
return self.wallet.get_unpaid_invoices()
def get_invoice_for_key(self, key: str): def get_invoice_for_key(self, key: str):
return self.wallet.get_invoice(key) return self.wallet.get_invoice(key)
def get_invoice_as_dict(self, invoice: Invoice):
return self.wallet.export_invoice(invoice)
class QERequestListModel(QEAbstractInvoiceListModel): class QERequestListModel(QEAbstractInvoiceListModel):
def __init__(self, wallet, parent=None): def __init__(self, wallet, parent=None):
super().__init__(wallet, parent) super().__init__(wallet, parent)
_logger = get_logger(__name__) _logger = get_logger(__name__)
def get_invoice_list(self):
return self.wallet.get_unpaid_requests()
def invoice_to_model(self, req: Invoice): def invoice_to_model(self, req: Invoice):
item = self.wallet.export_request(req) item = super().invoice_to_model(req)
item['key'] = req.get_rhash() if req.is_lightning() else req.get_address()
item['is_lightning'] = req.is_lightning()
item['date'] = format_time(item['timestamp'])
item['amount'] = QEAmount(amount_sat=req.get_amount_sat())
item['type'] = 'request' item['type'] = 'request'
return item return item
def get_invoice_list(self):
return self.wallet.get_unpaid_requests()
def get_invoice_for_key(self, key: str): def get_invoice_for_key(self, key: str):
return self.wallet.get_request(key) return self.wallet.get_request(key)
def get_invoice_as_dict(self, req: Invoice):
return self.wallet.export_request(req)
@pyqtSlot(str, int) @pyqtSlot(str, int)
def updateRequest(self, key, status): def updateRequest(self, key, status):
self.updateInvoice(key, status) self.updateInvoice(key, status)

15
electrum/gui/qml/qetypes.py

@ -15,11 +15,18 @@ from electrum.util import profiler
class QEAmount(QObject): class QEAmount(QObject):
_logger = get_logger(__name__) _logger = get_logger(__name__)
def __init__(self, *, amount_sat: int = 0, amount_msat: int = 0, is_max: bool = False, parent=None): def __init__(self, *, amount_sat: int = 0, amount_msat: int = 0, is_max: bool = False, from_invoice = None, parent=None):
super().__init__(parent) super().__init__(parent)
self._amount_sat = amount_sat self._amount_sat = amount_sat
self._amount_msat = amount_msat self._amount_msat = amount_msat
self._is_max = is_max self._is_max = is_max
if from_invoice:
inv_amt = from_invoice.get_amount_msat()
if inv_amt == '!':
self._is_max = True
elif inv_amt is not None:
self._amount_msat = inv_amt
self._amount_sat = from_invoice.get_amount_sat()
valueChanged = pyqtSignal() valueChanged = pyqtSignal()
@ -43,6 +50,10 @@ class QEAmount(QObject):
def isMax(self): def isMax(self):
return self._is_max return self._is_max
@pyqtProperty(bool, notify=valueChanged)
def isEmpty(self):
return not(self._is_max or self._amount_sat or self._amount_msat)
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, QEAmount): if isinstance(other, QEAmount):
return self._amount_sat == other._amount_sat and self._amount_msat == other._amount_msat and self._is_max == other._is_max return self._amount_sat == other._amount_sat and self._amount_msat == other._amount_msat and self._is_max == other._is_max
@ -60,4 +71,4 @@ class QEAmount(QObject):
return '%s(sats=%d, msats=%d)' % (s, self._amount_sat, self._amount_msat) return '%s(sats=%d, msats=%d)' % (s, self._amount_sat, self._amount_msat)
def __repr__(self): def __repr__(self):
return f"<QEAmount max={self._is_max} sats={self._amount_sat} msats={self._amount_msat}>" return f"<QEAmount max={self._is_max} sats={self._amount_sat} msats={self._amount_msat} empty={self.isEmpty}>"

Loading…
Cancel
Save