1
0

SwapProvidersButton class

factorize code that is used in both SwapDialog and Submarine payments
This commit is contained in:
ThomasV
2025-11-27 13:25:41 +01:00
parent 8bec3eafd2
commit 128026a442
2 changed files with 53 additions and 45 deletions

View File

@@ -28,7 +28,6 @@ from decimal import Decimal
from functools import partial
from typing import TYPE_CHECKING, Optional, Union
from electrum_aionostr.util import from_nip19
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import (QHBoxLayout, QVBoxLayout, QLabel, QGridLayout, QPushButton, QToolButton,
@@ -47,14 +46,13 @@ from electrum.submarine_swaps import NostrTransport, HttpTransport
from .seed_dialog import seed_warning_msg
from .util import (WindowModalDialog, ColorScheme, HelpLabel, Buttons, CancelButton, WWLabel,
read_QIcon, debug_widget_layouts, qt_event_listener, QtEventListener, IconLabel,
pubkey_to_q_icon)
read_QIcon, debug_widget_layouts, qt_event_listener, QtEventListener, IconLabel)
from .transaction_dialog import TxSizeLabel, TxFiatLabel, TxInOutWidget
from .fee_slider import FeeSlider, FeeComboBox
from .amountedit import FeerateEdit, BTCAmountEdit
from .locktimeedit import LockTimeEdit
from .my_treeview import QMenuWithConfig
from .swap_dialog import SwapServerDialog
from .swap_dialog import SwapProvidersButton
if TYPE_CHECKING:
from .main_window import ElectrumWindow
@@ -303,7 +301,7 @@ class TxEditor(WindowModalDialog, QtEventListener, Logger):
self.tab_widget.removeTab(0)
# always show onchain payment tab
self.tab_widget.addTab(self.onchain_tab, _('Onchain'))
self.tab_widget.addTab(self.onchain_tab, _('Onchain Transaction'))
allow_swaps = self.allow_preview and self.payee_outputs # allow_preview is false for ln channel opening txs
if self.config.WALLET_ENABLE_SUBMARINE_PAYMENTS and allow_swaps:
@@ -728,10 +726,7 @@ class TxEditor(WindowModalDialog, QtEventListener, Logger):
vbox.addWidget(self.submarine_stacked_widget)
vbox.addStretch(1)
self.server_button = QPushButton()
self.server_button.clicked.connect(self.choose_swap_server)
self.server_button.setEnabled(False)
self.server_button.setVisible(False)
self.server_button = SwapProvidersButton(lambda: self.swap_transport, self.config, self.main_window)
self.submarine_ok_button = QPushButton(_('OK'))
self.submarine_ok_button.setDefault(True)
@@ -830,9 +825,8 @@ class TxEditor(WindowModalDialog, QtEventListener, Logger):
# we couldn't even connect to the relays, this transport is useless. maybe network issues.
return False
def choose_swap_server(self) -> None:
assert isinstance(self.swap_transport, NostrTransport), self.swap_transport
self.main_window.choose_swapserver_dialog(self.swap_transport)
@qt_event_listener
def on_event_swap_provider_changed(self):
self.update_submarine_tab()
def start_submarine_swap(self):
@@ -881,20 +875,7 @@ class TxEditor(WindowModalDialog, QtEventListener, Logger):
return
# Update the swapserver selection button text
if isinstance(self.swap_transport, NostrTransport):
self.server_button.setVisible(True)
self.server_button.setEnabled(True)
if self.config.SWAPSERVER_NPUB:
pubkey = from_nip19(self.config.SWAPSERVER_NPUB)['object'].hex()
self.server_button.setIcon(pubkey_to_q_icon(pubkey))
self.server_button.setText(
f' {len(self.swap_transport.get_recent_offers())} ' +
(_('providers') if len(self.swap_transport.get_recent_offers()) != 1 else _('provider'))
)
else:
# HTTPTransport or no Network, not showing server selection button
self.server_button.setEnabled(False)
self.server_button.setVisible(False)
self.server_button.update()
if not self.swap_manager.is_initialized.is_set():
# connected to nostr relays but couldn't find swapserver announcement
@@ -996,6 +977,7 @@ class TxEditor(WindowModalDialog, QtEventListener, Logger):
@qt_event_listener
def on_event_swap_offers_changed(self, _):
self.server_button.update()
if self.ongoing_swap_transport_connection_attempt \
and not self.ongoing_swap_transport_connection_attempt.done():
return

View File

