diff --git a/src/seedsigner/gui/components.py b/src/seedsigner/gui/components.py index 9193e451b..ef7f69368 100644 --- a/src/seedsigner/gui/components.py +++ b/src/seedsigner/gui/components.py @@ -129,6 +129,7 @@ class SeedSignerIconConstants: ERROR = "\ue912" SUCCESS = "\ue913" WARNING = "\ue914" + INFO = "\ue921" # Informational icons ADDRESS = "\ue915" @@ -146,7 +147,7 @@ class SeedSignerIconConstants: QRCODE = "\ue91f" MIN_VALUE = SCAN - MAX_VALUE = QRCODE + MAX_VALUE = INFO diff --git a/src/seedsigner/gui/toast.py b/src/seedsigner/gui/toast.py index d21ff02c9..eda690ee1 100644 --- a/src/seedsigner/gui/toast.py +++ b/src/seedsigner/gui/toast.py @@ -123,6 +123,7 @@ def toggle_renderer_lock(self): def run(self): logger.info(f"{self.__class__.__name__}: started") start = time.time() + time.sleep(0.1) while time.time() - start < self.activation_delay: if self.hw_inputs.has_any_input(): # User has pressed a button, cancel the toast @@ -230,3 +231,22 @@ def instantiate_toast(self) -> ToastOverlay: icon_name=SeedSignerIconConstants.MICROSD, label_text=self.message, ) + +class AlreadyLoadedSeedToastManagerThread(BaseToastOverlayManagerThread): + def __init__(self, activation_delay=0): + # Note: activation_delay is configurable so the screenshot generator can get the + # toast to immediately render. + super().__init__( + activation_delay=activation_delay, # seconds + duration=5, # seconds + ) + + + def instantiate_toast(self) -> ToastOverlay: + return ToastOverlay( + icon_name=SeedSignerIconConstants.INFO, + label_text="Seed already in memory", + font_size=GUIConstants.BODY_FONT_SIZE, + height=GUIConstants.BODY_FONT_SIZE * 2 + GUIConstants.BODY_LINE_SPACING + GUIConstants.EDGE_PADDING, + color=GUIConstants.INFO_COLOR, + ) diff --git a/src/seedsigner/models/seed_storage.py b/src/seedsigner/models/seed_storage.py index 85feb49cb..db9948989 100644 --- a/src/seedsigner/models/seed_storage.py +++ b/src/seedsigner/models/seed_storage.py @@ -31,6 +31,9 @@ def finalize_pending_seed(self) -> int: return index + def pending_seed_is_loaded(self) -> bool: + return self.pending_seed in self.seeds + def clear_pending_seed(self): self.pending_seed = None diff --git a/src/seedsigner/resources/fonts/seedsigner-icons.otf b/src/seedsigner/resources/fonts/seedsigner-icons.otf index ef4292b28..661e3e6a7 100644 Binary files a/src/seedsigner/resources/fonts/seedsigner-icons.otf and b/src/seedsigner/resources/fonts/seedsigner-icons.otf differ diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 9483c1119..2b85332eb 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -15,6 +15,7 @@ from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen, DireWarningScreen, seed_screens) from seedsigner.gui.screens.screen import LargeIconStatusScreen, QRDisplayScreen +from seedsigner.gui.toast import AlreadyLoadedSeedToastManagerThread from seedsigner.helpers import embit_utils from seedsigner.models.decode_qr import DecodeQR from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder @@ -325,6 +326,8 @@ def run(self): ) if button_data[selected_menu_num] == self.FINALIZE: + if self.controller.storage.pending_seed_is_loaded(): + self.controller.activate_toast(AlreadyLoadedSeedToastManagerThread()) seed_num = self.controller.storage.finalize_pending_seed() return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True) @@ -428,6 +431,8 @@ def run(self): return Destination(SeedAddPassphraseView) elif button_data[selected_menu_num] == self.DONE: + if self.controller.storage.pending_seed_is_loaded(): + self.controller.activate_toast(AlreadyLoadedSeedToastManagerThread()) seed_num = self.controller.storage.finalize_pending_seed() return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True) diff --git a/tests/screenshot_generator/generator.py b/tests/screenshot_generator/generator.py index 4c0c092a8..3633bf7be 100644 --- a/tests/screenshot_generator/generator.py +++ b/tests/screenshot_generator/generator.py @@ -27,7 +27,7 @@ from seedsigner.controller import Controller from seedsigner.gui.renderer import Renderer -from seedsigner.gui.toast import BaseToastOverlayManagerThread, RemoveSDCardToastManagerThread, SDCardStateChangeToastManagerThread +from seedsigner.gui.toast import BaseToastOverlayManagerThread, RemoveSDCardToastManagerThread, SDCardStateChangeToastManagerThread, AlreadyLoadedSeedToastManagerThread from seedsigner.hardware.microsd import MicroSD from seedsigner.models.decode_qr import DecodeQR from seedsigner.models.qr_type import QRType @@ -180,6 +180,7 @@ def add_op_return_to_psbt(psbt: PSBT, raw_payload_data: bytes): seed_views.SeedReviewPassphraseView, (seed_views.SeedOptionsView, dict(seed_num=0)), + (seed_views.SeedOptionsView, dict(seed_num=0), "SeedOptionsView_AlreadyLoadedSeed", AlreadyLoadedSeedToastManagerThread(activation_delay=0)), (seed_views.SeedBackupView, dict(seed_num=0)), (seed_views.SeedExportXpubSigTypeView, dict(seed_num=0)), (seed_views.SeedExportXpubScriptTypeView, dict(seed_num=0, sig_type="msig")), @@ -326,6 +327,7 @@ def screencap_view(view_cls: View, view_args: dict = {}, view_name: str = None, readme += """""" readme += f"""
\n""" for screenshot in screenshot_list: + toast_thread = None if type(screenshot) == tuple: if len(screenshot) == 2: view_cls, view_args = screenshot @@ -338,7 +340,6 @@ def screencap_view(view_cls: View, view_args: dict = {}, view_name: str = None, view_cls = screenshot view_args = {} view_name = view_cls.__name__ - toast_thread = None screencap_view(view_cls, view_args=view_args, view_name=view_name, toast_thread=toast_thread) readme += """ """