1
0

qt: show terms of use as first window on setup

This commit is contained in:
f321x
2025-05-07 09:50:42 +02:00
parent cec2089917
commit 802c316edb
6 changed files with 145 additions and 30 deletions

View File

@@ -78,3 +78,13 @@ MSG_LN_UTXO_RESERVE = (
_("You do not have enough on-chain funds to protect your Lightning channels.") + ' ' +
_("You should have at least {} on-chain in order to be able to sweep channel outputs.")
)
# not to be translated
MSG_TERMS_OF_USE = """
1. Electrum is distributed under the MIT licence by Electrum Technologies GmbH. Most notably, this means that the Electrum software is provided as is, and that it comes without warranty.
2. We are neither a bank nor a financial service provider. In addition, we do not not store user account data, and we are not an intermediary in the interaction between our software and the Bitcoin blockchain. Therefore, we do not have the possibility to freeze funds or to undo a fraudulent transaction.
3. We do not provide private user support. All issue resolutions are public, and take place on Github or public forums. If someone posing as 'Electrum support' proposes to help you via a private channel, this person is most likely an imposter trying to steal your bitcoins.
"""

View File

@@ -500,6 +500,19 @@ class ElectrumGui(BaseElectrumGui, Logger):
window.close()
self._create_window_for_wallet(wallet)
def ask_terms_of_use(self):
"""Ask the user to accept the terms of use.
This is only shown if the user has not accepted them yet.
"""
if self.config.TERMS_OF_USE_ACCEPTED:
return
from electrum.gui.qt.wizard.terms_of_use import QETermsOfUseWizard
dialog = QETermsOfUseWizard(self.config, self.app)
result = dialog.exec()
if result == QDialog.DialogCode.Rejected:
self.logger.info('terms of use not accepted by user')
raise UserCancelled()
def init_network(self):
"""Start the network, including showing a first-start network dialog if config does not exist."""
if self.daemon.network:
@@ -524,6 +537,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
Exception_Hook.maybe_setup(config=self.config)
# start network, and maybe show first-start network-setup
try:
self.ask_terms_of_use()
self.init_network()
except UserCancelled:
return

View File

@@ -27,7 +27,7 @@ class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard):
# attach gui classes
self.navmap_merge({
'welcome': {'gui': WCWelcome, 'params': {'icon': ''}},
'welcome': {'gui': WCWelcome},
'proxy_config': {'gui': WCProxyConfig},
'server_config': {'gui': WCServerConfig},
})
@@ -35,31 +35,22 @@ class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard):
class WCWelcome(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title='')
WizardComponent.__init__(self, parent, wizard, title='Network Configuration')
self.wizard_title = _('Electrum Bitcoin Wallet')
self.use_advanced_w = QCheckBox(_('Advanced network settings'))
self.use_advanced_w.setChecked(False)
self.use_advanced_w.stateChanged.connect(self.on_advanced_changed)
self.img_label = QLabel()
pixmap = QPixmap(icon_path('electrum_darkblue_1.png'))
self.img_label.setPixmap(pixmap)
self.img_label2 = QLabel()
pixmap = QPixmap(icon_path('electrum_text.png'))
self.img_label2.setPixmap(pixmap)
hbox_img = QHBoxLayout()
hbox_img.addStretch(1)
hbox_img.addWidget(self.img_label)
hbox_img.addWidget(self.img_label2)
hbox_img.addStretch(1)
self.help_label = QLabel()
self.help_label.setText("\n".join([
_("Optional settings to customize your network connection."),
_("If you are unsure what this is, leave them unchecked and Electrum will automatically "
"select servers."),
]))
self.help_label.setWordWrap(True)
self.config_proxy_w = QCheckBox(_('Configure Proxy'))
self.config_proxy_w.setChecked(False)
self.config_proxy_w.setVisible(False)
self.config_proxy_w.stateChanged.connect(self.on_updated)
self.config_server_w = QCheckBox(_('Select Server'))
self.config_server_w.setChecked(False)
self.config_server_w.setVisible(False)
self.config_server_w.stateChanged.connect(self.on_updated)
options_w = QWidget()
vbox = QVBoxLayout()
@@ -68,21 +59,15 @@ class WCWelcome(WizardComponent):
vbox.addStretch(1)
options_w.setLayout(vbox)
self.layout().addLayout(hbox_img)
self.layout().addSpacing(50)
self.layout().addWidget(self.use_advanced_w, False, Qt.AlignmentFlag.AlignHCenter)
self.layout().addWidget(options_w, False, Qt.AlignmentFlag.AlignHCenter)
self.layout().addWidget(self.help_label)
self.layout().addSpacing(30)
self.layout().addWidget(options_w, False, Qt.AlignmentFlag.AlignLeft)
self._valid = True
def on_advanced_changed(self):
self.config_proxy_w.setVisible(self.use_advanced_w.isChecked())
self.config_server_w.setVisible(self.use_advanced_w.isChecked())
self.on_updated()
def apply(self):
self.wizard_data['use_defaults'] = not self.use_advanced_w.isChecked()
self.wizard_data['want_proxy'] = self.use_advanced_w.isChecked() and self.config_proxy_w.isChecked()
self.wizard_data['autoconnect'] = not self.use_advanced_w.isChecked() or not self.config_server_w.isChecked()
self.wizard_data['use_defaults'] = not (self.config_server_w.isChecked() or self.config_proxy_w.isChecked())
self.wizard_data['want_proxy'] = self.config_proxy_w.isChecked()
self.wizard_data['autoconnect'] = not self.config_server_w.isChecked()
class WCProxyConfig(WizardComponent):

