1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-18 20:38:10 +00:00

feat(core/ui): T3T1 separate reset flows

Create separate flows for wallet creating, prompt of backup and
recovery.

Put more strings to copy.

Change style of Frame subheader.
This commit is contained in:
obrusvit 2024-04-28 13:46:39 +02:00 committed by Martin Milata
parent 4471281adc
commit 1028c3500f
19 changed files with 414 additions and 203 deletions

View File

@ -54,6 +54,7 @@ static void _librust_qstrs(void) {
MP_QSTR_auto_lock__change_template;
MP_QSTR_auto_lock__title;
MP_QSTR_backup__can_back_up_anytime;
MP_QSTR_backup__create_backup_to_prevent_loss;
MP_QSTR_backup__it_should_be_backed_up;
MP_QSTR_backup__it_should_be_backed_up_now;
MP_QSTR_backup__new_wallet_created;
@ -150,10 +151,8 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_action;
MP_QSTR_confirm_address;
MP_QSTR_confirm_backup;
MP_QSTR_confirm_backup_written_down;
MP_QSTR_confirm_blob;
MP_QSTR_confirm_coinjoin;
MP_QSTR_confirm_create_wallet;
MP_QSTR_confirm_emphasized;
MP_QSTR_confirm_fido;
MP_QSTR_confirm_firmware_update;
@ -175,7 +174,6 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_value;
MP_QSTR_confirm_with_info;
MP_QSTR_count;
MP_QSTR_create_backup_flow;
MP_QSTR_data;
MP_QSTR_data_hash;
MP_QSTR_data_len;
@ -207,7 +205,11 @@ static void _librust_qstrs(void) {
MP_QSTR_fingerprint;
MP_QSTR_firmware_update__title;
MP_QSTR_firmware_update__title_fingerprint;
MP_QSTR_flow_confirm_reset_create;
MP_QSTR_flow_confirm_reset_recover;
MP_QSTR_flow_get_address;
MP_QSTR_flow_prompt_backup;
MP_QSTR_flow_show_share_words;
MP_QSTR_get_language;
MP_QSTR_hold;
MP_QSTR_hold_danger;
@ -399,6 +401,7 @@ static void _librust_qstrs(void) {
MP_QSTR_reset__button_create;
MP_QSTR_reset__button_recover;
MP_QSTR_reset__by_continuing;
MP_QSTR_reset__check_backup_instructions;
MP_QSTR_reset__check_backup_title;
MP_QSTR_reset__check_group_share_title_template;
MP_QSTR_reset__check_share_title_template;
@ -462,6 +465,7 @@ static void _librust_qstrs(void) {
MP_QSTR_reset__tos_link;
MP_QSTR_reset__total_number_of_shares_in_group_template;
MP_QSTR_reset__use_your_backup;
MP_QSTR_reset__words_written_down_template;
MP_QSTR_reset__write_down_words_template;
MP_QSTR_reset__wrong_word_selected;
MP_QSTR_reset__you_need_one_share;
@ -553,6 +557,8 @@ static void _librust_qstrs(void) {
MP_QSTR_storage_msg__wrong_pin;
MP_QSTR_subprompt;
MP_QSTR_subtitle;
MP_QSTR_text_confirm;
MP_QSTR_text_info;
MP_QSTR_text_mono;
MP_QSTR_time_ms;
MP_QSTR_timer;
@ -626,10 +632,12 @@ static void _librust_qstrs(void) {
MP_QSTR_words__fee;
MP_QSTR_words__from;
MP_QSTR_words__important;
MP_QSTR_words__instructions;
MP_QSTR_words__keep_it_safe;
MP_QSTR_words__know_what_your_doing;
MP_QSTR_words__my_trezor;
MP_QSTR_words__no;
MP_QSTR_words__not_recommended;
MP_QSTR_words__outputs;
MP_QSTR_words__please_check_again;
MP_QSTR_words__please_try_again;

View File

@ -1255,6 +1255,11 @@ pub enum TranslatedString {
instructions__tap_to_confirm = 854, // "Tap to confirm"
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"
reset__check_backup_instructions = 859, // "Let's do a quick check of your backup."
words__instructions = 860, // "Instructions"
words__not_recommended = 861, // "Not recommended!"
}
impl TranslatedString {
@ -2505,6 +2510,11 @@ impl TranslatedString {
Self::instructions__tap_to_confirm => "Tap to confirm",
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.",
Self::backup__create_backup_to_prevent_loss => "Create a 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!",
}
}
@ -3756,6 +3766,11 @@ impl TranslatedString {
Qstr::MP_QSTR_instructions__tap_to_confirm => Some(Self::instructions__tap_to_confirm),
Qstr::MP_QSTR_instructions__hold_to_confirm => Some(Self::instructions__hold_to_confirm),
Qstr::MP_QSTR_words__important => Some(Self::words__important),
Qstr::MP_QSTR_reset__words_written_down_template => Some(Self::reset__words_written_down_template),
Qstr::MP_QSTR_backup__create_backup_to_prevent_loss => Some(Self::backup__create_backup_to_prevent_loss),
Qstr::MP_QSTR_reset__check_backup_instructions => Some(Self::reset__check_backup_instructions),
Qstr::MP_QSTR_words__instructions => Some(Self::words__instructions),
Qstr::MP_QSTR_words__not_recommended => Some(Self::words__not_recommended),
_ => None,
}
}

View File

@ -72,7 +72,7 @@ where
}
pub fn with_subtitle(mut self, subtitle: TString<'static>) -> Self {
let style = theme::TEXT_SUB_GREY_LIGHT;
let style = theme::TEXT_SUB_GREY;
self.title = Child::new(self.title.into_inner().top_aligned());
self.subtitle = Some(Child::new(Label::new(
subtitle,

View File

@ -65,7 +65,7 @@ pub use prompt_screen::PromptScreen;
pub use result::{ResultFooter, ResultScreen, ResultStyle};
pub use scroll::ScrollBar;
#[cfg(feature = "translations")]
pub use share_words::ShareWords;
pub use share_words::{ShareWords, ShareWordsMsg};
pub use simple_page::SimplePage;
pub use status_screen::StatusScreen;
pub use swipe::{Swipe, SwipeDirection};

View File

@ -3,8 +3,9 @@ use crate::{
strutil::TString,
translations::TR,
ui::{
component::{Component, Event, EventCtx, PageMsg, Paginate},
component::{Component, Event, EventCtx, Paginate},
constant::SPACING,
flow::Swipable,
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
model_mercury::component::{Footer, Swipe, SwipeDirection},
shape,
@ -30,6 +31,11 @@ pub struct ShareWords<'a> {
footer: Footer<'static>,
}
pub enum ShareWordsMsg {
GoPrevScreen,
WordsSeen,
}
impl<'a> ShareWords<'a> {
const AREA_WORD_HEIGHT: i16 = 91;
@ -44,13 +50,17 @@ impl<'a> ShareWords<'a> {
}
}
fn is_first_page(&self) -> bool {
self.page_index == 0
}
fn is_final_page(&self) -> bool {
self.page_index == self.share_words.len() - 1
}
}
impl<'a> Component for ShareWords<'a> {
type Msg = PageMsg<()>;
type Msg = ShareWordsMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds;
@ -72,17 +82,20 @@ impl<'a> Component for ShareWords<'a> {
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
ctx.set_page_count(self.share_words.len());
// ctx.set_page_count(self.share_words.len());
let swipe = self.swipe.event(ctx, event);
match swipe {
Some(SwipeDirection::Up) => {
if self.is_final_page() {
return Some(PageMsg::Confirmed);
return Some(ShareWordsMsg::WordsSeen);
}
self.change_page(self.page_index + 1);
ctx.request_paint();
}
Some(SwipeDirection::Down) => {
if self.is_first_page() {
return Some(ShareWordsMsg::GoPrevScreen);
}
self.change_page(self.page_index.saturating_sub(1));
ctx.request_paint();
}
@ -136,6 +149,8 @@ impl<'a> Component for ShareWords<'a> {
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
}
impl<'a> Swipable for ShareWords<'a> {}
impl<'a> Paginate for ShareWords<'a> {
fn page_count(&mut self) -> usize {
self.share_words.len()

View File

@ -18,34 +18,43 @@ use super::super::{
};
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
pub enum ConfirmResetDevice {
pub enum ConfirmResetCreate {
Intro,
Menu,
Confirm,
}
impl FlowState for ConfirmResetDevice {
impl FlowState for ConfirmResetCreate {
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
match (self, direction) {
(ConfirmResetDevice::Intro, SwipeDirection::Left) => {
Decision::Goto(ConfirmResetDevice::Menu, direction)
(ConfirmResetCreate::Intro, SwipeDirection::Left) => {
Decision::Goto(ConfirmResetCreate::Menu, direction)
}
(ConfirmResetDevice::Menu, SwipeDirection::Right) => {
Decision::Goto(ConfirmResetDevice::Intro, direction)
(ConfirmResetCreate::Menu, SwipeDirection::Right) => {
Decision::Goto(ConfirmResetCreate::Intro, direction)
}
(ConfirmResetCreate::Intro, SwipeDirection::Up) => {
Decision::Goto(ConfirmResetCreate::Confirm, direction)
}
(ConfirmResetCreate::Confirm, SwipeDirection::Down) => {
Decision::Goto(ConfirmResetCreate::Intro, direction)
}
(ConfirmResetDevice::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
_ => Decision::Nothing,
}
}
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
match (self, msg) {
(ConfirmResetDevice::Intro, FlowMsg::Info) => {
Decision::Goto(ConfirmResetDevice::Menu, SwipeDirection::Left)
(ConfirmResetCreate::Intro, FlowMsg::Info) => {
Decision::Goto(ConfirmResetCreate::Menu, SwipeDirection::Left)
}
(ConfirmResetDevice::Menu, FlowMsg::Cancelled) => {
Decision::Goto(ConfirmResetDevice::Intro, SwipeDirection::Right)
(ConfirmResetCreate::Menu, FlowMsg::Cancelled) => {
Decision::Goto(ConfirmResetCreate::Intro, SwipeDirection::Right)
}
(ConfirmResetCreate::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled),
(ConfirmResetCreate::Confirm, FlowMsg::Confirmed) => {
Decision::Return(FlowMsg::Confirmed)
}
(ConfirmResetDevice::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled),
_ => Decision::Nothing,
}
}
@ -56,17 +65,17 @@ use crate::{
ui::layout::obj::LayoutObj,
};
pub extern "C" fn new_confirm_reset_device(
pub extern "C" fn new_confirm_reset_create(
n_args: usize,
args: *const Obj,
kwargs: *mut Map,
) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmResetDevice::new) }
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmResetCreate::new) }
}
impl ConfirmResetDevice {
impl ConfirmResetCreate {
fn new(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let title: TString = TR::reset__title_create_wallet.into();
let par_array: [Paragraph<'static>; 3] = [
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, TR::reset__by_continuing)
.with_bottom_padding(17),
@ -81,7 +90,7 @@ impl ConfirmResetDevice {
let content_menu = Frame::left_aligned(
"".into(),
VerticalMenu::empty().danger(theme::ICON_CANCEL, "Cancel".into()),
VerticalMenu::empty().danger(theme::ICON_CANCEL, "Cancel".into()), // TODO: use TR
)
.with_cancel_button()
.map(|msg| match msg {
@ -100,14 +109,11 @@ impl ConfirmResetDevice {
});
let store = flow_store()
// Intro,
.add(content_intro)?
// Context Menu,
.add(content_menu)?
// Confirm prompt
.add(content_confirm)?;
let res = SwipeFlow::new(ConfirmResetDevice::Intro, store)?;
let res = SwipeFlow::new(ConfirmResetCreate::Intro, store)?;
Ok(LayoutObj::new(res)?.into())
}
}

View File

@ -0,0 +1,115 @@
use crate::{
error,
translations::TR,
ui::{
component::{
text::paragraphs::{Paragraph, Paragraphs},
ComponentExt, SwipeDirection,
},
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
},
};
use super::super::{
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
theme,
};
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
pub enum ConfirmResetRecover {
Intro,
Menu,
}
impl FlowState for ConfirmResetRecover {
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
match (self, direction) {
(ConfirmResetRecover::Intro, SwipeDirection::Left) => {
Decision::Goto(ConfirmResetRecover::Menu, direction)
}
(ConfirmResetRecover::Menu, SwipeDirection::Right) => {
Decision::Goto(ConfirmResetRecover::Intro, direction)
}
(ConfirmResetRecover::Intro, SwipeDirection::Up) => {
Decision::Return(FlowMsg::Confirmed)
}
_ => Decision::Nothing,
}
}
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
match (self, msg) {
(ConfirmResetRecover::Intro, FlowMsg::Info) => {
Decision::Goto(ConfirmResetRecover::Menu, SwipeDirection::Left)
}
(ConfirmResetRecover::Menu, FlowMsg::Cancelled) => {
Decision::Goto(ConfirmResetRecover::Intro, SwipeDirection::Right)
}
(ConfirmResetRecover::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled),
_ => Decision::Nothing,
}
}
}
use crate::{
micropython::{map::Map, obj::Obj, util},
ui::layout::obj::LayoutObj,
};
pub extern "C" fn new_confirm_reset_recover(
n_args: usize,
args: *const Obj,
kwargs: *mut Map,
) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmResetRecover::new) }
}
impl ConfirmResetRecover {
fn new(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
let par_array: [Paragraph<'static>; 3] = [
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, TR::reset__by_continuing)
.with_bottom_padding(17),
Paragraph::new(&theme::TEXT_SUB_GREY, TR::reset__more_info_at),
Paragraph::new(&theme::TEXT_SUB_GREY_LIGHT, TR::reset__tos_link),
];
let paragraphs = Paragraphs::new(par_array);
let content_intro = Frame::left_aligned(
TR::recovery__title_recover.into(),
SwipePage::vertical(paragraphs),
)
.with_menu_button()
.with_footer(TR::instructions__swipe_up.into(), None)
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
let content_menu = Frame::left_aligned(
"".into(),
VerticalMenu::empty().danger(
theme::ICON_CANCEL,
TR::recovery__title_cancel_recovery.into(),
),
)
.with_cancel_button()
.map(|msg| match msg {
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
});
let content_confirm = Frame::left_aligned(
TR::reset__title_create_wallet.into(),
PromptScreen::new_hold_to_confirm(),
)
.with_footer(TR::instructions__hold_to_confirm.into(), None)
.map(|msg| match msg {
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
_ => Some(FlowMsg::Cancelled),
});
let store = flow_store()
.add(content_intro)?
.add(content_menu)?
.add(content_confirm)?;
let res = SwipeFlow::new(ConfirmResetRecover::Intro, store)?;
Ok(LayoutObj::new(res)?.into())
}
}

View File

@ -1,7 +1,11 @@
pub mod confirm_reset_device;
pub mod create_backup;
pub mod confirm_reset_create;
pub mod confirm_reset_recover;
pub mod get_address;
pub mod prompt_backup;
pub mod show_share_words;
pub use confirm_reset_device::ConfirmResetDevice;
pub use create_backup::CreateBackup;
pub use confirm_reset_create::ConfirmResetCreate;
pub use confirm_reset_recover::ConfirmResetRecover;
pub use get_address::GetAddress;
pub use prompt_backup::PromptBackup;
pub use show_share_words::ShowShareWords;

View File

@ -19,48 +19,48 @@ use super::super::{
};
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
pub enum CreateBackup {
pub enum PromptBackup {
Intro,
Menu,
SkipBackupIntro,
SkipBackupConfirm,
}
impl FlowState for CreateBackup {
impl FlowState for PromptBackup {
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
match (self, direction) {
(CreateBackup::Intro, SwipeDirection::Left) => {
Decision::Goto(CreateBackup::Menu, direction)
(PromptBackup::Intro, SwipeDirection::Left) => {
Decision::Goto(PromptBackup::Menu, direction)
}
(CreateBackup::SkipBackupIntro, SwipeDirection::Up) => {
Decision::Goto(CreateBackup::SkipBackupConfirm, direction)
(PromptBackup::SkipBackupIntro, SwipeDirection::Up) => {
Decision::Goto(PromptBackup::SkipBackupConfirm, direction)
}
(CreateBackup::SkipBackupConfirm, SwipeDirection::Down) => {
Decision::Goto(CreateBackup::SkipBackupIntro, direction)
(PromptBackup::SkipBackupConfirm, SwipeDirection::Down) => {
Decision::Goto(PromptBackup::SkipBackupIntro, direction)
}
(CreateBackup::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
(PromptBackup::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
_ => Decision::Nothing,
}
}
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
match (self, msg) {
(CreateBackup::Intro, FlowMsg::Info) => {
Decision::Goto(CreateBackup::Menu, SwipeDirection::Left)
(PromptBackup::Intro, FlowMsg::Info) => {
Decision::Goto(PromptBackup::Menu, SwipeDirection::Left)
}
(CreateBackup::Menu, FlowMsg::Choice(0)) => {
Decision::Goto(CreateBackup::SkipBackupIntro, SwipeDirection::Left)
(PromptBackup::Menu, FlowMsg::Choice(0)) => {
Decision::Goto(PromptBackup::SkipBackupIntro, SwipeDirection::Left)
}
(CreateBackup::Menu, FlowMsg::Cancelled) => {
Decision::Goto(CreateBackup::Intro, SwipeDirection::Right)
(PromptBackup::Menu, FlowMsg::Cancelled) => {
Decision::Goto(PromptBackup::Intro, SwipeDirection::Right)
}
(CreateBackup::SkipBackupIntro, FlowMsg::Cancelled) => {
Decision::Goto(CreateBackup::Menu, SwipeDirection::Right)
(PromptBackup::SkipBackupIntro, FlowMsg::Cancelled) => {
Decision::Goto(PromptBackup::Menu, SwipeDirection::Right)
}
(CreateBackup::SkipBackupConfirm, FlowMsg::Cancelled) => {
Decision::Goto(CreateBackup::SkipBackupIntro, SwipeDirection::Right)
(PromptBackup::SkipBackupConfirm, FlowMsg::Cancelled) => {
Decision::Goto(PromptBackup::SkipBackupIntro, SwipeDirection::Right)
}
(CreateBackup::SkipBackupConfirm, FlowMsg::Confirmed) => {
(PromptBackup::SkipBackupConfirm, FlowMsg::Confirmed) => {
Decision::Return(FlowMsg::Cancelled)
}
_ => Decision::Nothing,
@ -73,16 +73,18 @@ use crate::{
ui::layout::obj::LayoutObj,
};
pub extern "C" fn new_create_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, CreateBackup::new) }
pub extern "C" fn new_prompt_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, PromptBackup::new) }
}
impl CreateBackup {
impl PromptBackup {
fn new(_args: &[Obj], _kwargs: &Map) -> Result<Obj, error::Error> {
let title: TString = TR::backup__title_backup_wallet.into();
let par_array: [Paragraph<'static>; 1] = [Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TString::from_str("Your wallet backup contains X words in a specific order."),
// FIXME: should be "contains X words" but the mnemonic/shares are not yet generated at
// this point. We might need to merge the PromptBackup and ShowShareWords flows
TString::from_str("Your wallet backup contains words in a specific order."),
)];
let paragraphs = Paragraphs::new(par_array);
let content_intro = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
@ -94,7 +96,7 @@ impl CreateBackup {
let content_menu = Frame::left_aligned(
"".into(),
VerticalMenu::empty().danger(theme::ICON_CANCEL, "Skip backup".into()),
VerticalMenu::empty().danger(theme::ICON_CANCEL, TR::backup__title_skip.into()),
)
.with_cancel_button()
.map(|msg| match msg {
@ -104,10 +106,10 @@ impl CreateBackup {
});
let par_array_skip_intro: [Paragraph<'static>; 2] = [
Paragraph::new(&theme::TEXT_WARNING, TString::from_str("Not recommended!")),
Paragraph::new(&theme::TEXT_WARNING, TR::words__not_recommended),
Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TString::from_str("Create a backup to avoid losing access to your funds"),
TR::backup__create_backup_to_prevent_loss,
),
];
let paragraphs_skip_intro = Paragraphs::new(par_array_skip_intro);
@ -141,7 +143,7 @@ impl CreateBackup {
.add(content_menu)?
.add(content_skip_intro)?
.add(content_skip_confirm)?;
let res = SwipeFlow::new(CreateBackup::Intro, store)?;
let res = SwipeFlow::new(PromptBackup::Intro, store)?;
Ok(LayoutObj::new(res)?.into())
}
}

View File

@ -0,0 +1,120 @@
use crate::{
error,
micropython::{map::Map, obj::Obj, qstr::Qstr, util},
strutil::TString,
translations::TR,
ui::{
component::{
text::paragraphs::{Paragraph, Paragraphs},
ComponentExt,
},
flow::{
base::Decision, flow_store, FlowMsg, FlowState, FlowStore, IgnoreSwipe, SwipeFlow,
SwipePage,
},
layout::obj::LayoutObj,
},
};
use heapless::Vec;
use super::super::{
component::{Frame, FrameMsg, PromptScreen, ShareWords, ShareWordsMsg, SwipeDirection},
theme,
};
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
pub enum ShowShareWords {
// TODO: potentially also add there the 'never put anywhere digital' warning?
Instruction,
Words,
Confirm,
CheckBackupIntro,
}
impl FlowState for ShowShareWords {
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
match (self, direction) {
(ShowShareWords::Instruction, SwipeDirection::Up) => {
Decision::Goto(ShowShareWords::Words, direction)
}
(ShowShareWords::Confirm, SwipeDirection::Down) => {
Decision::Goto(ShowShareWords::Words, direction)
}
(ShowShareWords::CheckBackupIntro, SwipeDirection::Up) => {
Decision::Return(FlowMsg::Confirmed)
}
_ => Decision::Nothing,
}
}
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
match (self, msg) {
(ShowShareWords::Words, FlowMsg::Cancelled) => {
Decision::Goto(ShowShareWords::Instruction, SwipeDirection::Down)
}
(ShowShareWords::Words, FlowMsg::Confirmed) => {
Decision::Goto(ShowShareWords::Confirm, SwipeDirection::Up)
}
(ShowShareWords::Confirm, FlowMsg::Confirmed) => {
Decision::Goto(ShowShareWords::CheckBackupIntro, SwipeDirection::Up)
}
_ => Decision::Nothing,
}
}
}
pub extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ShowShareWords::new) }
}
impl ShowShareWords {
fn new(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let share_words_vec: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
let text_info: TString = kwargs.get(Qstr::MP_QSTR_text_info)?.try_into()?;
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
let content_instruction = Frame::left_aligned(
title,
SwipePage::vertical(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
text_info,
))),
)
.with_subtitle(TR::words__instructions.into())
.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(|msg| match msg {
FrameMsg::Content(ShareWordsMsg::GoPrevScreen) => Some(FlowMsg::Cancelled),
FrameMsg::Content(ShareWordsMsg::WordsSeen) => Some(FlowMsg::Confirmed),
_ => None,
});
let content_confirm =
Frame::left_aligned(text_confirm, PromptScreen::new_hold_to_confirm())
.with_footer(TR::instructions__hold_to_confirm.into(), None)
.map(|_| Some(FlowMsg::Confirmed));
let content_check_backup_intro = Frame::left_aligned(
TR::reset__check_backup_title.into(),
SwipePage::vertical(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TR::reset__check_backup_instructions,
))),
)
.with_subtitle(TR::words__instructions.into())
.with_footer(TR::instructions__swipe_up.into(), None)
.map(|_| Some(FlowMsg::Confirmed));
let store = flow_store()
.add(content_instruction)?
.add(content_words)?
.add(content_confirm)?
.add(content_check_backup_intro)?;
let res = SwipeFlow::new(ShowShareWords::Instruction, store)?;
Ok(LayoutObj::new(res)?.into())
}
}

View File

@ -46,8 +46,8 @@ use super::{
FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput,
MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputDialog, NumberInputDialogMsg,
PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress,
PromptScreen, SelectWordCount, SelectWordCountMsg, ShareWords, SimplePage, Slip39Input,
StatusScreen, SwipeUpScreen, SwipeUpScreenMsg, VerticalMenu, VerticalMenuChoiceMsg,
PromptScreen, SelectWordCount, SelectWordCountMsg, SimplePage, Slip39Input, StatusScreen,
SwipeUpScreen, SwipeUpScreenMsg, VerticalMenu, VerticalMenuChoiceMsg,
},
flow, theme,
};
@ -183,15 +183,6 @@ where
}
}
impl ComponentMsgObj for ShareWords<'_> {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
PageMsg::Confirmed => Ok(CONFIRMED.as_obj()),
_ => Err(Error::TypeError),
}
}
}
impl ComponentMsgObj for SelectWordCount {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -767,18 +758,6 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_create_wallet(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], _kwargs: &Map| {
let content = PromptScreen::new_hold_to_confirm();
let obj = LayoutObj::new(
Frame::left_aligned(TR::reset__title_create_wallet.into(), content)
.with_footer(TR::instructions__hold_to_confirm.into(), None),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_value(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()?;
@ -1316,37 +1295,6 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_share_words(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 share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_pages)?;
let share_words_vec: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
let share_words = ShareWords::new(share_words_vec);
let frame_with_share_words = Frame::left_aligned(title, share_words);
let obj = LayoutObj::new(frame_with_share_words)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_backup_written_down(
n_args: usize,
args: *const Obj,
kwargs: *mut Map,
) -> Obj {
let block = move |_args: &[Obj], _kwargs: &Map| {
let content = PromptScreen::new_hold_to_confirm();
// TODO: use TR
let frame_with_hold_to_confirm =
Frame::left_aligned("I wrote down all words in order.".into(), content)
.with_footer(TR::instructions__hold_to_confirm.into(), None);
let obj = LayoutObj::new(frame_with_hold_to_confirm)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_request_number(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()?;
@ -1436,7 +1384,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
let obj = LayoutObj::new(
Frame::left_aligned(notification, content)
.with_footer(TR::instructions__swipe_up.into(), None)
.with_subtitle("Instructions".into()), // FIXME: use TR
.with_subtitle(TR::words__instructions.into()),
)?;
Ok(obj.into())
};
@ -1797,17 +1745,13 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// the value is to be rendered as binary with monospace font, False otherwise."""
Qstr::MP_QSTR_confirm_properties => obj_fn_kw!(0, new_confirm_properties).as_obj(),
/// def confirm_reset_device(
/// *,
/// title: str,
/// button: str,
/// ) -> LayoutObj[UiResult]:
/// """Confirm TOS before device setup."""
Qstr::MP_QSTR_confirm_reset_device => obj_fn_kw!(0, flow::confirm_reset_device::new_confirm_reset_device).as_obj(),
/// def flow_confirm_reset_recover() -> LayoutObj[UiResult]:
/// """Confirm TOS before recovery process."""
Qstr::MP_QSTR_flow_confirm_reset_recover => obj_fn_kw!(0, flow::confirm_reset_recover::new_confirm_reset_recover).as_obj(),
/// def confirm_create_wallet() -> LayoutObj[UiResult]:
/// """Confirm creating wallet"""
Qstr::MP_QSTR_confirm_create_wallet => obj_fn_kw!(0, new_confirm_create_wallet).as_obj(),
/// def flow_confirm_reset_create() -> LayoutObj[UiResult]:
/// """Confirm TOS before creating a wallet and have a user hold to confirm creation."""
Qstr::MP_QSTR_flow_confirm_reset_create => obj_fn_kw!(0, flow::confirm_reset_create::new_confirm_reset_create).as_obj(),
/// def show_address_details(
/// *,
@ -2024,23 +1968,20 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
// TODO: This is just POC
/// def create_backup_flow() -> LayoutObj[UiResult]
/// """Start create backup or skip flow."""
Qstr::MP_QSTR_create_backup_flow => obj_fn_kw!(0, flow::create_backup::new_create_backup).as_obj(),
/// def flow_prompt_backup() -> LayoutObj[UiResult]
/// """Prompt a user to create backup with an option to skip."""
Qstr::MP_QSTR_flow_prompt_backup => obj_fn_kw!(0, flow::prompt_backup::new_prompt_backup).as_obj(),
/// def show_share_words(
/// def flow_show_share_words(
/// *,
/// title: str,
/// pages: Iterable[str],
/// words: Iterable[str],
/// text_info: str,
/// text_confirm: str,
/// ) -> LayoutObj[UiResult]:
/// """Show mnemonic for backup."""
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
// TODO: This is just POC
/// def confirm_backup_written_down() -> LayoutObj[UiResult]
/// """Confirm with the user that backup words are written down."""
Qstr::MP_QSTR_confirm_backup_written_down => obj_fn_kw!(0, new_confirm_backup_written_down).as_obj(),
/// """Show wallet backup words preceded by an instruction screen and followed by
/// confirmation."""
Qstr::MP_QSTR_flow_show_share_words => obj_fn_kw!(0, flow::show_share_words::new_show_share_words).as_obj(),
/// def request_number(
/// *,

View File

@ -146,17 +146,13 @@ def confirm_properties(
# rust/src/ui/model_mercury/layout.rs
def confirm_reset_device(
*,
title: str,
button: str,
) -> LayoutObj[UiResult]:
"""Confirm TOS before device setup."""
def flow_confirm_reset_recover() -> LayoutObj[UiResult]:
"""Confirm TOS before recovery process."""
# rust/src/ui/model_mercury/layout.rs
def confirm_create_wallet() -> LayoutObj[UiResult]:
"""Confirm creating wallet"""
def flow_confirm_reset_create() -> LayoutObj[UiResult]:
"""Confirm TOS before creating a wallet and have a user hold to confirm creation."""
# rust/src/ui/model_mercury/layout.rs
@ -395,22 +391,20 @@ def select_word(
# rust/src/ui/model_mercury/layout.rs
def create_backup_flow() -> LayoutObj[UiResult]
"""Start create backup or skip flow."""
def flow_prompt_backup() -> LayoutObj[UiResult]
"""Prompt a user to create backup with an option to skip."""
# rust/src/ui/model_mercury/layout.rs
def show_share_words(
def flow_show_share_words(
*,
title: str,
pages: Iterable[str],
words: Iterable[str],
text_info: str,
text_confirm: str,
) -> LayoutObj[UiResult]:
"""Show mnemonic for backup."""
# rust/src/ui/model_mercury/layout.rs
def confirm_backup_written_down() -> LayoutObj[UiResult]
"""Confirm with the user that backup words are written down."""
"""Show wallet backup words preceded by an instruction screen and followed by
confirmation."""
# rust/src/ui/model_mercury/layout.rs

View File

@ -20,6 +20,7 @@ class TR:
auto_lock__change_template: str = "Auto-lock your Trezor after {0} of inactivity?"
auto_lock__title: str = "Auto-lock delay"
backup__can_back_up_anytime: str = "You can back up your Trezor once, at any time."
backup__create_backup_to_prevent_loss: str = "Create a backup to avoid losing access to your funds"
backup__it_should_be_backed_up: str = "You should back up your new wallet right now."
backup__it_should_be_backed_up_now: str = "It should be backed up now!"
backup__new_wallet_created: str = "New wallet created.\n"
@ -560,6 +561,7 @@ class TR:
reset__button_create: str = "Create wallet"
reset__button_recover: str = "Recover wallet"
reset__by_continuing: str = "By continuing you agree to Trezor Company's terms and conditions."
reset__check_backup_instructions: str = "Let's do a quick check of your backup."
reset__check_backup_title: str = "Check backup"
reset__check_group_share_title_template: str = "Check g{0} - share {1}"
reset__check_share_title_template: str = "Check share #{0}"
@ -622,6 +624,7 @@ class TR:
reset__tos_link: str = "trezor.io/tos"
reset__total_number_of_shares_in_group_template: str = "Set the total number of shares in Group {0}."
reset__use_your_backup: str = "Use your backup when you need to recover your wallet."
reset__words_written_down_template: str = "I wrote down all {0} words in order."
reset__write_down_words_template: str = "Write down all {0} words in order."
reset__wrong_word_selected: str = "Wrong word selected!"
reset__you_need_one_share: str = "For recovery you need 1 share."
@ -834,10 +837,12 @@ class TR:
words__fee: str = "Fee"
words__from: str = "from"
words__important: str = "Important"
words__instructions: str = "Instructions"
words__keep_it_safe: str = "Keep it safe!"
words__know_what_your_doing: str = "Continue only if you know what you are doing!"
words__my_trezor: str = "My Trezor"
words__no: str = "No"
words__not_recommended: str = "Not recommended!"
words__outputs: str = "outputs"
words__please_check_again: str = "Please check again"
words__please_try_again: str = "Please try again"

View File

@ -322,33 +322,19 @@ async def confirm_single(
)
async def confirm_reset_device(title: str, recovery: bool = False) -> None:
async def confirm_reset_device(_title: str, recovery: bool = False) -> None:
if recovery:
await raise_if_not_confirmed(
interact(
RustLayout(
trezorui2.confirm_reset_device(
title=title,
button="", # not used
)
),
RustLayout(trezorui2.flow_confirm_reset_recover()),
"recover_device",
ButtonRequestType.ProtectCall,
)
)
else:
# creating wallet shows TOS first and then an extra screen confirms
await raise_if_not_confirmed(
RustLayout(
trezorui2.confirm_reset_device(
title=title,
button="", # not used
)
),
)
await raise_if_not_confirmed(
interact(
RustLayout(trezorui2.confirm_create_wallet()),
RustLayout(trezorui2.flow_confirm_reset_create()),
"setup_device",
ButtonRequestType.ResetDevice,
)
@ -356,7 +342,7 @@ async def confirm_reset_device(title: str, recovery: bool = False) -> None:
async def prompt_backup() -> bool:
# result = await interact(RustLayout(trezorui2.))
# TODO: should we move this to `flow_prompt_backup`?
await interact(
RustLayout(trezorui2.show_success(title=TR.backup__new_wallet_created)),
"backup_device",
@ -364,8 +350,7 @@ async def prompt_backup() -> bool:
)
result = await interact(
# TODO: this is just POC
RustLayout(trezorui2.create_backup_flow()),
RustLayout(trezorui2.flow_prompt_backup()),
"backup_device",
ButtonRequestType.ResetDevice,
)

View File

@ -23,7 +23,9 @@ async def show_share_words(
share_index: int | None = None,
group_index: int | None = None,
) -> None:
if share_index is None:
# FIXME use TR.reset__recovery_wallet_backup_title after #3710 merged
title = TR.reset__recovery_seed_title
elif group_index is None:
title = TR.reset__recovery_share_title_template.format(share_index + 1)
@ -31,28 +33,23 @@ async def show_share_words(
title = TR.reset__group_share_title_template.format(
group_index + 1, share_index + 1
)
await RustLayout(
trezorui2.show_share_words(
title=title,
pages=share_words,
),
)
words_count = len(share_words)
text_info = TR.reset__write_down_words_template.format(words_count)
text_confirm = TR.reset__words_written_down_template.format(words_count)
result = await interact(
RustLayout(
trezorui2.confirm_backup_written_down(),
trezorui2.flow_show_share_words(
title=title,
words=share_words,
text_info=text_info,
text_confirm=text_confirm,
)
),
"backup_words",
ButtonRequestType.ResetDevice,
)
result = await RustLayout(
trezorui2.show_info(
title="Check wallet backup",
description="Let's do a quick check of your backup.",
)
)
if result != CONFIRMED:
raise ActionCancelled
@ -317,12 +314,6 @@ async def show_warning_backup(slip39: bool) -> None:
"backup_warning",
ButtonRequestType.ResetDevice,
)
result = await RustLayout(
trezorui2.show_info(
title="Wallet backup",
description="Write the following words in order on your wallet backup card.",
)
)
if result != CONFIRMED:
raise ActionCancelled

View File

@ -22,6 +22,7 @@
"auto_lock__change_template": "Auto-lock your Trezor after {0} of inactivity?",
"auto_lock__title": "Auto-lock delay",
"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__it_should_be_backed_up": "You should back up your new wallet right now.",
"backup__it_should_be_backed_up_now": "It should be backed up now!",
"backup__new_wallet_created": "New wallet created.\n",
@ -566,6 +567,7 @@
"reset__check_group_share_title_template": "Check g{0} - share {1}",
"reset__check_wallet_backup_title": "Check wallet backup",
"reset__check_share_title_template": "Check share #{0}",
"reset__check_backup_instructions": "Let's do a quick check of your backup.",
"reset__continue_with_next_share": "Continue with the next share.",
"reset__continue_with_share_template": "Continue with share #{0}.",
"reset__create_x_of_y_multi_share_backup_template": "Do you want to create a {0} of {1} multi-share backup?",
@ -624,6 +626,7 @@
"reset__tos_link": "trezor.io/tos",
"reset__total_number_of_shares_in_group_template": "Set the total number of shares in Group {0}.",
"reset__use_your_backup": "Use your backup when you need to recover your wallet.",
"reset__words_written_down_template": "I wrote down all {0} words in order.",
"reset__write_down_words_template": "Write down all {0} words in order.",
"reset__wrong_word_selected": "Wrong word selected!",
"reset__you_need_one_share": "For recovery you need 1 share.",
@ -836,10 +839,12 @@
"words__fee": "Fee",
"words__from": "from",
"words__important": "Important",
"words__instructions": "Instructions",
"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",
"words__no": "No",
"words__not_recommended": "Not recommended!",
"words__outputs": "outputs",
"words__please_check_again": "Please check again",
"words__please_try_again": "Please try again",

View File

@ -855,5 +855,10 @@
"853": "instructions__swipe_up",
"854": "instructions__tap_to_confirm",
"855": "instructions__hold_to_confirm",
"856": "words__important"
"856": "words__important",
"857": "reset__words_written_down_template",
"858": "backup__create_backup_to_prevent_loss",
"859": "reset__check_backup_instructions",
"860": "words__instructions",
"861": "words__not_recommended"
}

View File

@ -1,8 +1,8 @@
{
"current": {
"merkle_root": "04a116dbd20d79235dd81e26fa428eb7c5f4971e76f08a47cde1ec98c959e586",
"datetime": "2024-05-17T10:12:24.161735",
"commit": "aea8bdbe8f9cc2fe629e624a06f4f1b1af6e0a8f"
"merkle_root": "5261bdad43d4e3cda9263ec6ce080218a0d897e02307ca8cafa2525d6a8d3d6b",
"datetime": "2024-05-17T10:18:14.246905",
"commit": "58db509b926fd99b3a36eb3bd550945bf1a139c4"
},
"history": [
{

View File

@ -256,7 +256,7 @@ def read_mnemonic_from_screen_mercury(
debug.wait_layout()
for i in range(br.pages):
for _ in range(br.pages):
words = debug.wait_layout().seed_words()
mnemonic.extend(words)
debug.swipe_up()