diff --git a/jmclient/jmclient/payjoin.py b/jmclient/jmclient/payjoin.py index 551c0b3..d3d0952 100644 --- a/jmclient/jmclient/payjoin.py +++ b/jmclient/jmclient/payjoin.py @@ -1043,7 +1043,8 @@ class JMBIP78ReceiverManager(object): """ A class to encapsulate receiver construction """ def __init__(self, wallet_service, mixdepth, amount, port, - info_callback=None, uri_created_callback = None, + info_callback=None, uri_created_callback=None, + shutdown_callback=None, mode="command-line"): assert isinstance(wallet_service, WalletService) assert isinstance(mixdepth, int) @@ -1068,6 +1069,9 @@ class JMBIP78ReceiverManager(object): self.uri_created_callback = self.info_callback else: self.uri_created_callback = uri_created_callback + # This callback used by GUI as a signal that it can + # signal the user that the dialog is close-able: + self.shutdown_callback = shutdown_callback self.receiving_address = None self.mode = mode @@ -1157,4 +1161,6 @@ class JMBIP78ReceiverManager(object): def shutdown(self): self.tor_connection.protocol.transport.loseConnection() process_shutdown(self.mode) - self.info_callback("Hidden service shutdown complete") \ No newline at end of file + self.info_callback("Hidden service shutdown complete") + if self.shutdown_callback: + self.shutdown_callback() diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index 94e553c..6bf3d5f 100755 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -1571,6 +1571,7 @@ class JMMainWindow(QMainWindow): self.backend_receiver = JMBIP78ReceiverManager(self.wallet_service, mixdepth, amount, 80, self.receiver_bip78_dialog.info_update, uri_created_callback=self.receiver_bip78_dialog.update_uri, + shutdown_callback=self.receiver_bip78_dialog.process_complete, mode="gui") self.backend_receiver.start_pj_server_and_tor() return True diff --git a/scripts/qtsupport.py b/scripts/qtsupport.py index 3260298..030d0b4 100644 --- a/scripts/qtsupport.py +++ b/scripts/qtsupport.py @@ -948,8 +948,8 @@ class ReceiveBIP78Dialog(QDialog): parameter_names = ['Amount to receive', 'Mixdepth'] parameter_tooltips = [ "How much you should receive (after any fees) in BTC or sats.", - "The mixdepth you source coins from to create inputs for the " - "payjoin. Note your receiving address will be chosen from the " + "The mixdepth you source coins from to create inputs for the\n" + "payjoin. Note your receiving address will be chosen from the\n" "*next* mixdepth after this (or 0 if last)."] parameter_types = ["btc", int] parameter_settings = ["", 0] @@ -1007,6 +1007,16 @@ class ReceiveBIP78Dialog(QDialog): self.cancel_fn() self.close() + def process_complete(self): + """ Called by the owning Qt object + when the BIP78 workflow is complete, + whether successful or not. + """ + # Give user indication that they + # can quit without cancelling: + self.close_btn.setVisible(True) + self.btnbox.button(QDialogButtonBox.Cancel).setDisabled(True) + def start_generate(self): """ Before starting up the hidden service and initiating the payment @@ -1015,9 +1025,9 @@ class ReceiveBIP78Dialog(QDialog): If the 'start generate request' action is aborted, we reset the generate button. """ - self.btnbox.buttons()[1].setDisabled(True) + self.generate_btn.setDisabled(True) if not self.action_fn(): - self.btnbox.buttons()[1].setDisabled(False) + self.generate_btn.setDisabled(False) def get_receive_bip78_dialog(self): """ Displays editable parameters and @@ -1062,12 +1072,20 @@ class ReceiveBIP78Dialog(QDialog): layout.addWidget(self.updates_label, i+2, 0, 1, 2) layout.addWidget(self.bip21_widget, i+3, 0, 1, 2) - # Buttons for start/cancel: + # Buttons for start/cancel/close: self.btnbox = QDialogButtonBox() self.btnbox.setStandardButtons(QDialogButtonBox.Cancel) - btnname = "Generate request" - self.btnbox.addButton(btnname, QDialogButtonBox.ActionRole) + self.generate_btn = self.btnbox.addButton("&Generate request", + QDialogButtonBox.ActionRole) + self.close_btn = self.btnbox.addButton("C&lose", + QDialogButtonBox.AcceptRole) + self.close_btn.setVisible(False) layout.addWidget(self.btnbox, i+4, 0) + # note that we don't use a standard 'Close' button because + # it is also associated with 'rejection' (and we don't use "OK" because + # concept doesn't quite fit here: self.btnbox.rejected.connect(self.shutdown_actions) - self.btnbox.buttons()[1].clicked.connect(self.start_generate) + self.generate_btn.clicked.connect(self.start_generate) + # does not trigger cancel_fn callback: + self.close_btn.clicked.connect(self.close) return layout