Browse Source

qml: add QERequestDetails component.

Auto binds to wallet request status update signal so qml doesn't have to.
implements timer to update status string when near expiry.
patch-4
Sander van Grieken 2 years ago
parent
commit
f05ff0c9b8
  1. 8
      electrum/gui/qml/components/Receive.qml
  2. 59
      electrum/gui/qml/components/RequestDialog.qml
  3. 2
      electrum/gui/qml/qeapp.py
  4. 161
      electrum/gui/qml/qerequestdetails.py
  5. 4
      electrum/gui/qml/qewallet.py

8
electrum/gui/qml/components/Receive.qml

@ -153,7 +153,7 @@ Pane {
model: Daemon.currentWallet.requestModel
delegate: InvoiceDelegate {
onClicked: {
var dialog = requestdialog.createObject(app, {'modelItem': model})
var dialog = requestdialog.createObject(app, {key: model.key})
dialog.open()
}
}
@ -204,12 +204,10 @@ Pane {
Connections {
target: Daemon.currentWallet
function onRequestCreateSuccess() {
function onRequestCreateSuccess(key) {
message.text = ''
amount.text = ''
var dialog = requestdialog.createObject(app, {
'modelItem': delegateModel.items.get(0).model
})
var dialog = requestdialog.createObject(app, { key: key })
dialog.open()
}
function onRequestCreateError(code, error) {

59
electrum/gui/qml/components/RequestDialog.qml

@ -11,7 +11,7 @@ ElDialog {
id: dialog
title: qsTr('Payment Request')
property var modelItem
property string key
property string _bolt11
property string _bip21uri
@ -174,7 +174,7 @@ ElDialog {
icon.source: '../../icons/delete.png'
text: qsTr('Delete')
onClicked: {
Daemon.currentWallet.delete_request(modelItem.key)
Daemon.currentWallet.delete_request(request.key)
dialog.close()
}
}
@ -183,7 +183,7 @@ ElDialog {
icon.color: 'transparent'
text: 'Copy'
onClicked: {
if (modelItem.is_lightning && rootLayout.state == 'bolt11')
if (request.isLightning && rootLayout.state == 'bolt11')
AppController.textToClipboard(_bolt11)
else if (rootLayout.state == 'bip21uri')
AppController.textToClipboard(_bip21uri)
@ -196,7 +196,7 @@ ElDialog {
text: 'Share'
onClicked: {
enabled = false
if (modelItem.is_lightning && rootLayout.state == 'bolt11')
if (request.isLightning && rootLayout.state == 'bolt11')
AppController.doShare(_bolt11, qsTr('Payment Request'))
else if (rootLayout.state == 'bip21uri')
AppController.doShare(_bip21uri, qsTr('Payment Request'))
@ -212,25 +212,25 @@ ElDialog {
columns: 2
Label {
visible: modelItem.message != ''
visible: request.message != ''
text: qsTr('Description')
}
Label {
visible: modelItem.message != ''
visible: request.message != ''
Layout.fillWidth: true
wrapMode: Text.Wrap
text: modelItem.message
text: request.message
font.pixelSize: constants.fontSizeLarge
}
Label {
visible: modelItem.amount.satsInt != 0
visible: request.amount.satsInt != 0
text: qsTr('Amount')
}
RowLayout {
visible: modelItem.amount.satsInt != 0
visible: request.amount.satsInt != 0
Label {
text: Config.formatSats(modelItem.amount)
text: Config.formatSats(request.amount)
font.family: FixedFont
font.pixelSize: constants.fontSizeLarge
font.bold: true
@ -245,7 +245,7 @@ ElDialog {
id: fiatValue
Layout.fillWidth: true
text: Daemon.fx.enabled
? '(' + Daemon.fx.fiatValue(modelItem.amount, false) + ' ' + Daemon.fx.fiatCurrency + ')'
? '(' + Daemon.fx.fiatValue(request.amount, false) + ' ' + Daemon.fx.fiatCurrency + ')'
: ''
font.pixelSize: constants.fontSizeMedium
wrapMode: Text.Wrap
@ -253,17 +253,17 @@ ElDialog {
}
Label {
visible: modelItem.address
visible: request.address
text: qsTr('Address')
}
Label {
visible: modelItem.address
visible: request.address
Layout.fillWidth: true
font.family: FixedFont
font.pixelSize: constants.fontSizeLarge
wrapMode: Text.WrapAnywhere
text: modelItem.address
text: request.address
}
Label {
@ -272,37 +272,30 @@ ElDialog {
Label {
Layout.fillWidth: true
font.pixelSize: constants.fontSizeLarge
text: modelItem.status_str
text: request.status_str
}
}
}
}
Connections {
target: Daemon.currentWallet
function onRequestStatusChanged(key, status) {
if (key != modelItem.key)
return
modelItem = Daemon.currentWallet.get_request(key)
}
}
Component.onCompleted: {
if (!modelItem.is_lightning) {
_bip21uri = bitcoin.create_bip21_uri(modelItem.address, modelItem.amount, modelItem.message, modelItem.timestamp, modelItem.expiration - modelItem.timestamp)
_address = modelItem.address
if (!request.isLightning) {
_bip21uri = request.bip21
_address = request.address
rootLayout.state = 'bip21uri'
} else {
_bolt11 = modelItem.lightning_invoice
_bolt11 = request.bolt11
rootLayout.state = 'bolt11'
if (modelItem.address != '') {
_bip21uri = bitcoin.create_bip21_uri(modelItem.address, modelItem.amount, modelItem.message, modelItem.timestamp, modelItem.expiration - modelItem.timestamp)
_address = modelItem.address
if (request.address != '') {
_bip21uri = request.bip21
_address = request.address
}
}
}
Bitcoin {
id: bitcoin
RequestDetails {
id: request
wallet: Daemon.currentWallet
key: dialog.key
}
}

2
electrum/gui/qml/qeapp.py

@ -20,6 +20,7 @@ from .qebitcoin import QEBitcoin
from .qefx import QEFX
from .qetxfinalizer import QETxFinalizer
from .qeinvoice import QEInvoice, QEInvoiceParser, QEUserEnteredPayment
from .qerequestdetails import QERequestDetails
from .qetypes import QEAmount
from .qeaddressdetails import QEAddressDetails
from .qetxdetails import QETxDetails
@ -156,6 +157,7 @@ class ElectrumQmlApplication(QGuiApplication):
qmlRegisterType(QELnPaymentDetails, 'org.electrum', 1, 0, 'LnPaymentDetails')
qmlRegisterType(QEChannelDetails, 'org.electrum', 1, 0, 'ChannelDetails')
qmlRegisterType(QESwapHelper, 'org.electrum', 1, 0, 'SwapHelper')
qmlRegisterType(QERequestDetails, 'org.electrum', 1, 0, 'RequestDetails')
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')

161
electrum/gui/qml/qerequestdetails.py

@ -0,0 +1,161 @@
from time import time
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer
from electrum.logging import get_logger
from electrum.invoices import PR_UNPAID, LN_EXPIRY_NEVER
from .qewallet import QEWallet
from .qetypes import QEAmount
class QERequestDetails(QObject):
_logger = get_logger(__name__)
_wallet = None
_key = None
_req = None
_timer = None
_amount = None
detailsChanged = pyqtSignal() # generic request properties changed signal
def __init__(self, parent=None):
super().__init__(parent)
def __del__(self):
if self._wallet:
self._wallet.requestStatusChanged.disconnect(self.updateRequestStatus)
if self._timer:
self._timer.stop()
self._timer = None
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
if self._wallet != wallet:
if self._wallet:
self._wallet.requestStatusChanged.disconnect(self.updateRequestStatus)
self._wallet = wallet
self.walletChanged.emit()
wallet.requestStatusChanged.connect(self.updateRequestStatus)
self.initRequest()
keyChanged = pyqtSignal()
@pyqtProperty(str, notify=keyChanged)
def key(self):
return self._key
@key.setter
def key(self, key):
if self._key != key:
self._key = key
self._logger.debug(f'key={key}')
self.keyChanged.emit()
self.initRequest()
statusChanged = pyqtSignal()
@pyqtProperty(int, notify=statusChanged)
def status(self):
return self._wallet.wallet.get_request_status(self._key)
statusStringChanged = pyqtSignal()
@pyqtProperty(str, notify=statusStringChanged)
def status_str(self):
return self._req.get_status_str(self.status)
@pyqtProperty(bool, notify=detailsChanged)
def isLightning(self):
return self._req.is_lightning()
@pyqtProperty(str, notify=detailsChanged)
def address(self):
addr = self._req.get_address()
return addr if addr else ''
@pyqtProperty(str, notify=detailsChanged)
def message(self):
return self._req.get_message()
@pyqtProperty(QEAmount, notify=detailsChanged)
def amount(self):
return self._amount
@pyqtProperty(int, notify=detailsChanged)
def timestamp(self):
return self._req.get_time()
@pyqtProperty(int, notify=detailsChanged)
def expiration(self):
return self._req.get_expiration_date()
@pyqtProperty(str, notify=detailsChanged)
def bolt11(self):
return self._req.lightning_invoice
@pyqtProperty(str, notify=detailsChanged)
def bip21(self):
return self._req.get_bip21_URI()
@pyqtSlot(str, int)
def updateRequestStatus(self, key, status):
if key == self._key:
self._logger.debug(f'request with key {key} updated status ({status})')
self.statusChanged.emit()
def initRequest(self):
if self._wallet is None or self._key is None:
return
self._req = self._wallet.wallet.get_request(self._key)
if self._req is None:
self._logger.error(f'payment request key {key} unknown in wallet {self._wallet.name}')
return
self._amount = QEAmount(from_invoice=self._req)
self.initStatusStringTimer()
def initStatusStringTimer(self):
if self.status == PR_UNPAID:
if self.expiration > 0 and self.expiration != LN_EXPIRY_NEVER:
self._timer = QTimer(self)
self._timer.setSingleShot(True)
self._timer.timeout.connect(self.updateStatusString)
# very roughly according to util.time_difference
exp_in = int(self.expiration - time())
exp_in_min = int(exp_in/60)
interval = 0
if exp_in < 0:
interval = 0
if exp_in_min < 2:
interval = 1000
elif exp_in_min < 90:
interval = 1000 * 60
elif exp_in_min < 1440:
interval = 1000 * 60 * 60
if interval > 0:
self._logger.debug(f'setting status update timer to {interval}, req expires in {exp_in} seconds')
self._timer.setInterval(interval) # msec
self._timer.start()
@pyqtSlot()
def updateStatusString(self):
self.statusStringChanged.emit()
self.initStatusStringTimer()

4
electrum/gui/qml/qewallet.py

@ -51,7 +51,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
isUptodateChanged = pyqtSignal()
requestStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
requestCreateSuccess = pyqtSignal()
requestCreateSuccess = pyqtSignal([str], arguments=['key'])
requestCreateError = pyqtSignal([str,str], arguments=['code','error'])
invoiceStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
invoiceCreateSuccess = pyqtSignal()
@ -526,7 +526,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
assert key is not None
self._requestModel.add_invoice(self.wallet.get_request(key))
self.requestCreateSuccess.emit()
self.requestCreateSuccess.emit(key)
@pyqtSlot(str)
def delete_request(self, key: str):

Loading…
Cancel
Save