diff --git a/electrum/plugins/psbt_nostr/qml.py b/electrum/plugins/psbt_nostr/qml.py index 46d4ba2fd..7275fc382 100644 --- a/electrum/plugins/psbt_nostr/qml.py +++ b/electrum/plugins/psbt_nostr/qml.py @@ -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') diff --git a/electrum/plugins/psbt_nostr/qml/PsbtReceiveDialog.qml b/electrum/plugins/psbt_nostr/qml/PsbtReceiveDialog.qml new file mode 100644 index 000000000..19d1352e4 --- /dev/null +++ b/electrum/plugins/psbt_nostr/qml/PsbtReceiveDialog.qml @@ -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:
%1
').arg(tx_label) + : qsTr('A transaction was received from your cosigner.'), + qsTr('Do you want to open it now?') + ].join('
') + } + } + + 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() + } + } + } + } +} diff --git a/electrum/plugins/psbt_nostr/qml/main.qml b/electrum/plugins/psbt_nostr/qml/main.qml index 8067d105d..2c2d5f9d6 100644 --- a/electrum/plugins/psbt_nostr/qml/main.qml +++ b/electrum/plugins/psbt_nostr/qml/main.qml @@ -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:

%1').arg(label) - : qsTr('A transaction was received from your cosigner.'), - qsTr('Do you want to open it now?') - ].join('

'), - 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