Large refactor to add ButtonOption and mark_for_translation

This commit is contained in:
kdmukai
2024-11-03 06:55:54 -06:00
parent e4c4cb4edf
commit d412be2080
20 changed files with 397 additions and 407 deletions
+6 -23
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: seedsigner 0.8.5\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-11-02 12:38-0500\n"
"POT-Creation-Date: 2024-11-02 16:48-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -151,9 +151,6 @@ msgstr[1] ""
msgid "{} change"
msgstr ""
#. First variable is either "Multisig" or the fingerprint for single sig;
#. Second is "Change" or "Addr" (change vs receive addr); Third is the address
#. index number (e.g. your #4 receive addr).
#: src/seedsigner/gui/screens/psbt_screens.py
#: src/seedsigner/models/settings_definition.py
#: src/seedsigner/views/seed_views.py
@@ -164,6 +161,7 @@ msgstr ""
msgid "Change"
msgstr ""
#. Abbreviation for receive address
#: src/seedsigner/gui/screens/psbt_screens.py
msgid "Addr"
msgstr ""
@@ -263,6 +261,7 @@ msgstr ""
#. a label for the shortened Key-id of a BIP-32 master HD wallet
#: src/seedsigner/gui/screens/seed_screens.py
#: src/seedsigner/gui/screens/tools_screens.py
#: src/seedsigner/views/seed_views.py
msgid "fingerprint"
msgstr ""
@@ -817,7 +816,7 @@ msgid ""
"addresses."
msgstr ""
#: src/seedsigner/views/psbt_views.py src/seedsigner/views/view.py
#: src/seedsigner/views/psbt_views.py
msgid "Continue"
msgstr ""
@@ -1295,11 +1294,6 @@ msgstr ""
msgid "Return to PSBT"
msgstr ""
#. Inserts mainnet/testnet/regtest and derivation path
#: src/seedsigner/views/seed_views.py
msgid "Current network setting ({}) doesn't match {}"
msgstr ""
#: src/seedsigner/views/seed_views.py
msgid "Signing messages for custom derivation paths not supported"
msgstr ""
@@ -1470,9 +1464,9 @@ msgstr ""
msgid "Change Setting"
msgstr ""
#. Inserts mainnet/testnet/regtest
#. Inserts mainnet/testnet/regtest and derivation path
#: src/seedsigner/views/view.py
msgid "Current network setting ({}) doesn't match current action."
msgid "Current network setting ({}) doesn't match {}."
msgstr ""
#: src/seedsigner/views/view.py
@@ -1493,14 +1487,3 @@ msgstr ""
msgid "Option Disabled"
msgstr ""
#: src/seedsigner/views/view.py
msgid "Security Tip"
msgstr ""
#: src/seedsigner/views/view.py
msgid ""
"For maximum security,\n"
"remove the MicroSD card\n"
"before continuing."
msgstr ""
+1
View File
@@ -1,4 +1,5 @@
[extract_messages]
keywords = _ _mft mark_for_translation ButtonOption
add_comments = TRANSLATOR_NOTE:
strip_comments = yes
add_location = file
+14 -5
View File
@@ -11,7 +11,7 @@ from seedsigner.gui.components import (BtcAmount, Icon, FontAwesomeIconConstants
from seedsigner.gui.renderer import Renderer
from seedsigner.models.threads import BaseThread
from .screen import ButtonListScreen
from .screen import ButtonListScreen, ButtonOption
@@ -31,7 +31,7 @@ class PSBTOverviewScreen(ButtonListScreen):
# Customize defaults
self.title = _("Review PSBT")
self.is_bottom_list = True
self.button_data = [_("Review Details")]
self.button_data = [ButtonOption("Review Details")]
# This screen can take a while to load while parsing the PSBT
self.show_loading_screen = True
@@ -479,7 +479,7 @@ class PSBTMathScreen(ButtonListScreen):
def __post_init__(self):
# Customize defaults
self.title = _("PSBT Math")
self.button_data = [_("Review Recipients")]
self.button_data = [ButtonOption("Review Recipients")]
self.is_bottom_list = True
super().__post_init__()
@@ -675,9 +675,18 @@ class PSBTChangeDetailsScreen(ButtonListScreen):
))
screen_y = self.components[-1].screen_y + self.components[-1].height + 2*GUIConstants.COMPONENT_PADDING
change_type = _("Multisig") if self.is_multisig else self.fingerprint
if self.is_change_derivation_path :
addr_type = _("Change")
else:
# TRANSLATOR_NOTE: Abbreviation for receive address
addr_type = _("Addr")
value_text = "{}: {} #{}".format(change_type, addr_type, self.derivation_path_addr_index)
self.components.append(IconTextLine(
# TRANSLATOR_NOTE: First variable is either "Multisig" or the fingerprint for single sig; Second is "Change" or "Addr" (change vs receive addr); Third is the address index number (e.g. your #4 receive addr).
value_text="""{}: {} #{}""".format(_("Multisig") if self.is_multisig else self.fingerprint, _("Change") if self.is_change_derivation_path else _("Addr"), self.derivation_path_addr_index),
value_text=value_text,
icon_name=SeedSignerIconConstants.FINGERPRINT,
icon_color=GUIConstants.INFO_COLOR,
is_text_centered=False,
+61 -23
View File
@@ -5,6 +5,7 @@ from gettext import gettext as _
from PIL import Image, ImageDraw, ImageColor
from typing import Any, List, Tuple
from seedsigner.helpers.l10n import mark_for_translation as _mft
from seedsigner.gui.components import (GUIConstants,
BaseComponent, Button, Icon, IconButton, LargeIconButton,
SeedSignerIconConstants, TopNav, TextArea, load_image)
@@ -252,9 +253,23 @@ class BaseTopNavScreen(BaseScreen):
@dataclass
class ButtonOption:
"""
Note: The babel config in setup.cfg will extract the `button_label` string for translation
"""
button_label: str
icon_name: str = None
icon_color: str = None
right_icon_name: str = None
button_label_color: str = None
return_data: Any = None
@dataclass
class ButtonListScreen(BaseTopNavScreen):
button_data: list = None # list can be a mix of str or tuple(label: str, icon_name: str)
button_data: list[ButtonOption] = None
selected_button: int = 0
is_button_text_centered: bool = True
is_bottom_list: bool = False
@@ -295,26 +310,38 @@ class ButtonListScreen(BaseTopNavScreen):
self.has_scroll_arrows = True
self.buttons: List[Button] = []
for i, button_label in enumerate(self.button_data):
for i, button_option in enumerate(self.button_data):
icon_name = None
icon_color = None
right_icon_name = None
button_label_color = None
# TODO: Define an actual class for button_data?
if type(button_label) == tuple:
if len(button_label) == 2:
(button_label, icon_name) = button_label
icon_color = GUIConstants.BUTTON_FONT_COLOR
if type(button_option) == ButtonOption:
button_label = button_option.button_label
icon_name = button_option.icon_name
icon_color = button_option.icon_color
right_icon_name = button_option.right_icon_name
button_label_color = button_option.button_label_color
elif len(button_label) == 3:
(button_label, icon_name, icon_color) = button_label
else:
raise Exception("Refactor needed!")
elif len(button_label) == 4:
(button_label, icon_name, icon_color, button_label_color) = button_label
# # TODO: Complete refactor away from str|tuple to ButtonOption
# elif type(button_option) == str:
# button_label = button_option
# elif type(button_option) == tuple:
# if len(button_option) == 2:
# (button_label, icon_name) = button_option
# icon_color = GUIConstants.BUTTON_FONT_COLOR
elif len(button_label) == 5:
(button_label, icon_name, icon_color, button_label_color, right_icon_name) = button_label
# elif len(button_option) == 3:
# (button_label, icon_name, icon_color) = button_option
# elif len(button_option) == 4:
# (button_label, icon_name, icon_color, button_label_color) = button_option
# elif len(button_option) == 5:
# (button_label, icon_name, icon_color, button_label_color, right_icon_name) = button_option
button_kwargs = dict(
text=_(button_label), # Wrap here for just-in-time translations
@@ -547,11 +574,20 @@ class LargeButtonScreen(BaseTopNavScreen):
button_start_y = self.top_nav.height + int((self.canvas_height - (self.top_nav.height + GUIConstants.COMPONENT_PADDING) - (2 * button_height) - GUIConstants.COMPONENT_PADDING) / 2)
self.buttons = []
for i, button_label in enumerate(self.button_data):
if type(button_label) == tuple:
(button_label, icon_name) = button_label
for i, button_option in enumerate(self.button_data):
if type(button_option) == ButtonOption:
button_label = button_option.button_label
icon_name = button_option.icon_name
else:
icon_name = None
raise Exception("Refactor needed!")
# elif type(button_option) == str:
# button_label = button_option
# icon_name = None
# elif type(button_option) == tuple:
# (button_label, icon_name) = button_option
# else:
# print(type(button_option))
if i % 2 == 0:
button_start_x = GUIConstants.EDGE_PADDING
@@ -848,18 +884,20 @@ class QRDisplayScreen(BaseScreen):
@dataclass
class LargeIconStatusScreen(ButtonListScreen):
title: str = _("Success!")
title: str = _mft("Success!")
status_icon_name: str = SeedSignerIconConstants.SUCCESS
status_icon_size: int = GUIConstants.ICON_PRIMARY_SCREEN_SIZE
status_color: str = GUIConstants.SUCCESS_COLOR
status_headline: str = None
text: str = "" # The body text of the screen
text_edge_padding: int = GUIConstants.EDGE_PADDING
button_data: list = field(default_factory=lambda: [_("OK")])
button_data: list = None
allow_text_overflow: bool = False
def __post_init__(self):
if not self.button_data:
self.button_data = [ButtonOption("OK")]
self.is_bottom_list = True
super().__post_init__()
@@ -971,17 +1009,17 @@ class WarningEdgesMixin:
@dataclass
class WarningScreen(WarningEdgesMixin, LargeIconStatusScreen):
title: str = _("Caution")
title: str = _mft("Caution")
status_icon_name: str = SeedSignerIconConstants.WARNING
status_color: str = "yellow"
status_headline: str = _("Privacy Leak!") # The colored text under the alert icon
button_data: list = field(default_factory=lambda: [_("I Understand")])
status_headline: str = _mft("Privacy Leak!") # The colored text under the alert icon
button_data: list = field(default_factory=lambda: [ButtonOption("I Understand")])
@dataclass
class DireWarningScreen(WarningScreen):
status_headline: str = _("Classified Info!") # The colored text under the alert icon
status_headline: str = _mft("Classified Info!") # The colored text under the alert icon
status_color: str = GUIConstants.DIRE_WARNING_COLOR
+6 -5
View File
@@ -11,7 +11,7 @@ from seedsigner.gui.renderer import Renderer
from seedsigner.helpers.qr import QR
from seedsigner.models.threads import BaseThread, ThreadsafeCounter
from .screen import RET_CODE__BACK_BUTTON, BaseScreen, BaseTopNavScreen, ButtonListScreen, KeyboardScreen, WarningEdgesMixin
from .screen import RET_CODE__BACK_BUTTON, BaseScreen, BaseTopNavScreen, ButtonListScreen, ButtonOption, KeyboardScreen, WarningEdgesMixin
from ..components import (Button, FontAwesomeIconConstants, Fonts, FormattedAddress, IconButton,
IconTextLine, SeedSignerIconConstants, TextArea, GUIConstants, reflow_text_into_pages)
@@ -607,7 +607,7 @@ class SeedExportXpubDetailsScreen(WarningEdgesMixin, ButtonListScreen):
def __post_init__(self):
# Programmatically set up other args
self.button_data = [_("Export Xpub")]
self.button_data = [ButtonOption("Export Xpub")]
self.title = _("Xpub Details")
# Initialize the base class
@@ -1138,7 +1138,8 @@ class SeedTranscribeSeedQRWholeQRScreen(WarningEdgesMixin, ButtonListScreen):
def __post_init__(self):
self.title = _("Transcribe SeedQR")
# TRANSLATOR_NOTE: Refers to the QR code size: 21x21, 25x25, or 29x29
self.button_data = [_("Begin {}x{}").format(self.num_modules, self.num_modules)]
button_label = _("Begin {}x{}").format(self.num_modules, self.num_modules)
self.button_data = [ButtonOption(button_label)]
self.is_bottom_list = True
self.status_color = GUIConstants.DIRE_WARNING_COLOR
super().__post_init__()
@@ -1564,7 +1565,7 @@ class SeedSignMessageConfirmMessageScreen(ButtonListScreen):
self.title = f"""Message (pt {self.page_num + 1}/{len(self.sign_message_data["paged_message"])})"""
self.is_bottom_list = True
self.is_button_text_centered = True
self.button_data = [_("Next")]
self.button_data = [ButtonOption("Next")]
super().__post_init__()
message_display = TextArea(
@@ -1586,7 +1587,7 @@ class SeedSignMessageConfirmAddressScreen(ButtonListScreen):
self.title = _("Confirm Address")
self.is_bottom_list = True
self.is_button_text_centered = True
self.button_data = [_("Sign Message")]
self.button_data = [ButtonOption("Sign Message")]
super().__post_init__()
derivation_path_display = IconTextLine(
@@ -5,9 +5,10 @@ from gettext import gettext as _
from PIL.ImageOps import autocontrast
from typing import List
from seedsigner.helpers.l10n import mark_for_translation as _mft
from seedsigner.gui.components import Button, CheckboxButton, CheckedSelectionButton, FontAwesomeIconConstants, Fonts, GUIConstants, Icon, IconButton, IconTextLine, TextArea
from seedsigner.gui.screens.scan_screens import ScanScreen
from seedsigner.gui.screens.screen import BaseScreen, BaseTopNavScreen, ButtonListScreen
from seedsigner.gui.screens.screen import BaseScreen, BaseTopNavScreen, ButtonListScreen, ButtonOption
from seedsigner.hardware.buttons import HardwareButtonsConstants
from seedsigner.hardware.camera import Camera
from seedsigner.models.settings import SettingsConstants
@@ -309,13 +310,13 @@ class DonateScreen(BaseTopNavScreen):
@dataclass
class SettingsQRConfirmationScreen(ButtonListScreen):
config_name: str = None
title: str = _("Settings QR")
status_message: str = _("Settings updated...")
title: str = _mft("Settings QR")
status_message: str = _mft("Settings updated...")
is_bottom_list: bool = True
def __post_init__(self):
# Customize defaults
self.button_data = [_("Home")]
self.button_data = [ButtonOption("Home")]
self.show_back_button = False
super().__post_init__()
+1 -1
View File
@@ -50,7 +50,7 @@ def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, walle
elif script_type == SettingsConstants.NATIVE_SEGWIT:
return f"m/48'/{network_path}/0'/2'"
elif script_type == SettingsConstants.TAPROOT:
raise Exception("Taproot multisig/musig not yet supported")
raise Exception("Taproot multisig not yet supported")
else:
raise Exception("Unexpected script type")
else:
+3
View File
@@ -0,0 +1,3 @@
def mark_for_translation(message: str) -> str:
# Wraps the target string literal for translation but does NOT return the translated string.
return message
+58 -58
View File
@@ -1,7 +1,7 @@
from dataclasses import dataclass
from typing import Any, List
from gettext import gettext as _
from seedsigner.helpers.l10n import mark_for_translation as _mft
class SettingsConstants:
@@ -11,25 +11,25 @@ class SettingsConstants:
OPTION__PROMPT = "P"
OPTION__REQUIRED = "R"
OPTIONS__ENABLED_DISABLED = [
(OPTION__ENABLED, _("Enabled")),
(OPTION__DISABLED, _("Disabled")),
(OPTION__ENABLED, _mft("Enabled")),
(OPTION__DISABLED, _mft("Disabled")),
]
OPTIONS__ONLY_DISABLED = [
(OPTION__DISABLED, _("Disabled")),
(OPTION__DISABLED, _mft("Disabled")),
]
OPTIONS__PROMPT_REQUIRED_DISABLED = [
(OPTION__PROMPT, _("Prompt")),
(OPTION__REQUIRED, _("Required")),
(OPTION__DISABLED, _("Disabled")),
(OPTION__PROMPT, _mft("Prompt")),
(OPTION__REQUIRED, _mft("Required")),
(OPTION__DISABLED, _mft("Disabled")),
]
OPTIONS__ENABLED_DISABLED_REQUIRED = OPTIONS__ENABLED_DISABLED +[
(OPTION__REQUIRED, _("Required")),
(OPTION__REQUIRED, _mft("Required")),
]
OPTIONS__ENABLED_DISABLED_PROMPT = OPTIONS__ENABLED_DISABLED + [
(OPTION__PROMPT, _("Prompt")),
(OPTION__PROMPT, _mft("Prompt")),
]
ALL_OPTIONS = OPTIONS__ENABLED_DISABLED_PROMPT + [
(OPTION__REQUIRED, _("Required")),
(OPTION__REQUIRED, _mft("Required")),
]
# User-facing selection options
@@ -77,10 +77,10 @@ class SettingsConstants:
BTC_DENOMINATION__THRESHOLD = "thr"
BTC_DENOMINATION__BTCSATSHYBRID = "hyb"
ALL_BTC_DENOMINATIONS = [
(BTC_DENOMINATION__BTC, _("BTC")),
(BTC_DENOMINATION__SATS, _("sats")),
(BTC_DENOMINATION__THRESHOLD, _("Threshold at 0.01")),
(BTC_DENOMINATION__BTCSATSHYBRID, _("BTC | sats hybrid")),
(BTC_DENOMINATION__BTC, _mft("BTC")),
(BTC_DENOMINATION__SATS, _mft("sats")),
(BTC_DENOMINATION__THRESHOLD, _mft("Threshold at 0.01")),
(BTC_DENOMINATION__BTCSATSHYBRID, _mft("BTC | sats hybrid")),
]
CAMERA_ROTATION__0 = 0
@@ -88,10 +88,10 @@ class SettingsConstants:
CAMERA_ROTATION__180 = 180
CAMERA_ROTATION__270 = 270
ALL_CAMERA_ROTATIONS = [
(CAMERA_ROTATION__0, _("0°")),
(CAMERA_ROTATION__90, _("90°")),
(CAMERA_ROTATION__180, _("180°")),
(CAMERA_ROTATION__270, _("270°")),
(CAMERA_ROTATION__0, _mft("0°")),
(CAMERA_ROTATION__90, _mft("90°")),
(CAMERA_ROTATION__180, _mft("180°")),
(CAMERA_ROTATION__270, _mft("270°")),
]
# QR code constants
@@ -99,11 +99,11 @@ class SettingsConstants:
DENSITY__MEDIUM = "M"
DENSITY__HIGH = "H"
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High
density_low = _("Low")
density_low = _mft("Low")
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High
density_medium = _("Medium")
density_medium = _mft("Medium")
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High
density_high = _("High")
density_high = _mft("High")
ALL_DENSITIES = [
(DENSITY__LOW, density_low),
(DENSITY__MEDIUM, density_medium),
@@ -115,9 +115,9 @@ class SettingsConstants:
TESTNET = "T"
REGTEST = "R"
ALL_NETWORKS = [
(MAINNET, _("Mainnet")),
(TESTNET, _("Testnet")),
(REGTEST, _("Regtest"))
(MAINNET, _mft("Mainnet")),
(TESTNET, _mft("Testnet")),
(REGTEST, _mft("Regtest"))
]
@classmethod
@@ -130,14 +130,14 @@ class SettingsConstants:
if network == SettingsConstants.REGTEST:
return "regtest"
PERSISTENT_SETTINGS__SD_INSERTED__HELP_TEXT = _("Store Settings on SD card")
PERSISTENT_SETTINGS__SD_REMOVED__HELP_TEXT = _("Insert SD card to enable")
PERSISTENT_SETTINGS__SD_INSERTED__HELP_TEXT = _mft("Store Settings on SD card")
PERSISTENT_SETTINGS__SD_REMOVED__HELP_TEXT = _mft("Insert SD card to enable")
SINGLE_SIG = "ss"
MULTISIG = "ms"
ALL_SIG_TYPES = [
(SINGLE_SIG, _("Single Sig")),
(MULTISIG, _("Multisig")),
(SINGLE_SIG, _mft("Single Sig")),
(MULTISIG, _mft("Multisig")),
]
LEGACY_P2PKH = "leg"
@@ -146,11 +146,11 @@ class SettingsConstants:
TAPROOT = "tr"
CUSTOM_DERIVATION = "cus"
ALL_SCRIPT_TYPES = [
(NATIVE_SEGWIT, _("Native Segwit")),
(NESTED_SEGWIT, _("Nested Segwit")),
(LEGACY_P2PKH, _("Legacy")),
(TAPROOT, _("Taproot")),
(CUSTOM_DERIVATION, _("Custom Derivation")),
(NATIVE_SEGWIT, _mft("Native Segwit")),
(NESTED_SEGWIT, _mft("Nested Segwit")),
(LEGACY_P2PKH, _mft("Legacy")),
(TAPROOT, _mft("Taproot")),
(CUSTOM_DERIVATION, _mft("Custom Derivation")),
]
WORDLIST_LANGUAGE__ENGLISH = "en"
@@ -237,9 +237,9 @@ class SettingsConstants:
ELECTRUM_PBKDF2_ROUNDS=2048
# Label strings
LABEL__BIP39_PASSPHRASE = _("BIP-39 Passphrase")
LABEL__BIP39_PASSPHRASE = _mft("BIP-39 Passphrase")
# TRANSLATOR_NOTE: Terminology used by Electrum seeds; equivalent to bip39 passphrase
custom_extension = _("Custom Extension")
custom_extension = _mft("Custom Extension")
LABEL__CUSTOM_EXTENSION = custom_extension
@@ -311,7 +311,7 @@ class SettingsEntry:
option_value = option
display_name = option
if option_value == value:
return _(display_name)
return _mft(display_name)
def get_selection_option_value_by_display_name(self, display_name: str):
@@ -380,7 +380,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__LOCALE,
abbreviated_name="lang",
display_name=_("Language"),
display_name=_mft("Language"),
type=SettingsConstants.TYPE__SELECT_1,
selection_options=SettingsConstants.ALL_LOCALES,
default_value=SettingsConstants.LOCALE__ENGLISH),
@@ -389,7 +389,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__WORDLIST_LANGUAGE,
abbreviated_name="wordlist_lang",
display_name=_("Mnemonic language"),
display_name=_mft("Mnemonic language"),
type=SettingsConstants.TYPE__SELECT_1,
visibility=SettingsConstants.VISIBILITY__HIDDEN,
selection_options=SettingsConstants.ALL_WORDLIST_LANGUAGES,
@@ -398,14 +398,14 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__PERSISTENT_SETTINGS,
abbreviated_name="persistent",
display_name=_("Persistent settings"),
display_name=_mft("Persistent settings"),
help_text=SettingsConstants.PERSISTENT_SETTINGS__SD_INSERTED__HELP_TEXT,
default_value=SettingsConstants.OPTION__DISABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__WALLET,
attr_name=SettingsConstants.SETTING__COORDINATORS,
abbreviated_name="coords",
display_name=_("Coordinator software"),
display_name=_mft("Coordinator software"),
type=SettingsConstants.TYPE__MULTISELECT,
selection_options=SettingsConstants.ALL_COORDINATORS,
default_value=[
@@ -418,7 +418,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__BTC_DENOMINATION,
abbreviated_name="denom",
display_name=_("Denomination display"),
display_name=_mft("Denomination display"),
type=SettingsConstants.TYPE__SELECT_1,
selection_options=SettingsConstants.ALL_BTC_DENOMINATIONS,
default_value=SettingsConstants.BTC_DENOMINATION__THRESHOLD),
@@ -427,7 +427,7 @@ class SettingsDefinition:
# Advanced options
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__NETWORK,
display_name=_("Bitcoin network"),
display_name=_mft("Bitcoin network"),
type=SettingsConstants.TYPE__SELECT_1,
visibility=SettingsConstants.VISIBILITY__ADVANCED,
selection_options=SettingsConstants.ALL_NETWORKS,
@@ -435,7 +435,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__QR_DENSITY,
display_name=_("QR code density"),
display_name=_mft("QR code density"),
type=SettingsConstants.TYPE__SELECT_1,
visibility=SettingsConstants.VISIBILITY__ADVANCED,
selection_options=SettingsConstants.ALL_DENSITIES,
@@ -443,14 +443,14 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__XPUB_EXPORT,
display_name=_("Xpub export"),
display_name=_mft("Xpub export"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__SIG_TYPES,
abbreviated_name="sigs",
display_name=_("Sig types"),
display_name=_mft("Sig types"),
type=SettingsConstants.TYPE__MULTISELECT,
visibility=SettingsConstants.VISIBILITY__ADVANCED,
selection_options=SettingsConstants.ALL_SIG_TYPES,
@@ -459,7 +459,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__SCRIPT_TYPES,
abbreviated_name="scripts",
display_name=_("Script types"),
display_name=_mft("Script types"),
type=SettingsConstants.TYPE__MULTISELECT,
visibility=SettingsConstants.VISIBILITY__ADVANCED,
selection_options=SettingsConstants.ALL_SCRIPT_TYPES,
@@ -467,13 +467,13 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__XPUB_DETAILS,
display_name=_("Show xpub details"),
display_name=_mft("Show xpub details"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__PASSPHRASE,
display_name=_("BIP-39 passphrase"),
display_name=_mft("BIP-39 passphrase"),
type=SettingsConstants.TYPE__SELECT_1,
visibility=SettingsConstants.VISIBILITY__ADVANCED,
selection_options=SettingsConstants.OPTIONS__ENABLED_DISABLED_REQUIRED,
@@ -482,7 +482,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__CAMERA_ROTATION,
abbreviated_name="camera",
display_name=_("Camera rotation"),
display_name=_mft("Camera rotation"),
type=SettingsConstants.TYPE__SELECT_1,
visibility=SettingsConstants.VISIBILITY__ADVANCED,
selection_options=SettingsConstants.ALL_CAMERA_ROTATIONS,
@@ -490,14 +490,14 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__COMPACT_SEEDQR,
display_name=_("Compact SeedQR"),
display_name=_mft("Compact SeedQR"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__BIP85_CHILD_SEEDS,
abbreviated_name="bip85",
display_name=_("BIP-85 child seeds"),
display_name=_mft("BIP-85 child seeds"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__DISABLED),
@@ -505,40 +505,40 @@ class SettingsDefinition:
attr_name=SettingsConstants.SETTING__ELECTRUM_SEEDS,
abbreviated_name="electrum",
display_name="Electrum seeds",
help_text=_("Native Segwit only"),
help_text=_mft("Native Segwit only"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__DISABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__MESSAGE_SIGNING,
display_name=_("Message signing"),
display_name=_mft("Message signing"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__DISABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__PRIVACY_WARNINGS,
abbreviated_name="priv_warn",
display_name=_("Show privacy warnings"),
display_name=_mft("Show privacy warnings"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__DIRE_WARNINGS,
abbreviated_name="dire_warn",
display_name=_("Show dire warnings"),
display_name=_mft("Show dire warnings"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__QR_BRIGHTNESS_TIPS,
display_name=_("Show QR brightness tips"),
display_name=_mft("Show QR brightness tips"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__PARTNER_LOGOS,
abbreviated_name="partners",
display_name=_("Show partner logos"),
display_name=_mft("Show partner logos"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
@@ -554,7 +554,7 @@ class SettingsDefinition:
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__QR_BRIGHTNESS,
abbreviated_name="qr_brightness",
display_name=_("QR background color"),
display_name=_mft("QR background color"),
type=SettingsConstants.TYPE__FREE_ENTRY,
visibility=SettingsConstants.VISIBILITY__HIDDEN,
default_value=62),
+19 -21
View File
@@ -3,16 +3,16 @@ from gettext import gettext as _
from seedsigner.models.psbt_parser import PSBTParser
from seedsigner.models.settings import SettingsConstants
from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants
from seedsigner.gui.screens.screen import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen, DireWarningScreen, QRDisplayScreen)
from seedsigner.gui.screens.screen import (RET_CODE__BACK_BUTTON, ButtonListScreen, ButtonOption, WarningScreen, DireWarningScreen, QRDisplayScreen)
from seedsigner.views.view import BackStackView, MainMenuView, NotYetImplementedView, View, Destination
class PSBTSelectSeedView(View):
SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
SCAN_SEED = ButtonOption("Scan a seed", SeedSignerIconConstants.QRCODE)
TYPE_12WORD = ButtonOption("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = ButtonOption("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = ButtonOption("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
def run(self):
@@ -38,7 +38,7 @@ class PSBTSelectSeedView(View):
# TRANSLATOR_NOTE: Inserts fingerprint w/"?" to indicate that this seed can't sign the current PSBT
button_str = _("{} (?)").format(button_str)
button_data.append((button_str, SeedSignerIconConstants.FINGERPRINT))
button_data.append(ButtonOption(button_str, SeedSignerIconConstants.FINGERPRINT))
button_data.append(self.SCAN_SEED)
button_data.append(self.TYPE_12WORD)
@@ -169,7 +169,7 @@ class PSBTUnsupportedScriptTypeWarningView(View):
selected_menu_num = WarningScreen(
status_headline=_("Unsupported Script Type!"),
text=_("PSBT has unsupported input script type, please verify your change addresses."),
button_data=[_("Continue")],
button_data=[ButtonOption("Continue")],
).display()
if selected_menu_num == RET_CODE__BACK_BUTTON:
@@ -190,7 +190,7 @@ class PSBTNoChangeWarningView(View):
# TRANSLATOR_NOTE: User will receive no change back; the inputs to this transaction are fully spent
status_headline=_("Full Spend!"),
text=_("This PSBT spends its entire input value. No change is coming back to your wallet."),
button_data=[_("Continue")],
button_data=[ButtonOption("Continue")],
).display()
if selected_menu_num == RET_CODE__BACK_BUTTON:
@@ -265,10 +265,10 @@ class PSBTAddressDetailsView(View):
button_data = []
if self.address_num < psbt_parser.num_destinations - 1:
button_data.append(_("Next Recipient"))
button_data.append(ButtonOption("Next Recipient"))
else:
# TRANSLATOR_NOTE: Short for "Next step"
button_data.append(_("Next"))
button_data.append(ButtonOption("Next"))
selected_menu_num = self.run_screen(
PSBTAddressDetailsScreen,
@@ -299,10 +299,9 @@ class PSBTAddressDetailsView(View):
class PSBTChangeDetailsView(View):
NEXT = _("Next")
SKIP_VERIFICATION = _("Skip Verification")
VERIFY_MULTISIG = _("Verify Multisig Change")
NEXT = ButtonOption("Next")
SKIP_VERIFICATION = ButtonOption("Skip Verification")
VERIFY_MULTISIG = ButtonOption("Verify Multisig Change")
def __init__(self, change_address_num):
super().__init__()
@@ -349,7 +348,7 @@ class PSBTChangeDetailsView(View):
title = _("Your Change")
else:
title = _("Self-Transfer")
self.VERIFY_MULTISIG = _("Verify Multisig Addr")
self.VERIFY_MULTISIG.button_label = _("Verify Multisig Addr")
# if psbt_parser.num_change_outputs > 1:
# title += f" (#{self.change_address_num + 1})"
@@ -473,7 +472,7 @@ class PSBTAddressVerificationFailedView(View):
title=title,
status_headline=_("Address Verification Failed"),
text=text,
button_data=[_("Discard PSBT")],
button_data=[ButtonOption("Discard PSBT")],
show_back_button=False,
).display()
@@ -495,8 +494,8 @@ class PSBTOpReturnView(View):
# Should not be able to get here
raise Exception("Routing error")
title = "OP_RETURN"
button_data = ["Next"]
title = _("OP_RETURN")
button_data = [ButtonOption("Next")]
selected_menu_num = self.run_screen(
PSBTOpReturnScreen,
@@ -515,7 +514,7 @@ class PSBTOpReturnView(View):
class PSBTFinalizeView(View):
"""
"""
APPROVE_PSBT = "Approve PSBT"
APPROVE_PSBT = ButtonOption("Approve PSBT")
def run(self):
@@ -524,7 +523,6 @@ class PSBTFinalizeView(View):
psbt_parser: PSBTParser = self.controller.psbt_parser
psbt: PSBT = self.controller.psbt
self.APPROVE_PSBT = _("Approve PSBT")
if not psbt_parser:
# Should not be able to get here
@@ -573,7 +571,7 @@ class PSBTSignedQRDisplayView(View):
class PSBTSigningErrorView(View):
SELECT_DIFF_SEED = _("Select Diff Seed")
SELECT_DIFF_SEED = ButtonOption("Select Diff Seed")
def run(self):
psbt_parser: PSBTParser = self.controller.psbt_parser
+11 -11
View File
@@ -2,7 +2,7 @@ import logging
import re
from gettext import gettext as _
from seedsigner.helpers.l10n import mark_for_translation as _mft
from seedsigner.models.settings import SettingsConstants
from seedsigner.views.view import BackStackView, ErrorView, MainMenuView, NotYetImplementedView, View, Destination
@@ -19,8 +19,8 @@ class ScanView(View):
dedicated errors when an unexpected QR type is scanned (e.g. Scan PSBT was
selected but a SeedQR was scanned).
"""
instructions_text = _("Scan a QR code")
invalid_qr_type_message = _("QRCode not recognized or not yet supported.")
instructions_text = _mft("Scan a QR code")
invalid_qr_type_message = _mft("QRCode not recognized or not yet supported.")
def __init__(self):
@@ -176,8 +176,8 @@ class ScanView(View):
class ScanPSBTView(ScanView):
instructions_text = _("Scan PSBT")
invalid_qr_type_message = _("Expected a PSBT")
instructions_text = _mft("Scan PSBT")
invalid_qr_type_message = _mft("Expected a PSBT")
@property
def is_valid_qr_type(self):
@@ -186,8 +186,8 @@ class ScanPSBTView(ScanView):
class ScanSeedQRView(ScanView):
instructions_text = _("Scan SeedQR")
invalid_qr_type_message = _("Expected a SeedQR")
instructions_text = _mft("Scan SeedQR")
invalid_qr_type_message = _mft("Expected a SeedQR")
@property
def is_valid_qr_type(self):
@@ -196,8 +196,8 @@ class ScanSeedQRView(ScanView):
class ScanWalletDescriptorView(ScanView):
instructions_text = _("Scan descriptor")
invalid_qr_type_message = _("Expected a wallet descriptor QR")
instructions_text = _mft("Scan descriptor")
invalid_qr_type_message = _mft("Expected a wallet descriptor QR")
@property
def is_valid_qr_type(self):
@@ -206,8 +206,8 @@ class ScanWalletDescriptorView(ScanView):
class ScanAddressView(ScanView):
instructions_text = _("Scan address QR")
invalid_qr_type_message = _("Expected an address QR")
instructions_text = _mft("Scan address QR")
invalid_qr_type_message = _mft("Expected an address QR")
@property
def is_valid_qr_type(self):
+93 -110
View File
@@ -10,6 +10,7 @@ from embit.descriptor import Descriptor
from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants
from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen,
WarningScreen, DireWarningScreen, seed_screens)
from seedsigner.gui.screens.screen import ButtonOption
from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
@@ -23,7 +24,7 @@ logger = logging.getLogger(__name__)
class SeedsMenuView(View):
LOAD = "Load a seed"
LOAD = ButtonOption("Load a seed")
def __init__(self):
super().__init__()
@@ -41,8 +42,8 @@ class SeedsMenuView(View):
button_data = []
for seed in self.seeds:
button_data.append((seed["fingerprint"], SeedSignerIconConstants.FINGERPRINT))
button_data.append(_("Load a seed"))
button_data.append(ButtonOption(seed["fingerprint"], SeedSignerIconConstants.FINGERPRINT))
button_data.append(self.LOAD)
selected_menu_num = self.run_screen(
ButtonListScreen,
@@ -54,7 +55,7 @@ class SeedsMenuView(View):
if len(self.seeds) > 0 and selected_menu_num < len(self.seeds):
return Destination(SeedOptionsView, view_args={"seed_num": selected_menu_num})
elif selected_menu_num == len(self.seeds):
elif button_data[selected_menu_num] == self.LOAD:
return Destination(LoadSeedView)
elif selected_menu_num == RET_CODE__BACK_BUTTON:
@@ -70,10 +71,10 @@ class SeedSelectSeedView(View):
* `flow`: indicates which user flow is in progress during seed selection (e.g.
verify single sig addr or sign message).
"""
SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
SCAN_SEED = ButtonOption("Scan a seed", SeedSignerIconConstants.QRCODE)
TYPE_12WORD = ButtonOption("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = ButtonOption("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = ButtonOption("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
def __init__(self, flow: str = None):
@@ -108,11 +109,7 @@ class SeedSelectSeedView(View):
button_data = []
for seed in seeds:
button_str = seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
if seed.passphrase is not None:
# TODO: Include lock icon on right side of button
pass
button_data.append((button_str, SeedSignerIconConstants.FINGERPRINT, "blue"))
button_data.append(ButtonOption(button_str, SeedSignerIconConstants.FINGERPRINT, icon_color="blue"))
button_data.append(self.SCAN_SEED)
button_data.append(self.TYPE_12WORD)
@@ -165,11 +162,11 @@ class SeedSelectSeedView(View):
Loading seeds, passphrases, etc
****************************************************************************"""
class LoadSeedView(View):
SEED_QR = (_("Scan a SeedQR"), SeedSignerIconConstants.QRCODE)
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
CREATE = (_(" Create a seed"), SeedSignerIconConstants.PLUS)
SEED_QR = ButtonOption("Scan a SeedQR", SeedSignerIconConstants.QRCODE)
TYPE_12WORD = ButtonOption("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = ButtonOption("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = ButtonOption("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
CREATE = ButtonOption(" Create a seed", SeedSignerIconConstants.PLUS)
def run(self):
button_data = [
@@ -274,8 +271,8 @@ class SeedMnemonicEntryView(View):
class SeedMnemonicInvalidView(View):
EDIT = _("Review & Edit")
DISCARD = (_("Discard"), None, None, "red")
EDIT = ButtonOption("Review & Edit")
DISCARD = ButtonOption("Discard", button_label_color="red")
def __init__(self):
super().__init__()
@@ -303,8 +300,8 @@ class SeedMnemonicInvalidView(View):
class SeedFinalizeView(View):
FINALIZE = _("Done")
PASSPHRASE = _("BIP-39 Passphrase")
FINALIZE = ButtonOption("Done")
PASSPHRASE = ButtonOption("BIP-39 Passphrase")
def __init__(self):
super().__init__()
@@ -327,9 +324,9 @@ class SeedFinalizeView(View):
def run(self):
button_data = [self.FINALIZE]
passphrase_button = self.seed.passphrase_label
self.PASSPHRASE.button_label = self.seed.passphrase_label
if self.settings.get_value(SettingsConstants.SETTING__PASSPHRASE) != SettingsConstants.OPTION__DISABLED:
button_data.append(passphrase_button)
button_data.append(self.PASSPHRASE)
selected_menu_num = self.run_screen(
seed_screens.SeedFinalizeScreen,
@@ -341,7 +338,7 @@ class SeedFinalizeView(View):
seed_num = self.controller.storage.finalize_pending_seed()
return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True)
elif button_data[selected_menu_num] == passphrase_button:
elif button_data[selected_menu_num] == self.PASSPHRASE:
return Destination(SeedAddPassphraseView)
elif selected_menu_num == RET_CODE__BACK_BUTTON:
@@ -377,8 +374,8 @@ class SeedAddPassphraseView(View):
class SeedAddPassphraseExitDialogView(View):
EDIT = _("Edit passphrase")
DISCARD = (_("Discard passphrase"), None, None, "red")
EDIT = ButtonOption("Edit passphrase")
DISCARD = ButtonOption("Discard passphrase", button_label_color="red")
def __init__(self):
super().__init__()
@@ -410,8 +407,8 @@ class SeedReviewPassphraseView(View):
"""
Display the completed passphrase back to the user.
"""
EDIT = _("Edit passphrase")
DONE = _("Done")
EDIT = ButtonOption("Edit passphrase")
DONE = ButtonOption("Done")
def __init__(self):
super().__init__()
@@ -450,8 +447,8 @@ class SeedReviewPassphraseView(View):
class SeedDiscardView(View):
KEEP = _("Keep Seed")
DISCARD = (_("Discard"), None, None, "red")
KEEP = ButtonOption("Keep Seed")
DISCARD = ButtonOption("Discard", button_label_color="red")
def __init__(self, seed_num: int = None):
super().__init__()
@@ -518,14 +515,14 @@ class SeedElectrumMnemonicStartView(View):
Views for actions on individual seeds:
****************************************************************************"""
class SeedOptionsView(View):
SCAN_PSBT = (_("Scan PSBT"), SeedSignerIconConstants.QRCODE)
VERIFY_ADDRESS = _("Verify Addr")
EXPORT_XPUB = _("Export Xpub")
EXPLORER = _("Address Explorer")
SIGN_MESSAGE = _("Sign Message")
BACKUP = (_("Backup Seed"), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT)
BIP85_CHILD_SEED = _("BIP-85 Child Seed")
DISCARD = (_("Discard Seed"), None, None, "red")
SCAN_PSBT = ButtonOption("Scan PSBT", SeedSignerIconConstants.QRCODE)
VERIFY_ADDRESS = ButtonOption("Verify Addr")
EXPORT_XPUB = ButtonOption("Export Xpub")
EXPLORER = ButtonOption("Address Explorer")
SIGN_MESSAGE = ButtonOption("Sign Message")
BACKUP = ButtonOption("Backup Seed", right_icon_name=SeedSignerIconConstants.CHEVRON_RIGHT)
BIP85_CHILD_SEED = ButtonOption("BIP-85 Child Seed")
DISCARD = ButtonOption("Discard Seed", button_label_color="red")
def __init__(self, seed_num: int):
@@ -630,8 +627,8 @@ class SeedOptionsView(View):
class SeedBackupView(View):
VIEW_WORDS = _("View Seed Words")
EXPORT_SEEDQR = _("Export as SeedQR")
VIEW_WORDS = ButtonOption("View Seed Words")
EXPORT_SEEDQR = ButtonOption("Export as SeedQR")
def __init__(self, seed_num):
super().__init__()
@@ -667,8 +664,8 @@ class SeedBackupView(View):
Export Xpub flow
****************************************************************************"""
class SeedExportXpubSigTypeView(View):
SINGLE_SIG = _("Single Sig")
MULTISIG = _("Multisig")
SINGLE_SIG = ButtonOption("Single Sig", return_data=SettingsConstants.SINGLE_SIG)
MULTISIG = ButtonOption("Multisig", return_data=SettingsConstants.MULTISIG)
def __init__(self, seed_num: int):
super().__init__()
@@ -680,7 +677,7 @@ class SeedExportXpubSigTypeView(View):
# Nothing to select; skip this screen
return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": self.settings.get_value(SettingsConstants.SETTING__SIG_TYPES)[0]}, skip_current_view=True)
button_data=[self.SINGLE_SIG, self.MULTISIG]
button_data = [self.SINGLE_SIG, self.MULTISIG]
selected_menu_num = self.run_screen(
ButtonListScreen,
@@ -691,11 +688,7 @@ class SeedExportXpubSigTypeView(View):
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
if button_data[selected_menu_num] == self.SINGLE_SIG:
return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": SettingsConstants.SINGLE_SIG})
elif button_data[selected_menu_num] == self.MULTISIG:
return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": SettingsConstants.MULTISIG})
return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": button_data[selected_menu_num].return_data})
@@ -737,13 +730,13 @@ class SeedExportXpubScriptTypeView(View):
button_data = []
for script_type, display_name in SettingsConstants.ALL_SCRIPT_TYPES:
if script_type in self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES):
button_data.append((script_type, display_name))
button_data.append(ButtonOption(display_name, return_data=script_type))
selected_menu_num = self.run_screen(
ButtonListScreen,
title=title,
is_button_text_centered=False,
button_data=[x[1] for x in button_data],
button_data=button_data,
is_bottom_list=True,
)
@@ -754,7 +747,7 @@ class SeedExportXpubScriptTypeView(View):
return Destination(BackStackView)
else:
args["script_type"] = button_data[selected_menu_num][0]
args["script_type"] = button_data[selected_menu_num].return_data
if args["script_type"] == SettingsConstants.CUSTOM_DERIVATION:
return Destination(SeedExportXpubCustomDerivationView, view_args=args)
@@ -826,7 +819,9 @@ class SeedExportXpubCoordinatorView(View):
args["coordinator"] = self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)[0]
return Destination(SeedExportXpubWarningView, view_args=args, skip_current_view=True)
button_data = self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)
button_data = []
for display_name, setting_option in zip(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS), self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)):
button_data.append(ButtonOption(display_name, return_data=setting_option))
selected_menu_num = self.run_screen(
ButtonListScreen,
@@ -838,9 +833,10 @@ class SeedExportXpubCoordinatorView(View):
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
coordinators_settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__COORDINATORS)
selected_display_name = button_data[selected_menu_num]
args["coordinator"] = coordinators_settings_entry.get_selection_option_value_by_display_name(selected_display_name)
# coordinators_settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__COORDINATORS)
# selected_display_name = button_data[selected_menu_num]
# args["coordinator"] = coordinators_settings_entry.get_selection_option_value_by_display_name(selected_display_name)
args["coordinator"] = button_data[selected_menu_num].return_data
return Destination(SeedExportXpubWarningView, view_args=args)
@@ -1048,8 +1044,8 @@ class SeedWordsWarningView(View):
class SeedWordsView(View):
NEXT = _("Next")
DONE = _("Done")
NEXT = ButtonOption("Next")
DONE = ButtonOption("Done")
def __init__(self, seed_num: int, bip85_data: dict = None, page_index: int = 0):
super().__init__()
@@ -1126,14 +1122,14 @@ class SeedBIP85ApplicationModeView(View):
* XPRV (BIP32)
"""
# TODO: Future enhancement to display WIF (HD-SEED) and XPRV (Bip32)?
WORDS_12 = _("12 Words")
WORDS_24 = _("24 Words")
WORDS_12 = ButtonOption("12 Words")
WORDS_24 = ButtonOption("24 Words")
def __init__(self, seed_num: int):
super().__init__()
self.seed_num = seed_num
self.num_words = 0
self.bip85_app_num = 39 # TODO: Support other Application numbers
self.bip85_app_num = 39 # TODO: Support other Application numbers; TODO: Define this as a constant
def run(self):
@@ -1159,8 +1155,8 @@ class SeedBIP85ApplicationModeView(View):
# View to retrieve the derived seed index
class SeedBIP85SelectChildIndexView(View):
# View to retrieve the derived seed index
def __init__(self, seed_num: int, num_words: int):
super().__init__()
self.seed_num = seed_num
@@ -1168,7 +1164,7 @@ class SeedBIP85SelectChildIndexView(View):
def run(self):
# Change this later to use the generic Screen input keyboard
# TODO: Change this later to use the generic Screen input keyboard
ret = seed_screens.SeedBIP85SelectChildIndexScreen().display()
if ret == RET_CODE__BACK_BUTTON:
@@ -1206,7 +1202,7 @@ class SeedBIP85InvalidChildIndexView(View):
show_back_button=False,
status_headline=_("Invalid Child Index"),
text=_("BIP-85 Child Index must be between 0 and 2^31-1."),
button_data=[_("Try Again")]
button_data=[ButtonOption("Try Again")]
).display()
return Destination(
@@ -1224,8 +1220,8 @@ class SeedBIP85InvalidChildIndexView(View):
Seed Words Backup Test
****************************************************************************"""
class SeedWordsBackupTestPromptView(View):
VERIFY = _("Verify")
SKIP = _("Skip")
VERIFY = ButtonOption("Verify")
SKIP = ButtonOption("Skip")
def __init__(self, seed_num: int, bip85_data: dict = None):
super().__init__()
@@ -1283,10 +1279,10 @@ class SeedWordsBackupTestView(View):
while self.cur_index in self.confirmed_list:
self.cur_index = int(random.random() * len(self.mnemonic_list))
real_word = self.mnemonic_list[self.cur_index]
fake_word1 = bip39.WORDLIST[int(random.random() * 2047)]
fake_word2 = bip39.WORDLIST[int(random.random() * 2047)]
fake_word3 = bip39.WORDLIST[int(random.random() * 2047)]
real_word = ButtonOption(self.mnemonic_list[self.cur_index])
fake_word1 = ButtonOption(bip39.WORDLIST[int(random.random() * 2047)])
fake_word2 = ButtonOption(bip39.WORDLIST[int(random.random() * 2047)])
fake_word3 = ButtonOption(bip39.WORDLIST[int(random.random() * 2047)])
button_data = [real_word, fake_word1, fake_word2, fake_word3]
random.shuffle(button_data)
@@ -1332,8 +1328,8 @@ class SeedWordsBackupTestView(View):
class SeedWordsBackupTestMistakeView(View):
REVIEW = _("Review Seed Words")
RETRY = _("Try Again")
REVIEW = ButtonOption("Review Seed Words")
RETRY = ButtonOption("Try Again")
def __init__(self, seed_num: int, bip85_data: dict = None, cur_index: int = None, wrong_word: str = None, confirmed_list: list[bool] = None):
super().__init__()
@@ -1392,7 +1388,7 @@ class SeedWordsBackupTestSuccessView(View):
show_back_button=False,
status_headline=_("Success!"),
text=_("All mnemonic backup words were successfully verified!"),
button_data=[_("OK")]
button_data=[ButtonOption("OK")]
).display()
if self.seed_num is not None:
@@ -1414,15 +1410,11 @@ class SeedTranscribeSeedQRFormatView(View):
def run(self):
seed = self.controller.get_seed(self.seed_num)
if len(seed.mnemonic_list) == 12:
STANDARD = _("Standard: 25x25")
COMPACT = _("Compact: 21x21")
num_modules_standard = 25
num_modules_compact = 21
STANDARD = ButtonOption("Standard: 25x25", return_data=25)
COMPACT = ButtonOption("Compact: 21x21", return_data=21)
else:
STANDARD = _("Standard: 29x29")
COMPACT = _("Compact: 25x25")
num_modules_standard = 29
num_modules_compact = 25
STANDARD = ButtonOption("Standard: 29x29", return_data=29)
COMPACT = ButtonOption("Compact: 25x25", return_data=25)
if self.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) != SettingsConstants.OPTION__ENABLED:
# Only configured for standard SeedQR
@@ -1431,7 +1423,7 @@ class SeedTranscribeSeedQRFormatView(View):
view_args={
"seed_num": self.seed_num,
"seedqr_format": QRType.SEED__SEEDQR,
"num_modules": num_modules_standard,
"num_modules": STANDARD.return_data,
},
skip_current_view=True,
)
@@ -1450,10 +1442,10 @@ class SeedTranscribeSeedQRFormatView(View):
if button_data[selected_menu_num] == STANDARD:
seedqr_format = QRType.SEED__SEEDQR
num_modules = num_modules_standard
else:
seedqr_format = QRType.SEED__COMPACTSEEDQR
num_modules = num_modules_compact
num_modules = button_data[selected_menu_num].return_data
return Destination(
SeedTranscribeSeedQRWarningView,
@@ -1580,8 +1572,8 @@ class SeedTranscribeSeedQRZoomedInView(View):
class SeedTranscribeSeedQRConfirmQRPromptView(View):
SCAN = (_("Confirm SeedQR"), SeedSignerIconConstants.QRCODE)
DONE = _("Done")
SCAN = ButtonOption("Confirm SeedQR", SeedSignerIconConstants.QRCODE)
DONE = ButtonOption("Done")
def __init__(self, seed_num: int):
super().__init__()
@@ -1726,8 +1718,8 @@ class AddressVerificationStartView(View):
class AddressVerificationSigTypeView(View):
SINGLE_SIG = _("Single Sig")
MULTISIG = _("Multisig")
SINGLE_SIG = ButtonOption("Single Sig")
MULTISIG = ButtonOption("Multisig")
def run(self):
from seedsigner.helpers import embit_utils
@@ -1781,8 +1773,8 @@ class SeedAddressVerificationView(View):
multisig.
"""
# TRANSLATOR_NOTE: Option when scanning for a matching address; skips ten addresses ahead
SKIP_10 = _("Skip 10")
CANCEL = _("Cancel")
SKIP_10 = ButtonOption("Skip 10")
CANCEL = ButtonOption("Cancel")
def __init__(self, seed_num: int = None):
super().__init__()
@@ -2000,8 +1992,8 @@ class AddressVerificationSuccessView(View):
class LoadMultisigWalletDescriptorView(View):
SCAN = (_("Scan Descriptor"), SeedSignerIconConstants.QRCODE)
CANCEL = _("Cancel")
SCAN = ButtonOption("Scan Descriptor", SeedSignerIconConstants.QRCODE)
CANCEL = ButtonOption("Cancel")
def run(self):
button_data = [self.SCAN, self.CANCEL]
@@ -2025,10 +2017,10 @@ class LoadMultisigWalletDescriptorView(View):
class MultisigWalletDescriptorView(View):
RETURN = _("Return to PSBT")
VERIFY_ADDR = _("Verify Addr")
ADDRESS_EXPLORER = _("Address Explorer")
OK = _("OK")
RETURN = ButtonOption("Return to PSBT")
VERIFY_ADDR = ButtonOption("Verify Addr")
ADDRESS_EXPLORER = ButtonOption("Address Explorer")
OK = ButtonOption("OK")
def run(self):
descriptor = self.controller.multisig_wallet_descriptor
@@ -2047,8 +2039,8 @@ class MultisigWalletDescriptorView(View):
if self.controller.resume_main_flow == Controller.FLOW__PSBT:
button_data = [self.RETURN]
elif self.controller.resume_main_flow == Controller.FLOW__VERIFY_MULTISIG_ADDR and self.controller.unverified_address:
verify_addr_display = f"""{self.VERIFY_ADDR} {self.controller.unverified_address["address"][:7]}"""
button_data = [verify_addr_display]
verify_addr_display = f"""{_(self.VERIFY_ADDR.button_label)} {self.controller.unverified_address["address"][:7]}"""
button_data = [ButtonOption(verify_addr_display)]
elif self.controller.resume_main_flow == Controller.FLOW__ADDRESS_EXPLORER:
button_data = [self.ADDRESS_EXPLORER]
@@ -2069,7 +2061,7 @@ class MultisigWalletDescriptorView(View):
self.controller.resume_main_flow = None
return Destination(PSBTChangeDetailsView, view_args=dict(change_address_num=0))
elif button_data[selected_menu_num].startswith(self.VERIFY_ADDR):
elif button_data[selected_menu_num].button_label.startswith(_(self.VERIFY_ADDR.button_label)):
self.controller.resume_main_flow = None
return Destination(SeedAddressVerificationView)
@@ -2111,12 +2103,7 @@ class SeedSignMessageStartView(View):
# Note: addr_format["network"] can be MAINNET or [TESTNET, REGTEST]
if self.settings.get_value(SettingsConstants.SETTING__NETWORK) not in addr_format["network"]:
from seedsigner.views.view import NetworkMismatchErrorView
# TRANSLATOR_NOTE: Inserts mainnet/testnet/regtest and derivation path
text = _("Current network setting ({}) doesn't match {}").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
self.derivation_path,
)
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(text=text)))
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(derivation_path=self.derivation_path)))
# cleanup. Note: We could leave this in place so the user can resume the
# flow, but for now we avoid complications and keep things simple.
@@ -2203,11 +2190,7 @@ class SeedSignMessageConfirmAddressView(View):
addr_format["network"] = self.settings.get_value(SettingsConstants.SETTING__NETWORK)
else:
from seedsigner.views.view import NetworkMismatchErrorView
text = _("Current network setting ({}) doesn't match {}").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
self.derivation_path,
)
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(text=text)))
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(derivation_path=self.derivation_path)))
# cleanup. Note: We could leave this in place so the user can resume the
# flow, but for now we avoid complications and keep things simple.
+6 -6
View File
@@ -3,6 +3,7 @@ from gettext import gettext as _
from seedsigner.gui.components import SeedSignerIconConstants
from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen, settings_screens)
from seedsigner.gui.screens.screen import ButtonOption
from seedsigner.models.settings import Settings, SettingsConstants, SettingsDefinition
from .view import View, Destination, MainMenuView
@@ -12,8 +13,8 @@ logger = logging.getLogger(__name__)
class SettingsMenuView(View):
IO_TEST = _("I/O test")
DONATE = _("Donate")
IO_TEST = ButtonOption("I/O test")
DONATE = ButtonOption("Donate")
def __init__(self, visibility: str = SettingsConstants.VISIBILITY__GENERAL, selected_attr: str = None, initial_scroll: int = 0):
super().__init__()
@@ -28,7 +29,7 @@ class SettingsMenuView(View):
settings_entries = SettingsDefinition.get_settings_entries(
visibility=self.visibility
)
button_data=[e.display_name for e in settings_entries]
button_data=[ButtonOption(e.display_name) for e in settings_entries]
selected_button = 0
if self.selected_attr:
@@ -41,7 +42,7 @@ class SettingsMenuView(View):
title = _("Settings")
# Set up the next nested level of menuing
button_data.append((_("Advanced"), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT))
button_data.append(ButtonOption("Advanced", right_icon_name=SeedSignerIconConstants.CHEVRON_RIGHT))
next_destination = Destination(SettingsMenuView, view_args={"visibility": SettingsConstants.VISIBILITY__ADVANCED})
button_data.append(self.IO_TEST)
@@ -114,7 +115,7 @@ class SettingsEntryUpdateSelectionView(View):
value, display_name = value
else:
display_name = value
button_data.append(display_name)
button_data.append(ButtonOption(display_name))
if (type(initial_value) == list and value in initial_value) or value == initial_value:
checked_buttons.append(i)
@@ -211,7 +212,6 @@ class SettingsIngestSettingsQRView(View):
SettingsQRConfirmationScreen,
title=_("Settings QR"),
config_name=self.config_name,
button_data=[_("Home")],
status_message=self.status_message,
)
+46 -51
View File
@@ -7,6 +7,7 @@ from gettext import gettext as _
from seedsigner.gui.components import FontAwesomeIconConstants, GUIConstants, SeedSignerIconConstants
from seedsigner.gui.screens import RET_CODE__BACK_BUTTON, ButtonListScreen
from seedsigner.gui.screens.screen import ButtonOption
from seedsigner.helpers import mnemonic_generation
from seedsigner.models.seed import Seed
from seedsigner.models.settings_definition import SettingsConstants
@@ -19,11 +20,11 @@ logger = logging.getLogger(__name__)
class ToolsMenuView(View):
IMAGE = (_("New seed"), FontAwesomeIconConstants.CAMERA)
DICE = (_("New seed"), FontAwesomeIconConstants.DICE)
KEYBOARD = (_("Calc 12th/24th word"), FontAwesomeIconConstants.KEYBOARD)
ADDRESS_EXPLORER = _("Address Explorer")
VERIFY_ADDRESS = _("Verify Address")
IMAGE = ButtonOption("New seed", FontAwesomeIconConstants.CAMERA)
DICE = ButtonOption("New seed", FontAwesomeIconConstants.DICE)
KEYBOARD = ButtonOption("Calc 12th/24th word", FontAwesomeIconConstants.KEYBOARD)
ADDRESS_EXPLORER = ButtonOption("Address Explorer")
VERIFY_ADDRESS = ButtonOption("Verify Address")
def run(self):
button_data = [self.IMAGE, self.DICE, self.KEYBOARD, self.ADDRESS_EXPLORER, self.VERIFY_ADDRESS]
@@ -113,8 +114,8 @@ class ToolsImageEntropyFinalImageView(View):
class ToolsImageEntropyMnemonicLengthView(View):
TWELVE_WORDS = _("12 words")
TWENTYFOUR_WORDS = _("24 words")
TWELVE_WORDS = ButtonOption("12 words", return_data=12)
TWENTYFOUR_WORDS = ButtonOption("24 words", return_data=24)
def run(self):
button_data = [self.TWELVE_WORDS, self.TWENTYFOUR_WORDS]
@@ -127,10 +128,7 @@ class ToolsImageEntropyMnemonicLengthView(View):
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
if button_data[selected_menu_num] == self.TWELVE_WORDS:
mnemonic_length = 12
else:
mnemonic_length = 24
mnemonic_length = button_data[selected_menu_num].return_data
preview_images = self.controller.image_entropy_preview_frames
seed_entropy_image = self.controller.image_entropy_final_image
@@ -186,15 +184,19 @@ class ToolsImageEntropyMnemonicLengthView(View):
Dice rolls Views
****************************************************************************"""
class ToolsDiceEntropyMnemonicLengthView(View):
def run(self):
# Since we're dynamically building the ButtonOption button_labels here, it's too
# awkward to use the usual class-level attr approach.
# TRANSLATOR_NOTE: Inserts the number of dice rolls needed for a 12-word mnemonic
TWELVE = _("12 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__12WORD)
twelve = _("12 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__12WORD)
TWELVE = ButtonOption(twelve, return_data=mnemonic_generation.DICE__NUM_ROLLS__12WORD)
# TRANSLATOR_NOTE: Inserts the number of dice rolls needed for a 24-word mnemonic
TWENTY_FOUR = _("24 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__24WORD)
twenty_four = _("24 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__24WORD)
TWENTY_FOUR = ButtonOption(twenty_four, return_data=mnemonic_generation.DICE__NUM_ROLLS__24WORD)
def run(self):
button_data = [self.TWELVE, self.TWENTY_FOUR]
button_data = [TWELVE, TWENTY_FOUR]
selected_menu_num = ButtonListScreen(
title=_("Mnemonic Length"),
is_bottom_list=True,
@@ -205,10 +207,10 @@ class ToolsDiceEntropyMnemonicLengthView(View):
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
elif button_data[selected_menu_num] == self.TWELVE:
elif button_data[selected_menu_num] == TWELVE:
return Destination(ToolsDiceEntropyEntryView, view_args=dict(total_rolls=mnemonic_generation.DICE__NUM_ROLLS__12WORD))
elif button_data[selected_menu_num] == self.TWENTY_FOUR:
elif button_data[selected_menu_num] == TWENTY_FOUR:
return Destination(ToolsDiceEntropyEntryView, view_args=dict(total_rolls=mnemonic_generation.DICE__NUM_ROLLS__24WORD))
@@ -243,8 +245,8 @@ class ToolsDiceEntropyEntryView(View):
Calc final word Views
****************************************************************************"""
class ToolsCalcFinalWordNumWordsView(View):
TWELVE = _("12 words")
TWENTY_FOUR = _("24 words")
TWELVE = ButtonOption("12 words", return_data=12)
TWENTY_FOUR = ButtonOption("24 words", return_data=24)
def run(self):
button_data = [self.TWELVE, self.TWENTY_FOUR]
@@ -260,29 +262,21 @@ class ToolsCalcFinalWordNumWordsView(View):
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
elif button_data[selected_menu_num] == self.TWELVE:
self.controller.storage.init_pending_mnemonic(12)
self.controller.storage.init_pending_mnemonic(button_data[selected_menu_num].return_data)
# return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
elif button_data[selected_menu_num] == self.TWENTY_FOUR:
self.controller.storage.init_pending_mnemonic(24)
# return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
class ToolsCalcFinalWordFinalizePromptView(View):
# TRANSLATOR_NOTE: Label to gather entropy through coin tosses
COIN_FLIPS = _("Coin flip entropy")
COIN_FLIPS = ButtonOption("Coin flip entropy")
# TRANSLATOR_NOTE: Label to gather entropy through user specified BIP-39 word
SELECT_WORD = _("Word selection entropy")
SELECT_WORD = ButtonOption("Word selection entropy")
# TRANSLATOR_NOTE: Label to allow user to default entropy as all-zeros
ZEROS = _("Finalize with zeros")
ZEROS = ButtonOption("Finalize with zeros")
def run(self):
from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordFinalizePromptScreen
@@ -343,7 +337,7 @@ class ToolsCalcFinalWordCoinFlipsView(View):
class ToolsCalcFinalWordShowFinalWordView(View):
NEXT = _("Next")
NEXT = ButtonOption("Next")
def __init__(self, coin_flips: str = None):
super().__init__()
@@ -399,10 +393,13 @@ class ToolsCalcFinalWordShowFinalWordView(View):
def run(self):
from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordScreen
button_data = [self.NEXT]
# TRANSLATOR_NOTE: label to calculate the last word of a BIP-39 mnemonic seed phrase
title = _("Final Word Calc")
selected_menu_num = self.run_screen(
ToolsCalcFinalWordScreen,
# TRANSLATOR_NOTE: label to calculate the last word of a BIP-39 mnemonic seed phrase
title=_("Final Word Calc"),
title=title,
button_data=button_data,
selected_final_word=self.selected_final_word,
selected_final_bits=self.selected_final_bits,
@@ -419,8 +416,8 @@ class ToolsCalcFinalWordShowFinalWordView(View):
class ToolsCalcFinalWordDoneView(View):
LOAD = _("Load seed")
DISCARD = (_("Discard"), None, None, "red")
LOAD = ButtonOption("Load seed")
DISCARD = ButtonOption("Discard", button_label_color="red")
def run(self):
from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordDoneScreen
@@ -454,11 +451,11 @@ class ToolsCalcFinalWordDoneView(View):
Address Explorer Views
****************************************************************************"""
class ToolsAddressExplorerSelectSourceView(View):
SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
SCAN_DESCRIPTOR = (_("Scan wallet descriptor"), SeedSignerIconConstants.QRCODE)
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
SCAN_SEED = ButtonOption("Scan a seed", SeedSignerIconConstants.QRCODE)
SCAN_DESCRIPTOR = ButtonOption("Scan wallet descriptor", SeedSignerIconConstants.QRCODE)
TYPE_12WORD = ButtonOption("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD, return_data=12)
TYPE_24WORD = ButtonOption("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD, return_data=24)
TYPE_ELECTRUM = ButtonOption("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
def run(self):
from seedsigner.controller import Controller
@@ -467,7 +464,7 @@ class ToolsAddressExplorerSelectSourceView(View):
button_data = []
for seed in seeds:
button_str = seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
button_data.append((button_str, SeedSignerIconConstants.FINGERPRINT))
button_data.append(ButtonOption(button_str, SeedSignerIconConstants.FINGERPRINT))
button_data = button_data + [self.SCAN_SEED, self.SCAN_DESCRIPTOR, self.TYPE_12WORD, self.TYPE_24WORD]
if self.settings.get_value(SettingsConstants.SETTING__ELECTRUM_SEEDS) == SettingsConstants.OPTION__ENABLED:
button_data.append(self.TYPE_ELECTRUM)
@@ -508,10 +505,7 @@ class ToolsAddressExplorerSelectSourceView(View):
elif button_data[selected_menu_num] in [self.TYPE_12WORD, self.TYPE_24WORD]:
from seedsigner.views.seed_views import SeedMnemonicEntryView
if button_data[selected_menu_num] == self.TYPE_12WORD:
self.controller.storage.init_pending_mnemonic(num_words=12)
else:
self.controller.storage.init_pending_mnemonic(num_words=24)
self.controller.storage.init_pending_mnemonic(num_words=button_data[selected_menu_num].return_data)
return Destination(SeedMnemonicEntryView)
elif button_data[selected_menu_num] == self.TYPE_ELECTRUM:
@@ -522,10 +516,10 @@ class ToolsAddressExplorerSelectSourceView(View):
class ToolsAddressExplorerAddressTypeView(View):
# TRANSLATOR_NOTE: label for addresses where others send us incoming payments
RECEIVE = _("Receive Addresses")
RECEIVE = ButtonOption("Receive Addresses")
# TRANSLATOR_NOTE: label for addresses that collect the change from our own outgoing payments
CHANGE = _("Change Addresses")
CHANGE = ButtonOption("Change Addresses")
def __init__(self, seed_num: int = None, script_type: str = None, custom_derivation: str = None):
@@ -689,10 +683,11 @@ class ToolsAddressExplorerAddressListView(View):
end_digits = -5
else:
end_digits = -4
button_data.append(f"{cur_index}:{address[:8]}...{address[end_digits:]}")
button_data.append(ButtonOption(f"{cur_index}:{address[:8]}...{address[end_digits:]}"))
# TRANSLATOR_NOTE: Insert the number of addrs displayed per screen (e.g. "Next 10")
button_data.append((_("Next {}").format(addrs_per_screen), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT))
button_label = _("Next {}").format(addrs_per_screen)
button_data.append(ButtonOption(button_label, right_icon_name=SeedSignerIconConstants.CHEVRON_RIGHT))
selected_menu_num = self.run_screen(
ButtonListScreen,
+30 -63
View File
@@ -1,10 +1,11 @@
from dataclasses import dataclass, field
from dataclasses import dataclass
from gettext import gettext as _
from typing import Type
from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants
from seedsigner.helpers.l10n import mark_for_translation as _mft
from seedsigner.gui.components import SeedSignerIconConstants
from seedsigner.gui.screens import RET_CODE__POWER_BUTTON, RET_CODE__BACK_BUTTON
from seedsigner.gui.screens.screen import BaseScreen, DireWarningScreen, LargeButtonScreen, PowerOffScreen, PowerOffNotRequiredScreen, ResetScreen, WarningScreen
from seedsigner.gui.screens.screen import BaseScreen, ButtonOption, DireWarningScreen, LargeButtonScreen, PowerOffScreen, PowerOffNotRequiredScreen, ResetScreen, WarningScreen
from seedsigner.models.settings import Settings, SettingsConstants
from seedsigner.models.settings_definition import SettingsDefinition
from seedsigner.models.threads import BaseThread
@@ -18,6 +19,7 @@ class BackStackView:
pass
"""
Views contain the biz logic to handle discrete tasks, exactly analogous to a Flask
request/response function or a Django View. Each page/screen displayed to the user
@@ -182,14 +184,13 @@ class Destination:
#
#########################################################################################
class MainMenuView(View):
SCAN = (_("Scan"), SeedSignerIconConstants.SCAN)
SEEDS = (_("Seeds"), SeedSignerIconConstants.SEEDS)
TOOLS = (_("Tools"), SeedSignerIconConstants.TOOLS)
SETTINGS = (_("Settings"), SeedSignerIconConstants.SETTINGS)
SCAN = ButtonOption("Scan", SeedSignerIconConstants.SCAN)
SEEDS = ButtonOption("Seeds", SeedSignerIconConstants.SEEDS)
TOOLS = ButtonOption("Tools", SeedSignerIconConstants.TOOLS)
SETTINGS = ButtonOption("Settings", SeedSignerIconConstants.SETTINGS)
def run(self):
from seedsigner.gui.screens.screen import MainMenuScreen
button_data = [self.SCAN, self.SEEDS, self.TOOLS, self.SETTINGS]
selected_menu_num = self.run_screen(
MainMenuScreen,
@@ -219,8 +220,8 @@ class MainMenuView(View):
class PowerOptionsView(View):
RESET = (_("Restart"), SeedSignerIconConstants.RESTART)
POWER_OFF = (_("Power Off"), SeedSignerIconConstants.POWER)
RESET = ButtonOption("Restart", SeedSignerIconConstants.RESTART)
POWER_OFF = ButtonOption("Power Off", SeedSignerIconConstants.POWER)
def run(self):
button_data = [self.RESET, self.POWER_OFF]
@@ -293,12 +294,7 @@ class NotYetImplementedView(View):
"""
Temporary View to use during dev.
"""
text: str = None
def __post_init__(self):
if not self.text:
self.text = _("This is still on our to-do list!")
return super().__post_init__()
text: str = _mft("This is still on our to-do list!")
def run(self):
@@ -307,7 +303,7 @@ class NotYetImplementedView(View):
title=_("Work In Progress"),
status_headline=_("Not Yet Implemented"),
text=self.text,
button_data=[_("Back to Main Menu")],
button_data=[ButtonOption("Back to Main Menu")],
)
return Destination(MainMenuView)
@@ -316,13 +312,12 @@ class NotYetImplementedView(View):
@dataclass
class ErrorView(View):
title: str = _("Error")
title: str = _mft("Error")
show_back_button: bool = True
status_headline: str = None
text: str = None
button_text: str = None
next_destination: Destination = field(default_factory=lambda: Destination(MainMenuView, clear_history=True))
next_destination: Destination = None
def run(self):
self.run_screen(
@@ -330,33 +325,30 @@ class ErrorView(View):
title=self.title,
status_headline=self.status_headline,
text=self.text,
button_data=[self.button_text],
button_data=[ButtonOption(self.button_text)],
show_back_button=self.show_back_button,
)
return self.next_destination
return self.next_destination if self.next_destination else Destination(MainMenuView, clear_history=True)
@dataclass
class NetworkMismatchErrorView(ErrorView):
title: str = _("Network Mismatch")
show_back_button: bool = False
button_text: str = _("Change Setting")
next_destination: Destination = None
derivation_path: str = None
def __post_init__(self):
super().__post_init__()
if not self.text:
# TRANSLATOR_NOTE: Inserts mainnet/testnet/regtest
self.text = _("Current network setting ({}) doesn't match current action.").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
)
if not self.next_destination:
from seedsigner.views.settings_views import SettingsEntryUpdateSelectionView
self.title: str = _("Network Mismatch")
self.show_back_button: bool = False
self.button_text: str = _("Change Setting")
self.next_destination = Destination(SettingsEntryUpdateSelectionView, view_args=dict(attr_name=SettingsConstants.SETTING__NETWORK), clear_history=True)
super().__post_init__()
# TRANSLATOR_NOTE: Inserts mainnet/testnet/regtest and derivation path
self.text = _("Current network setting ({}) doesn't match {}.").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
self.derivation_path,
)
@@ -380,14 +372,12 @@ class UnhandledExceptionView(View):
@dataclass
class OptionDisabledView(View):
UPDATE_SETTING = "Update Setting"
DONE = "Done"
UPDATE_SETTING = ButtonOption("Update Setting")
DONE = ButtonOption("Done")
settings_attr: str
def __post_init__(self):
super().__post_init__()
self.UPDATE_SETTING = _("Update Setting")
self.DONE = _("Done")
self.settings_entry = SettingsDefinition.get_settings_entry(self.settings_attr)
# TRANSLATOR_NOTE: Inserts the name of a settings option (e.g. "Persistent Settings" is currently...)
@@ -413,26 +403,3 @@ class OptionDisabledView(View):
return Destination(SettingsEntryUpdateSelectionView, view_args=dict(attr_name=self.settings_attr), clear_history=True)
else:
return Destination(MainMenuView, clear_history=True)
class RemoveMicroSDWarningView(View):
"""
Warning to remove the microsd
"""
def __init__(self, next_view: View):
super().__init__()
self.next_view = next_view
def run(self):
self.run_screen(
WarningScreen,
title=_("Security Tip"),
status_icon_name=FontAwesomeIconConstants.SDCARD,
status_headline="",
text=_("For maximum security,\nremove the MicroSD card\nbefore continuing."),
show_back_button=False,
button_data=[_("Continue")],
)
return Destination(self.next_view, clear_history=True)
+1 -1
View File
@@ -295,7 +295,7 @@ def test_generate_screenshots(target_locale):
"Misc Error Views": [
NotYetImplementedView,
(UnhandledExceptionView, dict(error=["IndexError", "line 1, in some_buggy_code.py", "list index out of range"])),
NetworkMismatchErrorView,
(NetworkMismatchErrorView, dict(derivation_path="m/84'/1'/0'")),
(OptionDisabledView, dict(settings_attr=SettingsConstants.SETTING__MESSAGE_SIGNING)),
(ErrorView, dict(
title="Error",
+1 -1
View File
@@ -115,7 +115,7 @@ class TestPSBTFlows(FlowTest):
FlowStep(psbt_views.PSBTSigningErrorView, button_data_selection=psbt_views.PSBTSigningErrorView.SELECT_DIFF_SEED),
FlowStep(psbt_views.PSBTSelectSeedView, button_data_selection=psbt_views.PSBTSelectSeedView.SCAN_SEED),
FlowStep(scan_views.ScanSeedQRView, before_run=load_seed_into_decoder),
FlowStep(seed_views.SeedFinalizeView, button_data_selection=SettingsConstants.LABEL__BIP39_PASSPHRASE),
FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.PASSPHRASE),
FlowStep(seed_views.SeedAddPassphraseView, screen_return_value=dict(passphrase="abc")),
FlowStep(seed_views.SeedReviewPassphraseView, button_data_selection=seed_views.SeedReviewPassphraseView.DONE),
FlowStep(seed_views.SeedOptionsView, is_redirect=True),
+23 -12
View File
@@ -5,7 +5,7 @@ import pytest
from base import BaseTest, FlowTest, FlowStep
from base import FlowTestInvalidButtonDataSelectionException
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, ButtonOption
from seedsigner.models.settings import Settings, SettingsConstants
from seedsigner.models.seed import ElectrumSeed, Seed
from seedsigner.views.view import ErrorView, MainMenuView, OptionDisabledView, View, NetworkMismatchErrorView
@@ -40,10 +40,10 @@ class TestSeedFlows(FlowTest):
self.run_sequence([
FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN),
FlowStep(scan_views.ScanView, before_run=load_seed_into_decoder), # simulate read SeedQR; ret val is ignored
FlowStep(seed_views.SeedFinalizeView, button_data_selection=SettingsConstants.LABEL__BIP39_PASSPHRASE),
FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.PASSPHRASE),
FlowStep(seed_views.SeedAddPassphraseView, screen_return_value=dict(passphrase="muhpassphrase", is_back_button=True)),
FlowStep(seed_views.SeedAddPassphraseExitDialogView, button_data_selection=seed_views.SeedAddPassphraseExitDialogView.DISCARD),
FlowStep(seed_views.SeedFinalizeView, button_data_selection=SettingsConstants.LABEL__BIP39_PASSPHRASE),
FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.PASSPHRASE),
FlowStep(seed_views.SeedAddPassphraseView, screen_return_value=dict(passphrase="muhpassphrase", is_back_button=True)),
FlowStep(seed_views.SeedAddPassphraseExitDialogView, button_data_selection=seed_views.SeedAddPassphraseExitDialogView.EDIT),
FlowStep(seed_views.SeedAddPassphraseView, screen_return_value=dict(passphrase="muhpassphrase")),
@@ -183,15 +183,18 @@ class TestSeedFlows(FlowTest):
"""
Selecting "Export XPUB" from the SeedOptionsView should enter the Export XPUB flow and end at the MainMenuView
"""
def flowtest_standard_xpub(sig_tuple, script_tuple, coord_tuple):
if sig_tuple[0] == SettingsConstants.SINGLE_SIG:
sig_selection = seed_views.SeedExportXpubSigTypeView.SINGLE_SIG
else:
sig_selection = seed_views.SeedExportXpubSigTypeView.MULTISIG
self.run_sequence(
initial_destination_view_args=dict(seed_num=0),
sequence=[
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=sig_tuple[1]),
FlowStep(seed_views.SeedExportXpubScriptTypeView, button_data_selection=script_tuple[1]),
FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=coord_tuple[1]),
FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=sig_selection),
FlowStep(seed_views.SeedExportXpubScriptTypeView, button_data_selection=ButtonOption(script_tuple[1], return_data=script_tuple[0])),
FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=ButtonOption(coord_tuple[1], return_data=coord_tuple[0])),
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
@@ -303,10 +306,18 @@ class TestSeedFlows(FlowTest):
SettingsConstants.CUSTOM_DERIVATION
])
# get display names to access button choices in the views (ugh: hardcoding, is there a better way?)
sig_type = self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__SIG_TYPES)[0] # single sig
script_type = self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__SCRIPT_TYPES)[2] # custom derivation
coordinator = self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[3] # specter
# Ensure that all coordinators are enabled
self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x, y in SettingsConstants.ALL_COORDINATORS])
# Set up button_data selections
sig_type = seed_views.SeedExportXpubSigTypeView.SINGLE_SIG
custom_derivation = SettingsConstants.CUSTOM_DERIVATION
script_type = ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__SCRIPT_TYPES)[2], return_data=custom_derivation)
specter = SettingsConstants.COORDINATOR__SPECTER_DESKTOP
assert SettingsConstants.ALL_COORDINATORS[3][0] == specter
coordinator = ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[3], return_data=specter)
self.run_sequence(
initial_destination_view_args=dict(seed_num=0),
@@ -377,7 +388,7 @@ class TestSeedFlows(FlowTest):
# Skips past the script type options via redirect
FlowStep(seed_views.SeedExportXpubScriptTypeView, is_redirect=True),
FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[0]),
FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[0], return_data=SettingsConstants.ALL_COORDINATORS[0][0])),
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
+4 -4
View File
@@ -8,7 +8,7 @@ from base import FlowTest, FlowStep
from seedsigner.models.settings import Settings
from seedsigner.models.settings_definition import SettingsDefinition, SettingsConstants
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, ButtonOption
from seedsigner.hardware.microsd import MicroSD
from seedsigner.views.view import MainMenuView
from seedsigner.views import scan_views, settings_views
@@ -26,8 +26,8 @@ class TestSettingsFlows(FlowTest):
self.run_sequence([
FlowStep(MainMenuView, button_data_selection=MainMenuView.SETTINGS),
FlowStep(settings_views.SettingsMenuView, button_data_selection=settings_entry.display_name),
FlowStep(settings_views.SettingsEntryUpdateSelectionView, button_data_selection=settings_entry.get_selection_option_display_name_by_value(SettingsConstants.OPTION__ENABLED)),
FlowStep(settings_views.SettingsMenuView, button_data_selection=ButtonOption(settings_entry.display_name)),
FlowStep(settings_views.SettingsEntryUpdateSelectionView, button_data_selection=ButtonOption(settings_entry.get_selection_option_display_name_by_value(SettingsConstants.OPTION__ENABLED))),
FlowStep(settings_views.SettingsEntryUpdateSelectionView, screen_return_value=RET_CODE__BACK_BUTTON),
FlowStep(settings_views.SettingsMenuView),
])
@@ -43,7 +43,7 @@ class TestSettingsFlows(FlowTest):
self.run_sequence([
FlowStep(MainMenuView, button_data_selection=MainMenuView.SETTINGS),
FlowStep(settings_views.SettingsMenuView, button_data_selection=settings_entry.display_name),
FlowStep(settings_views.SettingsMenuView, button_data_selection=ButtonOption(settings_entry.display_name)),
FlowStep(settings_views.SettingsEntryUpdateSelectionView, screen_return_value=0), # select/deselect first option
FlowStep(settings_views.SettingsEntryUpdateSelectionView, screen_return_value=1), # select/deselect second option
FlowStep(settings_views.SettingsEntryUpdateSelectionView, screen_return_value=1), # select/deselect second option
+3 -3
View File
@@ -2,7 +2,7 @@
from base import FlowTest, FlowStep
from seedsigner.controller import Controller
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, ButtonOption
from seedsigner.models.seed import Seed
from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition
from seedsigner.views.view import ErrorView, MainMenuView
@@ -25,7 +25,7 @@ class TestToolsFlows(FlowTest):
FlowStep(MainMenuView, button_data_selection=MainMenuView.TOOLS),
FlowStep(tools_views.ToolsMenuView, button_data_selection=tools_views.ToolsMenuView.ADDRESS_EXPLORER),
FlowStep(tools_views.ToolsAddressExplorerSelectSourceView, screen_return_value=0), # ret 1st onboard seed
FlowStep(seed_views.SeedExportXpubScriptTypeView, button_data_selection=SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__SCRIPT_TYPES).get_selection_option_display_name_by_value(SettingsConstants.NATIVE_SEGWIT)),
FlowStep(seed_views.SeedExportXpubScriptTypeView, button_data_selection=ButtonOption(SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__SCRIPT_TYPES).get_selection_option_display_name_by_value(SettingsConstants.NATIVE_SEGWIT), return_data=SettingsConstants.NATIVE_SEGWIT)),
FlowStep(tools_views.ToolsAddressExplorerAddressTypeView, button_data_selection=tools_views.ToolsAddressExplorerAddressTypeView.RECEIVE),
FlowStep(tools_views.ToolsAddressExplorerAddressListView, screen_return_value=10), # ret NEXT page of addrs
FlowStep(tools_views.ToolsAddressExplorerAddressListView, screen_return_value=4), # ret a specific addr from the list
@@ -62,7 +62,7 @@ class TestToolsFlows(FlowTest):
# Finalize the new seed w/passphrase
self.run_sequence(
sequence=[
FlowStep(seed_views.SeedFinalizeView, button_data_selection=SettingsConstants.LABEL__BIP39_PASSPHRASE),
FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.PASSPHRASE),
FlowStep(seed_views.SeedAddPassphraseView, screen_return_value=dict(passphrase="mypassphrase")),
FlowStep(seed_views.SeedReviewPassphraseView, button_data_selection=seed_views.SeedReviewPassphraseView.DONE),
FlowStep(seed_views.SeedOptionsView, is_redirect=True),