qt: initial trustedcoin wizard pages
This commit is contained in:
@@ -148,6 +148,9 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
|||||||
self._default_qtstylesheet = self.app.styleSheet()
|
self._default_qtstylesheet = self.app.styleSheet()
|
||||||
self.reload_app_stylesheet()
|
self.reload_app_stylesheet()
|
||||||
|
|
||||||
|
# always load 2fa
|
||||||
|
self.plugins.load_plugin('trustedcoin')
|
||||||
|
|
||||||
run_hook('init_qt', self)
|
run_hook('init_qt', self)
|
||||||
|
|
||||||
def _init_tray(self):
|
def _init_tray(self):
|
||||||
|
|||||||
@@ -10,13 +10,16 @@ from ..util import ChoicesLayout
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from electrum.simple_config import SimpleConfig
|
from electrum.simple_config import SimpleConfig
|
||||||
|
from electrum.plugin import Plugins
|
||||||
|
from electrum.daemon import Daemon
|
||||||
|
from electrum.gui.qt import QElectrumApplication
|
||||||
|
|
||||||
|
|
||||||
class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard):
|
class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard):
|
||||||
|
|
||||||
def __init__(self, config: 'SimpleConfig', app: QApplication, daemon, parent=None):
|
def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication', plugins: 'Plugins', daemon: 'Daemon', parent=None):
|
||||||
ServerConnectWizard.__init__(self, daemon)
|
ServerConnectWizard.__init__(self, daemon)
|
||||||
QEAbstractWizard.__init__(self, config, app, parent)
|
QEAbstractWizard.__init__(self, config, app, plugins, daemon)
|
||||||
self._daemon = daemon
|
self._daemon = daemon
|
||||||
|
|
||||||
# attach view names
|
# attach view names
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from electrum.bip32 import is_bip32_derivation, BIP32Node, normalize_bip32_deriv
|
|||||||
from electrum.daemon import Daemon
|
from electrum.daemon import Daemon
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.keystore import bip44_derivation, bip39_to_seed, purpose48_derivation
|
from electrum.keystore import bip44_derivation, bip39_to_seed, purpose48_derivation
|
||||||
|
from electrum.plugin import run_hook
|
||||||
from electrum.storage import StorageReadWriteError
|
from electrum.storage import StorageReadWriteError
|
||||||
from electrum.util import WalletFileException, get_new_wallet_name
|
from electrum.util import WalletFileException, get_new_wallet_name
|
||||||
from electrum.wallet import wallet_types
|
from electrum.wallet import wallet_types
|
||||||
@@ -24,6 +25,9 @@ from ..util import ChoicesLayout, PasswordLineEdit, char_width_in_lineedit, WWLa
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from electrum.simple_config import SimpleConfig
|
from electrum.simple_config import SimpleConfig
|
||||||
|
from electrum.plugin import Plugins
|
||||||
|
from electrum.daemon import Daemon
|
||||||
|
from electrum.gui.qt import QElectrumApplication
|
||||||
|
|
||||||
WIF_HELP_TEXT = (_('WIF keys are typed in Electrum, based on script type.') + '\n\n' +
|
WIF_HELP_TEXT = (_('WIF keys are typed in Electrum, based on script type.') + '\n\n' +
|
||||||
_('A few examples') + ':\n' +
|
_('A few examples') + ':\n' +
|
||||||
@@ -35,10 +39,10 @@ WIF_HELP_TEXT = (_('WIF keys are typed in Electrum, based on script type.') + '\
|
|||||||
class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
def __init__(self, config: 'SimpleConfig', app: QApplication, daemon: Daemon, path, parent=None):
|
def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication', plugins: 'Plugins', daemon: Daemon, path, parent=None):
|
||||||
NewWalletWizard.__init__(self, daemon)
|
NewWalletWizard.__init__(self, daemon)
|
||||||
QEAbstractWizard.__init__(self, config, app, parent)
|
QEAbstractWizard.__init__(self, config, app, plugins, daemon)
|
||||||
self._daemon = daemon
|
self._daemon = daemon # TODO: dedupe
|
||||||
self._path = path
|
self._path = path
|
||||||
|
|
||||||
# attach gui classes to views
|
# attach gui classes to views
|
||||||
@@ -63,15 +67,15 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
|||||||
# modify default flow, insert seed extension entry/confirm as separate views
|
# modify default flow, insert seed extension entry/confirm as separate views
|
||||||
self.navmap_merge({
|
self.navmap_merge({
|
||||||
'create_seed': {
|
'create_seed': {
|
||||||
'next': lambda d: 'create_ext' if d['seed_extend'] else 'confirm_seed'
|
'next': lambda d: 'create_ext' if self.wants_ext(d) else 'confirm_seed'
|
||||||
},
|
},
|
||||||
'create_ext': {
|
'create_ext': {
|
||||||
'next': 'confirm_seed',
|
'next': 'confirm_seed',
|
||||||
'gui': WCEnterExt
|
'gui': WCEnterExt
|
||||||
},
|
},
|
||||||
'confirm_seed': {
|
'confirm_seed': {
|
||||||
'next': lambda d: 'confirm_ext' if d['seed_extend'] else self.on_have_or_confirm_seed(d),
|
'next': lambda d: 'confirm_ext' if self.wants_ext(d) else self.on_have_or_confirm_seed(d),
|
||||||
'accept': lambda d: None if d['seed_extend'] else self.maybe_master_pubkey(d)
|
'accept': lambda d: None if self.wants_ext(d) else self.maybe_master_pubkey(d)
|
||||||
},
|
},
|
||||||
'confirm_ext': {
|
'confirm_ext': {
|
||||||
'next': self.on_have_or_confirm_seed,
|
'next': self.on_have_or_confirm_seed,
|
||||||
@@ -100,6 +104,9 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
run_hook('init_wallet_wizard', self)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self):
|
||||||
return self._path
|
return self._path
|
||||||
|
|||||||
@@ -12,16 +12,20 @@ from electrum.logging import get_logger
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from electrum.simple_config import SimpleConfig
|
from electrum.simple_config import SimpleConfig
|
||||||
|
from electrum.plugin import Plugins
|
||||||
|
from electrum.daemon import Daemon
|
||||||
|
from electrum.gui.qt import QElectrumApplication
|
||||||
|
|
||||||
|
|
||||||
class QEAbstractWizard(QDialog):
|
class QEAbstractWizard(QDialog):
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
# def __init__(self, config: 'SimpleConfig', app: QApplication, plugins: 'Plugins', *, gui_object: 'ElectrumGui'):
|
# def __init__(self, config: 'SimpleConfig', app: QApplication, plugins: 'Plugins', *, gui_object: 'ElectrumGui'):
|
||||||
def __init__(self, config: 'SimpleConfig', app: QApplication, daemon):
|
def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication', plugins: 'Plugins', daemon: 'Daemon'):
|
||||||
QDialog.__init__(self, None)
|
QDialog.__init__(self, None)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.plugins = plugins
|
||||||
# self.gui_thread = gui_object.gui_thread
|
# self.gui_thread = gui_object.gui_thread
|
||||||
self.setMinimumSize(600, 400)
|
self.setMinimumSize(600, 400)
|
||||||
|
|
||||||
@@ -91,10 +95,12 @@ class QEAbstractWizard(QDialog):
|
|||||||
view = self.start_wizard()
|
view = self.start_wizard()
|
||||||
self.load_next_component(view)
|
self.load_next_component(view)
|
||||||
|
|
||||||
def load_next_component(self, view, wdata=None):
|
def load_next_component(self, view, wdata=None, params=None):
|
||||||
if wdata is None:
|
if wdata is None:
|
||||||
wdata = {}
|
wdata = {}
|
||||||
|
if params is None:
|
||||||
|
params = {}
|
||||||
|
|
||||||
comp = self.view_to_component(view)
|
comp = self.view_to_component(view)
|
||||||
try:
|
try:
|
||||||
page = comp(self.main_widget, self)
|
page = comp(self.main_widget, self)
|
||||||
@@ -102,6 +108,7 @@ class QEAbstractWizard(QDialog):
|
|||||||
self._logger.error(f'not a class: {comp!r}')
|
self._logger.error(f'not a class: {comp!r}')
|
||||||
raise e
|
raise e
|
||||||
page.wizard_data = wdata
|
page.wizard_data = wdata
|
||||||
|
page.params = params
|
||||||
page.updated.connect(self.on_page_updated)
|
page.updated.connect(self.on_page_updated)
|
||||||
self._logger.debug(f'{page!r}')
|
self._logger.debug(f'{page!r}')
|
||||||
|
|
||||||
@@ -134,6 +141,9 @@ class QEAbstractWizard(QDialog):
|
|||||||
self.next_button.setEnabled(page.valid)
|
self.next_button.setEnabled(page.valid)
|
||||||
self.main_widget.setVisible(not page.busy)
|
self.main_widget.setVisible(not page.busy)
|
||||||
self.please_wait.setVisible(page.busy)
|
self.please_wait.setVisible(page.busy)
|
||||||
|
icon = page.params.get('icon', icon_path('electrum.png'))
|
||||||
|
if icon != self.icon_filename:
|
||||||
|
self.set_icon(icon)
|
||||||
|
|
||||||
def on_back_button_clicked(self):
|
def on_back_button_clicked(self):
|
||||||
if self.can_go_back():
|
if self.can_go_back():
|
||||||
@@ -153,7 +163,7 @@ class QEAbstractWizard(QDialog):
|
|||||||
self.accept()
|
self.accept()
|
||||||
else:
|
else:
|
||||||
next = self.submit(wd)
|
next = self.submit(wd)
|
||||||
self.load_next_component(next['view'], next['wizard_data'])
|
self.load_next_component(next.view, next.wizard_data, next.params)
|
||||||
|
|
||||||
def start_wizard(self) -> str:
|
def start_wizard(self) -> str:
|
||||||
self.start()
|
self.start()
|
||||||
@@ -165,10 +175,7 @@ class QEAbstractWizard(QDialog):
|
|||||||
def submit(self, wizard_data) -> dict:
|
def submit(self, wizard_data) -> dict:
|
||||||
wdata = wizard_data.copy()
|
wdata = wizard_data.copy()
|
||||||
view = self.resolve_next(self._current.view, wdata)
|
view = self.resolve_next(self._current.view, wdata)
|
||||||
return {
|
return view
|
||||||
'view': view.view,
|
|
||||||
'wizard_data': view.wizard_data
|
|
||||||
}
|
|
||||||
|
|
||||||
def prev(self) -> dict:
|
def prev(self) -> dict:
|
||||||
viewstate = self.resolve_prev()
|
viewstate = self.resolve_prev()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (QTextEdit, QVBoxLayout, QLabel, QGridLayout, QHBoxL
|
|||||||
QRadioButton, QCheckBox, QLineEdit)
|
QRadioButton, QCheckBox, QLineEdit)
|
||||||
|
|
||||||
from electrum.gui.qt.util import (read_QIcon, WindowModalDialog, WaitingDialog, OkButton,
|
from electrum.gui.qt.util import (read_QIcon, WindowModalDialog, WaitingDialog, OkButton,
|
||||||
CancelButton, Buttons, icon_path, WWLabel, CloseButton)
|
CancelButton, Buttons, icon_path, WWLabel, CloseButton, ChoicesLayout)
|
||||||
from electrum.gui.qt.qrcodewidget import QRCodeWidget
|
from electrum.gui.qt.qrcodewidget import QRCodeWidget
|
||||||
from electrum.gui.qt.amountedit import AmountEdit
|
from electrum.gui.qt.amountedit import AmountEdit
|
||||||
from electrum.gui.qt.main_window import StatusBarButton
|
from electrum.gui.qt.main_window import StatusBarButton
|
||||||
@@ -46,7 +46,9 @@ from electrum.util import is_valid_email
|
|||||||
from electrum.logging import Logger
|
from electrum.logging import Logger
|
||||||
from electrum.base_wizard import GoBack, UserCancelled
|
from electrum.base_wizard import GoBack, UserCancelled
|
||||||
|
|
||||||
from .trustedcoin import TrustedCoinPlugin, server
|
from .trustedcoin import TrustedCoinPlugin, server, DISCLAIMER
|
||||||
|
from ...gui.qt.wizard.wallet import WCCreateSeed, WCConfirmSeed, WCHaveSeed, WCEnterExt, WCConfirmExt
|
||||||
|
from ...gui.qt.wizard.wizard import WizardComponent
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from electrum.gui.qt.main_window import ElectrumWindow
|
from electrum.gui.qt.main_window import ElectrumWindow
|
||||||
@@ -327,3 +329,125 @@ class Plugin(TrustedCoinPlugin):
|
|||||||
cb_lost.toggled.connect(set_enabled)
|
cb_lost.toggled.connect(set_enabled)
|
||||||
window.exec_layout(vbox, next_enabled=False, raise_on_cancel=False)
|
window.exec_layout(vbox, next_enabled=False, raise_on_cancel=False)
|
||||||
self.check_otp(window, short_id, otp_secret, xpub3, pw.get_amount(), cb_lost.isChecked())
|
self.check_otp(window, short_id, otp_secret, xpub3, pw.get_amount(), cb_lost.isChecked())
|
||||||
|
|
||||||
|
@hook
|
||||||
|
def init_qt(self, gui: 'ElectrumGui'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@hook
|
||||||
|
def init_wallet_wizard(self, wizard: 'QEWalletWizard'):
|
||||||
|
self.extend_wizard(wizard)
|
||||||
|
|
||||||
|
def extend_wizard(self, wizard):
|
||||||
|
# wizard = self._app.daemon.newWalletWizard
|
||||||
|
# self.logger.debug(repr(wizard))
|
||||||
|
# TODO: move non-gui parts to base plugin
|
||||||
|
views = {
|
||||||
|
'trustedcoin_start': {
|
||||||
|
'gui': WCDisclaimer,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_choose_seed'
|
||||||
|
},
|
||||||
|
'trustedcoin_choose_seed': {
|
||||||
|
'gui': WCChooseSeed,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': lambda d: 'trustedcoin_create_seed' if d['keystore_type'] == 'createseed'
|
||||||
|
else 'trustedcoin_have_seed'
|
||||||
|
},
|
||||||
|
'trustedcoin_create_seed': {
|
||||||
|
'gui': WCCreateSeed,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_confirm_seed'
|
||||||
|
},
|
||||||
|
'trustedcoin_confirm_seed': {
|
||||||
|
'gui': WCConfirmSeed,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_tos_email'
|
||||||
|
},
|
||||||
|
'trustedcoin_have_seed': {
|
||||||
|
'gui': WCHaveSeed,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_keep_disable'
|
||||||
|
},
|
||||||
|
# 'trustedcoin_keep_disable': {
|
||||||
|
# 'gui': '../../../../plugins/trustedcoin/qml/KeepDisable',
|
||||||
|
# 'next': lambda d: 'trustedcoin_tos_email' if d['trustedcoin_keepordisable'] != 'disable'
|
||||||
|
# else 'wallet_password',
|
||||||
|
# 'accept': self.recovery_disable,
|
||||||
|
# 'last': lambda d: wizard.is_single_password() and d['trustedcoin_keepordisable'] == 'disable'
|
||||||
|
# },
|
||||||
|
# 'trustedcoin_tos_email': {
|
||||||
|
# 'gui': '../../../../plugins/trustedcoin/qml/Terms',
|
||||||
|
# 'next': 'trustedcoin_show_confirm_otp'
|
||||||
|
# },
|
||||||
|
# 'trustedcoin_show_confirm_otp': {
|
||||||
|
# 'gui': '../../../../plugins/trustedcoin/qml/ShowConfirmOTP',
|
||||||
|
# 'accept': self.on_accept_otp_secret,
|
||||||
|
# 'next': 'wallet_password',
|
||||||
|
# 'last': lambda d: wizard.is_single_password()
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
wizard.navmap_merge(views)
|
||||||
|
|
||||||
|
# modify default flow, insert seed extension entry/confirm as separate views
|
||||||
|
ext = {
|
||||||
|
'trustedcoin_create_seed': {
|
||||||
|
'next': lambda d: 'trustedcoin_create_ext' if wizard.wants_ext(d) else 'trustedcoin_confirm_seed'
|
||||||
|
},
|
||||||
|
'trustedcoin_create_ext': {
|
||||||
|
'gui': WCEnterExt,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_confirm_seed',
|
||||||
|
},
|
||||||
|
'trustedcoin_confirm_seed': {
|
||||||
|
'next': lambda d: 'trustedcoin_confirm_ext' if wizard.wants_ext(d) else 'trustedcoin_tos_email'
|
||||||
|
},
|
||||||
|
'trustedcoin_confirm_ext': {
|
||||||
|
'gui': WCConfirmExt,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_tos_email',
|
||||||
|
},
|
||||||
|
'trustedcoin_have_seed': {
|
||||||
|
'next': lambda d: 'trustedcoin_have_ext' if wizard.wants_ext(d) else 'trustedcoin_keep_disable'
|
||||||
|
},
|
||||||
|
'trustedcoin_have_ext': {
|
||||||
|
'gui': WCEnterExt,
|
||||||
|
'params': {'icon': icon_path('trustedcoin-wizard.png')},
|
||||||
|
'next': 'trustedcoin_keep_disable',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wizard.navmap_merge(ext)
|
||||||
|
|
||||||
|
|
||||||
|
class WCDisclaimer(WizardComponent):
|
||||||
|
def __init__(self, parent, wizard):
|
||||||
|
WizardComponent.__init__(self, parent, wizard, title=_('Disclaimer'))
|
||||||
|
|
||||||
|
self.layout().addWidget(WWLabel('\n\n'.join(DISCLAIMER)))
|
||||||
|
self.layout().addStretch(1)
|
||||||
|
|
||||||
|
self._valid = True
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WCChooseSeed(WizardComponent):
|
||||||
|
def __init__(self, parent, wizard):
|
||||||
|
WizardComponent.__init__(self, parent, wizard, title=_('Create or restore'))
|
||||||
|
message = _('Do you want to create a new seed, or restore a wallet using an existing seed?')
|
||||||
|
choices = [
|
||||||
|
('createseed', _('Create a new seed')),
|
||||||
|
('haveseed', _('I already have a seed')),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.c_values = [x[0] for x in choices]
|
||||||
|
c_titles = [x[1] for x in choices]
|
||||||
|
self.clayout = ChoicesLayout(message, c_titles)
|
||||||
|
self.layout().addLayout(self.clayout.layout())
|
||||||
|
self.layout().addStretch(1)
|
||||||
|
|
||||||
|
self._valid = True
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
self.wizard_data['keystore_type'] = self.c_values[self.clayout.selected_index()]
|
||||||
|
|||||||
Reference in New Issue
Block a user