diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index bd7a90819..fb5661c6d 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -518,6 +518,7 @@ static void _librust_qstrs(void) { MP_QSTR_show_homescreen; MP_QSTR_show_info; MP_QSTR_show_info_with_cancel; + MP_QSTR_show_instructions; MP_QSTR_show_lockscreen; MP_QSTR_show_mismatch; MP_QSTR_show_passphrase; diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 5753e44e6..a0fc3eed8 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -1410,11 +1410,11 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?; - let show_info: bool = kwargs.get(Qstr::MP_QSTR_show_info)?.try_into()?; + let show_instructions: bool = kwargs.get(Qstr::MP_QSTR_show_instructions)?.try_into()?; let mut paragraphs = ParagraphVecShort::new(); paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, description)); - if show_info { + if show_instructions { paragraphs .add(Paragraph::new( &theme::TEXT_NORMAL, @@ -1984,7 +1984,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// button: str, /// dry_run: bool, /// info_button: bool, # unused on TR - /// show_info: bool, + /// show_instructions: bool, /// ) -> LayoutObj[UiResult]: /// """Device recovery homescreen.""" Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(), diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 8c29b60d7..5445d70dd 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -2054,6 +2054,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// button: str, /// dry_run: bool, /// info_button: bool = False, + /// show_instructions: bool = False, # unused on TT /// ) -> LayoutObj[UiResult]: /// """Device recovery homescreen.""" Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(), diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 1d9b4917b..ba00c39ae 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -375,7 +375,7 @@ def confirm_recovery( button: str, dry_run: bool, info_button: bool, # unused on TR - show_info: bool, + show_instructions: bool, ) -> LayoutObj[UiResult]: """Device recovery homescreen.""" @@ -886,6 +886,7 @@ def confirm_recovery( button: str, dry_run: bool, info_button: bool = False, + show_instructions: bool = False, # unused on TT ) -> LayoutObj[UiResult]: """Device recovery homescreen.""" diff --git a/core/src/apps/management/recovery_device/homescreen.py b/core/src/apps/management/recovery_device/homescreen.py index 2691c6cbb..00ce49e12 100644 --- a/core/src/apps/management/recovery_device/homescreen.py +++ b/core/src/apps/management/recovery_device/homescreen.py @@ -188,12 +188,7 @@ async def _request_share_first_screen(word_count: int) -> None: if remaining: await _request_share_next_screen() else: - await layout.homescreen_dialog( - TR.buttons__enter_share, - TR.recovery__enter_any_share, - TR.recovery__word_count_template.format(word_count), - show_info=True, - ) + await layout.enter_share(word_count=word_count) else: # BIP-39 await layout.homescreen_dialog( TR.buttons__continue, @@ -204,8 +199,6 @@ async def _request_share_first_screen(word_count: int) -> None: async def _request_share_next_screen() -> None: - from trezor import strings - remaining = storage_recovery.fetch_slip39_remaining_shares() group_count = storage_recovery.get_slip39_group_count() if not remaining: @@ -213,25 +206,10 @@ async def _request_share_next_screen() -> None: raise RuntimeError if group_count > 1: - await layout.homescreen_dialog( - TR.buttons__enter, - TR.recovery__more_shares_needed, - info_func=_show_remaining_groups_and_shares, - ) + await layout.enter_share(info_func=_show_remaining_groups_and_shares) else: - still_needed_shares = remaining[0] - already_entered_shares = len(storage_recovery_shares.fetch_group(0)) - overall_needed = still_needed_shares + already_entered_shares - # TODO: consider kwargs in format here - entered = TR.recovery__x_of_y_entered_template.format( - already_entered_shares, overall_needed - ) - needed = strings.format_plural( - TR.recovery__x_more_shares_needed_template_plural, - still_needed_shares, - TR.plurals__x_shares_needed, - ) - await layout.homescreen_dialog(TR.buttons__enter_share, entered, needed) + entered = len(storage_recovery_shares.fetch_group(0)) + await layout.enter_share(entered_remaining=(entered, remaining[0])) async def _show_remaining_groups_and_shares() -> None: diff --git a/core/src/apps/management/recovery_device/layout.py b/core/src/apps/management/recovery_device/layout.py index 580b6da6e..e91c4032b 100644 --- a/core/src/apps/management/recovery_device/layout.py +++ b/core/src/apps/management/recovery_device/layout.py @@ -13,6 +13,8 @@ from trezor.ui.layouts.recovery import ( # noqa: F401 from .. import backup_types if TYPE_CHECKING: + from typing import Awaitable, Callable + from trezor.enums import BackupType from trezor.ui.layouts.common import InfoFunc @@ -140,6 +142,47 @@ async def show_invalid_mnemonic(word_count: int) -> None: ) +def enter_share( + word_count: int | None = None, + entered_remaining: tuple[int, int] | None = None, + info_func: Callable | None = None, +) -> Awaitable[None]: + from trezor import strings + + show_instructions = False + + if word_count is not None: + # First-time entry. Show instructions and word count. + text = TR.recovery__enter_any_share + subtext = TR.recovery__word_count_template.format(word_count) + show_instructions = True + + elif entered_remaining is not None: + # Basic Shamir. There is only one group, we report entered/remaining count. + entered, remaining = entered_remaining + total = entered + remaining + text = TR.recovery__x_of_y_entered_template.format(entered, total) + subtext = strings.format_plural( + TR.recovery__x_more_shares_needed_template_plural, + remaining, + TR.plurals__x_shares_needed, + ) + + else: + # SuperShamir. We cannot easily show entered/remaining across groups, + # the caller provided an info_func that has the details. + text = TR.recovery__more_shares_needed + subtext = None + + return homescreen_dialog( + TR.buttons__enter_share, + text, + subtext, + info_func, + show_instructions, + ) + + async def homescreen_dialog( button_label: str, text: str, diff --git a/core/src/trezor/ui/layouts/tr/recovery.py b/core/src/trezor/ui/layouts/tr/recovery.py index d9df83d21..37ccf9942 100644 --- a/core/src/trezor/ui/layouts/tr/recovery.py +++ b/core/src/trezor/ui/layouts/tr/recovery.py @@ -81,7 +81,7 @@ async def continue_recovery( subtext: str | None, info_func: InfoFunc | None, dry_run: bool, - show_info: bool = False, + show_instructions: bool = False, ) -> bool: # TODO: implement info_func? # There is very limited space on the screen @@ -89,7 +89,7 @@ async def continue_recovery( # Never showing info for dry-run, user already saw it and it is disturbing if dry_run: - show_info = False + show_instructions = False if subtext: text += f"\n\n{subtext}" @@ -100,7 +100,7 @@ async def continue_recovery( button=button_label.upper(), info_button=False, dry_run=dry_run, - show_info=show_info, # type: ignore [No parameter named "show_info"] + show_instructions=show_instructions, ) result = await interact( homepage, diff --git a/core/src/trezor/ui/layouts/tt/recovery.py b/core/src/trezor/ui/layouts/tt/recovery.py index bef5fd905..df75ae77b 100644 --- a/core/src/trezor/ui/layouts/tt/recovery.py +++ b/core/src/trezor/ui/layouts/tt/recovery.py @@ -106,9 +106,9 @@ async def continue_recovery( subtext: str | None, info_func: InfoFunc | None, dry_run: bool, - show_info: bool = False, # unused on TT + show_instructions: bool = False, ) -> bool: - if show_info: + if show_instructions: # Show this just one-time description = TR.recovery__only_first_n_letters else: