Skip to content

Commit 60b12a7

Browse files
committed
plugins: psbt_nostr: qt: offer 3 choices for each PSBT; 'Open, Discard, Save to wallet'
1 parent 60bd632 commit 60b12a7

File tree

4 files changed

+72
-45
lines changed

4 files changed

+72
-45
lines changed

electrum/gui/qt/util.py

+56-27
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88
import webbrowser
99
from functools import partial, lru_cache, wraps
10-
from typing import (NamedTuple, Callable, Optional, TYPE_CHECKING, List, Any, Sequence, Tuple)
10+
from typing import (NamedTuple, Callable, Optional, TYPE_CHECKING, List, Any, Sequence, Tuple, Union)
1111

1212
from PyQt6 import QtCore
1313
from PyQt6.QtGui import (QFont, QColor, QCursor, QPixmap, QImage,
@@ -17,7 +17,7 @@
1717
QStyle, QDialog, QGroupBox, QButtonGroup, QRadioButton,
1818
QFileDialog, QWidget, QToolButton, QPlainTextEdit, QApplication, QToolTip,
1919
QGraphicsEffect, QGraphicsScene, QGraphicsPixmapItem, QLayoutItem, QLayout, QMenu,
20-
QFrame)
20+
QFrame, QAbstractButton)
2121

