1
0

qml: offer same choices as desktop; Open, Discard, Save to wallet

This commit is contained in:
Sander van Grieken
2025-05-09 11:15:10 +02:00
parent 8368469e2c
commit 77407fa206
3 changed files with 137 additions and 30 deletions

View File

@@ -36,14 +36,12 @@ from electrum.util import EventListener, event_listener
from electrum.gui.qml.qewallet import QEWallet
from .psbt_nostr import PsbtNostrPlugin, CosignerWallet, now
from .psbt_nostr import PsbtNostrPlugin, CosignerWallet
if TYPE_CHECKING:
from electrum.wallet import Abstract_Wallet
from electrum.gui.qml import ElectrumQmlApplication
USER_PROMPT_COOLDOWN = 10
class QReceiveSignalObject(QObject):
def __init__(self, plugin: 'Plugin'):
@@ -73,12 +71,23 @@ class QReceiveSignalObject(QObject):
return
cosigner_wallet.send_psbt(tx_from_any(tx, deserialize=True), label)
@pyqtSlot(QEWallet, str)
def acceptPsbt(self, wallet: 'QEWallet', event_id: str):
@pyqtSlot(QEWallet, str, str)
def saveTxLabel(self, wallet: 'QEWallet', tx: str, label: str):
cosigner_wallet = self._plugin.cosigner_wallets.get(wallet.wallet)
if not cosigner_wallet:
return
cosigner_wallet.accept_psbt(event_id)
cosigner_wallet.save_tx_label(tx_from_any(tx, deserialize=True), label)
@pyqtSlot(QEWallet, str)
@pyqtSlot(QEWallet, str, bool)
def acceptPsbt(self, wallet: 'QEWallet', event_id: str, save_to_wallet: bool = False):
cosigner_wallet = self._plugin.cosigner_wallets.get(wallet.wallet)
if not cosigner_wallet:
return
cosigner_wallet.accept_psbt(event_id, save_to_wallet)
if save_to_wallet:
# let GUI update view through wallet_updated callback
util.trigger_callback('wallet_updated', wallet.wallet)
@pyqtSlot(QEWallet, str)
def rejectPsbt(self, wallet: 'QEWallet', event_id: str):
@@ -124,17 +133,12 @@ class QmlCosignerWallet(EventListener, CosignerWallet):
self.register_callbacks()
self.tx = None
self.user_prompt_cooldown = None
@event_listener
def on_event_psbt_nostr_received(self, wallet, pubkey, event_id, tx: 'PartialTransaction', label: str):
if self.wallet == wallet:
self.tx = tx
if not (self.user_prompt_cooldown and self.user_prompt_cooldown > now()):
self.plugin.so.cosignerReceivedPsbt.emit(pubkey, event_id, tx.serialize(), label)
else:
self.mark_pending_event_rcvd(event_id)
self.add_transaction_to_wallet(self.tx, label=label, on_failure=self.on_add_fail)
self.plugin.so.cosignerReceivedPsbt.emit(pubkey, event_id, tx.serialize(), label)
def close(self):
super().close()
@@ -158,13 +162,16 @@ class QmlCosignerWallet(EventListener, CosignerWallet):
except Exception as e:
self.plugin.so.sendPsbtFailed.emit(str(e))
def accept_psbt(self, event_id):
def save_tx_label(self, tx, label):
self.wallet.set_label(tx.txid(), label)
def accept_psbt(self, event_id, save: bool = False):
if save:
self.add_transaction_to_wallet(self.tx, on_failure=self.on_add_fail)
self.mark_pending_event_rcvd(event_id)
def reject_psbt(self, event_id):
self.user_prompt_cooldown = now() + USER_PROMPT_COOLDOWN
self.mark_pending_event_rcvd(event_id)
self.add_transaction_to_wallet(self.tx, on_failure=self.on_add_fail)
def on_add_fail(self):
self.logger.error('failed to add tx to wallet')

View File

@@ -0,0 +1,91 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import "../../../gui/qml/components/controls"
ElDialog {
id: dialog
title: qsTr("PSBT received")
iconSource: Qt.resolvedUrl('../../../gui/icons/question.png')
enum Choice {
None,
Open,
Save
}
property string tx_label
property int choice: PsbtReceiveDialog.Choice.None
// TODO: it might be better to defer popup until no dialogs are shown
z: 1 // raise z so it also covers dialogs using overlay as parent
anchors.centerIn: parent
padding: 0
width: rootLayout.width
ColumnLayout {
id: rootLayout
width: dialog.parent.width * 2/3
ColumnLayout {
Layout.margins: constants.paddingMedium
Layout.fillWidth: true
TextArea {
id: message
Layout.fillWidth: true
readOnly: true
wrapMode: TextInput.WordWrap
textFormat: TextEdit.RichText
background: Rectangle {
color: 'transparent'
}
text: [
tx_label
? qsTr('A transaction was received from your cosigner with label: <br/><b>%1</b><br/>').arg(tx_label)
: qsTr('A transaction was received from your cosigner.'),
qsTr('Do you want to open it now?')
].join('<br/>')
}
}
ButtonContainer {
Layout.fillWidth: true
FlatButton {
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('Open')
icon.source: Qt.resolvedUrl('../../../gui/icons/confirmed.png')
onClicked: {
choice = PsbtReceiveDialog.Choice.Open
doAccept()
}
}
FlatButton {
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('Discard')
icon.source: Qt.resolvedUrl('../../../gui/icons/closebutton.png')
onClicked: doReject()
}
FlatButton {
Layout.fillWidth: true
Layout.preferredWidth: 1
text: qsTr('Save to Wallet')
icon.source: Qt.resolvedUrl('../../../gui/icons/wallet.png')
onClicked: {
choice = PsbtReceiveDialog.Choice.Save
doAccept()
}
}
}
}
}

View File

@@ -8,23 +8,25 @@ Item {
Connections {
target: AppController ? AppController.plugin('psbt_nostr') : null
function onCosignerReceivedPsbt(pubkey, event, tx, label) {
var dialog = app.messageDialog.createObject(app, {
text: [
label
? qsTr('A transaction was received from your cosigner with label: <br/><br/><b>%1</b>').arg(label)
: qsTr('A transaction was received from your cosigner.'),
qsTr('Do you want to open it now?')
].join('<br/><br/>'),
yesno: true,
richText: true
var dialog = psbtReceiveDialog.createObject(app, {
tx_label: label
})
dialog.accepted.connect(function () {
var page = app.stack.push(Qt.resolvedUrl('../../../gui/qml/components/TxDetails.qml'), {
rawtx: tx
})
page.closed.connect(function () {
target.acceptPsbt(Daemon.currentWallet, event)
})
if (dialog.choice == PsbtReceiveDialog.Choice.Open) {
console.log('label:' + label)
console.log('tx:' + tx)
target.saveTxLabel(Daemon.currentWallet, tx, label)
var page = app.stack.push(Qt.resolvedUrl('../../../gui/qml/components/TxDetails.qml'), {
rawtx: tx
})
page.closed.connect(function () {
target.acceptPsbt(Daemon.currentWallet, event)
})
} else if (dialog.choice == PsbtReceiveDialog.Choice.Save) {
target.acceptPsbt(Daemon.currentWallet, event, true)
} else {
console.log('choice not set')
}
})
dialog.rejected.connect(function () {
target.rejectPsbt(Daemon.currentWallet, event)
@@ -33,6 +35,13 @@ Item {
}
}
Component {
id: psbtReceiveDialog
PsbtReceiveDialog {
onClosed: destroy()
}
}
property variant export_tx_button: Component {
FlatButton {
id: psbt_nostr_send_button