mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-05 09:55:44 +00:00
feat(eckahrt): continue recovery flow
This commit is contained in:
parent
2a22ce3ca3
commit
beab435a81
@ -0,0 +1,285 @@
|
||||
use crate::{
|
||||
error,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::{ButtonRequest, ButtonRequestCode},
|
||||
component::{
|
||||
button_request::ButtonRequestExt,
|
||||
text::paragraphs::{
|
||||
Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, VecExt,
|
||||
},
|
||||
ComponentExt,
|
||||
},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
FlowController, FlowMsg, SwipeFlow,
|
||||
},
|
||||
geometry::{Direction, LinearPlacement},
|
||||
layout::util::RecoveryType,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::Button,
|
||||
firmware::{
|
||||
ActionBar, Header, TextScreen, TextScreenMsg, VerticalMenu, VerticalMenuScreen,
|
||||
VerticalMenuScreenMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ContinueRecoveryBeforeShares {
|
||||
Main,
|
||||
Menu,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ContinueRecoveryBetweenShares {
|
||||
Main,
|
||||
Menu,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ContinueRecoveryBetweenSharesAdvanced {
|
||||
Main,
|
||||
Menu,
|
||||
Cancel,
|
||||
RemainingShares,
|
||||
}
|
||||
|
||||
impl FlowController for ContinueRecoveryBeforeShares {
|
||||
#[inline]
|
||||
fn index(&'static self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
fn handle_swipe(&'static self, _direction: Direction) -> Decision {
|
||||
self.do_nothing()
|
||||
}
|
||||
|
||||
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||
match (self, msg) {
|
||||
(Self::Main, FlowMsg::Info) => Self::Menu.goto(),
|
||||
(Self::Main, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Main.goto(),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowController for ContinueRecoveryBetweenShares {
|
||||
#[inline]
|
||||
fn index(&'static self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
fn handle_swipe(&'static self, _direction: Direction) -> Decision {
|
||||
self.do_nothing()
|
||||
}
|
||||
|
||||
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||
match (self, msg) {
|
||||
(Self::Main, FlowMsg::Info) => Self::Menu.goto(),
|
||||
(Self::Main, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => Self::Cancel.goto(),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Main.goto(),
|
||||
(Self::Cancel, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||
(Self::Cancel, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowController for ContinueRecoveryBetweenSharesAdvanced {
|
||||
#[inline]
|
||||
fn index(&'static self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
fn handle_swipe(&'static self, _direction: Direction) -> Decision {
|
||||
self.do_nothing()
|
||||
}
|
||||
|
||||
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||
match (self, msg) {
|
||||
(Self::Main, FlowMsg::Info) => Self::Menu.goto(),
|
||||
(Self::Main, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => Self::RemainingShares.goto(),
|
||||
(Self::Menu, FlowMsg::Choice(1)) => Self::Cancel.goto(),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Main.goto(),
|
||||
(Self::Cancel, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||
(Self::Cancel, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
||||
(Self::RemainingShares, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_continue_recovery_homepage(
|
||||
text: TString<'static>,
|
||||
subtext: Option<TString<'static>>,
|
||||
recovery_type: RecoveryType,
|
||||
show_instructions: bool, // 1st screen of the recovery process
|
||||
pages: Option<ParagraphVecLong<'static>>,
|
||||
) -> Result<SwipeFlow, error::Error> {
|
||||
let (header, confirm_btn, cancel_btn, cancel_title, cancel_intro) = match recovery_type {
|
||||
RecoveryType::Normal if show_instructions => (
|
||||
Header::new(TR::recovery__title.into()).with_menu_button(),
|
||||
TR::buttons__continue,
|
||||
TR::recovery__title_cancel_recovery,
|
||||
TR::recovery__title_cancel_recovery,
|
||||
TR::recovery__wanna_cancel_recovery,
|
||||
),
|
||||
RecoveryType::Normal => (
|
||||
Header::new(TR::words__title_done.into())
|
||||
.with_text_style(theme::label_title_confirm())
|
||||
.with_icon(theme::ICON_DONE, theme::GREEN_LIGHT)
|
||||
.with_menu_button(),
|
||||
TR::instructions__enter_next_share,
|
||||
TR::recovery__title_cancel_recovery,
|
||||
TR::recovery__title_cancel_recovery,
|
||||
TR::recovery__wanna_cancel_recovery,
|
||||
),
|
||||
_ => (
|
||||
Header::new(TR::recovery__title_dry_run.into()).with_menu_button(),
|
||||
TR::buttons__continue,
|
||||
TR::recovery__cancel_dry_run,
|
||||
TR::recovery__title_cancel_dry_run,
|
||||
TR::recovery__wanna_cancel_dry_run,
|
||||
),
|
||||
};
|
||||
|
||||
let mut pars_main = ParagraphVecShort::new();
|
||||
if show_instructions {
|
||||
pars_main.add(Paragraph::new(
|
||||
&theme::TEXT_REGULAR,
|
||||
TR::recovery__enter_each_word,
|
||||
));
|
||||
} else {
|
||||
pars_main.add(Paragraph::new(&theme::TEXT_REGULAR, text));
|
||||
if let Some(sub) = subtext {
|
||||
pars_main.add(Paragraph::new(&theme::TEXT_REGULAR, sub));
|
||||
}
|
||||
};
|
||||
|
||||
let content_main = TextScreen::new(
|
||||
pars_main
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
)
|
||||
.with_header(header)
|
||||
.with_action_bar(ActionBar::new_single(Button::with_text(confirm_btn.into())))
|
||||
.repeated_button_request(ButtonRequest::new(
|
||||
ButtonRequestCode::RecoveryHomepage,
|
||||
"recovery".into(),
|
||||
))
|
||||
.map(|msg| match msg {
|
||||
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let paragraphs_cancel = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_REGULAR, cancel_intro).with_bottom_padding(17),
|
||||
Paragraph::new(&theme::TEXT_REGULAR, TR::recovery__progress_will_be_lost),
|
||||
])
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical());
|
||||
|
||||
let content_cancel = TextScreen::new(paragraphs_cancel)
|
||||
.with_header(Header::new(cancel_title.into()).with_close_button())
|
||||
.map(|msg| match msg {
|
||||
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||
TextScreenMsg::Cancelled | TextScreenMsg::Menu => Some(FlowMsg::Cancelled),
|
||||
})
|
||||
.repeated_button_request(ButtonRequest::new(
|
||||
ButtonRequestCode::ProtectCall,
|
||||
"abort_recovery".into(),
|
||||
));
|
||||
|
||||
let res = if show_instructions {
|
||||
let content_menu = VerticalMenuScreen::new(
|
||||
VerticalMenu::empty()
|
||||
.item(Button::with_text(cancel_btn.into()).styled(theme::menu_item_title_orange())),
|
||||
)
|
||||
.with_header(Header::new(TString::empty()).with_close_button())
|
||||
.map(|msg| match msg {
|
||||
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
|
||||
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mut res = SwipeFlow::new(&ContinueRecoveryBeforeShares::Main)?;
|
||||
res.add_page(&ContinueRecoveryBeforeShares::Main, content_main)?
|
||||
.add_page(&ContinueRecoveryBeforeShares::Menu, content_menu)?;
|
||||
res
|
||||
} else if pages.is_none() {
|
||||
let content_menu = VerticalMenuScreen::new(
|
||||
VerticalMenu::empty()
|
||||
.item(Button::with_text(cancel_btn.into()).styled(theme::menu_item_title_orange())),
|
||||
)
|
||||
.with_header(Header::new(TString::empty()).with_close_button())
|
||||
.map(|msg| match msg {
|
||||
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
|
||||
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mut res = SwipeFlow::new(&ContinueRecoveryBetweenShares::Main)?;
|
||||
res.add_page(&ContinueRecoveryBetweenShares::Main, content_main)?
|
||||
.add_page(&ContinueRecoveryBetweenShares::Menu, content_menu)?
|
||||
.add_page(&ContinueRecoveryBetweenShares::Cancel, content_cancel)?;
|
||||
res
|
||||
} else {
|
||||
let content_menu = VerticalMenuScreen::new(
|
||||
VerticalMenu::empty()
|
||||
.item(Button::with_text(
|
||||
TR::recovery__title_remaining_shares.into(),
|
||||
))
|
||||
.item(Button::with_text(cancel_btn.into()).styled(theme::menu_item_title_orange())),
|
||||
)
|
||||
.with_header(Header::new(TString::empty()).with_close_button())
|
||||
.map(|msg| match msg {
|
||||
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
|
||||
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let n_remaining_shares = pages.as_ref().unwrap().len() / 2;
|
||||
let content_remaining_shares = TextScreen::new(
|
||||
pages
|
||||
.unwrap()
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
)
|
||||
.with_header(Header::new(TR::recovery__title_remaining_shares.into()).with_close_button())
|
||||
.map(|msg| match msg {
|
||||
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||
_ => None,
|
||||
})
|
||||
.repeated_button_request(ButtonRequest::new(
|
||||
ButtonRequestCode::Other,
|
||||
"show_shares".into(),
|
||||
))
|
||||
.with_pages(move |_| n_remaining_shares);
|
||||
|
||||
let mut res = SwipeFlow::new(&ContinueRecoveryBetweenSharesAdvanced::Main)?;
|
||||
res.add_page(&ContinueRecoveryBetweenSharesAdvanced::Main, content_main)?
|
||||
.add_page(&ContinueRecoveryBetweenSharesAdvanced::Menu, content_menu)?
|
||||
.add_page(
|
||||
&ContinueRecoveryBetweenSharesAdvanced::Cancel,
|
||||
content_cancel,
|
||||
)?
|
||||
.add_page(
|
||||
&ContinueRecoveryBetweenSharesAdvanced::RemainingShares,
|
||||
content_remaining_shares,
|
||||
)?;
|
||||
res
|
||||
};
|
||||
Ok(res)
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod confirm_reset;
|
||||
pub mod confirm_set_new_pin;
|
||||
pub mod continue_recovery_homepage;
|
||||
pub mod get_address;
|
||||
pub mod prompt_backup;
|
||||
pub mod request_passphrase;
|
||||
@ -8,6 +9,7 @@ pub mod show_share_words;
|
||||
|
||||
pub use confirm_reset::new_confirm_reset;
|
||||
pub use confirm_set_new_pin::new_set_new_pin;
|
||||
pub use continue_recovery_homepage::new_continue_recovery_homepage;
|
||||
pub use get_address::GetAddress;
|
||||
pub use prompt_backup::PromptBackup;
|
||||
pub use request_passphrase::RequestPassphrase;
|
||||
|
@ -11,7 +11,8 @@ use crate::{
|
||||
text::{
|
||||
op::OpTextLayout,
|
||||
paragraphs::{
|
||||
Checklist, Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt,
|
||||
Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort,
|
||||
Paragraphs, VecExt,
|
||||
},
|
||||
},
|
||||
Empty, FormattedText,
|
||||
@ -326,14 +327,33 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn continue_recovery_homepage(
|
||||
_text: TString<'static>,
|
||||
_subtext: Option<TString<'static>>,
|
||||
text: TString<'static>,
|
||||
subtext: Option<TString<'static>>,
|
||||
_button: Option<TString<'static>>,
|
||||
_recovery_type: RecoveryType,
|
||||
_show_instructions: bool,
|
||||
_remaining_shares: Option<Obj>,
|
||||
recovery_type: RecoveryType,
|
||||
show_instructions: bool,
|
||||
remaining_shares: Option<Obj>,
|
||||
) -> Result<Gc<LayoutObj>, Error> {
|
||||
Err::<Gc<LayoutObj>, Error>(Error::ValueError(c"not implemented"))
|
||||
let pages_vec = if let Some(pages_obj) = remaining_shares {
|
||||
let mut vec = ParagraphVecLong::new();
|
||||
for page in IterBuf::new().try_iterate(pages_obj)? {
|
||||
let [title, description]: [TString; 2] = util::iter_into_array(page)?;
|
||||
vec.add(Paragraph::new(&theme::TEXT_REGULAR, title))
|
||||
.add(Paragraph::new(&theme::TEXT_MONO_LIGHT, description).break_after());
|
||||
}
|
||||
Some(vec)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let flow = flow::continue_recovery_homepage::new_continue_recovery_homepage(
|
||||
text,
|
||||
subtext,
|
||||
recovery_type,
|
||||
show_instructions,
|
||||
pages_vec,
|
||||
)?;
|
||||
LayoutObj::new_root(flow)
|
||||
}
|
||||
|
||||
fn flow_confirm_output(
|
||||
|
@ -99,7 +99,7 @@ async def show_group_share_success(share_index: int, group_index: int) -> None:
|
||||
|
||||
|
||||
async def continue_recovery(
|
||||
_button_label: str, # unused on delizia
|
||||
_button_label: str, # unused on eckhart
|
||||
text: str,
|
||||
subtext: str | None,
|
||||
recovery_type: RecoveryType,
|
||||
|
Loading…
Reference in New Issue
Block a user