2222
from electrum.i18n import _
2323
from electrum.util import (FileImportFailed, FileExportFailed, resource_path, EventListener, event_listener,
@@ -262,13 +262,13 @@ def top_level_window(self, test_func=None):
262262
return self.top_level_window_recurse(test_func)
263263

264264
def question(self, msg, parent=None, title=None, icon=None, **kwargs) -> bool:
265-
Yes, No = QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No
266-
return Yes == self.msg_box(icon=icon or QMessageBox.Icon.Question,
265+
yes, no = QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No
266+
return yes == self.msg_box(icon=icon or QMessageBox.Icon.Question,
267267
parent=parent,
268268
title=title or '',
269269
text=msg,
270-
buttons=Yes|No,
271-
defaultButton=No,
270+
buttons=yes | no,
271+
defaultButton=no,
272272
**kwargs)
273273

274274
def show_warning(self, msg, parent=None, title=None, **kwargs):
@@ -283,22 +283,27 @@ def show_critical(self, msg, parent=None, title=None, **kwargs):
283283
return self.msg_box(QMessageBox.Icon.Critical, parent,
284284
title or _('Critical Error'), msg, **kwargs)
285285

286-
def show_message(self, msg, parent=None, title=None, **kwargs):
287-
return self.msg_box(QMessageBox.Icon.Information, parent,
288-
title or _('Information'), msg, **kwargs)
286+
def show_message(self, msg, parent=None, title=None, icon=QMessageBox.Icon.Information, **kwargs):
287+
return self.msg_box(icon, parent, title or _('Information'), msg, **kwargs)
289288

290-
def msg_box(self, icon, parent, title, text, *, buttons=QMessageBox.StandardButton.Ok,
291-
defaultButton=QMessageBox.StandardButton.NoButton, rich_text=False,
292-
checkbox=None):
289+
def msg_box(
290+
self,
291+
icon: Union[QMessageBox.Icon, QPixmap],
292+
parent: QWidget,
293+
title: str,
294+
text: str,
295+
*,
296+
buttons: Union[QMessageBox.StandardButton,
297+
List[Union[QMessageBox.StandardButton, Tuple[QAbstractButton, QMessageBox.ButtonRole]]]] = QMessageBox.StandardButton.Ok,
298+
defaultButton: QMessageBox.StandardButton = QMessageBox.StandardButton.NoButton,
299+
rich_text: bool = False,
300+
checkbox: Optional[bool] = None
301+
):
293302
parent = parent or self.top_level_window()
294-
return custom_message_box(icon=icon,
295-
parent=parent,
296-
title=title,
297-
text=text,
298-
buttons=buttons,
299-
defaultButton=defaultButton,
300-
rich_text=rich_text,
301-
checkbox=checkbox)
303+
return custom_message_box(
304+
icon=icon, parent=parent, title=title, text=text, buttons=buttons, defaultButton=defaultButton,
305+
rich_text=rich_text, checkbox=checkbox
306+
)
302307

303308
def query_choice(self,
304309
msg: Optional[str],
@@ -327,15 +332,35 @@ def password_dialog(self, msg=None, parent=None):
327332
return d.run()
328333

329334

330-
331-
def custom_message_box(*, icon, parent, title, text, buttons=QMessageBox.StandardButton.Ok,
332-
defaultButton=QMessageBox.StandardButton.NoButton, rich_text=False,
333-
checkbox=None):
335+
def custom_message_box(
336+
*,
337+
icon: Union[QMessageBox.Icon, QPixmap],
338+
parent: QWidget,
339+
title: str,
340+
text: str,
341+
buttons: Union[QMessageBox.StandardButton,
342+
List[Union[QMessageBox.StandardButton, Tuple[QAbstractButton, QMessageBox.ButtonRole, int]]]] = QMessageBox.StandardButton.Ok,
343+
defaultButton: QMessageBox.StandardButton = QMessageBox.StandardButton.NoButton,
344+
rich_text: bool = False,
345+
checkbox: Optional[bool] = None
346+
) -> int:
347+
custom_buttons = []
348+
standard_buttons = QMessageBox.StandardButton.NoButton
349+
if buttons:
350+
if not isinstance(buttons, list):
351+
buttons = [buttons]
352+
for button in buttons:
353+
if isinstance(button, QMessageBox.StandardButton):
354+
standard_buttons |= button
355+
else:
356+
custom_buttons.append(button)
334357
if type(icon) is QPixmap:
335-
d = QMessageBox(QMessageBox.Icon.Information, title, str(text), buttons, parent)
358+
d = QMessageBox(QMessageBox.Icon.Information, title, str(text), standard_buttons, parent)
336359
d.setIconPixmap(icon)
337360
else:
338-
d = QMessageBox(icon, title, str(text), buttons, parent)
361+
d = QMessageBox(icon, title, str(text), standard_buttons, parent)
362+
for button, role, _ in custom_buttons:
363+
d.addButton(button, role)
339364
d.setWindowModality(Qt.WindowModality.WindowModal)
340365
d.setDefaultButton(defaultButton)
341366
if rich_text:
@@ -350,7 +375,11 @@ def custom_message_box(*, icon, parent, title, text, buttons=QMessageBox.Standar
350375
d.setTextFormat(Qt.TextFormat.PlainText)
351376
if checkbox is not None:
352377
d.setCheckBox(checkbox)
353-
return d.exec()
378+
result = d.exec()
379+
for button, _, value in custom_buttons:
380+
if button == d.clickedButton():
381+
return value
382+
return result
354383

355384

356385
class WindowModalDialog(QDialog, MessageBoxMixin):

electrum/plugins/psbt_nostr/psbt_nostr.py

+1
Original file line numberDiff line numberDiff line change
@@ -244,5 +244,6 @@ def add_transaction_to_wallet(self, tx, *, on_failure=None, on_success=None):
244244
if on_failure:
245245
on_failure(str(e))
246246
else:
247+
self.wallet.save_db()
247248
if on_success:
248249
on_success()

electrum/plugins/psbt_nostr/qml.py

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def reject_psbt(self, event_id):
154154
self.user_prompt_cooldown = now() + USER_PROMPT_COOLDOWN
155155
self.mark_pending_event_rcvd(event_id)
156156
self.add_transaction_to_wallet(self.tx, on_failure=self.on_add_fail)
157+
self.wallet.save_db()
157158

158159
def on_add_fail(self):
159160
self.logger.error('failed to add tx to wallet')

electrum/plugins/psbt_nostr/qt.py

+14-18
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,19 @@
2727
from typing import TYPE_CHECKING, List, Tuple, Optional
2828

2929
from PyQt6.QtCore import QObject, pyqtSignal
30-
from PyQt6.QtWidgets import QPushButton
30+
from PyQt6.QtWidgets import QPushButton, QMessageBox
3131

3232
from electrum.plugin import hook
3333
from electrum.i18n import _
3434
from electrum.wallet import Multisig_Wallet, Abstract_Wallet
3535
from electrum.util import UserCancelled, event_listener, EventListener
3636
from electrum.gui.qt.transaction_dialog import show_transaction, TxDialog
3737

38-
from .psbt_nostr import PsbtNostrPlugin, CosignerWallet, now
38+
from .psbt_nostr import PsbtNostrPlugin, CosignerWallet
3939

4040
if TYPE_CHECKING:
4141
from electrum.gui.qt.main_window import ElectrumWindow
4242

43-
USER_PROMPT_COOLDOWN = 10
44-
4543

4644
class QReceiveSignalObject(QObject):
4745
cosignerReceivedPsbt = pyqtSignal(str, str, object)
@@ -83,7 +81,6 @@ def __init__(self, wallet: 'Multisig_Wallet', window: 'ElectrumWindow'):
8381
self.obj = QReceiveSignalObject()
8482
self.obj.cosignerReceivedPsbt.connect(self.on_receive)
8583
self.register_callbacks()
86-
self.user_prompt_cooldown = None
8784

8885
def close(self):
8986
super().close()
@@ -113,11 +110,7 @@ def hook_transaction_dialog_update(self, d: 'TxDialog'):
113110
d.cosigner_send_button.setVisible(False)
114111

115112
def send_to_cosigners(self, tx):
116-
def ok():
117-
self.logger.debug('ADDED')
118-
def nok(msg: str):
119-
self.logger.debug(f'NOT ADDED: {msg}')
120-
self.add_transaction_to_wallet(tx, on_success=ok, on_failure=nok)
113+
self.add_transaction_to_wallet(tx, on_failure=self.on_add_fail)
121114
self.send_psbt(tx)
122115

123116
def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None):
@@ -139,18 +132,21 @@ def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None):
139132
_("Your transaction was sent to your cosigners via Nostr.") + '\n\n' + txid)
140133

141134
def on_receive(self, pubkey, event_id, tx):
142-
open_now = False
143-
if not (self.user_prompt_cooldown and self.user_prompt_cooldown > now()):
144-
open_now = self.window.question(
145-
_("A transaction was received from your cosigner ({}).").format(str(event_id)[0:8]) + '\n' +
146-
_("Do you want to open it now?"))
147-
if not open_now:
148-
self.user_prompt_cooldown = now() + USER_PROMPT_COOLDOWN
149-
if open_now:
135+
msg = _("A transaction was received from your cosigner ({}).").format(str(event_id)[0:8]) + '\n' + \
136+
_("Do you want to open it now?")
137+
result = self.window.show_message(msg, icon=QMessageBox.Icon.Question, buttons=[
138+
QMessageBox.StandardButton.Open,
139+
(QPushButton('Discard'), QMessageBox.ButtonRole.DestructiveRole, 100),
140+
(QPushButton('Save to wallet'), QMessageBox.ButtonRole.AcceptRole, 101)]
141+
)
142+
if result == QMessageBox.StandardButton.Open:
150143
show_transaction(tx, parent=self.window, prompt_if_unsaved=True, on_closed=partial(self.on_tx_dialog_closed, event_id))
151144
else:
152145
self.mark_pending_event_rcvd(event_id)
146+
if result == 100: # Discard
147+
return
153148
self.add_transaction_to_wallet(tx, on_failure=self.on_add_fail)
149+
self.window.update_tabs()
154150

155151
def on_tx_dialog_closed(self, event_id):
156152
self.mark_pending_event_rcvd(event_id)

0 commit comments

Comments
 (0)