diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 89ef5c0394..3e8c55e164 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -498,6 +498,7 @@ static void _librust_qstrs(void) { MP_QSTR_reset__share_completed_template; MP_QSTR_reset__share_words_title; MP_QSTR_reset__slip39_checklist_num_groups; + MP_QSTR_reset__slip39_checklist_num_groups_x_template; MP_QSTR_reset__slip39_checklist_num_shares; MP_QSTR_reset__slip39_checklist_num_shares_x_template; MP_QSTR_reset__slip39_checklist_set_num_groups; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index 0ccfe35a0d..966cde4726 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -1307,11 +1307,11 @@ pub enum TranslatedString { reset__incorrect_word_selected = 907, // "Incorrect word selected" reset__more_at = 908, // "More at" reset__num_of_shares_how_many = 909, // "How many wallet backup shares do you want to create?" - reset__num_of_shares_long_info = 910, // "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet." + reset__num_of_shares_long_info = 910, // "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet." reset__select_threshold = 911, // "Select the minimum shares required to recover your wallet." reset__share_completed_template = 912, // "Share #{0} completed" - reset__slip39_checklist_num_shares_x_template = 913, // "Number of shares" - reset__slip39_checklist_threshold_x_template = 914, // "Recovery threshold:" + reset__slip39_checklist_num_shares_x_template = 913, // "Number of shares: {0}" + reset__slip39_checklist_threshold_x_template = 914, // "Recovery threshold: {0}" send__transaction_signed = 915, // "Transaction signed" tutorial__continue = 916, // "Continue tutorial" tutorial__exit = 917, // "Exit tutorial" @@ -1328,6 +1328,7 @@ pub enum TranslatedString { words__operation_cancelled = 928, // "Operation cancelled" words__settings = 929, // "Settings" words__try_again = 930, // "Try again." + reset__slip39_checklist_num_groups_x_template = 931, // "Number of groups: {0}" } impl TranslatedString { @@ -2630,11 +2631,11 @@ impl TranslatedString { Self::reset__incorrect_word_selected => "Incorrect word selected", Self::reset__more_at => "More at", Self::reset__num_of_shares_how_many => "How many wallet backup shares do you want to create?", - Self::reset__num_of_shares_long_info => "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.", + Self::reset__num_of_shares_long_info => "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.", Self::reset__select_threshold => "Select the minimum shares required to recover your wallet.", Self::reset__share_completed_template => "Share #{0} completed", - Self::reset__slip39_checklist_num_shares_x_template => "Number of shares", - Self::reset__slip39_checklist_threshold_x_template => "Recovery threshold:", + Self::reset__slip39_checklist_num_shares_x_template => "Number of shares: {0}", + Self::reset__slip39_checklist_threshold_x_template => "Recovery threshold: {0}", Self::send__transaction_signed => "Transaction signed", Self::tutorial__continue => "Continue tutorial", Self::tutorial__exit => "Exit tutorial", @@ -2651,6 +2652,7 @@ impl TranslatedString { Self::words__operation_cancelled => "Operation cancelled", Self::words__settings => "Settings", Self::words__try_again => "Try again.", + Self::reset__slip39_checklist_num_groups_x_template => "Number of groups: {0}", } } @@ -3975,6 +3977,7 @@ impl TranslatedString { Qstr::MP_QSTR_words__operation_cancelled => Some(Self::words__operation_cancelled), Qstr::MP_QSTR_words__settings => Some(Self::words__settings), Qstr::MP_QSTR_words__try_again => Some(Self::words__try_again), + Qstr::MP_QSTR_reset__slip39_checklist_num_groups_x_template => Some(Self::reset__slip39_checklist_num_groups_x_template), _ => None, } } diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index f2b59d3a47..f475698e6b 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -4,7 +4,7 @@ use crate::{ strutil::TString, ui::{ component::{Component, Event, EventCtx, Never, Paginate}, - display::toif::Icon, + display::{toif::Icon, Color, Font}, geometry::{ Alignment, Alignment2D, Dimensions, Insets, LinearPlacement, Offset, Point, Rect, }, @@ -14,6 +14,7 @@ use crate::{ }; use super::layout::{LayoutFit, TextLayout, TextStyle}; +use heapless::String; /// Used as an upper bound of number of different styles we may render on single /// page. @@ -557,6 +558,8 @@ pub struct Checklist { current: usize, icon_current: Icon, icon_done: Icon, + icon_done_color: Option, + show_numerals: bool, /// How wide will the left icon column be check_width: i16, /// Offset of the icon representing DONE @@ -565,7 +568,10 @@ pub struct Checklist { current_offset: Offset, } -impl Checklist { +impl<'a, T> Checklist +where + T: ParagraphSource<'a>, +{ pub fn from_paragraphs( icon_current: Icon, icon_done: Icon, @@ -578,6 +584,8 @@ impl Checklist { current, icon_current, icon_done, + icon_done_color: None, + show_numerals: false, check_width: 0, done_offset: Offset::zero(), current_offset: Offset::zero(), @@ -599,6 +607,38 @@ impl Checklist { self } + pub fn with_icon_done_color(mut self, col: Color) -> Self { + self.icon_done_color = Some(col); + self + } + + pub fn with_numerals(mut self, show_numerals: bool) -> Self { + self.show_numerals = show_numerals; + self + } + + fn render_left_column<'s>(&self, target: &mut impl Renderer<'s>) { + let current_visible = self.current.saturating_sub(self.paragraphs.offset.par); + for (i, layout) in self.paragraphs.visible.iter().enumerate() { + let l = &layout.layout(&self.paragraphs.source); + let base = Point::new(self.area.x0, l.bounds.y0); + if i < current_visible { + // finished tasks - labeled with icon "done" + let color = self.icon_done_color.unwrap_or(l.style.text_color); + self.render_icon(base + self.done_offset, self.icon_done, color, target) + } else { + // current and future tasks - ordinal numbers or icon on current task + if self.show_numerals { + let num_offset = Offset::y(Font::NORMAL.visible_text_height("1")); + self.render_numeral(base + num_offset, i, l.style.text_color, target); + } else if i == current_visible { + let color = l.style.text_color; + self.render_icon(base + self.current_offset, self.icon_current, color, target); + } + } + } + } + fn paint_icon(&self, layout: &TextLayout, icon: Icon, offset: Offset) { let top_left = Point::new(self.area.x0, layout.bounds.y0); icon.draw( @@ -609,16 +649,29 @@ impl Checklist { ); } - fn render_icon<'s>( + fn render_numeral<'s>( &self, - layout: &TextLayout, - icon: Icon, - offset: Offset, + base_point: Point, + n: usize, + color: Color, target: &mut impl Renderer<'s>, ) { - let top_left = Point::new(self.area.x0, layout.bounds.y0); - shape::ToifImage::new(top_left + offset, icon.toif) - .with_fg(layout.style.text_color) + let numeral = build_string!(10, inttostr!(n as u8 + 1), "."); + shape::Text::new(base_point, numeral.as_str()) + .with_font(Font::NORMAL) + .with_fg(color) + .render(target); + } + + fn render_icon<'s>( + &self, + base_point: Point, + icon: Icon, + color: Color, + target: &mut impl Renderer<'s>, + ) { + shape::ToifImage::new(base_point, icon.toif) + .with_fg(color) .render(target); } } @@ -662,24 +715,7 @@ where fn render<'s>(&self, target: &mut impl Renderer<'s>) { self.paragraphs.render(target); - - let current_visible = self.current.saturating_sub(self.paragraphs.offset.par); - for layout in self.paragraphs.visible.iter().take(current_visible) { - self.render_icon( - &layout.layout(&self.paragraphs.source), - self.icon_done, - self.done_offset, - target, - ); - } - if let Some(layout) = self.paragraphs.visible.iter().nth(current_visible) { - self.render_icon( - &layout.layout(&self.paragraphs.source), - self.icon_current, - self.current_offset, - target, - ); - } + self.render_left_column(target); } #[cfg(feature = "ui_bounds")] diff --git a/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs b/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs index f9d00971d5..c5a3c82675 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs @@ -74,6 +74,7 @@ pub extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: impl ShowShareWords { fn new_obj(_args: &[Obj], kwargs: &Map) -> Result { let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let subtitle: TString = kwargs.get(Qstr::MP_QSTR_subtitle)?.try_into()?; let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_words)?; let share_words_vec: Vec = util::iter_into_vec(share_words_obj)?; let text_info: TString = kwargs.get(Qstr::MP_QSTR_text_info)?.try_into()?; @@ -90,8 +91,9 @@ impl ShowShareWords { .with_footer(TR::instructions__swipe_up.into(), None) .map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed)); - let content_words = - Frame::left_aligned(title, ShareWords::new(share_words_vec)).map(|_| None); + let content_words = Frame::left_aligned(title, ShareWords::new(share_words_vec)) + .with_subtitle(subtitle) + .map(|_| None); let content_confirm = Frame::left_aligned(text_confirm, PromptScreen::new_hold_to_confirm()) diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index 02fc549745..4a299836b8 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -1165,7 +1165,7 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; + let _button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; let active: usize = kwargs.get(Qstr::MP_QSTR_active)?.try_into()?; let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; @@ -1180,25 +1180,22 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M paragraphs.add(Paragraph::new(style, text)); } - let obj = LayoutObj::new(Frame::left_aligned( - title, - Dialog::new( - Checklist::from_paragraphs( - theme::ICON_LIST_CURRENT, - theme::ICON_LIST_CHECK, - active, - paragraphs - .into_paragraphs() - .with_spacing(theme::CHECKLIST_SPACING), - ) - .with_check_width(theme::CHECKLIST_CHECK_WIDTH) - .with_current_offset(theme::CHECKLIST_CURRENT_OFFSET) - .with_done_offset(theme::CHECKLIST_DONE_OFFSET), - theme::button_bar(Button::with_text(button).map(|msg| { - (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) - })), - ), - ))?; + let checklist_content = SwipeUpScreen::new( + Checklist::from_paragraphs( + theme::ICON_CHEVRON_RIGHT, + theme::ICON_BULLET_CHECKMARK, + active, + paragraphs + .into_paragraphs() + .with_spacing(theme::CHECKLIST_SPACING), + ) + .with_check_width(theme::CHECKLIST_CHECK_WIDTH) + .with_icon_done_color(theme::GREEN), + ); + let obj = LayoutObj::new( + Frame::left_aligned(title, checklist_content) + .with_footer(TR::instructions__swipe_up.into(), None), + )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1812,6 +1809,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// def flow_show_share_words( /// *, /// title: str, + /// subtitle: str, /// words: Iterable[str], /// text_info: str, /// text_confirm: str, diff --git a/core/embed/rust/src/ui/model_mercury/theme/mod.rs b/core/embed/rust/src/ui/model_mercury/theme/mod.rs index 0309576682..d85a0ed683 100644 --- a/core/embed/rust/src/ui/model_mercury/theme/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/theme/mod.rs @@ -161,10 +161,6 @@ include_icon!(ICON_CLICK, "model_tt/res/finger24.toif"); include_icon!(ICON_CORNER_CANCEL, "model_tt/res/x32.toif"); include_icon!(ICON_CORNER_INFO, "model_tt/res/info32.toif"); -// Checklist symbols. -include_icon!(ICON_LIST_CURRENT, "model_tt/res/arrow-right16.toif"); -include_icon!(ICON_LIST_CHECK, "model_tt/res/check16.toif"); - // Homescreen notifications. include_icon!(ICON_WARN, "model_tt/res/warning16.toif"); include_icon!(ICON_WARNING40, "model_tt/res/warning40.toif"); @@ -689,23 +685,23 @@ pub const fn button_counter() -> ButtonStyleSheet { ButtonStyleSheet { normal: &ButtonStyle { font: Font::DEMIBOLD, - text_color: FG, - button_color: GREY_DARK, - icon_color: GREY_LIGHT, + text_color: GREY, + button_color: GREY_EXTRA_DARK, + icon_color: GREY, background_color: BG, }, active: &ButtonStyle { font: Font::DEMIBOLD, - text_color: FG, - button_color: GREY_MEDIUM, - icon_color: GREY_LIGHT, + text_color: BG, + button_color: GREY_LIGHT, + icon_color: BG, background_color: BG, }, disabled: &ButtonStyle { font: Font::DEMIBOLD, - text_color: GREY_LIGHT, - button_color: GREY_DARK, - icon_color: GREY_LIGHT, + text_color: GREY_DARK, + button_color: BG, + icon_color: GREY_DARK, background_color: BG, }, } @@ -797,12 +793,10 @@ pub fn textstyle_number(num: i32) -> &'static TextStyle { pub const TEXT_NORMAL_OFF_WHITE: TextStyle = TextStyle::new(Font::NORMAL, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT); -pub const TEXT_CHECKLIST_DEFAULT: TextStyle = - TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT); +pub const TEXT_CHECKLIST_DEFAULT: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY); pub const TEXT_CHECKLIST_SELECTED: TextStyle = - TextStyle::new(Font::NORMAL, FG, BG, GREY_LIGHT, GREY_LIGHT); -pub const TEXT_CHECKLIST_DONE: TextStyle = - TextStyle::new(Font::NORMAL, GREEN_DARK, BG, GREY_LIGHT, GREY_LIGHT); + TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT); +pub const TEXT_CHECKLIST_DONE: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY); /// Spacing between components (e.g. header and main content) and offsets from /// the side of the screen. Generally applied everywhere except the top side of @@ -814,10 +808,11 @@ pub const BUTTON_HEIGHT: i16 = 62; pub const BUTTON_WIDTH: i16 = 78; pub const BUTTON_SPACING: i16 = SPACING; pub const KEYBOARD_SPACING: i16 = BUTTON_SPACING; -pub const CHECKLIST_SPACING: i16 = 10; +pub const CHECKLIST_SPACING: i16 = 12; pub const RECOVERY_SPACING: i16 = 18; pub const CORNER_BUTTON_SIDE: i16 = 44; pub const CORNER_BUTTON_SPACING: i16 = BUTTON_SPACING; +pub const COUNTER_BUTTON_HEIGHT: i16 = 60; pub const INFO_BUTTON_HEIGHT: i16 = 44; pub const PIN_BUTTON_HEIGHT: i16 = 52; pub const MNEMONIC_BUTTON_HEIGHT: i16 = 62; @@ -828,9 +823,7 @@ pub const RESULT_FOOTER_HEIGHT: i16 = 62; pub const DETAILS_SPACING: i16 = 8; // checklist settings -pub const CHECKLIST_CHECK_WIDTH: i16 = 16; -pub const CHECKLIST_DONE_OFFSET: Offset = Offset::new(-2, 6); -pub const CHECKLIST_CURRENT_OFFSET: Offset = Offset::new(2, 3); +pub const CHECKLIST_CHECK_WIDTH: i16 = 32; // icon width (20px) + padding (12px) pub const fn button_bar(inner: T) -> FixedHeightBar { FixedHeightBar::bottom(inner, BUTTON_HEIGHT) diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index fd2f956ca7..00fa91c97a 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -396,6 +396,7 @@ def flow_prompt_backup() -> LayoutObj[UiResult] def flow_show_share_words( *, title: str, + subtitle: str, words: Iterable[str], text_info: str, text_confirm: str, @@ -404,18 +405,6 @@ def flow_show_share_words( confirmation.""" -# rust/src/ui/model_mercury/layout.rs -def request_number( - *, - title: str, - count: int, - min_count: int, - max_count: int, - description: Callable[[int], str] | None = None, -) -> LayoutObj[tuple[UiResult, int]]: - """Number input with + and - buttons, description, and info button.""" - - # rust/src/ui/model_mercury/layout.rs def flow_request_number( *, diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index 675404a334..cb49e88df3 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -627,7 +627,7 @@ class TR: reset__num_of_shares_advanced_info_template: str = "Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {0}." reset__num_of_shares_basic_info: str = "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet." reset__num_of_shares_how_many: str = "How many wallet backup shares do you want to create?" - reset__num_of_shares_long_info: str = "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet." + reset__num_of_shares_long_info: str = "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet." reset__num_shares_for_group_template: str = "The required number of shares to form Group {0}." reset__number_of_shares_info: str = "= total number of unique word lists used for wallet backup." reset__one_share: str = "1 share" @@ -644,14 +644,15 @@ class TR: reset__share_completed_template: str = "Share #{0} completed" reset__share_words_title: str = "Standard backup" reset__slip39_checklist_num_groups: str = "Number of groups" + reset__slip39_checklist_num_groups_x_template: str = "Number of groups: {0}" reset__slip39_checklist_num_shares: str = "Number of shares" - reset__slip39_checklist_num_shares_x_template: str = "Number of shares" + reset__slip39_checklist_num_shares_x_template: str = "Number of shares: {0}" reset__slip39_checklist_set_num_groups: str = "Set number of groups" reset__slip39_checklist_set_num_shares: str = "Set number of shares" reset__slip39_checklist_set_sizes: str = "Set sizes and thresholds" reset__slip39_checklist_set_sizes_longer: str = "Set size and threshold for each group" reset__slip39_checklist_set_threshold: str = "Set threshold" - reset__slip39_checklist_threshold_x_template: str = "Recovery threshold:" + reset__slip39_checklist_threshold_x_template: str = "Recovery threshold: {0}" reset__slip39_checklist_title: str = "Backup checklist" reset__slip39_checklist_write_down: str = "Write down and check all shares" reset__slip39_checklist_write_down_recovery: str = "Write down & check all wallet backup shares" diff --git a/core/src/apps/management/reset_device/__init__.py b/core/src/apps/management/reset_device/__init__.py index 9abb77a4c7..a37804672f 100644 --- a/core/src/apps/management/reset_device/__init__.py +++ b/core/src/apps/management/reset_device/__init__.py @@ -138,7 +138,7 @@ async def _backup_slip39_basic( share_count = await layout.slip39_prompt_number_of_shares() # get threshold - await layout.slip39_show_checklist(1, advanced=False) + await layout.slip39_show_checklist(1, advanced=False, count=share_count) share_threshold = await layout.slip39_prompt_threshold(share_count) mnemonics = _get_slip39_mnemonics( @@ -149,7 +149,9 @@ async def _backup_slip39_basic( ) # show and confirm individual shares - await layout.slip39_show_checklist(2, advanced=False) + await layout.slip39_show_checklist( + 2, advanced=False, count=share_count, threshold=share_threshold + ) await layout.slip39_basic_show_and_confirm_shares(mnemonics[0]) diff --git a/core/src/trezor/ui/layouts/mercury/reset.py b/core/src/trezor/ui/layouts/mercury/reset.py index 0069cc7505..6c8ad0f0a1 100644 --- a/core/src/trezor/ui/layouts/mercury/reset.py +++ b/core/src/trezor/ui/layouts/mercury/reset.py @@ -23,12 +23,13 @@ async def show_share_words( group_index: int | None = None, ) -> None: + title = TR.reset__recovery_wallet_backup_title if share_index is None: - title = TR.reset__recovery_wallet_backup_title + subtitle = "" elif group_index is None: - title = TR.reset__recovery_share_title_template.format(share_index + 1) + subtitle = TR.reset__recovery_share_title_template.format(share_index + 1) else: - title = TR.reset__group_share_title_template.format( + subtitle = TR.reset__group_share_title_template.format( group_index + 1, share_index + 1 ) words_count = len(share_words) @@ -39,6 +40,7 @@ async def show_share_words( RustLayout( trezorui2.flow_show_share_words( title=title, + subtitle=subtitle, words=share_words, text_info=text_info, text_confirm=text_confirm, @@ -90,29 +92,17 @@ async def select_word( return words[result] -async def slip39_show_checklist(step: int, backup_type: BackupType) -> None: - from trezor.enums import BackupType - - assert backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced) - - items = ( - ( - TR.reset__slip39_checklist_set_num_shares, - TR.reset__slip39_checklist_set_threshold, - TR.reset__slip39_checklist_write_down_recovery, - ) - if backup_type == BackupType.Slip39_Basic - else ( - TR.reset__slip39_checklist_set_num_groups, - TR.reset__slip39_checklist_set_num_shares, - TR.reset__slip39_checklist_set_sizes_longer, - ) - ) - +async def slip39_show_checklist( + step: int, + backup_type: BackupType, + count: int | None = None, + threshold: int | None = None, +) -> None: + items = _slip_39_checklist_items(step, backup_type, count, threshold) result = await interact( RustLayout( trezorui2.show_checklist( - title=TR.reset__slip39_checklist_title, + title=TR.reset__title_shamir_backup, button=TR.buttons__continue, active=step, items=items, @@ -125,6 +115,44 @@ async def slip39_show_checklist(step: int, backup_type: BackupType) -> None: raise ActionCancelled +def _slip_39_checklist_items( + step: int, + backup_type: BackupType, + count: int | None = None, + threshold: int | None = None, +): + from trezor.enums import BackupType + + assert backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced) + + if backup_type == BackupType.Slip39_Basic: + entry_1 = ( + TR.reset__slip39_checklist_num_shares_x_template.format(count) + if count + else TR.reset__slip39_checklist_set_num_shares + ) + entry_2 = ( + TR.reset__slip39_checklist_threshold_x_template.format(threshold) + if threshold + else TR.reset__slip39_checklist_set_threshold + ) + entry_3 = TR.reset__slip39_checklist_write_down_recovery + return (entry_1, entry_2, entry_3) + else: + entry_1 = ( + TR.reset__slip39_checklist_num_groups_x_template.format(count) + if count + else TR.reset__slip39_checklist_set_num_groups + ) + entry_2 = ( + TR.reset__slip39_checklist_threshold_x_template.format(threshold) + if threshold + else TR.reset__slip39_checklist_set_threshold + ) + entry_3 = TR.reset__slip39_checklist_set_sizes_longer + return (entry_1, entry_2, entry_3) + + async def _prompt_number( title: str, description: Callable[[int], str], @@ -149,7 +177,6 @@ async def _prompt_number( ) if __debug__: - # TODO: is this still relevant? if not isinstance(result, tuple): # DebugLink currently can't send number of shares and it doesn't # change the counter either so just use the initial value. @@ -173,12 +200,7 @@ async def slip39_prompt_threshold( def description(count: int) -> str: if group_id is None: - if count == 1: - return TR.reset__you_need_one_share - elif count == max_count: - return TR.reset__need_all_share_template.format(count) - else: - return TR.reset__need_any_share_template.format(count) + return TR.reset__select_threshold else: return TR.reset__num_shares_for_group_template.format(group_id + 1) @@ -186,6 +208,8 @@ async def slip39_prompt_threshold( # TODO: this is madness... text = TR.reset__the_threshold_sets_the_number_of_shares if group_id is None: + # FIXME: need to propagate the argument in rust, temporary hack to show plausible value + count = num_of_shares - 1 text += TR.reset__needed_to_recover_your_wallet text += TR.reset__set_it_to_count_template.format(count) if num_of_shares == 1: @@ -225,17 +249,14 @@ async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int: def description(i: int): if group_id is None: - if i == 1: - return TR.reset__only_one_share_will_be_created - else: - return TR.reset__num_of_shares_how_many + return TR.reset__num_of_shares_how_many else: return TR.reset__total_number_of_shares_in_group_template.format( group_id + 1 ) if group_id is None: - info = TR.reset__num_of_shares_basic_info + info = TR.reset__num_of_shares_long_info else: info = TR.reset__num_of_shares_advanced_info_template.format(group_id + 1) diff --git a/core/src/trezor/ui/layouts/tr/reset.py b/core/src/trezor/ui/layouts/tr/reset.py index d75378222c..6dcc593195 100644 --- a/core/src/trezor/ui/layouts/tr/reset.py +++ b/core/src/trezor/ui/layouts/tr/reset.py @@ -99,7 +99,12 @@ async def select_word( return words[result] -async def slip39_show_checklist(step: int, advanced: bool) -> None: +async def slip39_show_checklist( + step: int, + advanced: bool, + count: int | None = None, + threshold: int | None = None, +) -> None: items = ( ( TR.reset__slip39_checklist_num_shares, diff --git a/core/src/trezor/ui/layouts/tt/reset.py b/core/src/trezor/ui/layouts/tt/reset.py index cb4a4aff07..bdb88043dc 100644 --- a/core/src/trezor/ui/layouts/tt/reset.py +++ b/core/src/trezor/ui/layouts/tt/reset.py @@ -103,7 +103,12 @@ async def select_word( return words[result] -async def slip39_show_checklist(step: int, advanced: bool) -> None: +async def slip39_show_checklist( + step: int, + advanced: bool, + count: int | None = None, + threshold: int | None = None, +) -> None: items = ( ( TR.reset__slip39_checklist_set_num_shares, diff --git a/core/translations/en.json b/core/translations/en.json index 22672d7f23..c0709312de 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -629,7 +629,7 @@ "reset__num_of_shares_how_many": "How many wallet backup shares do you want to create?", "reset__num_of_shares_advanced_info_template": "Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {0}.", "reset__num_of_shares_basic_info": "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet.", - "reset__num_of_shares_long_info": "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.", + "reset__num_of_shares_long_info": "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.", "reset__num_shares_for_group_template": "The required number of shares to form Group {0}.", "reset__number_of_shares_info": "= total number of unique word lists used for wallet backup.", "reset__one_share": "1 share", @@ -647,13 +647,14 @@ "reset__share_words_title": "Standard backup", "reset__slip39_checklist_num_groups": "Number of groups", "reset__slip39_checklist_num_shares": "Number of shares", - "reset__slip39_checklist_num_shares_x_template": "Number of shares", + "reset__slip39_checklist_num_shares_x_template": "Number of shares: {0}", + "reset__slip39_checklist_num_groups_x_template": "Number of groups: {0}", "reset__slip39_checklist_set_num_groups": "Set number of groups", "reset__slip39_checklist_set_num_shares": "Set number of shares", "reset__slip39_checklist_set_sizes": "Set sizes and thresholds", "reset__slip39_checklist_set_sizes_longer": "Set size and threshold for each group", "reset__slip39_checklist_set_threshold": "Set threshold", - "reset__slip39_checklist_threshold_x_template": "Recovery threshold:", + "reset__slip39_checklist_threshold_x_template": "Recovery threshold: {0}", "reset__slip39_checklist_title": "Backup checklist", "reset__slip39_checklist_write_down": "Write down and check all shares", "reset__slip39_checklist_write_down_recovery": "Write down & check all wallet backup shares", diff --git a/core/translations/order.json b/core/translations/order.json index 310497d629..4a3816259d 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -929,5 +929,6 @@ "927": "words__good_to_know", "928": "words__operation_cancelled", "929": "words__settings", - "930": "words__try_again" + "930": "words__try_again", + "931": "reset__slip39_checklist_num_groups_x_template" } diff --git a/core/translations/signatures.json b/core/translations/signatures.json index 0c2086a5d2..05db498731 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "070867d6d288bb46d1eb69dd95bc6d7b6fc09a3f299c75e997d756e7915860d6", - "datetime": "2024-05-22T11:43:31.930638", - "commit": "6bd6027a57b814483947f98aeb118a15f846e3bb" + "merkle_root": "e79fdd3c9052dfd140aef2b4800b6b0b8000ae99822cf3e8be083da0dc2b376c", + "datetime": "2024-05-26T15:18:17.124044", + "commit": "3e9598245d57044418f4eeb5ce5bc792f20587c3" }, "history": [ {