Browse Source

add initial Transaction Details page and backing qobject

patch-4
Sander van Grieken 3 years ago
parent
commit
a6e72ae42f
  1. 2
      electrum/gui/qml/components/Constants.qml
  2. 8
      electrum/gui/qml/components/History.qml
  3. 257
      electrum/gui/qml/components/TxDetails.qml
  4. 2
      electrum/gui/qml/qeapp.py
  5. 11
      electrum/gui/qml/qetransactionlistmodel.py
  6. 143
      electrum/gui/qml/qetxdetails.py

2
electrum/gui/qml/components/Constants.qml

@ -3,6 +3,7 @@ import QtQuick.Controls.Material 2.0
Item {
readonly property int paddingTiny: 4
readonly property int paddingXSmall: 6
readonly property int paddingSmall: 8
readonly property int paddingMedium: 12
readonly property int paddingLarge: 16
@ -25,4 +26,5 @@ Item {
property color colorCredit: "#ff80ff80"
property color colorDebit: "#ffff8080"
property color mutedForeground: 'gray' //Qt.lighter(Material.background, 2)
property color colorMine: "yellow"
}

8
electrum/gui/qml/components/History.qml

@ -66,6 +66,14 @@ Pane {
Layout.fillWidth: true
Layout.preferredHeight: txinfo.height
onClicked: {
var page = app.stack.push(Qt.resolvedUrl('TxDetails.qml'), {'txid': model.txid})
page.txDetailsChanged.connect(function() {
// update listmodel when details change
visualModel.model.update_tx_label(model.txid, page.label)
})
}
GridLayout {
id: txinfo
columns: 3

257
electrum/gui/qml/components/TxDetails.qml

@ -0,0 +1,257 @@
import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0
import org.electrum 1.0
import "controls"
Pane {
id: root
width: parent.width
height: parent.height
property string title: qsTr("Transaction details")
property string txid
property alias label: txdetails.label
signal txDetailsChanged
property QtObject menu: Menu {
id: menu
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Bump fee')
enabled: txdetails.canBump
//onTriggered:
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Cancel double-spend')
enabled: txdetails.canCancel
}
}
}
Flickable {
anchors.fill: parent
contentHeight: rootLayout.height
clip: true
interactive: height < contentHeight
GridLayout {
id: rootLayout
width: parent.width
columns: 2
Label {
text: qsTr('Status')
color: Material.accentColor
}
Label {
text: txdetails.status
}
Label {
text: qsTr('Mempool depth')
color: Material.accentColor
visible: !txdetails.isMined
}
Label {
text: txdetails.mempoolDepth
visible: !txdetails.isMined
}
Label {
text: qsTr('Date')
color: Material.accentColor
}
Label {
text: txdetails.date
}
Label {
text: txdetails.amount.satsInt > 0
? qsTr('Amount received')
: qsTr('Amount sent')
color: Material.accentColor
}
RowLayout {
Label {
text: Config.formatSats(txdetails.amount)
}
Label {
text: Config.baseUnit
color: Material.accentColor
}
}
Label {
text: qsTr('Transaction fee')
color: Material.accentColor
}
RowLayout {
Label {
text: Config.formatSats(txdetails.fee)
}
Label {
text: Config.baseUnit
color: Material.accentColor
}
}
Label {
text: qsTr('Transaction ID')
Layout.columnSpan: 2
color: Material.accentColor
}
TextHighlightPane {
Layout.columnSpan: 2
Layout.fillWidth: true
padding: 0
leftPadding: constants.paddingSmall
RowLayout {
width: parent.width
Label {
text: root.txid
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
Layout.fillWidth: true
wrapMode: Text.Wrap
}
ToolButton {
icon.source: '../../icons/share.png'
icon.color: 'transparent'
onClicked: {
var dialog = share.createObject(root, { 'title': qsTr('Transaction ID'), 'text': root.txid })
dialog.open()
}
}
}
}
Label {
text: qsTr('Label')
Layout.columnSpan: 2
color: Material.accentColor
}
TextHighlightPane {
id: labelContent
property bool editmode: false
Layout.columnSpan: 2
Layout.fillWidth: true
padding: 0
leftPadding: constants.paddingSmall
RowLayout {
width: parent.width
Label {
visible: !labelContent.editmode
text: txdetails.label
wrapMode: Text.Wrap
Layout.fillWidth: true
font.pixelSize: constants.fontSizeLarge
}
ToolButton {
visible: !labelContent.editmode
icon.source: '../../icons/pen.png'
icon.color: 'transparent'
onClicked: {
labelEdit.text = txdetails.label
labelContent.editmode = true
}
}
TextField {
id: labelEdit
visible: labelContent.editmode
text: txdetails.label
font.pixelSize: constants.fontSizeLarge
Layout.fillWidth: true
}
ToolButton {
visible: labelContent.editmode
icon.source: '../../icons/confirmed.png'
icon.color: 'transparent'
onClicked: {
labelContent.editmode = false
txdetails.set_label(labelEdit.text)
}
}
ToolButton {
visible: labelContent.editmode
icon.source: '../../icons/delete.png'
icon.color: 'transparent'
onClicked: labelContent.editmode = false
}
}
}
Label {
text: qsTr('Outputs')
Layout.columnSpan: 2
color: Material.accentColor
}
Repeater {
model: txdetails.outputs
delegate: TextHighlightPane {
Layout.columnSpan: 2
Layout.fillWidth: true
padding: 0
leftPadding: constants.paddingSmall
RowLayout {
width: parent.width
Label {
text: modelData.address
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
color: modelData.is_mine ? constants.colorMine : Material.foreground
}
Label {
text: Config.formatSats(modelData.value)
font.pixelSize: constants.fontSizeLarge
}
Label {
text: Config.baseUnit
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
}
}
}
}
}
TxDetails {
id: txdetails
wallet: Daemon.currentWallet
txid: root.txid
onLabelChanged: txDetailsChanged()
}
Component {
id: share
GenericShareDialog {}
}
}

2
electrum/gui/qml/qeapp.py

@ -22,6 +22,7 @@ from .qetxfinalizer import QETxFinalizer
from .qeinvoice import QEInvoice
from .qetypes import QEAmount
from .qeaddressdetails import QEAddressDetails
from .qetxdetails import QETxDetails
notification = None
@ -120,6 +121,7 @@ class ElectrumQmlApplication(QGuiApplication):
qmlRegisterType(QETxFinalizer, 'org.electrum', 1, 0, 'TxFinalizer')
qmlRegisterType(QEInvoice, 'org.electrum', 1, 0, 'Invoice')
qmlRegisterType(QEAddressDetails, 'org.electrum', 1, 0, 'AddressDetails')
qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails')
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')

11
electrum/gui/qml/qetransactionlistmodel.py

@ -117,6 +117,17 @@ class QETransactionListModel(QAbstractListModel):
return
i = i + 1
@pyqtSlot(str, str)
def update_tx_label(self, txid, label):
i = 0
for tx in self.tx_history:
if tx['txid'] == txid:
tx['label'] = label
index = self.index(i,0)
self.dataChanged.emit(index, index, [self._ROLE_RMAP['label']])
return
i = i + 1
@pyqtSlot(int)
def updateBlockchainHeight(self, height):
self._logger.debug('updating height to %d' % height)

143
electrum/gui/qml/qetxdetails.py

@ -0,0 +1,143 @@
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
#from decimal import Decimal
from electrum.logging import get_logger
from electrum.util import format_time
from .qewallet import QEWallet
from .qetypes import QEAmount
class QETxDetails(QObject):
def __init__(self, parent=None):
super().__init__(parent)
_logger = get_logger(__name__)
_wallet = None
_txid = None
_mempool_depth = None
_date = None
detailsChanged = pyqtSignal()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
txidChanged = pyqtSignal()
@pyqtProperty(str, notify=txidChanged)
def txid(self):
return self._txid
@txid.setter
def txid(self, txid: str):
if self._txid != txid:
self._logger.debug('txid set -> %s' % txid)
self._txid = txid
self.txidChanged.emit()
self.update()
labelChanged = pyqtSignal()
@pyqtProperty(str, notify=labelChanged)
def label(self):
return self._label
@pyqtSlot(str)
def set_label(self, label: str):
if label != self._label:
self._wallet.wallet.set_label(self._txid, label)
self._label = label
self.labelChanged.emit()
@pyqtProperty(str, notify=detailsChanged)
def status(self):
return self._status
@pyqtProperty(str, notify=detailsChanged)
def date(self):
return self._date
@pyqtProperty(str, notify=detailsChanged)
def mempoolDepth(self):
return self._mempool_depth
@pyqtProperty(bool, notify=detailsChanged)
def isMined(self):
return self._is_mined
@pyqtProperty(bool, notify=detailsChanged)
def isLightningFundingTx(self):
return self._is_lightning_funding_tx
@pyqtProperty(bool, notify=detailsChanged)
def canBump(self):
return self._can_bump
@pyqtProperty(bool, notify=detailsChanged)
def canCancel(self):
return self._can_dscancel
@pyqtProperty(QEAmount, notify=detailsChanged)
def amount(self):
return self._amount
@pyqtProperty(QEAmount, notify=detailsChanged)
def fee(self):
return self._fee
@pyqtProperty('QVariantList', notify=detailsChanged)
def inputs(self):
return self._inputs
@pyqtProperty('QVariantList', notify=detailsChanged)
def outputs(self):
return self._outputs
def update(self):
if self._wallet is None:
self._logger.error('wallet undefined')
return
# abusing get_input_tx to get tx from txid
tx = self._wallet.wallet.get_input_tx(self._txid)
self._inputs = list(map(lambda x: x.to_json(), tx.inputs()))
self._outputs = list(map(lambda x: {
'address': x.get_ui_address_str(),
'value': QEAmount(amount_sat=x.value),
'is_mine': self._wallet.wallet.is_mine(x.get_ui_address_str())
}, tx.outputs()))
txinfo = self._wallet.wallet.get_tx_info(tx)
self._status = txinfo.status
self._label = txinfo.label
self._amount = QEAmount(amount_sat=txinfo.amount) # can be None?
self._fee = QEAmount(amount_sat=txinfo.fee)
self._is_mined = txinfo.tx_mined_status != None
if self._is_mined:
self._date = format_time(txinfo.tx_mined_status.timestamp)
else:
#TODO mempool_depth_bytes can be None?
self._mempool_depth = self._wallet.wallet.config.depth_tooltip(txinfo.mempool_depth_bytes)
self._is_lightning_funding_tx = txinfo.is_lightning_funding_tx
self._can_bump = txinfo.can_bump
self._can_dscancel = txinfo.can_dscancel
self._logger.debug(repr(txinfo.mempool_depth_bytes))
self._logger.debug(repr(txinfo.can_broadcast))
self._logger.debug(repr(txinfo.can_cpfp))
self._logger.debug(repr(txinfo.can_save_as_local))
self._logger.debug(repr(txinfo.can_remove))
self.detailsChanged.emit()
Loading…
Cancel
Save