add initial Transaction Details page and backing qobject
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
Normal file
257
electrum/gui/qml/components/TxDetails.qml
Normal file
@@ -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 {}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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
Normal file
143
electrum/gui/qml/qetxdetails.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user