From d04174199caa4c7f04098c51c385a9d1c694a252 Mon Sep 17 00:00:00 2001 From: Lukas Bielesch Date: Wed, 26 Feb 2025 12:17:30 +0100 Subject: [PATCH] feat(eckhart): prompt backup flow --- core/embed/rust/librust_qstr.h | 2 + .../generated/translated_string.rs | 24 +++- .../rust/src/ui/layout_eckhart/flow/mod.rs | 2 + .../ui/layout_eckhart/flow/prompt_backup.rs | 123 ++++++++++++++++++ .../rust/src/ui/layout_eckhart/ui_firmware.rs | 5 +- core/mocks/trezortranslate_keys.pyi | 2 + core/translations/en.json | 16 ++- core/translations/order.json | 4 +- core/translations/signatures.json | 6 +- 9 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 core/embed/rust/src/ui/layout_eckhart/flow/prompt_backup.rs diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 943d9113cf..25f579cfa7 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -89,6 +89,7 @@ static void _librust_qstrs(void) { MP_QSTR_backup__it_should_be_backed_up_now; MP_QSTR_backup__new_wallet_created; MP_QSTR_backup__new_wallet_successfully_created; + MP_QSTR_backup__not_recommend; MP_QSTR_backup__recover_anytime; MP_QSTR_backup__title_backup_completed; MP_QSTR_backup__title_backup_wallet; @@ -773,6 +774,7 @@ static void _librust_qstrs(void) { MP_QSTR_words__not_recommended; MP_QSTR_words__operation_cancelled; MP_QSTR_words__outputs; + MP_QSTR_words__pay_attention; MP_QSTR_words__please_check_again; MP_QSTR_words__please_try_again; MP_QSTR_words__really_wanna; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index bf34f3936c..456a644b64 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -29,7 +29,7 @@ pub enum TranslatedString { auto_lock__change_template = 15, // "Auto-lock Trezor after {0} of inactivity?" auto_lock__title = 16, // "Auto-lock delay" backup__can_back_up_anytime = 17, // "You can back up your Trezor once, at any time." - backup__it_should_be_backed_up = 18, // "You should back up your new wallet right now." + backup__it_should_be_backed_up = 18, // {"Bolt": "You should back up your new wallet right now.", "Caesar": "You should back up your new wallet right now.", "Delizia": "You should back up your new wallet right now.", "Eckhart": "Back up your new wallet now."} backup__it_should_be_backed_up_now = 19, // "It should be backed up now!" backup__new_wallet_created = 20, // "Wallet created.\n" backup__new_wallet_successfully_created = 21, // "Wallet created successfully." @@ -1249,7 +1249,7 @@ pub enum TranslatedString { instructions__hold_to_confirm = 855, // "Hold to confirm" words__important = 856, // "Important" reset__words_written_down_template = 857, // "I wrote down all {0} words in order." - backup__create_backup_to_prevent_loss = 858, // "Create a backup to avoid losing access to your funds" + backup__create_backup_to_prevent_loss = 858, // {"Bolt": "Create a backup to avoid losing access to your funds", "Caesar": "Create a backup to avoid losing access to your funds", "Delizia": "Create a backup to avoid losing access to your funds", "Eckhart": "Create a wallet backup to avoid losing access to your funds."} reset__check_backup_instructions = 859, // "Let's do a quick check of your backup." words__instructions = 860, // "Instructions" words__not_recommended = 861, // "Not recommended!" @@ -1403,6 +1403,8 @@ pub enum TranslatedString { sign_message__confirm_without_review = 990, // "Confirm without review" instructions__tap_to_continue = 991, // "Tap to continue" reset__share_words_first = 992, // "Write down the first word from the backup." + backup__not_recommend = 993, // "We don't recommend to skip wallet backup creation." + words__pay_attention = 994, // "Pay attention" } impl TranslatedString { @@ -1428,7 +1430,14 @@ impl TranslatedString { Self::auto_lock__change_template => "Auto-lock Trezor after {0} of inactivity?", Self::auto_lock__title => "Auto-lock delay", Self::backup__can_back_up_anytime => "You can back up your Trezor once, at any time.", + #[cfg(feature = "layout_bolt")] Self::backup__it_should_be_backed_up => "You should back up your new wallet right now.", + #[cfg(feature = "layout_caesar")] + Self::backup__it_should_be_backed_up => "You should back up your new wallet right now.", + #[cfg(feature = "layout_delizia")] + Self::backup__it_should_be_backed_up => "You should back up your new wallet right now.", + #[cfg(feature = "layout_eckhart")] + Self::backup__it_should_be_backed_up => "Back up your new wallet now.", Self::backup__it_should_be_backed_up_now => "It should be backed up now!", Self::backup__new_wallet_created => "Wallet created.\n", Self::backup__new_wallet_successfully_created => "Wallet created successfully.", @@ -2669,7 +2678,14 @@ impl TranslatedString { Self::instructions__hold_to_confirm => "Hold to confirm", Self::words__important => "Important", Self::reset__words_written_down_template => "I wrote down all {0} words in order.", + #[cfg(feature = "layout_bolt")] Self::backup__create_backup_to_prevent_loss => "Create a backup to avoid losing access to your funds", + #[cfg(feature = "layout_caesar")] + Self::backup__create_backup_to_prevent_loss => "Create a backup to avoid losing access to your funds", + #[cfg(feature = "layout_delizia")] + Self::backup__create_backup_to_prevent_loss => "Create a backup to avoid losing access to your funds", + #[cfg(feature = "layout_eckhart")] + Self::backup__create_backup_to_prevent_loss => "Create a wallet backup to avoid losing access to your funds.", Self::reset__check_backup_instructions => "Let's do a quick check of your backup.", Self::words__instructions => "Instructions", Self::words__not_recommended => "Not recommended!", @@ -2837,6 +2853,8 @@ impl TranslatedString { Self::sign_message__confirm_without_review => "Confirm without review", Self::instructions__tap_to_continue => "Tap to continue", Self::reset__share_words_first => "Write down the first word from the backup.", + Self::backup__not_recommend => "We don't recommend to skip wallet backup creation.", + Self::words__pay_attention => "Pay attention", } } @@ -4235,6 +4253,8 @@ impl TranslatedString { Qstr::MP_QSTR_sign_message__confirm_without_review => Some(Self::sign_message__confirm_without_review), Qstr::MP_QSTR_instructions__tap_to_continue => Some(Self::instructions__tap_to_continue), Qstr::MP_QSTR_reset__share_words_first => Some(Self::reset__share_words_first), + Qstr::MP_QSTR_backup__not_recommend => Some(Self::backup__not_recommend), + Qstr::MP_QSTR_words__pay_attention => Some(Self::words__pay_attention), _ => None, } } diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs index a83f738009..5835ae7e81 100644 --- a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs +++ b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs @@ -1,7 +1,9 @@ pub mod confirm_reset; +pub mod prompt_backup; pub mod request_passphrase; pub mod show_share_words; pub use confirm_reset::new_confirm_reset; +pub use prompt_backup::PromptBackup; pub use request_passphrase::RequestPassphrase; pub use show_share_words::new_show_share_words_flow; diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/prompt_backup.rs b/core/embed/rust/src/ui/layout_eckhart/flow/prompt_backup.rs new file mode 100644 index 0000000000..79c6ba2b39 --- /dev/null +++ b/core/embed/rust/src/ui/layout_eckhart/flow/prompt_backup.rs @@ -0,0 +1,123 @@ +use crate::{ + error, + strutil::TString, + translations::TR, + ui::{ + component::{ + text::paragraphs::{Paragraph, ParagraphSource, Paragraphs}, + ComponentExt, + }, + flow::{ + base::{Decision, DecisionBuilder as _}, + FlowController, FlowMsg, SwipeFlow, + }, + geometry::{Alignment, Direction, LinearPlacement, Offset}, + }, +}; + +use super::super::{ + component::{ + ActionBar, Button, Header, HeaderMsg, Hint, TextScreen, TextScreenMsg, VerticalMenu, + VerticalMenuScreen, VerticalMenuScreenMsg, + }, + theme, +}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum PromptBackup { + Intro, + Menu, + SkipBackup, +} + +impl FlowController for PromptBackup { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: Direction) -> Decision { + match (self, direction) { + _ => self.do_nothing(), + } + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Intro, FlowMsg::Info) => Self::Menu.goto(), + (Self::Intro, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), + (Self::Menu, FlowMsg::Choice(0)) => Self::SkipBackup.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::SkipBackup, FlowMsg::Cancelled) => Self::Menu.goto(), + (Self::SkipBackup, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), + } + } +} + +pub fn new_prompt_backup() -> Result { + let title: TString = TR::backup__title_create_wallet_backup.into(); + let content: TString = TR::backup__it_should_be_backed_up.into(); + + let paragraphs = Paragraphs::new(Paragraph::new(&theme::TEXT_REGULAR, content)) + .with_placement(LinearPlacement::vertical()); + + let content_intro = TextScreen::new(paragraphs) + .with_header(Header::new(title).with_menu_button()) + .with_action_bar(ActionBar::new_single(Button::with_text( + TR::buttons__continue.into(), + ))) + .map(|msg| match msg { + TextScreenMsg::Menu => Some(FlowMsg::Info), + TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed), + _ => None, + }); + + let content_menu = VerticalMenuScreen::new( + VerticalMenu::empty().item( + Button::with_text(TR::backup__title_skip.into()) + .styled(theme::menu_item_title_red()) + .with_text_align(Alignment::Start) + .with_content_offset(Offset::x(12)), + ), + ) + .with_header( + Header::new(title) + .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled), + ) + .map(|msg| match msg { + VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)), + VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled), + _ => None, + }); + + let paragraphs_skip_intro = Paragraph::new( + &theme::TEXT_REGULAR, + TR::backup__create_backup_to_prevent_loss, + ) + .into_paragraphs() + .with_placement(LinearPlacement::vertical()); + + let content_skip_intro = TextScreen::new(paragraphs_skip_intro) + .with_header( + Header::new(TR::words__pay_attention.into()) + .with_icon(theme::ICON_WARNING, theme::ORANGE) + .with_text_style(theme::label_title_danger()), + ) + .with_action_bar(ActionBar::new_double( + Button::with_icon(theme::ICON_CHEVRON_LEFT), + Button::with_text(TR::buttons__skip.into()).styled(theme::button_cancel()), + )) + .with_hint(Hint::new_instruction(TR::backup__not_recommend, None)) + .map(|msg| match msg { + TextScreenMsg::Menu => Some(FlowMsg::Cancelled), + TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed), + TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled), + }); + + let res = SwipeFlow::new(&PromptBackup::Intro)? + .with_page(&PromptBackup::Intro, content_intro)? + .with_page(&PromptBackup::Menu, content_menu)? + .with_page(&PromptBackup::SkipBackup, content_skip_intro)?; + Ok(res) +} diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs index 2ec8583c60..dbe3c0ff5f 100644 --- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs @@ -349,7 +349,8 @@ impl FirmwareUI for UIEckhart { } fn prompt_backup() -> Result { - Err::, Error>(Error::ValueError(c"not implemented")) + let flow = flow::prompt_backup::new_prompt_backup()?; + Ok(flow) } fn request_bip39( @@ -565,8 +566,6 @@ impl FirmwareUI for UIEckhart { Ok(layout) } - // TODO: This is a temporary implementation so the UI can be functional. - // TODO: This is a temporary implementation so the UI can be functional. fn show_progress( description: TString<'static>, _indeterminate: bool, diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index 849cf10734..42aa8deffd 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -35,6 +35,7 @@ class TR: backup__it_should_be_backed_up_now: str = "It should be backed up now!" backup__new_wallet_created: str = "Wallet created.\n" backup__new_wallet_successfully_created: str = "Wallet created successfully." + backup__not_recommend: str = "We don't recommend to skip wallet backup creation." backup__recover_anytime: str = "You can use your backup to recover your wallet at any time." backup__title_backup_completed: str = "Wallet backup completed" backup__title_backup_wallet: str = "Back up wallet" @@ -962,6 +963,7 @@ class TR: words__not_recommended: str = "Not recommended!" words__operation_cancelled: str = "Operation cancelled" words__outputs: str = "outputs" + words__pay_attention: str = "Pay attention" words__please_check_again: str = "Please check again" words__please_try_again: str = "Please try again" words__really_wanna: str = "Do you really want to" diff --git a/core/translations/en.json b/core/translations/en.json index ee9d447464..2380bd1403 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -30,10 +30,21 @@ "auto_lock__title": "Auto-lock delay", "auto_lock__turned_on": "Auto-lock turned on", "backup__can_back_up_anytime": "You can back up your Trezor once, at any time.", - "backup__create_backup_to_prevent_loss": "Create a backup to avoid losing access to your funds", + "backup__create_backup_to_prevent_loss": { + "Bolt": "Create a backup to avoid losing access to your funds", + "Caesar": "Create a backup to avoid losing access to your funds", + "Delizia": "Create a backup to avoid losing access to your funds", + "Eckhart": "Create a wallet backup to avoid losing access to your funds." + }, + "backup__not_recommend": "We don't recommend to skip wallet backup creation.", "backup__info_multi_share_backup": "Your wallet backup contains multiple lists of words in a specific order (shares).", "backup__info_single_share_backup": "Your wallet backup contains {0} words in a specific order.", - "backup__it_should_be_backed_up": "You should back up your new wallet right now.", + "backup__it_should_be_backed_up": { + "Bolt": "You should back up your new wallet right now.", + "Caesar": "You should back up your new wallet right now.", + "Delizia": "You should back up your new wallet right now.", + "Eckhart": "Back up your new wallet now." + }, "backup__it_should_be_backed_up_now": "It should be backed up now!", "backup__new_wallet_created": "Wallet created.\n", "backup__new_wallet_successfully_created": "Wallet created successfully.", @@ -977,6 +988,7 @@ "words__good_to_know": "Good to know", "words__important": "Important", "words__instructions": "Instructions", + "words__pay_attention": "Pay attention", "words__keep_it_safe": "Keep it safe!", "words__know_what_your_doing": "Continue only if you know what you are doing!", "words__my_trezor": "My Trezor", diff --git a/core/translations/order.json b/core/translations/order.json index f141a888f9..97666a13d3 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -991,5 +991,7 @@ "989": "solana__stake_on_question", "990": "sign_message__confirm_without_review", "991": "instructions__tap_to_continue", - "992": "reset__share_words_first" + "992": "reset__share_words_first", + "993": "backup__not_recommend", + "994": "words__pay_attention" } diff --git a/core/translations/signatures.json b/core/translations/signatures.json index 2176f2080b..df486155b0 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "6684203c68c3a64d607c948fac5eeaa9f349f0e81cf54c8dfc662d5a8d3662e8", - "datetime": "2025-02-26T16:20:10.257648", - "commit": "1f4a43feee5e3f959d638a300b6faf2235c69a82" + "merkle_root": "4df2339f6c6499539f2be851d4a4efe197415bd041c9742e69ad619ddf5786d0", + "datetime": "2025-02-27T08:00:38.586283", + "commit": "de25d208cddda457cd7841cd2854a2a6adc69052" }, "history": [ {