View File

@@ -0,0 +1,76 @@
from typing import TYPE_CHECKING
from PyQt6.QtCore import QTimer
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import QLabel, QHBoxLayout, QScrollArea
from electrum.i18n import _
from electrum.wizard import TermsOfUseWizard
from electrum.gui.qt.util import icon_path
from electrum.gui import messages
from .wizard import QEAbstractWizard, WizardComponent
if TYPE_CHECKING:
from electrum.simple_config import SimpleConfig
from electrum.gui.qt import QElectrumApplication
class QETermsOfUseWizard(TermsOfUseWizard, QEAbstractWizard):
def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication'):
TermsOfUseWizard.__init__(self, config)
QEAbstractWizard.__init__(self, config, app)
self.window_title = _('Terms of Use')
self.finish_label = _('I Accept')
self.title.setVisible(False)
# self.window().setMinimumHeight(565) # Enough to show the whole text without scrolling
self.next_button.setToolTip("You accept the Terms of Use by clicking this button.")
# attach gui classes
self.navmap_merge({
'terms_of_use': {'gui': WCTermsOfUseScreen, 'params': {'icon': ''}},
})
class WCTermsOfUseScreen(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title='')
self.wizard_title = _('Electrum Terms of Use')
self.img_label = QLabel()
pixmap = QPixmap(icon_path('electrum_darkblue_1.png'))
self.img_label.setPixmap(pixmap)
self.img_label2 = QLabel()
pixmap = QPixmap(icon_path('electrum_text.png'))
self.img_label2.setPixmap(pixmap)
hbox_img = QHBoxLayout()
hbox_img.addStretch(1)
hbox_img.addWidget(self.img_label)
hbox_img.addWidget(self.img_label2)
hbox_img.addStretch(1)
self.layout().addLayout(hbox_img)
self.tos_label = QLabel()
self.tos_label.setText(messages.MSG_TERMS_OF_USE)
self.tos_label.setWordWrap(True)
self.layout().addWidget(self.tos_label)
self._valid = False
# Find the scroll area and connect to its scrollbar
QTimer.singleShot(0, self.check_scroll_position)
def check_scroll_position(self):
# Find the scroll area
scroll_area = self.window().findChild(QScrollArea)
if scroll_area and scroll_area.verticalScrollBar():
scrollbar = scroll_area.verticalScrollBar()
def on_scroll_change(value):
if value >= scrollbar.maximum() - 5: # Allow 5 pixel margin
self._valid = True
self.on_updated()
scrollbar.valueChanged.connect(on_scroll_change)
else:
# Fallback if the scroll area is not detected
self._valid = True
self.on_updated()
def apply(self):
pass

View File

@@ -847,6 +847,7 @@ Warning: setting this to too low will result in lots of payment failures."""),
QR_READER_FLIP_X = ConfigVar('qrreader_flip_x', default=True, type_=bool)
WIZARD_DONT_CREATE_SEGWIT = ConfigVar('nosegwit', default=False, type_=bool)
CONFIG_FORGET_CHANGES = ConfigVar('forget_config', default=False, type_=bool)
TERMS_OF_USE_ACCEPTED = ConfigVar('terms_of_use_accepted', default=False, type_=bool)
# connect to remote submarine swap server
SWAPSERVER_URL = ConfigVar('swapserver_url', default='', type_=str)

View File

@@ -20,6 +20,7 @@ if TYPE_CHECKING:
from electrum.daemon import Daemon
from electrum.plugin import Plugins
from electrum.keystore import Hardware_KeyStore
from electrum.simple_config import SimpleConfig
class WizardViewState(NamedTuple):
@@ -766,3 +767,31 @@ class ServerConnectWizard(AbstractWizard):
params = self.navmap[start_view].get('params', {})
self._current = WizardViewState(start_view, initial_data, params)
return self._current
class TermsOfUseWizard(AbstractWizard):
_logger = get_logger(__name__)
def __init__(self, config: 'SimpleConfig'):
AbstractWizard.__init__(self)
self._config = config
self.navmap = {
'terms_of_use': {
'accept': self.accept_terms_of_use,
'last': True,
},
}
def accept_terms_of_use(self, _):
self._config.TERMS_OF_USE_ACCEPTED = True
def start(self, initial_data: dict = None) -> WizardViewState:
if initial_data is None:
initial_data = {}
self.reset()
start_view = 'terms_of_use'
params = self.navmap[start_view].get('params', {})
self._current = WizardViewState(start_view, initial_data, params)
return self._current