|
34 | 34 | from functools import partial
|
35 | 35 | import queue
|
36 | 36 | import asyncio
|
37 |
| -from typing import Optional, TYPE_CHECKING, Sequence, Union, Dict, Mapping |
| 37 | +from typing import Optional, TYPE_CHECKING, Sequence, Union, Dict, Mapping, Callable, List, Set |
38 | 38 | import concurrent.futures
|
39 | 39 |
|
40 | 40 | from PyQt6.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont, QFontMetrics, QAction, QShortcut
|
@@ -271,6 +271,9 @@ def add_optional_tab(tabs, tab, icon, description):
|
271 | 271 |
|
272 | 272 | # network callbacks
|
273 | 273 | self.register_callbacks()
|
| 274 | + # wallet closing warning callbacks |
| 275 | + self.closing_warning_callbacks = [] # type: List[Callable[[], Optional[str]]] |
| 276 | + self.register_closing_warning_callback(self._check_ongoing_submarine_swaps_callback) |
274 | 277 | # banner may already be there
|
275 | 278 | if self.network and self.network.banner:
|
276 | 279 | self.console.showMessage(self.network.banner)
|
@@ -2684,8 +2687,62 @@ def settings_dialog(self):
|
2684 | 2687 | if d.need_restart:
|
2685 | 2688 | self.show_warning(_('Please restart Electrum to activate the new GUI settings'), title=_('Success'))
|
2686 | 2689 |
|
| 2690 | + def _show_closing_warnings(self) -> bool: |
| 2691 | + """Show any closing warnings and return True if the user chose to quit anyway.""" |
| 2692 | + |
| 2693 | + warnings: Set[str] = set() |
| 2694 | + for cb in self.closing_warning_callbacks: |
| 2695 | + if warning := cb(): |
| 2696 | + warnings.add(warning) |
| 2697 | + |
| 2698 | + for warning in list(warnings)[:3]: |
| 2699 | + warning = _("An ongoing operation prevents Electrum from closing:") + "\n\n" + warning |
| 2700 | + buttons = QMessageBox.StandardButton.Cancel | QMessageBox.StandardButton.Close |
| 2701 | + result = self.show_warning( |
| 2702 | + msg=warning, |
| 2703 | + title=_("Don't close Electrum yet!"), |
| 2704 | + buttons=buttons, |
| 2705 | + defaultButton=QMessageBox.StandardButton.Cancel, |
| 2706 | + ) |
| 2707 | + if result == QMessageBox.StandardButton.Cancel: |
| 2708 | + break |
| 2709 | + else: |
| 2710 | + # user chose to cancel all warnings or there were no warnings |
| 2711 | + return True |
| 2712 | + return False |
| 2713 | + |
| 2714 | + def register_closing_warning_callback(self, callback: Callable[[], Optional[str]]) -> None: |
| 2715 | + """ |
| 2716 | + Registers a callback that will be called when the wallet is closed. If the callback |
| 2717 | + returns a string it will be shown to the user as a warning to prevent them closing the wallet. |
| 2718 | + """ |
| 2719 | + assert not asyncio.iscoroutinefunction(callback) |
| 2720 | + def warning_callback() -> Optional[str]: |
| 2721 | + try: |
| 2722 | + return callback() |
| 2723 | + except Exception: |
| 2724 | + self.logger.exception("Error in closing warning callback") |
| 2725 | + return None |
| 2726 | + self.logger.debug(f"registering wallet closing warning callback") |
| 2727 | + self.closing_warning_callbacks.append(warning_callback) |
| 2728 | + |
| 2729 | + def _check_ongoing_submarine_swaps_callback(self) -> Optional[str]: |
| 2730 | + """Callback that will return a warning string if there is a unconfirmed swap funding.""" |
| 2731 | + if not (self.wallet.has_lightning() and self.wallet.lnworker.swap_manager): |
| 2732 | + return None |
| 2733 | + if self.wallet.lnworker.swap_manager.has_pending_swaps(): |
| 2734 | + return "\n".join(( |
| 2735 | + _("Ongoing submarine swap:"), |
| 2736 | + _("Wait until the funding transaction of your swap confirms, " |
| 2737 | + "otherwise you risk losing funds.") |
| 2738 | + )) |
| 2739 | + return None |
| 2740 | + |
2687 | 2741 | def closeEvent(self, event):
|
2688 | 2742 | # note that closeEvent is NOT called if the user quits with Ctrl-C
|
| 2743 | + if not self._show_closing_warnings(): |
| 2744 | + event.ignore() |
| 2745 | + return |
2689 | 2746 | self.clean_up()
|
2690 | 2747 | event.accept()
|
2691 | 2748 |
|
|
0 commit comments