diff --git a/electrum/gui/qml/components/ExportTxDialog.qml b/electrum/gui/qml/components/ExportTxDialog.qml index 62b4291bf..249f07c18 100644 --- a/electrum/gui/qml/components/ExportTxDialog.qml +++ b/electrum/gui/qml/components/ExportTxDialog.qml @@ -13,6 +13,7 @@ ElDialog { // if text_qr is undefined text will be used property string text_help property string text_warn + property string tx_label title: qsTr('Share Transaction') diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index 5f8747c27..e17b4efae 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -98,7 +98,8 @@ Item { ? '' : [qsTr('Warning: Some data (prev txs / "full utxos") was left out of the QR code as it would not fit.'), qsTr('This might cause issues if signing offline.'), - qsTr('As a workaround, copy to clipboard or use the Share option instead.')].join(' ') + qsTr('As a workaround, copy to clipboard or use the Share option instead.')].join(' '), + tx_label: data[3] }) dialog.open() } diff --git a/electrum/gui/qml/qetxdetails.py b/electrum/gui/qml/qetxdetails.py index 5ddad940c..9f0a2ac61 100644 --- a/electrum/gui/qml/qetxdetails.py +++ b/electrum/gui/qml/qetxdetails.py @@ -383,9 +383,11 @@ class QETxDetails(QObject, QtEventListener): self.detailsChanged.emit() - if self._label != txinfo.label: - self._label = txinfo.label - self.labelChanged.emit() + if self._txid: + label = self._wallet.wallet.get_label_for_txid(self._txid) + if self._label != label: + self._label = label + self.labelChanged.emit() def update_mined_status(self, tx_mined_info: TxMinedInfo): self._mempool_depth = '' @@ -505,4 +507,5 @@ class QETxDetails(QObject, QtEventListener): @pyqtSlot(result='QVariantList') def getSerializedTx(self): txqr = self._tx.to_qr_data() - return [str(self._tx), txqr[0], txqr[1]] + label = self._wallet.wallet.get_label_for_txid(self._tx.txid()) + return [str(self._tx), txqr[0], txqr[1], label] diff --git a/electrum/gui/qml/qetxfinalizer.py b/electrum/gui/qml/qetxfinalizer.py index 2223dea80..3908c7e31 100644 --- a/electrum/gui/qml/qetxfinalizer.py +++ b/electrum/gui/qml/qetxfinalizer.py @@ -494,7 +494,8 @@ class QETxFinalizer(TxFeeSlider): @pyqtSlot(result='QVariantList') def getSerializedTx(self): txqr = self._tx.to_qr_data() - return [str(self._tx), txqr[0], txqr[1]] + label = self._wallet.wallet.get_label_for_txid(self._tx.txid()) + return [str(self._tx), txqr[0], txqr[1], label] class TxMonMixin(QtEventListener): diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index edf9cba3d..e70b0da20 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1169,7 +1169,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): tx: Transaction, *, external_keypairs: Mapping[bytes, bytes] = None, - payment_identifier: PaymentIdentifier = None, + invoice: Invoice = None, show_sign_button: bool = True, show_broadcast_button: bool = True, ): @@ -1177,7 +1177,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): tx, parent=self, external_keypairs=external_keypairs, - payment_identifier=payment_identifier, + invoice=invoice, show_sign_button=show_sign_button, show_broadcast_button=show_broadcast_button, ) @@ -1413,18 +1413,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): """ return self.utxo_list.get_spend_list() - def broadcast_or_show(self, tx: Transaction, *, payment_identifier: PaymentIdentifier = None): + def broadcast_or_show(self, tx: Transaction, *, invoice: 'Invoice' = None): if not tx.is_complete(): - self.show_transaction(tx, payment_identifier=payment_identifier) + self.show_transaction(tx, invoice=invoice) return if not self.network: self.show_error(_("You can't broadcast a transaction without a live network connection.")) - self.show_transaction(tx, payment_identifier=payment_identifier) + self.show_transaction(tx, invoice=invoice) return - self.broadcast_transaction(tx, payment_identifier=payment_identifier) + self.broadcast_transaction(tx, invoice=invoice) - def broadcast_transaction(self, tx: Transaction, *, payment_identifier: PaymentIdentifier = None): - self.send_tab.broadcast_transaction(tx, payment_identifier=payment_identifier) + def broadcast_transaction(self, tx: Transaction, *, invoice: Invoice = None): + self.send_tab.broadcast_transaction(tx, invoice=invoice) @protected def sign_tx( diff --git a/electrum/gui/qt/send_tab.py b/electrum/gui/qt/send_tab.py index 583749525..2817784bf 100644 --- a/electrum/gui/qt/send_tab.py +++ b/electrum/gui/qt/send_tab.py @@ -305,10 +305,6 @@ class SendTab(QWidget, MessageBoxMixin, Logger): if run_hook('abort_send', self): return - payment_identifier = None - if invoice and invoice.bip70: - payment_identifier = payment_identifier_from_invoice(self.wallet, invoice) - is_sweep = bool(external_keypairs) # we call get_coins inside make_tx, so that inputs can be changed dynamically if get_coins is None: @@ -354,12 +350,12 @@ class SendTab(QWidget, MessageBoxMixin, Logger): tx.swap_payment_hash = swap.payment_hash if is_preview: - self.window.show_transaction(tx, external_keypairs=external_keypairs, payment_identifier=payment_identifier) + self.window.show_transaction(tx, external_keypairs=external_keypairs, invoice=invoice) return self.save_pending_invoice() def sign_done(success): if success: - self.window.broadcast_or_show(tx, payment_identifier=payment_identifier) + self.window.broadcast_or_show(tx, invoice=invoice) self.window.sign_tx( tx, callback=sign_done, @@ -712,7 +708,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger): chan, swap_recv_amount_sat = can_pay_with_swap self.window.run_swap_dialog(is_reverse=False, recv_amount_sat=swap_recv_amount_sat, channels=[chan]) elif r == 'onchain': - self.pay_onchain_dialog(invoice.get_outputs(), nonlocal_only=True) + self.pay_onchain_dialog(invoice.get_outputs(), nonlocal_only=True, invoice=invoice) return assert lnworker is not None @@ -725,9 +721,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger): coro = lnworker.pay_invoice(invoice, amount_msat=amount_msat) self.window.run_coroutine_from_thread(coro, _('Sending payment')) - def broadcast_transaction(self, tx: Transaction, *, payment_identifier: PaymentIdentifier = None): - # note: payment_identifier is explicitly passed as self.payto_e.payment_identifier might - # already be cleared or otherwise have changed. + def broadcast_transaction(self, tx: Transaction, *, invoice: Invoice = None): if hasattr(tx, 'swap_payment_hash'): sm = self.wallet.lnworker.swap_manager swap = sm.get_swap(tx.swap_payment_hash) @@ -742,7 +736,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger): def broadcast_thread(): # non-GUI thread - if payment_identifier and payment_identifier.has_expired(): + if invoice and invoice.has_expired(): return False, _("Invoice has expired") try: self.network.run_from_another_thread(self.network.broadcast_transaction(tx)) @@ -751,19 +745,22 @@ class SendTab(QWidget, MessageBoxMixin, Logger): except BestEffortRequestFailed as e: return False, repr(e) # success - txid = tx.txid() - if payment_identifier and payment_identifier.need_merchant_notify(): - refund_address = self.wallet.get_receiving_address() - payment_identifier.notify_merchant( - tx=tx, - refund_address=refund_address, - on_finished=self.notify_merchant_done_signal.emit - ) - return True, txid + if invoice and invoice.bip70: + payment_identifier = payment_identifier_from_invoice(invoice) + # FIXME: this should move to backend + if payment_identifier and payment_identifier.need_merchant_notify(): + refund_address = self.wallet.get_receiving_address() + payment_identifier.notify_merchant( + tx=tx, + refund_address=refund_address, + on_finished=self.notify_merchant_done_signal.emit + ) + return True, tx.txid() # Capture current TL window; override might be removed on return parent = self.window.top_level_window(lambda win: isinstance(win, MessageBoxMixin)) + # FIXME: move to backend and let Abstract_Wallet set broadcasting state, not gui self.wallet.set_broadcasting(tx, broadcasting_status=PR_BROADCASTING) def broadcast_done(result): diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py index dfbfc6684..79312087d 100644 --- a/electrum/gui/qt/transaction_dialog.py +++ b/electrum/gui/qt/transaction_dialog.py @@ -64,7 +64,7 @@ from .my_treeview import create_toolbar_with_menu, QMenuWithConfig if TYPE_CHECKING: from .main_window import ElectrumWindow from electrum.wallet import Abstract_Wallet - from electrum.payment_identifier import PaymentIdentifier + from electrum.invoices import Invoice _logger = get_logger(__name__) @@ -418,7 +418,7 @@ def show_transaction( parent: 'ElectrumWindow', prompt_if_unsaved: bool = False, external_keypairs: Mapping[bytes, bytes] = None, - payment_identifier: 'PaymentIdentifier' = None, + invoice: 'Invoice' = None, on_closed: Callable[[], None] = None, show_sign_button: bool = True, show_broadcast_button: bool = True, @@ -429,7 +429,7 @@ def show_transaction( parent=parent, prompt_if_unsaved=prompt_if_unsaved, external_keypairs=external_keypairs, - payment_identifier=payment_identifier, + invoice=invoice, on_closed=on_closed, ) if not show_sign_button: @@ -454,7 +454,7 @@ class TxDialog(QDialog, MessageBoxMixin): parent: 'ElectrumWindow', prompt_if_unsaved: bool, external_keypairs: Mapping[bytes, bytes] = None, - payment_identifier: 'PaymentIdentifier' = None, + invoice: 'Invoice' = None, on_closed: Callable[[], None] = None, ): '''Transactions in the wallet will show their description. @@ -467,13 +467,15 @@ class TxDialog(QDialog, MessageBoxMixin): self.main_window = parent self.config = parent.config self.wallet = parent.wallet - self.payment_identifier = payment_identifier + self.invoice = invoice self.prompt_if_unsaved = prompt_if_unsaved self.on_closed = on_closed self.saved = False self.desc = None if txid := tx.txid(): self.desc = self.wallet.get_label_for_txid(txid) or None + if not self.desc and self.invoice: + self.desc = self.invoice.get_message() self.setMinimumWidth(640) self.psbt_only_widgets = [] # type: List[Union[QWidget, QAction]] @@ -492,13 +494,8 @@ class TxDialog(QDialog, MessageBoxMixin): self.tx_desc_label = QLabel(_("Description:")) vbox.addWidget(self.tx_desc_label) self.tx_desc = ButtonsLineEdit('') - def on_edited(): - text = self.tx_desc.text() - if self.wallet.set_label(txid, text): - self.main_window.history_list.update() - self.main_window.utxo_list.update() - self.main_window.labels_changed_signal.emit() - self.tx_desc.editingFinished.connect(on_edited) + + self.tx_desc.editingFinished.connect(self.store_tx_label) self.tx_desc.addCopyButton() vbox.addWidget(self.tx_desc) @@ -579,6 +576,13 @@ class TxDialog(QDialog, MessageBoxMixin): self.update() self.set_title() + def store_tx_label(self): + text = self.tx_desc.text() + if self.wallet.set_label(self.tx.txid(), text): + self.main_window.history_list.update() + self.main_window.utxo_list.update() + self.main_window.labels_changed_signal.emit() + def set_tx(self, tx: 'Transaction'): # Take a copy; it might get updated in the main window by # e.g. the FX plugin. If this happens during or after a long @@ -607,7 +611,7 @@ class TxDialog(QDialog, MessageBoxMixin): self.main_window.push_top_level_window(self) self.main_window.send_tab.save_pending_invoice() try: - self.main_window.broadcast_transaction(self.tx, payment_identifier=self.payment_identifier) + self.main_window.broadcast_transaction(self.tx, invoice=self.invoice) finally: self.main_window.pop_top_level_window(self) self.saved = True @@ -722,6 +726,7 @@ class TxDialog(QDialog, MessageBoxMixin): def save(self): self.main_window.push_top_level_window(self) if self.main_window.save_transaction_into_wallet(self.tx): + self.store_tx_label() self.save_button.setDisabled(True) self.saved = True self.main_window.pop_top_level_window(self) @@ -851,7 +856,8 @@ class TxDialog(QDialog, MessageBoxMixin): # note: when not finalized, RBF and locktime changes do not trigger # a make_tx, so the txid is unreliable, hence: self.tx_hash_e.setText(_('Unknown')) - if not self.wallet.adb.get_transaction(txid): + tx_in_db = bool(self.wallet.adb.get_transaction(txid)) + if not desc and not tx_in_db: self.tx_desc.hide() self.tx_desc_label.hide() else: diff --git a/electrum/plugins/psbt_nostr/psbt_nostr.py b/electrum/plugins/psbt_nostr/psbt_nostr.py index ea53c1a9d..1ebb0e4ab 100644 --- a/electrum/plugins/psbt_nostr/psbt_nostr.py +++ b/electrum/plugins/psbt_nostr/psbt_nostr.py @@ -23,6 +23,7 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import asyncio +import json import ssl import time from contextlib import asynccontextmanager @@ -30,7 +31,7 @@ from contextlib import asynccontextmanager import electrum_ecc as ecc import electrum_aionostr as aionostr from electrum_aionostr.key import PrivateKey -from typing import Dict, TYPE_CHECKING, Union, List, Tuple, Optional +from typing import Dict, TYPE_CHECKING, Union, List, Tuple, Optional, Callable from electrum import util, Transaction from electrum.crypto import sha256 @@ -38,8 +39,9 @@ from electrum.i18n import _ from electrum.logging import Logger from electrum.plugin import BasePlugin from electrum.transaction import PartialTransaction, tx_from_any -from electrum.util import (log_exceptions, OldTaskGroup, ca_path, trigger_callback, event_listener, - make_aiohttp_proxy_connector) +from electrum.util import ( + log_exceptions, OldTaskGroup, ca_path, trigger_callback, event_listener, json_decode, make_aiohttp_proxy_connector +) from electrum.wallet import Multisig_Wallet if TYPE_CHECKING: @@ -165,11 +167,11 @@ class CosignerWallet(Logger): yield manager @log_exceptions - async def send_direct_messages(self, messages: List[Tuple[str, str]]): + async def send_direct_messages(self, messages: List[Tuple[str, dict]]): our_private_key: PrivateKey = aionostr.key.PrivateKey(bytes.fromhex(self.nostr_privkey)) async with self.nostr_manager() as manager: for pubkey, msg in messages: - encrypted_msg: str = our_private_key.encrypt_message(msg, pubkey) + encrypted_msg: str = our_private_key.encrypt_message(json.dumps(msg), pubkey) eid = await aionostr._add_event( manager, kind=NOSTR_EVENT_KIND, @@ -209,13 +211,16 @@ class CosignerWallet(Logger): self.known_events[event.id] = now() continue try: - tx = tx_from_any(message) + message = json_decode(message) + tx_hex = message.get('tx') + label = message.get('label', '') + tx = tx_from_any(tx_hex) except Exception as e: self.logger.info(_("Unable to deserialize the transaction:") + "\n" + str(e)) self.known_events[event.id] = now() continue self.logger.info(f"received PSBT from {event.pubkey}") - trigger_callback('psbt_nostr_received', self.wallet, event.pubkey, event.id, tx) + trigger_callback('psbt_nostr_received', self.wallet, event.pubkey, event.id, tx, label) await self.pending.wait() self.pending.clear() @@ -245,25 +250,34 @@ class CosignerWallet(Logger): self.known_events[event_id] = now() self.pending.set() - def prepare_messages(self, tx: Union[Transaction, PartialTransaction]) -> List[Tuple[str, str]]: + def prepare_messages(self, tx: Union[Transaction, PartialTransaction], label: str = None) -> List[Tuple[str, dict]]: messages = [] for xpub, pubkey in self.cosigner_list: if not self.cosigner_can_sign(tx, xpub): continue - raw_tx_bytes = tx.serialize_as_bytes() - messages.append((pubkey, raw_tx_bytes.hex())) + payload = {'tx': tx.serialize_as_bytes().hex()} + if label: + payload['label'] = label + messages.append((pubkey, payload)) return messages - def send_psbt(self, tx: Union[Transaction, PartialTransaction]): - self.do_send(self.prepare_messages(tx), tx.txid()) + def send_psbt(self, tx: Union[Transaction, PartialTransaction], label: str): + self.do_send(self.prepare_messages(tx, label), tx.txid()) - def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None): + def do_send(self, messages: List[Tuple[str, dict]], txid: Optional[str] = None): raise NotImplementedError() - def on_receive(self, pubkey, event_id, tx): + def on_receive(self, pubkey, event_id, tx, label: str): raise NotImplementedError() - def add_transaction_to_wallet(self, tx, *, on_failure=None, on_success=None): + def add_transaction_to_wallet( + self, + tx: Union['Transaction', 'PartialTransaction'], + *, + label: str = None, + on_failure: Callable = None, + on_success: Callable = None + ) -> None: try: # TODO: adding tx should be handled more gracefully here: # 1) don't replace tx with same tx with less signatures @@ -272,6 +286,8 @@ class CosignerWallet(Logger): if not self.wallet.adb.add_transaction(tx): # TODO: instead of bool return value, we could use specific fail reason exceptions here raise Exception('transaction was not added') + if label: + self.wallet.set_label(tx.txid(), label) except Exception as e: if on_failure: on_failure(str(e)) diff --git a/electrum/plugins/psbt_nostr/qml.py b/electrum/plugins/psbt_nostr/qml.py index 5922eae28..46d4ba2fd 100644 --- a/electrum/plugins/psbt_nostr/qml.py +++ b/electrum/plugins/psbt_nostr/qml.py @@ -50,7 +50,7 @@ class QReceiveSignalObject(QObject): QObject.__init__(self) self._plugin = plugin - cosignerReceivedPsbt = pyqtSignal(str, str, str) + cosignerReceivedPsbt = pyqtSignal(str, str, str, str) sendPsbtFailed = pyqtSignal(str, arguments=['reason']) sendPsbtSuccess = pyqtSignal() @@ -66,11 +66,12 @@ class QReceiveSignalObject(QObject): return cosigner_wallet.can_send_psbt(tx_from_any(tx, deserialize=True)) @pyqtSlot(QEWallet, str) - def sendPsbt(self, wallet: 'QEWallet', tx: str): + @pyqtSlot(QEWallet, str, str) + def sendPsbt(self, wallet: 'QEWallet', tx: str, label: str = None): cosigner_wallet = self._plugin.cosigner_wallets.get(wallet.wallet) if not cosigner_wallet: return - cosigner_wallet.send_psbt(tx_from_any(tx, deserialize=True)) + cosigner_wallet.send_psbt(tx_from_any(tx, deserialize=True), label) @pyqtSlot(QEWallet, str) def acceptPsbt(self, wallet: 'QEWallet', event_id: str): @@ -126,20 +127,20 @@ class QmlCosignerWallet(EventListener, CosignerWallet): self.user_prompt_cooldown = None @event_listener - def on_event_psbt_nostr_received(self, wallet, pubkey, event_id, tx: 'PartialTransaction'): + 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()) + 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, on_failure=self.on_add_fail) + self.add_transaction_to_wallet(self.tx, label=label, on_failure=self.on_add_fail) def close(self): super().close() self.unregister_callbacks() - def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None): + def do_send(self, messages: List[Tuple[str, dict]], txid: Optional[str] = None): if not messages: return coro = self.send_direct_messages(messages) diff --git a/electrum/plugins/psbt_nostr/qml/main.qml b/electrum/plugins/psbt_nostr/qml/main.qml index 7df6d6aed..8067d105d 100644 --- a/electrum/plugins/psbt_nostr/qml/main.qml +++ b/electrum/plugins/psbt_nostr/qml/main.qml @@ -7,13 +7,16 @@ import "../../../gui/qml/components/controls" Item { Connections { target: AppController ? AppController.plugin('psbt_nostr') : null - function onCosignerReceivedPsbt(pubkey, event, tx) { + function onCosignerReceivedPsbt(pubkey, event, tx, label) { var dialog = app.messageDialog.createObject(app, { text: [ - qsTr('A transaction was received from your cosigner.'), + 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('\n'), - yesno: true + ].join('

'), + yesno: true, + richText: true }) dialog.accepted.connect(function () { var page = app.stack.push(Qt.resolvedUrl('../../../gui/qml/components/TxDetails.qml'), { @@ -40,16 +43,24 @@ Item { onClicked: { console.log('about to psbt nostr send') psbt_nostr_send_button.enabled = false - AppController.plugin('psbt_nostr').sendPsbt(Daemon.currentWallet, dialog.text) + AppController.plugin('psbt_nostr').sendPsbt(Daemon.currentWallet, dialog.text, dialog.tx_label) } Connections { target: AppController ? AppController.plugin('psbt_nostr') : null + function onSendPsbtSuccess() { + dialog.close() + var msgdialog = app.messageDialog.createObject(app, { + text: qsTr('PSBT sent successfully') + }) + msgdialog.open() + } function onSendPsbtFailed(message) { psbt_nostr_send_button.enabled = true - var dialog = app.messageDialog.createObject(app, { - text: qsTr('Sending PSBT to co-signer failed:\n%1').arg(message) + var msgdialog = app.messageDialog.createObject(app, { + text: qsTr('Sending PSBT to co-signer failed:\n%1').arg(message), + iconSource: Qt.resolvedUrl('../../../gui/icons/warning.png') }) - dialog.open() + msgdialog.open() } } diff --git a/electrum/plugins/psbt_nostr/qt.py b/electrum/plugins/psbt_nostr/qt.py index e18f31af5..b86c13e64 100644 --- a/electrum/plugins/psbt_nostr/qt.py +++ b/electrum/plugins/psbt_nostr/qt.py @@ -24,7 +24,7 @@ # SOFTWARE. import asyncio from functools import partial -from typing import TYPE_CHECKING, List, Tuple, Optional +from typing import TYPE_CHECKING, List, Tuple, Optional, Union from PyQt6.QtCore import QObject, pyqtSignal from PyQt6.QtWidgets import QPushButton, QMessageBox @@ -39,11 +39,12 @@ from electrum.gui.qt.util import read_QIcon_from_bytes from .psbt_nostr import PsbtNostrPlugin, CosignerWallet if TYPE_CHECKING: + from electrum.transaction import Transaction, PartialTransaction from electrum.gui.qt.main_window import ElectrumWindow class QReceiveSignalObject(QObject): - cosignerReceivedPsbt = pyqtSignal(str, str, object) + cosignerReceivedPsbt = pyqtSignal(str, str, object, str) class Plugin(PsbtNostrPlugin): @@ -71,7 +72,7 @@ class Plugin(PsbtNostrPlugin): d.cosigner_send_button = b = QPushButton(_("Send to cosigner")) icon = read_QIcon_from_bytes(self.read_file("nostr_multisig.png")) b.setIcon(icon) - b.clicked.connect(lambda: cw.send_to_cosigners(d.tx)) + b.clicked.connect(lambda: cw.send_to_cosigners(d.tx, d.desc)) d.buttons.insert(0, b) b.setVisible(False) @@ -100,11 +101,11 @@ class QtCosignerWallet(EventListener, CosignerWallet): if self.wallet == wallet: self.obj.cosignerReceivedPsbt.emit(*args) # put on UI thread via signal - def send_to_cosigners(self, tx): - self.add_transaction_to_wallet(tx, on_failure=self.on_add_fail) - self.send_psbt(tx) + def send_to_cosigners(self, tx: Union['Transaction', 'PartialTransaction'], label: str): + self.add_transaction_to_wallet(tx, label=label, on_failure=self.on_add_fail) + self.send_psbt(tx, label) - def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None): + def do_send(self, messages: List[Tuple[str, dict]], txid: Optional[str] = None): if not messages: return coro = self.send_direct_messages(messages) @@ -122,21 +123,26 @@ class QtCosignerWallet(EventListener, CosignerWallet): self.window.show_message( _("Your transaction was sent to your cosigners via Nostr.") + '\n\n' + txid) - def on_receive(self, pubkey, event_id, tx): - msg = _("A transaction was received from your cosigner ({}).").format(str(event_id)[0:8]) + '\n' + \ - _("Do you want to open it now?") - result = self.window.show_message(msg, icon=QMessageBox.Icon.Question, buttons=[ + def on_receive(self, pubkey, event_id, tx, label): + msg = '
'.join([ + _("A transaction was received from your cosigner.") if not label else + _("A transaction was received from your cosigner with label:
{}
").format(label), + _("Do you want to open it now?") + ]) + result = self.window.show_message(msg, rich_text=True, icon=QMessageBox.Icon.Question, buttons=[ QMessageBox.StandardButton.Open, (QPushButton('Discard'), QMessageBox.ButtonRole.DestructiveRole, 100), (QPushButton('Save to wallet'), QMessageBox.ButtonRole.AcceptRole, 101)] ) if result == QMessageBox.StandardButton.Open: + if label: + self.wallet.set_label(tx.txid(), label) show_transaction(tx, parent=self.window, prompt_if_unsaved=True, on_closed=partial(self.on_tx_dialog_closed, event_id)) else: self.mark_pending_event_rcvd(event_id) if result == 100: # Discard return - self.add_transaction_to_wallet(tx, on_failure=self.on_add_fail) + self.add_transaction_to_wallet(tx, label=label, on_failure=self.on_add_fail) self.window.update_tabs() def on_tx_dialog_closed(self, event_id):