add a QEAmount type for storing/passing BTC amounts in the widest sense
from a UI perspective. Stores sats, millisats (LN), whether MAX amount is requested etc some refactor QEInvoice type and Send page
This commit is contained in:
@@ -11,6 +11,7 @@ Dialog {
|
|||||||
id: dialog
|
id: dialog
|
||||||
|
|
||||||
property Invoice invoice
|
property Invoice invoice
|
||||||
|
property string invoice_key
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
@@ -84,9 +85,20 @@ Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr('Expiration')
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: expiration
|
||||||
|
text: invoice.time + invoice.expiration
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
|
Layout.fillHeight: true
|
||||||
spacing: constants.paddingMedium
|
spacing: constants.paddingMedium
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -115,4 +127,9 @@ Dialog {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (invoice_key != '') {
|
||||||
|
invoice.initFromKey(invoice_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,12 @@ Pane {
|
|||||||
model: DelegateModel {
|
model: DelegateModel {
|
||||||
id: delegateModel
|
id: delegateModel
|
||||||
model: Daemon.currentWallet.invoiceModel
|
model: Daemon.currentWallet.invoiceModel
|
||||||
delegate: InvoiceDelegate {}
|
delegate: InvoiceDelegate {
|
||||||
|
onClicked: {
|
||||||
|
var dialog = confirmInvoiceDialog.createObject(app, {'invoice' : invoice, 'invoice_key': model.key})
|
||||||
|
dialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove: Transition {
|
remove: Transition {
|
||||||
@@ -288,9 +293,7 @@ Pane {
|
|||||||
// and maybe store invoice if expiry allows
|
// and maybe store invoice if expiry allows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onInvoiceTypeChanged: {
|
onValidationSuccess: {
|
||||||
if (invoiceType == Invoice.Invalid)
|
|
||||||
return
|
|
||||||
// address only -> fill form fields
|
// address only -> fill form fields
|
||||||
// else -> show invoice confirmation dialog
|
// else -> show invoice confirmation dialog
|
||||||
if (invoiceType == Invoice.OnchainOnlyAddress)
|
if (invoiceType == Invoice.OnchainOnlyAddress)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import os
|
|||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QUrl, QLocale, qInstallMessageHandler, QTimer
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QUrl, QLocale, qInstallMessageHandler, QTimer
|
||||||
from PyQt5.QtGui import QGuiApplication, QFontDatabase
|
from PyQt5.QtGui import QGuiApplication, QFontDatabase
|
||||||
from PyQt5.QtQml import qmlRegisterType, QQmlApplicationEngine
|
from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType, QQmlApplicationEngine
|
||||||
|
|
||||||
from electrum.logging import Logger, get_logger
|
from electrum.logging import Logger, get_logger
|
||||||
from electrum import version
|
from electrum import version
|
||||||
@@ -20,6 +20,7 @@ from .qebitcoin import QEBitcoin
|
|||||||
from .qefx import QEFX
|
from .qefx import QEFX
|
||||||
from .qetxfinalizer import QETxFinalizer
|
from .qetxfinalizer import QETxFinalizer
|
||||||
from .qeinvoice import QEInvoice
|
from .qeinvoice import QEInvoice
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
notification = None
|
notification = None
|
||||||
|
|
||||||
@@ -118,6 +119,8 @@ class ElectrumQmlApplication(QGuiApplication):
|
|||||||
qmlRegisterType(QETxFinalizer, 'org.electrum', 1, 0, 'TxFinalizer')
|
qmlRegisterType(QETxFinalizer, 'org.electrum', 1, 0, 'TxFinalizer')
|
||||||
qmlRegisterType(QEInvoice, 'org.electrum', 1, 0, 'Invoice')
|
qmlRegisterType(QEInvoice, 'org.electrum', 1, 0, 'Invoice')
|
||||||
|
|
||||||
|
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')
|
||||||
|
|
||||||
self.engine = QQmlApplicationEngine(parent=self)
|
self.engine = QQmlApplicationEngine(parent=self)
|
||||||
self.engine.addImportPath('./qml')
|
self.engine.addImportPath('./qml')
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ from electrum.slip39 import decode_mnemonic, Slip39Error
|
|||||||
from electrum import mnemonic
|
from electrum import mnemonic
|
||||||
from electrum.util import parse_URI, create_bip21_uri, InvalidBitcoinURI
|
from electrum.util import parse_URI, create_bip21_uri, InvalidBitcoinURI
|
||||||
|
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
class QEBitcoin(QObject):
|
class QEBitcoin(QObject):
|
||||||
def __init__(self, config, parent=None):
|
def __init__(self, config, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -121,11 +123,11 @@ class QEBitcoin(QObject):
|
|||||||
except InvalidBitcoinURI as e:
|
except InvalidBitcoinURI as e:
|
||||||
return { 'error': str(e) }
|
return { 'error': str(e) }
|
||||||
|
|
||||||
@pyqtSlot(str, 'qint64', str, int, int, result=str)
|
@pyqtSlot(str, QEAmount, str, int, int, result=str)
|
||||||
def create_uri(self, address, satoshis, message, timestamp, expiry):
|
def create_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)
|
||||||
extra_params['exp'] = str(expiry)
|
extra_params['exp'] = str(expiry)
|
||||||
|
|
||||||
return create_bip21_uri(address, satoshis, message, extra_query_params=extra_params)
|
return create_bip21_uri(address, satoshis.satsInt, message, extra_query_params=extra_params)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from decimal import Decimal
|
|||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.util import DECIMAL_POINT_DEFAULT
|
from electrum.util import DECIMAL_POINT_DEFAULT
|
||||||
|
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
class QEConfig(QObject):
|
class QEConfig(QObject):
|
||||||
def __init__(self, config, parent=None):
|
def __init__(self, config, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -70,7 +72,11 @@ class QEConfig(QObject):
|
|||||||
|
|
||||||
@pyqtSlot('qint64', result=str)
|
@pyqtSlot('qint64', result=str)
|
||||||
@pyqtSlot('qint64', bool, result=str)
|
@pyqtSlot('qint64', bool, result=str)
|
||||||
|
@pyqtSlot(QEAmount, result=str)
|
||||||
|
@pyqtSlot(QEAmount, bool, result=str)
|
||||||
def formatSats(self, satoshis, with_unit=False):
|
def formatSats(self, satoshis, with_unit=False):
|
||||||
|
if isinstance(satoshis, QEAmount):
|
||||||
|
satoshis = satoshis.satsInt
|
||||||
if with_unit:
|
if with_unit:
|
||||||
return self.config.format_amount_and_units(satoshis)
|
return self.config.format_amount_and_units(satoshis)
|
||||||
else:
|
else:
|
||||||
@@ -85,11 +91,11 @@ class QEConfig(QObject):
|
|||||||
|
|
||||||
@pyqtSlot(str, result='qint64')
|
@pyqtSlot(str, result='qint64')
|
||||||
def unitsToSats(self, unitAmount):
|
def unitsToSats(self, unitAmount):
|
||||||
# returns amt in satoshis
|
|
||||||
try:
|
try:
|
||||||
x = Decimal(unitAmount)
|
x = Decimal(unitAmount)
|
||||||
except:
|
except:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# scale it to max allowed precision, make it an int
|
# scale it to max allowed precision, make it an int
|
||||||
max_prec_amount = int(pow(10, self.max_precision()) * x)
|
max_prec_amount = int(pow(10, self.max_precision()) * x)
|
||||||
# if the max precision is simply what unit conversion allows, just return
|
# if the max precision is simply what unit conversion allows, just return
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from electrum.simple_config import SimpleConfig
|
|||||||
from electrum.util import register_callback
|
from electrum.util import register_callback
|
||||||
from electrum.bitcoin import COIN
|
from electrum.bitcoin import COIN
|
||||||
|
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
class QEFX(QObject):
|
class QEFX(QObject):
|
||||||
def __init__(self, fxthread: FxThread, config: SimpleConfig, parent=None):
|
def __init__(self, fxthread: FxThread, config: SimpleConfig, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -88,25 +90,40 @@ class QEFX(QObject):
|
|||||||
|
|
||||||
@pyqtSlot(str, result=str)
|
@pyqtSlot(str, result=str)
|
||||||
@pyqtSlot(str, bool, result=str)
|
@pyqtSlot(str, bool, result=str)
|
||||||
|
@pyqtSlot(QEAmount, result=str)
|
||||||
|
@pyqtSlot(QEAmount, bool, result=str)
|
||||||
def fiatValue(self, satoshis, plain=True):
|
def fiatValue(self, satoshis, plain=True):
|
||||||
rate = self.fx.exchange_rate()
|
rate = self.fx.exchange_rate()
|
||||||
try:
|
if isinstance(satoshis, QEAmount):
|
||||||
sd = Decimal(satoshis)
|
satoshis = satoshis.satsInt
|
||||||
if sd == 0:
|
else:
|
||||||
|
try:
|
||||||
|
sd = Decimal(satoshis)
|
||||||
|
if sd == 0:
|
||||||
|
return ''
|
||||||
|
except:
|
||||||
return ''
|
return ''
|
||||||
except:
|
|
||||||
return ''
|
|
||||||
if plain:
|
if plain:
|
||||||
return self.fx.ccy_amount_str(self.fx.fiat_value(satoshis, rate), False)
|
return self.fx.ccy_amount_str(self.fx.fiat_value(satoshis, rate), False)
|
||||||
else:
|
else:
|
||||||
return self.fx.value_str(satoshis, rate)
|
return self.fx.value_str(satoshis, rate)
|
||||||
|
|
||||||
@pyqtSlot(str, str, result=str)
|
@pyqtSlot(str, str, result=str)
|
||||||
|
@pyqtSlot(str, str, bool, result=str)
|
||||||
|
@pyqtSlot(QEAmount, str, result=str)
|
||||||
|
@pyqtSlot(QEAmount, str, bool, result=str)
|
||||||
def fiatValueHistoric(self, satoshis, timestamp, plain=True):
|
def fiatValueHistoric(self, satoshis, timestamp, plain=True):
|
||||||
try:
|
if isinstance(satoshis, QEAmount):
|
||||||
sd = Decimal(satoshis)
|
satoshis = satoshis.satsInt
|
||||||
if sd == 0:
|
else:
|
||||||
|
try:
|
||||||
|
sd = Decimal(satoshis)
|
||||||
|
if sd == 0:
|
||||||
|
return ''
|
||||||
|
except:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
try:
|
||||||
td = Decimal(timestamp)
|
td = Decimal(timestamp)
|
||||||
if td == 0:
|
if td == 0:
|
||||||
return ''
|
return ''
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from electrum.invoices import Invoice, OnchainInvoice, LNInvoice
|
|||||||
from electrum.transaction import PartialTxOutput
|
from electrum.transaction import PartialTxOutput
|
||||||
|
|
||||||
from .qewallet import QEWallet
|
from .qewallet import QEWallet
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
class QEInvoice(QObject):
|
class QEInvoice(QObject):
|
||||||
|
|
||||||
@@ -30,28 +31,26 @@ class QEInvoice(QObject):
|
|||||||
_invoiceType = Type.Invalid
|
_invoiceType = Type.Invalid
|
||||||
_recipient = ''
|
_recipient = ''
|
||||||
_effectiveInvoice = None
|
_effectiveInvoice = None
|
||||||
_message = ''
|
|
||||||
_amount = 0
|
|
||||||
|
|
||||||
validationError = pyqtSignal([str,str], arguments=['code', 'message'])
|
invoiceChanged = pyqtSignal()
|
||||||
validationWarning = pyqtSignal([str,str], arguments=['code', 'message'])
|
|
||||||
invoiceSaved = pyqtSignal()
|
invoiceSaved = pyqtSignal()
|
||||||
|
|
||||||
|
validationSuccess = pyqtSignal()
|
||||||
|
validationWarning = pyqtSignal([str,str], arguments=['code', 'message'])
|
||||||
|
validationError = 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
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
invoiceTypeChanged = pyqtSignal()
|
@pyqtProperty(int, notify=invoiceChanged)
|
||||||
@pyqtProperty(int, notify=invoiceTypeChanged)
|
|
||||||
def invoiceType(self):
|
def invoiceType(self):
|
||||||
return self._invoiceType
|
return self._invoiceType
|
||||||
|
|
||||||
# not a qt setter, don't let outside set state
|
# not a qt setter, don't let outside set state
|
||||||
def setInvoiceType(self, invoiceType: Type):
|
def setInvoiceType(self, invoiceType: Type):
|
||||||
#if self._invoiceType != invoiceType:
|
self._invoiceType = invoiceType
|
||||||
self._invoiceType = invoiceType
|
|
||||||
self.invoiceTypeChanged.emit()
|
|
||||||
|
|
||||||
walletChanged = pyqtSignal()
|
walletChanged = pyqtSignal()
|
||||||
@pyqtProperty(QEWallet, notify=walletChanged)
|
@pyqtProperty(QEWallet, notify=walletChanged)
|
||||||
@@ -77,16 +76,29 @@ class QEInvoice(QObject):
|
|||||||
self.validateRecipient(recipient)
|
self.validateRecipient(recipient)
|
||||||
self.recipientChanged.emit()
|
self.recipientChanged.emit()
|
||||||
|
|
||||||
messageChanged = pyqtSignal()
|
@pyqtProperty(str, notify=invoiceChanged)
|
||||||
@pyqtProperty(str, notify=messageChanged)
|
|
||||||
def message(self):
|
def message(self):
|
||||||
return self._message
|
return self._effectiveInvoice.message if self._effectiveInvoice else ''
|
||||||
|
|
||||||
amountChanged = pyqtSignal()
|
@pyqtProperty(QEAmount, notify=invoiceChanged)
|
||||||
@pyqtProperty('quint64', notify=amountChanged)
|
|
||||||
def amount(self):
|
def amount(self):
|
||||||
|
# store ref to QEAmount on instance, otherwise we get destroyed when going out of scope
|
||||||
|
self._amount = QEAmount() #
|
||||||
|
if not self._effectiveInvoice:
|
||||||
|
return self._amount
|
||||||
|
sats = self._effectiveInvoice.get_amount_sat()
|
||||||
|
if not sats:
|
||||||
|
return self._amount
|
||||||
|
self._amount = QEAmount(amount_sat=sats)
|
||||||
return self._amount
|
return self._amount
|
||||||
|
|
||||||
|
@pyqtProperty('quint64', notify=invoiceChanged)
|
||||||
|
def expiration(self):
|
||||||
|
return self._effectiveInvoice.exp if self._effectiveInvoice else 0
|
||||||
|
|
||||||
|
@pyqtProperty('quint64', notify=invoiceChanged)
|
||||||
|
def time(self):
|
||||||
|
return self._effectiveInvoice.time if self._effectiveInvoice else 0
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def clear(self):
|
def clear(self):
|
||||||
@@ -94,31 +106,38 @@ class QEInvoice(QObject):
|
|||||||
self.invoiceSetsAmount = False
|
self.invoiceSetsAmount = False
|
||||||
self.setInvoiceType(QEInvoice.Type.Invalid)
|
self.setInvoiceType(QEInvoice.Type.Invalid)
|
||||||
self._bip21 = None
|
self._bip21 = None
|
||||||
|
self.invoiceChanged.emit()
|
||||||
|
|
||||||
|
# don't parse the recipient string, but init qeinvoice from an invoice key
|
||||||
|
# this should not emit validation signals
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def initFromKey(self, key):
|
||||||
|
invoice = self._wallet.wallet.get_invoice(key)
|
||||||
|
self._logger.debug(repr(invoice))
|
||||||
|
if invoice:
|
||||||
|
self.set_effective_invoice(invoice)
|
||||||
|
|
||||||
|
def set_effective_invoice(self, invoice: Invoice):
|
||||||
|
self._effectiveInvoice = invoice
|
||||||
|
if invoice.is_lightning():
|
||||||
|
self.setInvoiceType(QEInvoice.Type.LightningInvoice)
|
||||||
|
else:
|
||||||
|
self.setInvoiceType(QEInvoice.Type.OnchainInvoice)
|
||||||
|
self.invoiceChanged.emit()
|
||||||
|
|
||||||
def setValidAddressOnly(self):
|
def setValidAddressOnly(self):
|
||||||
self._logger.debug('setValidAddressOnly')
|
self._logger.debug('setValidAddressOnly')
|
||||||
self.setInvoiceType(QEInvoice.Type.OnchainOnlyAddress)
|
self.setInvoiceType(QEInvoice.Type.OnchainOnlyAddress)
|
||||||
self._effectiveInvoice = None ###TODO
|
self._effectiveInvoice = None ###TODO
|
||||||
|
self.invoiceChanged.emit()
|
||||||
|
|
||||||
def setValidOnchainInvoice(self, invoice: OnchainInvoice):
|
def setValidOnchainInvoice(self, invoice: OnchainInvoice):
|
||||||
self._logger.debug('setValidOnchainInvoice')
|
self._logger.debug('setValidOnchainInvoice')
|
||||||
self.setInvoiceType(QEInvoice.Type.OnchainInvoice)
|
self.set_effective_invoice(invoice)
|
||||||
self._amount = invoice.get_amount_sat()
|
|
||||||
self.amountChanged.emit()
|
|
||||||
self._message = invoice.message
|
|
||||||
self.messageChanged.emit()
|
|
||||||
|
|
||||||
self._effectiveInvoice = invoice
|
|
||||||
|
|
||||||
def setValidLightningInvoice(self, invoice: LNInvoice):
|
def setValidLightningInvoice(self, invoice: LNInvoice):
|
||||||
self._logger.debug('setValidLightningInvoice')
|
self._logger.debug('setValidLightningInvoice')
|
||||||
self.setInvoiceType(QEInvoice.Type.LightningInvoice)
|
self.set_effective_invoice(invoice)
|
||||||
self._effectiveInvoice = invoice
|
|
||||||
|
|
||||||
self._amount = int(invoice.get_amount_sat()) # TODO: float/str msat precision
|
|
||||||
self.amountChanged.emit()
|
|
||||||
self._message = invoice.message
|
|
||||||
self.messageChanged.emit()
|
|
||||||
|
|
||||||
def create_onchain_invoice(self, outputs, message, payment_request, uri):
|
def create_onchain_invoice(self, outputs, message, payment_request, uri):
|
||||||
return self._wallet.wallet.create_invoice(
|
return self._wallet.wallet.create_invoice(
|
||||||
@@ -150,6 +169,7 @@ class QEInvoice(QObject):
|
|||||||
if ':' not in recipient:
|
if ':' not in recipient:
|
||||||
# address only
|
# address only
|
||||||
self.setValidAddressOnly()
|
self.setValidAddressOnly()
|
||||||
|
self.validationSuccess.emit()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# fallback lightning invoice?
|
# fallback lightning invoice?
|
||||||
@@ -194,13 +214,15 @@ class QEInvoice(QObject):
|
|||||||
self._logger.debug(outputs)
|
self._logger.debug(outputs)
|
||||||
message = self._bip21['message'] if 'message' in self._bip21 else ''
|
message = self._bip21['message'] if 'message' in self._bip21 else ''
|
||||||
invoice = self.create_onchain_invoice(outputs, message, None, self._bip21)
|
invoice = self.create_onchain_invoice(outputs, message, None, self._bip21)
|
||||||
self._logger.debug(invoice)
|
self._logger.debug(repr(invoice))
|
||||||
self.setValidOnchainInvoice(invoice)
|
self.setValidOnchainInvoice(invoice)
|
||||||
|
self.validationSuccess.emit()
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def save_invoice(self):
|
def save_invoice(self):
|
||||||
if not self._effectiveInvoice:
|
if not self._effectiveInvoice:
|
||||||
return
|
return
|
||||||
|
# TODO detect duplicate?
|
||||||
self._wallet.wallet.save_invoice(self._effectiveInvoice)
|
self._wallet.wallet.save_invoice(self._effectiveInvoice)
|
||||||
self.invoiceSaved.emit()
|
self.invoiceSaved.emit()
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ from electrum.logging import get_logger
|
|||||||
from electrum.util import Satoshis, format_time
|
from electrum.util import Satoshis, format_time
|
||||||
from electrum.invoices import Invoice
|
from electrum.invoices import Invoice
|
||||||
|
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
class QEAbstractInvoiceListModel(QAbstractListModel):
|
class QEAbstractInvoiceListModel(QAbstractListModel):
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -35,6 +37,8 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
|
|||||||
return value
|
return value
|
||||||
if isinstance(value, Satoshis):
|
if isinstance(value, Satoshis):
|
||||||
return value.value
|
return value.value
|
||||||
|
if isinstance(value, QEAmount):
|
||||||
|
return value
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
@@ -111,7 +115,7 @@ class QEInvoiceListModel(QEAbstractInvoiceListModel):
|
|||||||
item = self.wallet.export_invoice(invoice)
|
item = self.wallet.export_invoice(invoice)
|
||||||
item['type'] = invoice.type # 0=onchain, 2=LN
|
item['type'] = invoice.type # 0=onchain, 2=LN
|
||||||
item['date'] = format_time(item['timestamp'])
|
item['date'] = format_time(item['timestamp'])
|
||||||
item['amount'] = invoice.get_amount_sat()
|
item['amount'] = QEAmount(amount_sat=invoice.get_amount_sat())
|
||||||
if invoice.type == 0:
|
if invoice.type == 0:
|
||||||
item['key'] = invoice.id
|
item['key'] = invoice.id
|
||||||
elif invoice.type == 2:
|
elif invoice.type == 2:
|
||||||
@@ -136,7 +140,7 @@ class QERequestListModel(QEAbstractInvoiceListModel):
|
|||||||
item['key'] = self.wallet.get_key_for_receive_request(req)
|
item['key'] = self.wallet.get_key_for_receive_request(req)
|
||||||
item['type'] = req.type # 0=onchain, 2=LN
|
item['type'] = req.type # 0=onchain, 2=LN
|
||||||
item['date'] = format_time(item['timestamp'])
|
item['date'] = format_time(item['timestamp'])
|
||||||
item['amount'] = req.get_amount_sat()
|
item['amount'] = QEAmount(amount_sat=req.get_amount_sat())
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
|
|||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.util import Satoshis, TxMinedInfo
|
from electrum.util import Satoshis, TxMinedInfo
|
||||||
|
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
class QETransactionListModel(QAbstractListModel):
|
class QETransactionListModel(QAbstractListModel):
|
||||||
def __init__(self, wallet, parent=None):
|
def __init__(self, wallet, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -36,6 +38,8 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
return value
|
return value
|
||||||
if isinstance(value, Satoshis):
|
if isinstance(value, Satoshis):
|
||||||
return value.value
|
return value.value
|
||||||
|
if isinstance(value, QEAmount):
|
||||||
|
return value
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
@@ -48,6 +52,9 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
for output in item['outputs']:
|
for output in item['outputs']:
|
||||||
output['value'] = output['value'].value
|
output['value'] = output['value'].value
|
||||||
|
|
||||||
|
item['bc_value'] = QEAmount(amount_sat=item['bc_value'].value)
|
||||||
|
item['bc_balance'] = QEAmount(amount_sat=item['bc_balance'].value)
|
||||||
|
|
||||||
# newly arriving txs have no (block) timestamp
|
# newly arriving txs have no (block) timestamp
|
||||||
# TODO?
|
# TODO?
|
||||||
if not item['timestamp']:
|
if not item['timestamp']:
|
||||||
|
|||||||
55
electrum/gui/qml/qetypes.py
Normal file
55
electrum/gui/qml/qetypes.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
||||||
|
|
||||||
|
from electrum.logging import get_logger
|
||||||
|
from electrum.i18n import _
|
||||||
|
from electrum.util import profiler
|
||||||
|
|
||||||
|
# container for satoshi amounts that can be passed around more
|
||||||
|
# easily between python, QML-property and QML-javascript contexts
|
||||||
|
# QML 'int' is 32 bit signed, so overflows on satoshi amounts
|
||||||
|
# QML 'quint64' and 'qint64' can be used, but this breaks
|
||||||
|
# down when passing through property bindings
|
||||||
|
# should also capture millisats amounts and MAX/'!' indicators
|
||||||
|
# and (unformatted) string representations
|
||||||
|
|
||||||
|
class QEAmount(QObject):
|
||||||
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
|
def __init__(self, *, amount_sat: int = 0, amount_msat: int = 0, is_max: bool = False, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._amount_sat = amount_sat
|
||||||
|
self._amount_msat = amount_msat
|
||||||
|
self._is_max = is_max
|
||||||
|
|
||||||
|
valueChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtProperty('qint64', notify=valueChanged)
|
||||||
|
def satsInt(self):
|
||||||
|
return self._amount_sat
|
||||||
|
|
||||||
|
@pyqtProperty('qint64', notify=valueChanged)
|
||||||
|
def msatsInt(self):
|
||||||
|
return self._amount_msat
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=valueChanged)
|
||||||
|
def satsStr(self):
|
||||||
|
return str(self._amount_sat)
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=valueChanged)
|
||||||
|
def msatsStr(self):
|
||||||
|
return str(self._amount_msat)
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=valueChanged)
|
||||||
|
def isMax(self):
|
||||||
|
return self._is_max
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
self._logger.debug('__eq__')
|
||||||
|
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
|
||||||
|
elif isinstance(other, int):
|
||||||
|
return self._amount_sat == other
|
||||||
|
elif isinstance(other, str):
|
||||||
|
return self.satsStr == other
|
||||||
|
|
||||||
|
return False
|
||||||
Reference in New Issue
Block a user