wip
This commit is contained in:
BIN
electrum/gui/icons/save.png
Normal file
BIN
electrum/gui/icons/save.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 946 B |
@@ -67,11 +67,19 @@ Pane {
|
|||||||
Layout.preferredHeight: txinfo.height
|
Layout.preferredHeight: txinfo.height
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var page = app.stack.push(Qt.resolvedUrl('TxDetails.qml'), {'txid': model.txid})
|
if (model.lightning) {
|
||||||
page.txDetailsChanged.connect(function() {
|
var page = app.stack.push(Qt.resolvedUrl('LightningPaymentDetails.qml'), {'key': model.key})
|
||||||
// update listmodel when details change
|
page.detailsChanged.connect(function() {
|
||||||
visualModel.model.update_tx_label(model.txid, page.label)
|
// update listmodel when details change
|
||||||
})
|
visualModel.model.update_tx_label(model.key, page.label)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
var page = app.stack.push(Qt.resolvedUrl('TxDetails.qml'), {'txid': model.key})
|
||||||
|
page.detailsChanged.connect(function() {
|
||||||
|
// update listmodel when details change
|
||||||
|
visualModel.model.update_tx_label(model.key, page.label)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
@@ -82,6 +90,7 @@ Pane {
|
|||||||
width: delegate.width - 2*constants.paddingSmall
|
width: delegate.width - 2*constants.paddingSmall
|
||||||
|
|
||||||
Item { Layout.columnSpan: 3; Layout.preferredWidth: 1; Layout.preferredHeight: 1}
|
Item { Layout.columnSpan: 3; Layout.preferredWidth: 1; Layout.preferredHeight: 1}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
readonly property variant tx_icons : [
|
readonly property variant tx_icons : [
|
||||||
"../../../gui/icons/unconfirmed.png",
|
"../../../gui/icons/unconfirmed.png",
|
||||||
@@ -97,7 +106,7 @@ Pane {
|
|||||||
Layout.preferredHeight: constants.iconSizeLarge
|
Layout.preferredHeight: constants.iconSizeLarge
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.rowSpan: 2
|
Layout.rowSpan: 2
|
||||||
source: tx_icons[Math.min(6,model.confirmations)]
|
source: model.lightning ? "../../../gui/icons/lightning.png" : tx_icons[Math.min(6,model.confirmations)]
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -118,7 +127,7 @@ Pane {
|
|||||||
color: model.incoming ? constants.colorCredit : constants.colorDebit
|
color: model.incoming ? constants.colorCredit : constants.colorDebit
|
||||||
|
|
||||||
function updateText() {
|
function updateText() {
|
||||||
text = Config.formatSats(model.bc_value)
|
text = Config.formatSats(model.value)
|
||||||
}
|
}
|
||||||
Component.onCompleted: updateText()
|
Component.onCompleted: updateText()
|
||||||
}
|
}
|
||||||
@@ -137,9 +146,9 @@ Pane {
|
|||||||
if (!Daemon.fx.enabled) {
|
if (!Daemon.fx.enabled) {
|
||||||
text = ''
|
text = ''
|
||||||
} else if (Daemon.fx.historicRates) {
|
} else if (Daemon.fx.historicRates) {
|
||||||
text = Daemon.fx.fiatValueHistoric(model.bc_value, model.timestamp) + ' ' + Daemon.fx.fiatCurrency
|
text = Daemon.fx.fiatValueHistoric(model.value, model.timestamp) + ' ' + Daemon.fx.fiatCurrency
|
||||||
} else {
|
} else {
|
||||||
text = Daemon.fx.fiatValue(model.bc_value, false) + ' ' + Daemon.fx.fiatCurrency
|
text = Daemon.fx.fiatValue(model.value, false) + ' ' + Daemon.fx.fiatCurrency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component.onCompleted: updateText()
|
Component.onCompleted: updateText()
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ Dialog {
|
|||||||
text: qsTr('Save')
|
text: qsTr('Save')
|
||||||
icon.source: '../../icons/save.png'
|
icon.source: '../../icons/save.png'
|
||||||
visible: invoice_key == ''
|
visible: invoice_key == ''
|
||||||
enabled: invoice.invoiceType == Invoice.OnchainInvoice
|
enabled: invoice.canSave
|
||||||
onClicked: {
|
onClicked: {
|
||||||
invoice.save_invoice()
|
invoice.save_invoice()
|
||||||
dialog.close()
|
dialog.close()
|
||||||
@@ -138,10 +138,13 @@ Dialog {
|
|||||||
icon.source: '../../icons/confirmed.png'
|
icon.source: '../../icons/confirmed.png'
|
||||||
enabled: invoice.invoiceType != Invoice.Invalid // TODO && has funds
|
enabled: invoice.invoiceType != Invoice.Invalid // TODO && has funds
|
||||||
onClicked: {
|
onClicked: {
|
||||||
invoice.save_invoice()
|
if (invoice_key == '')
|
||||||
|
invoice.save_invoice()
|
||||||
dialog.close()
|
dialog.close()
|
||||||
if (invoice.invoiceType == Invoice.OnchainInvoice) {
|
if (invoice.invoiceType == Invoice.OnchainInvoice) {
|
||||||
doPay() // only signal here
|
doPay() // only signal here
|
||||||
|
} else if (invoice.invoiceType == Invoice.LightningInvoice) {
|
||||||
|
doPay() // only signal here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
261
electrum/gui/qml/components/LightningPaymentDetails.qml
Normal file
261
electrum/gui/qml/components/LightningPaymentDetails.qml
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
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("Lightning payment details")
|
||||||
|
|
||||||
|
property string key
|
||||||
|
|
||||||
|
property alias label: lnpaymentdetails.label
|
||||||
|
|
||||||
|
signal detailsChanged
|
||||||
|
|
||||||
|
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: lnpaymentdetails.status
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr('Date')
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: lnpaymentdetails.date
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: lnpaymentdetails.amount.msatsInt > 0
|
||||||
|
? qsTr('Amount received')
|
||||||
|
: qsTr('Amount sent')
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: Config.formatMilliSats(lnpaymentdetails.amount)
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: Config.baseUnit
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
visible: lnpaymentdetails.amount.msatsInt < 0
|
||||||
|
text: qsTr('Transaction fee')
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
visible: lnpaymentdetails.amount.msatsInt < 0
|
||||||
|
Label {
|
||||||
|
text: Config.formatMilliSats(lnpaymentdetails.fee)
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: Config.baseUnit
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: lnpaymentdetails.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 = lnpaymentdetails.label
|
||||||
|
labelContent.editmode = true
|
||||||
|
labelEdit.focus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: labelEdit
|
||||||
|
visible: labelContent.editmode
|
||||||
|
text: lnpaymentdetails.label
|
||||||
|
font.pixelSize: constants.fontSizeLarge
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
ToolButton {
|
||||||
|
visible: labelContent.editmode
|
||||||
|
icon.source: '../../icons/confirmed.png'
|
||||||
|
icon.color: 'transparent'
|
||||||
|
onClicked: {
|
||||||
|
labelContent.editmode = false
|
||||||
|
lnpaymentdetails.set_label(labelEdit.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolButton {
|
||||||
|
visible: labelContent.editmode
|
||||||
|
icon.source: '../../icons/delete.png'
|
||||||
|
icon.color: 'transparent'
|
||||||
|
onClicked: labelContent.editmode = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr('Payment hash')
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
TextHighlightPane {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
padding: 0
|
||||||
|
leftPadding: constants.paddingSmall
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
Label {
|
||||||
|
text: lnpaymentdetails.payment_hash
|
||||||
|
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('Payment hash'), 'text': lnpaymentdetails.payment_hash })
|
||||||
|
dialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr('Preimage')
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
TextHighlightPane {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
padding: 0
|
||||||
|
leftPadding: constants.paddingSmall
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
Label {
|
||||||
|
text: lnpaymentdetails.preimage
|
||||||
|
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('Preimage'), 'text': lnpaymentdetails.preimage })
|
||||||
|
dialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr('Lightning invoice')
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
color: Material.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
TextHighlightPane {
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
padding: 0
|
||||||
|
leftPadding: constants.paddingSmall
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: lnpaymentdetails.invoice
|
||||||
|
font.pixelSize: constants.fontSizeLarge
|
||||||
|
font.family: FixedFont
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 3
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
ToolButton {
|
||||||
|
icon.source: '../../icons/share.png'
|
||||||
|
icon.color: enabled ? 'transparent' : constants.mutedForeground
|
||||||
|
enabled: lnpaymentdetails.invoice != ''
|
||||||
|
onClicked: {
|
||||||
|
var dialog = share.createObject(root, { 'title': qsTr('Lightning Invoice'), 'text': lnpaymentdetails.invoice })
|
||||||
|
dialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LnPaymentDetails {
|
||||||
|
id: lnpaymentdetails
|
||||||
|
wallet: Daemon.currentWallet
|
||||||
|
key: root.key
|
||||||
|
onLabelChanged: root.detailsChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: share
|
||||||
|
GenericShareDialog {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
127
electrum/gui/qml/components/LightningPaymentProgressDialog.qml
Normal file
127
electrum/gui/qml/components/LightningPaymentProgressDialog.qml
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import QtQuick 2.6
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Controls.Material 2.0
|
||||||
|
|
||||||
|
import org.electrum 1.0
|
||||||
|
|
||||||
|
import "controls"
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: dialog
|
||||||
|
|
||||||
|
required property string invoice_key
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
title: qsTr('Paying Lightning Invoice...')
|
||||||
|
standardButtons: Dialog.Cancel
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
parent: Overlay.overlay
|
||||||
|
Overlay.modal: Rectangle {
|
||||||
|
color: "#aa000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: s
|
||||||
|
state: ''
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: ''
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: 'success'
|
||||||
|
PropertyChanges { target: spinner; running: false }
|
||||||
|
PropertyChanges { target: helpText; text: qsTr('Paid!') }
|
||||||
|
PropertyChanges { target: dialog; standardButtons: Dialog.Ok }
|
||||||
|
PropertyChanges { target: icon; source: '../../icons/confirmed.png' }
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: 'failed'
|
||||||
|
PropertyChanges { target: spinner; running: false }
|
||||||
|
PropertyChanges { target: helpText; text: qsTr('Payment failed') }
|
||||||
|
PropertyChanges { target: dialog; standardButtons: Dialog.Ok }
|
||||||
|
PropertyChanges { target: errorText; visible: true }
|
||||||
|
PropertyChanges { target: icon; source: '../../icons/warning.png' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: ''
|
||||||
|
to: 'success'
|
||||||
|
PropertyAnimation { target: helpText; properties: 'text'; duration: 0}
|
||||||
|
NumberAnimation { target: icon; properties: 'opacity'; from: 0; to: 1; duration: 200 }
|
||||||
|
NumberAnimation { target: icon; properties: 'scale'; from: 0; to: 1; duration: 500
|
||||||
|
easing.type: Easing.OutBack
|
||||||
|
easing.overshoot: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
from: ''
|
||||||
|
to: 'failed'
|
||||||
|
PropertyAnimation { target: helpText; properties: 'text'; duration: 0}
|
||||||
|
NumberAnimation { target: icon; properties: 'opacity'; from: 0; to: 1; duration: 500 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: constants.iconSizeXXLarge
|
||||||
|
Layout.preferredHeight: constants.iconSizeXXLarge
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
id: spinner
|
||||||
|
visible: s.state == ''
|
||||||
|
width: constants.iconSizeXXLarge
|
||||||
|
height: constants.iconSizeXXLarge
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: icon
|
||||||
|
width: constants.iconSizeXXLarge
|
||||||
|
height: constants.iconSizeXXLarge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: helpText
|
||||||
|
text: qsTr('Paying...')
|
||||||
|
font.pixelSize: constants.fontSizeXXLarge
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: errorText
|
||||||
|
font.pixelSize: constants.fontSizeLarge
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Daemon.currentWallet
|
||||||
|
function onPaymentSucceeded(key) {
|
||||||
|
if (key != invoice_key) {
|
||||||
|
console.log('wrong invoice ' + key + ' != ' + invoice_key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('payment succeeded!')
|
||||||
|
s.state = 'success'
|
||||||
|
}
|
||||||
|
function onPaymentFailed(key, reason) {
|
||||||
|
if (key != invoice_key) {
|
||||||
|
console.log('wrong invoice ' + key + ' != ' + invoice_key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('payment failed: ' + reason)
|
||||||
|
s.state = 'failed'
|
||||||
|
errorText.text = reason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,6 +161,7 @@ Pane {
|
|||||||
rootItem.clear()
|
rootItem.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +245,11 @@ Pane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: lightningPaymentProgressDialog
|
||||||
|
LightningPaymentProgressDialog {}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: invoiceDialog
|
id: invoiceDialog
|
||||||
InvoiceDialog {
|
InvoiceDialog {
|
||||||
@@ -255,6 +261,17 @@ Pane {
|
|||||||
'message': invoice.message
|
'message': invoice.message
|
||||||
})
|
})
|
||||||
dialog.open()
|
dialog.open()
|
||||||
|
} else if (invoice.invoiceType == Invoice.LightningInvoice) {
|
||||||
|
console.log('About to pay lightning invoice')
|
||||||
|
if (invoice.key == '') {
|
||||||
|
console.log('No invoice key, aborting')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var dialog = lightningPaymentProgressDialog.createObject(rootItem, {
|
||||||
|
invoice_key: invoice.key
|
||||||
|
})
|
||||||
|
dialog.open()
|
||||||
|
Daemon.currentWallet.pay_lightning_invoice(invoice.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,8 +280,7 @@ Pane {
|
|||||||
Connections {
|
Connections {
|
||||||
target: Daemon.currentWallet
|
target: Daemon.currentWallet
|
||||||
function onInvoiceStatusChanged(key, status) {
|
function onInvoiceStatusChanged(key, status) {
|
||||||
// TODO: status from?
|
Daemon.currentWallet.invoiceModel.updateInvoice(key, status)
|
||||||
//Daemon.currentWallet.invoiceModel.updateInvoice(key, status)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Pane {
|
|||||||
|
|
||||||
property alias label: txdetails.label
|
property alias label: txdetails.label
|
||||||
|
|
||||||
signal txDetailsChanged
|
signal detailsChanged
|
||||||
|
|
||||||
property QtObject menu: Menu {
|
property QtObject menu: Menu {
|
||||||
id: menu
|
id: menu
|
||||||
@@ -97,11 +97,13 @@ Pane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
visible: txdetails.amount.satsInt < 0
|
||||||
text: qsTr('Transaction fee')
|
text: qsTr('Transaction fee')
|
||||||
color: Material.accentColor
|
color: Material.accentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
visible: txdetails.amount.satsInt < 0
|
||||||
Label {
|
Label {
|
||||||
text: Config.formatSats(txdetails.fee)
|
text: Config.formatSats(txdetails.fee)
|
||||||
}
|
}
|
||||||
@@ -111,38 +113,6 @@ Pane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
Label {
|
||||||
text: qsTr('Label')
|
text: qsTr('Label')
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
@@ -203,6 +173,37 @@ Pane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
Label {
|
||||||
text: qsTr('Outputs')
|
text: qsTr('Outputs')
|
||||||
@@ -247,7 +248,7 @@ Pane {
|
|||||||
id: txdetails
|
id: txdetails
|
||||||
wallet: Daemon.currentWallet
|
wallet: Daemon.currentWallet
|
||||||
txid: root.txid
|
txid: root.txid
|
||||||
onLabelChanged: txDetailsChanged()
|
onLabelChanged: root.detailsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from .qetypes import QEAmount
|
|||||||
from .qeaddressdetails import QEAddressDetails
|
from .qeaddressdetails import QEAddressDetails
|
||||||
from .qetxdetails import QETxDetails
|
from .qetxdetails import QETxDetails
|
||||||
from .qechannelopener import QEChannelOpener
|
from .qechannelopener import QEChannelOpener
|
||||||
|
from .qelnpaymentdetails import QELnPaymentDetails
|
||||||
|
|
||||||
notification = None
|
notification = None
|
||||||
|
|
||||||
@@ -145,6 +146,7 @@ class ElectrumQmlApplication(QGuiApplication):
|
|||||||
qmlRegisterType(QEAddressDetails, 'org.electrum', 1, 0, 'AddressDetails')
|
qmlRegisterType(QEAddressDetails, 'org.electrum', 1, 0, 'AddressDetails')
|
||||||
qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails')
|
qmlRegisterType(QETxDetails, 'org.electrum', 1, 0, 'TxDetails')
|
||||||
qmlRegisterType(QEChannelOpener, 'org.electrum', 1, 0, 'ChannelOpener')
|
qmlRegisterType(QEChannelOpener, 'org.electrum', 1, 0, 'ChannelOpener')
|
||||||
|
qmlRegisterType(QELnPaymentDetails, 'org.electrum', 1, 0, 'LnPaymentDetails')
|
||||||
|
|
||||||
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')
|
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
|||||||
from decimal import Decimal
|
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, format_satoshis
|
||||||
|
|
||||||
from .qetypes import QEAmount
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
@@ -92,6 +92,23 @@ class QEConfig(QObject):
|
|||||||
else:
|
else:
|
||||||
return self.config.format_amount(satoshis)
|
return self.config.format_amount(satoshis)
|
||||||
|
|
||||||
|
@pyqtSlot(QEAmount, result=str)
|
||||||
|
@pyqtSlot(QEAmount, bool, result=str)
|
||||||
|
def formatMilliSats(self, amount, with_unit=False):
|
||||||
|
if isinstance(amount, QEAmount):
|
||||||
|
msats = amount.msatsInt
|
||||||
|
else:
|
||||||
|
return '---'
|
||||||
|
|
||||||
|
s = format_satoshis(msats/1000,
|
||||||
|
decimal_point=self.decimal_point(),
|
||||||
|
precision=3)
|
||||||
|
return s
|
||||||
|
#if with_unit:
|
||||||
|
#return self.config.format_amount_and_units(msats)
|
||||||
|
#else:
|
||||||
|
#return self.config.format_amount(satoshis)
|
||||||
|
|
||||||
# TODO delegate all this to config.py/util.py
|
# TODO delegate all this to config.py/util.py
|
||||||
def decimal_point(self):
|
def decimal_point(self):
|
||||||
return self.config.get('decimal_point', DECIMAL_POINT_DEFAULT)
|
return self.config.get('decimal_point', DECIMAL_POINT_DEFAULT)
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class QEInvoice(QObject):
|
|||||||
_effectiveInvoice = None
|
_effectiveInvoice = None
|
||||||
_canSave = False
|
_canSave = False
|
||||||
_canPay = False
|
_canPay = False
|
||||||
|
_key = ''
|
||||||
|
|
||||||
invoiceChanged = pyqtSignal()
|
invoiceChanged = pyqtSignal()
|
||||||
invoiceSaved = pyqtSignal()
|
invoiceSaved = pyqtSignal()
|
||||||
@@ -128,6 +129,17 @@ class QEInvoice(QObject):
|
|||||||
status = self._wallet.wallet.get_invoice_status(self._effectiveInvoice)
|
status = self._wallet.wallet.get_invoice_status(self._effectiveInvoice)
|
||||||
return self._effectiveInvoice.get_status_str(status)
|
return self._effectiveInvoice.get_status_str(status)
|
||||||
|
|
||||||
|
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.keyChanged.emit()
|
||||||
|
|
||||||
# single address only, TODO: n outputs
|
# single address only, TODO: n outputs
|
||||||
@pyqtProperty(str, notify=invoiceChanged)
|
@pyqtProperty(str, notify=invoiceChanged)
|
||||||
def address(self):
|
def address(self):
|
||||||
@@ -170,6 +182,7 @@ class QEInvoice(QObject):
|
|||||||
self._logger.debug(repr(invoice))
|
self._logger.debug(repr(invoice))
|
||||||
if invoice:
|
if invoice:
|
||||||
self.set_effective_invoice(invoice)
|
self.set_effective_invoice(invoice)
|
||||||
|
self.key = key
|
||||||
|
|
||||||
def set_effective_invoice(self, invoice: Invoice):
|
def set_effective_invoice(self, invoice: Invoice):
|
||||||
self._effectiveInvoice = invoice
|
self._effectiveInvoice = invoice
|
||||||
@@ -264,9 +277,12 @@ class QEInvoice(QObject):
|
|||||||
else:
|
else:
|
||||||
self._logger.debug('flow with LN but not LN enabled AND having bip21 uri')
|
self._logger.debug('flow with LN but not LN enabled AND having bip21 uri')
|
||||||
self.setValidOnchainInvoice(self._bip21['address'])
|
self.setValidOnchainInvoice(self._bip21['address'])
|
||||||
elif not self._wallet.wallet.lnworker.channels:
|
else:
|
||||||
self.validationWarning.emit('no_channels',_('Detected valid Lightning invoice, but there are no open channels'))
|
|
||||||
self.setValidLightningInvoice(lninvoice)
|
self.setValidLightningInvoice(lninvoice)
|
||||||
|
if not self._wallet.wallet.lnworker.channels:
|
||||||
|
self.validationWarning.emit('no_channels',_('Detected valid Lightning invoice, but there are no open channels'))
|
||||||
|
else:
|
||||||
|
self.validationSuccess.emit()
|
||||||
else:
|
else:
|
||||||
self._logger.debug('flow without LN but having bip21 uri')
|
self._logger.debug('flow without LN but having bip21 uri')
|
||||||
if 'amount' not in self._bip21: #TODO can we have amount-less invoices?
|
if 'amount' not in self._bip21: #TODO can we have amount-less invoices?
|
||||||
@@ -286,6 +302,7 @@ class QEInvoice(QObject):
|
|||||||
if not self._effectiveInvoice:
|
if not self._effectiveInvoice:
|
||||||
return
|
return
|
||||||
# TODO detect duplicate?
|
# TODO detect duplicate?
|
||||||
|
self.key = self._wallet.wallet.get_key_for_outgoing_invoice(self._effectiveInvoice)
|
||||||
self._wallet.wallet.save_invoice(self._effectiveInvoice)
|
self._wallet.wallet.save_invoice(self._effectiveInvoice)
|
||||||
self.invoiceSaved.emit()
|
self.invoiceSaved.emit()
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
|
|||||||
invoices = []
|
invoices = []
|
||||||
for invoice in self.get_invoice_list():
|
for invoice in self.get_invoice_list():
|
||||||
item = self.invoice_to_model(invoice)
|
item = self.invoice_to_model(invoice)
|
||||||
self._logger.debug(str(item))
|
#self._logger.debug(str(item))
|
||||||
invoices.append(item)
|
invoices.append(item)
|
||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|||||||
114
electrum/gui/qml/qelnpaymentdetails.py
Normal file
114
electrum/gui/qml/qelnpaymentdetails.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
||||||
|
|
||||||
|
from electrum.logging import get_logger
|
||||||
|
from electrum.util import format_time, bfh, format_time
|
||||||
|
|
||||||
|
from .qewallet import QEWallet
|
||||||
|
from .qetypes import QEAmount
|
||||||
|
|
||||||
|
class QELnPaymentDetails(QObject):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
|
_wallet = None
|
||||||
|
_key = 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()
|
||||||
|
|
||||||
|
keyChanged = pyqtSignal()
|
||||||
|
@pyqtProperty(str, notify=keyChanged)
|
||||||
|
def key(self):
|
||||||
|
return self._key
|
||||||
|
|
||||||
|
@key.setter
|
||||||
|
def key(self, key: str):
|
||||||
|
if self._key != key:
|
||||||
|
self._logger.debug('key set -> %s' % key)
|
||||||
|
self._key = key
|
||||||
|
self.keyChanged.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._key, 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 payment_hash(self):
|
||||||
|
return self._phash
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=detailsChanged)
|
||||||
|
def preimage(self):
|
||||||
|
return self._preimage
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=detailsChanged)
|
||||||
|
def invoice(self):
|
||||||
|
return self._invoice
|
||||||
|
|
||||||
|
@pyqtProperty(QEAmount, notify=detailsChanged)
|
||||||
|
def amount(self):
|
||||||
|
return self._amount
|
||||||
|
|
||||||
|
@pyqtProperty(QEAmount, notify=detailsChanged)
|
||||||
|
def fee(self):
|
||||||
|
return self._fee
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self._wallet is None:
|
||||||
|
self._logger.error('wallet undefined')
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._key not in self._wallet.wallet.lnworker.payments:
|
||||||
|
self._logger.error('payment_hash not found')
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO this is horribly inefficient. need a payment getter/query method
|
||||||
|
tx = self._wallet.wallet.lnworker.get_lightning_history()[bfh(self._key)]
|
||||||
|
self._logger.debug(str(tx))
|
||||||
|
|
||||||
|
self._fee = QEAmount() if not tx['fee_msat'] else QEAmount(amount_msat=tx['fee_msat'])
|
||||||
|
self._amount = QEAmount(amount_msat=tx['amount_msat'])
|
||||||
|
self._label = tx['label']
|
||||||
|
self._date = format_time(tx['timestamp'])
|
||||||
|
self._status = 'settled' # TODO: other states? get_lightning_history is deciding the filter for us :(
|
||||||
|
self._phash = tx['payment_hash']
|
||||||
|
self._preimage = tx['preimage']
|
||||||
|
|
||||||
|
invoice = (self._wallet.wallet.get_invoice(self._key)
|
||||||
|
or self._wallet.wallet.get_request(self._key))
|
||||||
|
self._logger.debug(str(invoice))
|
||||||
|
if invoice:
|
||||||
|
self._invoice = invoice.lightning_invoice or ''
|
||||||
|
else:
|
||||||
|
self._invoice = ''
|
||||||
|
|
||||||
|
self.detailsChanged.emit()
|
||||||
@@ -18,8 +18,8 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
|
|
||||||
# define listmodel rolemap
|
# define listmodel rolemap
|
||||||
_ROLE_NAMES=('txid','fee_sat','height','confirmations','timestamp','monotonic_timestamp',
|
_ROLE_NAMES=('txid','fee_sat','height','confirmations','timestamp','monotonic_timestamp',
|
||||||
'incoming','bc_value','bc_balance','date','label','txpos_in_block','fee',
|
'incoming','value','balance','date','label','txpos_in_block','fee',
|
||||||
'inputs','outputs','section')
|
'inputs','outputs','section','type','lightning','payment_hash','key')
|
||||||
_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))
|
||||||
@@ -46,12 +46,23 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
|
|
||||||
def tx_to_model(self, tx):
|
def tx_to_model(self, tx):
|
||||||
|
#self._logger.debug(str(tx))
|
||||||
item = tx
|
item = tx
|
||||||
for output in item['outputs']:
|
|
||||||
output['value'] = output['value'].value
|
|
||||||
|
|
||||||
item['bc_value'] = QEAmount(amount_sat=item['bc_value'].value)
|
item['key'] = item['txid'] if 'txid' in item else item['payment_hash']
|
||||||
item['bc_balance'] = QEAmount(amount_sat=item['bc_balance'].value)
|
|
||||||
|
if not 'lightning' in item:
|
||||||
|
item['lightning'] = False
|
||||||
|
|
||||||
|
if item['lightning']:
|
||||||
|
item['value'] = QEAmount(amount_sat=item['value'].value, amount_msat=item['amount_msat'])
|
||||||
|
item['balance'] = QEAmount(amount_sat=item['balance'].value, amount_msat=item['amount_msat'])
|
||||||
|
if item['type'] == 'payment':
|
||||||
|
item['incoming'] = True if item['direction'] == 'received' else False
|
||||||
|
item['confirmations'] = 0
|
||||||
|
else:
|
||||||
|
item['value'] = QEAmount(amount_sat=item['value'].value)
|
||||||
|
item['balance'] = QEAmount(amount_sat=item['balance'].value)
|
||||||
|
|
||||||
# newly arriving txs have no (block) timestamp
|
# newly arriving txs have no (block) timestamp
|
||||||
# TODO?
|
# TODO?
|
||||||
@@ -90,9 +101,9 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
|
|
||||||
# initial model data
|
# initial model data
|
||||||
def init_model(self):
|
def init_model(self):
|
||||||
history = self.wallet.get_detailed_history(show_addresses = True)
|
history = self.wallet.get_full_history()
|
||||||
txs = []
|
txs = []
|
||||||
for tx in history['transactions']:
|
for key, tx in history.items():
|
||||||
txs.append(self.tx_to_model(tx))
|
txs.append(self.tx_to_model(tx))
|
||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
@@ -104,7 +115,7 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
def update_tx(self, txid, info):
|
def update_tx(self, txid, info):
|
||||||
i = 0
|
i = 0
|
||||||
for tx in self.tx_history:
|
for tx in self.tx_history:
|
||||||
if tx['txid'] == txid:
|
if 'txid' in tx and tx['txid'] == txid:
|
||||||
tx['height'] = info.height
|
tx['height'] = info.height
|
||||||
tx['confirmations'] = info.conf
|
tx['confirmations'] = info.conf
|
||||||
tx['timestamp'] = info.timestamp
|
tx['timestamp'] = info.timestamp
|
||||||
@@ -116,10 +127,10 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
@pyqtSlot(str, str)
|
@pyqtSlot(str, str)
|
||||||
def update_tx_label(self, txid, label):
|
def update_tx_label(self, key, label):
|
||||||
i = 0
|
i = 0
|
||||||
for tx in self.tx_history:
|
for tx in self.tx_history:
|
||||||
if tx['txid'] == txid:
|
if tx['key'] == key:
|
||||||
tx['label'] = label
|
tx['label'] = label
|
||||||
index = self.index(i,0)
|
index = self.index(i,0)
|
||||||
self.dataChanged.emit(index, index, [self._ROLE_RMAP['label']])
|
self.dataChanged.emit(index, index, [self._ROLE_RMAP['label']])
|
||||||
@@ -131,7 +142,7 @@ class QETransactionListModel(QAbstractListModel):
|
|||||||
self._logger.debug('updating height to %d' % height)
|
self._logger.debug('updating height to %d' % height)
|
||||||
i = 0
|
i = 0
|
||||||
for tx in self.tx_history:
|
for tx in self.tx_history:
|
||||||
if tx['height'] > 0:
|
if 'height' in tx and tx['height'] > 0:
|
||||||
tx['confirmations'] = height - tx['height'] + 1
|
tx['confirmations'] = height - tx['height'] + 1
|
||||||
index = self.index(i,0)
|
index = self.index(i,0)
|
||||||
roles = [self._ROLE_RMAP['confirmations']]
|
roles = [self._ROLE_RMAP['confirmations']]
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
||||||
|
|
||||||
#from decimal import Decimal
|
|
||||||
|
|
||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.util import format_time
|
from electrum.util import format_time
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from typing import Optional, TYPE_CHECKING, Sequence, List, Union
|
from typing import Optional, TYPE_CHECKING, Sequence, List, Union
|
||||||
import queue
|
import queue
|
||||||
import time
|
import time
|
||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer
|
||||||
|
|
||||||
@@ -47,9 +49,11 @@ class QEWallet(QObject):
|
|||||||
requestStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
|
requestStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
|
||||||
requestCreateSuccess = pyqtSignal()
|
requestCreateSuccess = pyqtSignal()
|
||||||
requestCreateError = pyqtSignal([str,str], arguments=['code','error'])
|
requestCreateError = pyqtSignal([str,str], arguments=['code','error'])
|
||||||
invoiceStatusChanged = pyqtSignal([str], arguments=['key'])
|
invoiceStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
|
||||||
invoiceCreateSuccess = pyqtSignal()
|
invoiceCreateSuccess = pyqtSignal()
|
||||||
invoiceCreateError = pyqtSignal([str,str], arguments=['code','error'])
|
invoiceCreateError = pyqtSignal([str,str], arguments=['code','error'])
|
||||||
|
paymentSucceeded = pyqtSignal([str], arguments=['key'])
|
||||||
|
paymentFailed = pyqtSignal([str,str], arguments=['key','reason'])
|
||||||
|
|
||||||
_network_signal = pyqtSignal(str, object)
|
_network_signal = pyqtSignal(str, object)
|
||||||
|
|
||||||
@@ -95,6 +99,10 @@ class QEWallet(QObject):
|
|||||||
def on_network_qt(self, event, args=None):
|
def on_network_qt(self, event, args=None):
|
||||||
# note: we get events from all wallets! args are heterogenous so we can't
|
# note: we get events from all wallets! args are heterogenous so we can't
|
||||||
# shortcut here
|
# shortcut here
|
||||||
|
if event != 'status':
|
||||||
|
wallet = args[0]
|
||||||
|
if wallet == self.wallet:
|
||||||
|
self._logger.debug('event %s' % event)
|
||||||
if event == 'status':
|
if event == 'status':
|
||||||
self.isUptodateChanged.emit()
|
self.isUptodateChanged.emit()
|
||||||
elif event == 'request_status':
|
elif event == 'request_status':
|
||||||
@@ -105,8 +113,11 @@ class QEWallet(QObject):
|
|||||||
elif event == 'invoice_status':
|
elif event == 'invoice_status':
|
||||||
wallet, key = args
|
wallet, key = args
|
||||||
if wallet == self.wallet:
|
if wallet == self.wallet:
|
||||||
self._logger.debug('invoice status %d for key %s' % (c, key))
|
self._logger.debug('invoice status update for key %s' % key)
|
||||||
self.invoiceStatusChanged.emit(key)
|
# FIXME event doesn't pass the new status, so we need to retrieve
|
||||||
|
invoice = self.wallet.get_invoice(key)
|
||||||
|
status = self.wallet.get_invoice_status(invoice)
|
||||||
|
self.invoiceStatusChanged.emit(key, status)
|
||||||
elif event == 'new_transaction':
|
elif event == 'new_transaction':
|
||||||
wallet, tx = args
|
wallet, tx = args
|
||||||
if wallet == self.wallet:
|
if wallet == self.wallet:
|
||||||
@@ -129,6 +140,15 @@ class QEWallet(QObject):
|
|||||||
wallet, = args
|
wallet, = args
|
||||||
if wallet == self.wallet:
|
if wallet == self.wallet:
|
||||||
self.balanceChanged.emit()
|
self.balanceChanged.emit()
|
||||||
|
elif event == 'payment_succeeded':
|
||||||
|
wallet, key = args
|
||||||
|
if wallet == self.wallet:
|
||||||
|
self.paymentSucceeded.emit(key)
|
||||||
|
self._historyModel.init_model() # TODO: be less dramatic
|
||||||
|
elif event == 'payment_failed':
|
||||||
|
wallet, key, reason = args
|
||||||
|
if wallet == self.wallet:
|
||||||
|
self.paymentFailed.emit(key, reason)
|
||||||
else:
|
else:
|
||||||
self._logger.debug('unhandled event: %s %s' % (event, str(args)))
|
self._logger.debug('unhandled event: %s %s' % (event, str(args)))
|
||||||
|
|
||||||
@@ -346,6 +366,24 @@ class QEWallet(QObject):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def pay_lightning_invoice(self, invoice_key):
|
||||||
|
self._logger.debug('about to pay LN')
|
||||||
|
invoice = self.wallet.get_invoice(invoice_key)
|
||||||
|
assert(invoice)
|
||||||
|
assert(invoice.lightning_invoice)
|
||||||
|
amount_msat = invoice.get_amount_msat()
|
||||||
|
def pay_thread():
|
||||||
|
try:
|
||||||
|
coro = self.wallet.lnworker.pay_invoice(invoice.lightning_invoice, amount_msat=amount_msat)
|
||||||
|
fut = asyncio.run_coroutine_threadsafe(coro, self.wallet.network.asyncio_loop)
|
||||||
|
fut.result()
|
||||||
|
except Exception as e:
|
||||||
|
self.userNotify(repr(e))
|
||||||
|
#self.app.show_error(repr(e))
|
||||||
|
#self.save_invoice(invoice)
|
||||||
|
threading.Thread(target=pay_thread).start()
|
||||||
|
|
||||||
def create_bitcoin_request(self, amount: int, message: str, expiration: int, ignore_gap: bool) -> Optional[str]:
|
def create_bitcoin_request(self, amount: int, message: str, expiration: int, ignore_gap: bool) -> Optional[str]:
|
||||||
addr = self.wallet.get_unused_address()
|
addr = self.wallet.get_unused_address()
|
||||||
if addr is None:
|
if addr is None:
|
||||||
|
|||||||
Reference in New Issue
Block a user