@@ -9,7 +9,7 @@ from PyQt6.QtWidgets import QTreeWidget, QTreeWidgetItem, QHeaderView
from electrum_aionostr.util import from_nip19
from electrum.i18n import _
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates, UserCancelled
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates, UserCancelled, trigger_callback
from electrum.bitcoin import DummyAddress
from electrum.transaction import PartialTxOutput, PartialTransaction
from electrum.fee_policy import FeePolicy
@@ -43,6 +43,43 @@ ROLE_NPUB = Qt.ItemDataRole.UserRole + 1000
class InvalidSwapParameters(Exception): pass
class SwapProvidersButton(QPushButton):
def __init__(self, transport_getter, config, main_window):
"""parent must have a transport() method"""
QPushButton.__init__(self)
self.config = config
self.transport_getter = transport_getter
self.main_window = main_window
self.clicked.connect(self.choose_swap_server)
self.update()
def update(self):
transport = self.transport_getter()
if not isinstance(transport, NostrTransport):
# HTTPTransport or no Network, not showing server selection button
self.setEnabled(False)
self.setVisible(False)
return
self.setEnabled(True)
self.setVisible(True)
offer_count = len(transport.get_recent_offers())
button_text = f' {offer_count} ' + (_('swap providers') if offer_count != 1 else _('swap provider'))
self.setText(button_text)
# update icon
if self.config.SWAPSERVER_NPUB:
pubkey = from_nip19(self.config.SWAPSERVER_NPUB)['object'].hex()
self.setIcon(pubkey_to_q_icon(pubkey))
def choose_swap_server(self) -> None:
transport = self.transport_getter()
assert isinstance(transport, NostrTransport), transport
self.main_window.choose_swapserver_dialog(transport) # type: ignore
self.update()
trigger_callback('swap_provider_changed')
class SwapDialog(WindowModalDialog, QtEventListener):
def __init__(
@@ -63,12 +100,8 @@ class SwapDialog(WindowModalDialog, QtEventListener):
self.is_reverse = is_reverse if is_reverse is not None else True
vbox = QVBoxLayout(self)
self.server_button = QPushButton()
self.set_server_button_text(len(transport.get_recent_offers()) \
if not self.config.SWAPSERVER_URL and isinstance(transport, NostrTransport) else 0
)
self.server_button.clicked.connect(lambda: self.choose_swap_server(transport))
self.server_button.setEnabled(not self.config.SWAPSERVER_URL)
self.transport = transport
self.server_button = SwapProvidersButton(lambda: self.transport, self.config, self.window)
self.description_label = WWLabel(self.get_description())
self.send_amount_e = BTCAmountEdit(self.window.get_decimal_point)
self.recv_amount_e = BTCAmountEdit(self.window.get_decimal_point)
@@ -158,7 +191,7 @@ class SwapDialog(WindowModalDialog, QtEventListener):
@qt_event_listener
def on_event_swap_offers_changed(self, recent_offers: Sequence['SwapOffer']):
self.set_server_button_text(len(recent_offers))
self.server_button.update()
if not self.ok_button.isEnabled():
# only update the dialog with the new offer if the user hasn't entered an amount yet.
# if the user has already entered an amount we prefer the swap to fail due to outdated
@@ -166,9 +199,10 @@ class SwapDialog(WindowModalDialog, QtEventListener):
# due to an update happening just before the user initiated the swap
self.update()
def set_server_button_text(self, offer_count: int):
button_text = f' {offer_count} ' + (_('providers') if offer_count != 1 else _('provider'))
self.server_button.setText(button_text)
@qt_event_listener
def on_event_swap_provider_changed(self):
self.update()
self.update_send_receive()
def timer_actions(self):
if self.needs_tx_update:
@@ -298,9 +332,6 @@ class SwapDialog(WindowModalDialog, QtEventListener):
self.server_fee_label.setText(server_fee_str)
self.server_fee_label.repaint() # macOS hack for #6269
self.needs_tx_update = True
# update icon
pubkey = from_nip19(self.config.SWAPSERVER_NPUB)['object'].hex() if self.config.SWAPSERVER_NPUB else ''
self.server_button.setIcon(pubkey_to_q_icon(pubkey))
def get_client_swap_limits_sat(self) -> Tuple[int, int]:
"""Returns the (min, max) client swap limits in sat."""
@@ -452,11 +483,6 @@ class SwapDialog(WindowModalDialog, QtEventListener):
capacityType="receiving" if self.is_reverse else "sending",
)
def choose_swap_server(self, transport: 'SwapServerTransport') -> None:
self.window.choose_swapserver_dialog(transport) # type: ignore
self.update()
self.update_send_receive()
class SwapServerDialog(WindowModalDialog, QtEventListener):