mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-23 14:58:09 +00:00
feat(core/ui): T3T1 instruction screens between shares
Changes the content and visual appearance of the screens between shares during multi-share (shamir) recovery. Context menu with the option to cancel is added to the screen.
This commit is contained in:
parent
6c75d9f97a
commit
11308f578d
1
core/.changelog.d/3992.changed
Normal file
1
core/.changelog.d/3992.changed
Normal file
@ -0,0 +1 @@
|
||||
[T3T1] Improve instruction screens during multi-share recovery process
|
@ -239,10 +239,12 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_fingerprint;
|
||||
MP_QSTR_firmware_update__title;
|
||||
MP_QSTR_firmware_update__title_fingerprint;
|
||||
MP_QSTR_first_screen;
|
||||
MP_QSTR_flow_confirm_output;
|
||||
MP_QSTR_flow_confirm_reset;
|
||||
MP_QSTR_flow_confirm_set_new_pin;
|
||||
MP_QSTR_flow_confirm_summary;
|
||||
MP_QSTR_flow_continue_recovery;
|
||||
MP_QSTR_flow_get_address;
|
||||
MP_QSTR_flow_prompt_backup;
|
||||
MP_QSTR_flow_request_number;
|
||||
@ -659,7 +661,9 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_storage_msg__verifying_pin;
|
||||
MP_QSTR_storage_msg__wrong_pin;
|
||||
MP_QSTR_subprompt;
|
||||
MP_QSTR_subtext;
|
||||
MP_QSTR_subtitle;
|
||||
MP_QSTR_text;
|
||||
MP_QSTR_text_confirm;
|
||||
MP_QSTR_text_info;
|
||||
MP_QSTR_text_mono;
|
||||
|
232
core/embed/rust/src/ui/model_mercury/flow/continue_recovery.rs
Normal file
232
core/embed/rust/src/ui/model_mercury/flow/continue_recovery.rs
Normal file
@ -0,0 +1,232 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::{map::Map, obj::Obj, qstr::Qstr, util},
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::{ButtonRequest, ButtonRequestCode},
|
||||
component::{
|
||||
swipe_detect::SwipeSettings,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
|
||||
ComponentExt, SwipeDirection,
|
||||
},
|
||||
flow::{
|
||||
base::{DecisionBuilder as _, StateChange},
|
||||
FlowMsg, FlowState, SwipeFlow,
|
||||
},
|
||||
layout::obj::LayoutObj,
|
||||
model_mercury::component::{CancelInfoConfirmMsg, PromptScreen, SwipeContent},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, VerticalMenu, VerticalMenuChoiceMsg},
|
||||
theme,
|
||||
};
|
||||
|
||||
const RECOVERY_TYPE_NORMAL: u32 = 0;
|
||||
const RECOVERY_TYPE_DRY_RUN: u32 = 1;
|
||||
const RECOVERY_TYPE_UNLOCK_REPEATED_BACKUP: u32 = 2;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ContinueRecoveryBeforeShares {
|
||||
Main,
|
||||
Menu,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ContinueRecoveryBetweenShares {
|
||||
Main,
|
||||
Menu,
|
||||
CancelIntro,
|
||||
CancelConfirm,
|
||||
}
|
||||
|
||||
impl FlowState for ContinueRecoveryBeforeShares {
|
||||
#[inline]
|
||||
fn index(&'static self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange {
|
||||
match (self, direction) {
|
||||
(Self::Main, SwipeDirection::Left) => Self::Menu.swipe(direction),
|
||||
(Self::Menu, SwipeDirection::Right) => Self::Main.swipe(direction),
|
||||
(Self::Main, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&'static self, msg: FlowMsg) -> StateChange {
|
||||
match (self, msg) {
|
||||
(Self::Main, FlowMsg::Info) => Self::Menu.transit(),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Main.swipe_right(),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowState for ContinueRecoveryBetweenShares {
|
||||
#[inline]
|
||||
fn index(&'static self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange {
|
||||
match (self, direction) {
|
||||
(Self::Main, SwipeDirection::Left) => Self::Menu.swipe(direction),
|
||||
(Self::Menu, SwipeDirection::Right) => Self::Main.swipe(direction),
|
||||
(Self::Main, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::CancelIntro, SwipeDirection::Up) => Self::CancelConfirm.swipe(direction),
|
||||
(Self::CancelIntro, SwipeDirection::Right) => Self::Menu.swipe(direction),
|
||||
(Self::CancelConfirm, SwipeDirection::Down) => Self::CancelIntro.swipe(direction),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&'static self, msg: FlowMsg) -> StateChange {
|
||||
match (self, msg) {
|
||||
(Self::Main, FlowMsg::Info) => Self::Menu.transit(),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => Self::CancelIntro.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Main.swipe_right(),
|
||||
(Self::CancelIntro, FlowMsg::Cancelled) => Self::Menu.transit(),
|
||||
(Self::CancelConfirm, FlowMsg::Cancelled) => Self::CancelIntro.swipe_right(),
|
||||
(Self::CancelConfirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub extern "C" fn new_continue_recovery(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
unsafe {
|
||||
util::try_with_args_and_kwargs(n_args, args, kwargs, ContinueRecoveryBeforeShares::new_obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl ContinueRecoveryBeforeShares {
|
||||
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let first_screen: bool = kwargs.get(Qstr::MP_QSTR_first_screen)?.try_into()?;
|
||||
let recovery_type: u32 = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
|
||||
let text: TString = kwargs.get(Qstr::MP_QSTR_text)?.try_into()?; // #shares entered
|
||||
let subtext: Option<TString> = kwargs.get(Qstr::MP_QSTR_subtext)?.try_into_option()?; // #shares remaining
|
||||
|
||||
let (title, cancel_btn, cancel_title, cancel_intro) =
|
||||
if recovery_type == RECOVERY_TYPE_NORMAL {
|
||||
(
|
||||
TR::recovery__title,
|
||||
TR::recovery__title_cancel_recovery,
|
||||
TR::recovery__title_cancel_recovery,
|
||||
TR::recovery__wanna_cancel_recovery,
|
||||
)
|
||||
} else {
|
||||
// dry-run
|
||||
(
|
||||
TR::recovery__title_dry_run,
|
||||
TR::recovery__cancel_dry_run,
|
||||
TR::recovery__title_cancel_dry_run,
|
||||
TR::recovery__wanna_cancel_dry_run,
|
||||
)
|
||||
};
|
||||
|
||||
let mut pars = ParagraphVecShort::new();
|
||||
let footer_instruction;
|
||||
let footer_description;
|
||||
if first_screen {
|
||||
pars.add(Paragraph::new(
|
||||
&theme::TEXT_MAIN_GREY_EXTRA_LIGHT,
|
||||
TR::recovery__enter_each_word,
|
||||
));
|
||||
footer_instruction = TR::instructions__swipe_up.into();
|
||||
footer_description = None;
|
||||
} else {
|
||||
pars.add(Paragraph::new(&theme::TEXT_MAIN_GREY_EXTRA_LIGHT, text));
|
||||
if let Some(sub) = subtext {
|
||||
pars.add(Paragraph::new(&theme::TEXT_SUB_GREY, sub));
|
||||
}
|
||||
footer_instruction = TR::instructions__swipe_up.into();
|
||||
footer_description = Some(TR::instructions__enter_next_share.into());
|
||||
}
|
||||
|
||||
let paragraphs_main = Paragraphs::new(pars);
|
||||
let content_main = Frame::left_aligned(title.into(), SwipeContent::new(paragraphs_main))
|
||||
.with_subtitle(TR::words__instructions.into())
|
||||
.with_menu_button()
|
||||
.with_footer(footer_instruction, footer_description)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Left, SwipeSettings::default())
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info))
|
||||
.repeated_button_request(ButtonRequest::new(
|
||||
ButtonRequestCode::RecoveryHomepage,
|
||||
"recovery".into(),
|
||||
));
|
||||
|
||||
let content_menu = Frame::left_aligned(
|
||||
TString::empty(),
|
||||
VerticalMenu::empty().danger(theme::ICON_CANCEL, cancel_btn.into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
let paragraphs_cancel = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, cancel_intro).with_bottom_padding(17),
|
||||
Paragraph::new(&theme::TEXT_WARNING, TR::recovery__progress_will_be_lost),
|
||||
])
|
||||
.into_paragraphs();
|
||||
let content_cancel_intro =
|
||||
Frame::left_aligned(cancel_title.into(), SwipeContent::new(paragraphs_cancel))
|
||||
.with_cancel_button()
|
||||
.with_footer(
|
||||
TR::instructions__swipe_up.into(),
|
||||
Some(TR::words__continue_anyway.into()),
|
||||
)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
})
|
||||
.repeated_button_request(ButtonRequest::new(
|
||||
ButtonRequestCode::ProtectCall,
|
||||
"abort_recovery".into(),
|
||||
));
|
||||
|
||||
let content_cancel_confirm = Frame::left_aligned(
|
||||
cancel_title.into(),
|
||||
SwipeContent::new(PromptScreen::new_tap_to_cancel()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let res = if first_screen {
|
||||
SwipeFlow::new(&ContinueRecoveryBeforeShares::Main)?
|
||||
.with_page(&ContinueRecoveryBeforeShares::Main, content_main)?
|
||||
.with_page(&ContinueRecoveryBeforeShares::Menu, content_menu)?
|
||||
} else {
|
||||
SwipeFlow::new(&ContinueRecoveryBetweenShares::Main)?
|
||||
.with_page(&ContinueRecoveryBetweenShares::Main, content_main)?
|
||||
.with_page(&ContinueRecoveryBetweenShares::Menu, content_menu)?
|
||||
.with_page(
|
||||
&ContinueRecoveryBetweenShares::CancelIntro,
|
||||
content_cancel_intro,
|
||||
)?
|
||||
.with_page(
|
||||
&ContinueRecoveryBetweenShares::CancelConfirm,
|
||||
content_cancel_confirm,
|
||||
)?
|
||||
};
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ pub mod confirm_output;
|
||||
pub mod confirm_reset;
|
||||
pub mod confirm_set_new_pin;
|
||||
pub mod confirm_summary;
|
||||
pub mod continue_recovery;
|
||||
pub mod get_address;
|
||||
pub mod prompt_backup;
|
||||
pub mod request_number;
|
||||
@ -20,6 +21,7 @@ pub use confirm_output::new_confirm_output;
|
||||
pub use confirm_reset::new_confirm_reset;
|
||||
pub use confirm_set_new_pin::SetNewPin;
|
||||
pub use confirm_summary::new_confirm_summary;
|
||||
pub use continue_recovery::new_continue_recovery;
|
||||
pub use get_address::GetAddress;
|
||||
pub use prompt_backup::PromptBackup;
|
||||
pub use request_number::RequestNumber;
|
||||
|
@ -395,9 +395,6 @@ impl ConfirmBlobParams {
|
||||
}
|
||||
}
|
||||
|
||||
const RECOVERY_TYPE_DRY_RUN: u32 = 1;
|
||||
const RECOVERY_TYPE_UNLOCK_REPEATED_BACKUP: u32 = 2;
|
||||
|
||||
extern "C" fn new_confirm_blob(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()?;
|
||||
@ -1053,34 +1050,6 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_recovery(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 description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let _button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||
let recovery_type: u32 = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
|
||||
let _info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
|
||||
|
||||
let paragraphs = Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, description));
|
||||
|
||||
let notification = match recovery_type {
|
||||
RECOVERY_TYPE_DRY_RUN => TR::recovery__title_dry_run.into(),
|
||||
RECOVERY_TYPE_UNLOCK_REPEATED_BACKUP => TR::recovery__title_dry_run.into(),
|
||||
_ => TR::recovery__title.into(),
|
||||
};
|
||||
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(notification, SwipeContent::new(paragraphs))
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_subtitle(TR::words__instructions.into())
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], _kwargs: &Map| {
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
@ -1688,16 +1657,15 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// mark next to them."""
|
||||
Qstr::MP_QSTR_show_checklist => obj_fn_kw!(0, new_show_checklist).as_obj(),
|
||||
|
||||
/// def confirm_recovery(
|
||||
/// def flow_continue_recovery(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// description: str,
|
||||
/// button: str,
|
||||
/// first_screen: bool,
|
||||
/// recovery_type: RecoveryType,
|
||||
/// info_button: bool = False,
|
||||
/// text: str,
|
||||
/// subtext: str | None = None,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Device recovery homescreen."""
|
||||
Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(),
|
||||
Qstr::MP_QSTR_flow_continue_recovery => obj_fn_kw!(0, flow::continue_recovery::new_continue_recovery).as_obj(),
|
||||
|
||||
/// def select_word_count(
|
||||
/// *,
|
||||
|
@ -445,13 +445,12 @@ def show_checklist(
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def confirm_recovery(
|
||||
def flow_continue_recovery(
|
||||
*,
|
||||
title: str,
|
||||
description: str,
|
||||
button: str,
|
||||
first_screen: bool,
|
||||
recovery_type: RecoveryType,
|
||||
info_button: bool = False,
|
||||
text: str,
|
||||
subtext: str | None = None,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Device recovery homescreen."""
|
||||
|
||||
|
@ -2,7 +2,6 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import TR
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.ui.layouts import confirm_action
|
||||
from trezor.ui.layouts.recovery import ( # noqa: F401
|
||||
request_word_count,
|
||||
show_group_share_success,
|
||||
@ -18,28 +17,6 @@ if TYPE_CHECKING:
|
||||
from trezor.enums import BackupType
|
||||
|
||||
|
||||
async def _confirm_abort(dry_run: bool = False) -> None:
|
||||
if dry_run:
|
||||
await confirm_action(
|
||||
"abort_recovery",
|
||||
TR.recovery__title_cancel_dry_run,
|
||||
TR.recovery__cancel_dry_run,
|
||||
description=TR.recovery__wanna_cancel_dry_run,
|
||||
verb=TR.buttons__cancel,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
else:
|
||||
await confirm_action(
|
||||
"abort_recovery",
|
||||
TR.recovery__title_cancel_recovery,
|
||||
TR.recovery__progress_will_be_lost,
|
||||
TR.recovery__wanna_cancel_recovery,
|
||||
verb=TR.buttons__cancel,
|
||||
reverse=True,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
|
||||
|
||||
async def request_mnemonic(
|
||||
word_count: int, backup_type: BackupType | None
|
||||
) -> str | None:
|
||||
@ -149,24 +126,12 @@ async def homescreen_dialog(
|
||||
show_info: bool = False,
|
||||
) -> None:
|
||||
import storage.recovery as storage_recovery
|
||||
from trezor.enums import RecoveryType
|
||||
from trezor.ui.layouts.recovery import continue_recovery
|
||||
from trezor.wire import ActionCancelled
|
||||
|
||||
from .recover import RecoveryAborted
|
||||
|
||||
recovery_type = storage_recovery.get_type()
|
||||
|
||||
while True:
|
||||
if await continue_recovery(
|
||||
button_label, text, subtext, info_func, recovery_type, show_info
|
||||
):
|
||||
# go forward in the recovery process
|
||||
break
|
||||
# user has chosen to abort, confirm the choice
|
||||
try:
|
||||
await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
|
||||
except ActionCancelled:
|
||||
pass
|
||||
else:
|
||||
raise RecoveryAborted
|
||||
if not await continue_recovery(
|
||||
button_label, text, subtext, info_func, recovery_type, show_info
|
||||
):
|
||||
raise RecoveryAborted
|
||||
|
@ -8,23 +8,10 @@ from ..common import interact
|
||||
from . import RustLayout, raise_if_not_confirmed
|
||||
|
||||
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
|
||||
CANCELLED = trezorui2.CANCELLED # global_import_cache
|
||||
INFO = trezorui2.INFO # global_import_cache
|
||||
|
||||
|
||||
async def _is_confirmed_info(
|
||||
dialog: RustLayout,
|
||||
info_func: Callable,
|
||||
) -> bool:
|
||||
while True:
|
||||
result = await dialog
|
||||
|
||||
if result is trezorui2.INFO:
|
||||
await info_func()
|
||||
dialog.request_complete_repaint()
|
||||
else:
|
||||
return result is CONFIRMED
|
||||
|
||||
|
||||
async def request_word_count(recovery_type: RecoveryType) -> int:
|
||||
selector = RustLayout(trezorui2.select_word_count(recovery_type=recovery_type))
|
||||
count = await interact(selector, "word_count", ButtonRequestType.MnemonicWordCount)
|
||||
@ -112,38 +99,30 @@ async def show_group_share_success(share_index: int, group_index: int) -> None:
|
||||
|
||||
|
||||
async def continue_recovery(
|
||||
button_label: str,
|
||||
button_label: str, # unused on mercury
|
||||
text: str,
|
||||
subtext: str | None,
|
||||
info_func: Callable | None,
|
||||
info_func: Callable | None, # TODO: see below
|
||||
recovery_type: RecoveryType,
|
||||
show_info: bool = False, # unused on TT
|
||||
show_info: bool = False,
|
||||
) -> bool:
|
||||
from ..common import button_request
|
||||
|
||||
if show_info:
|
||||
# Show this just one-time
|
||||
description = TR.recovery__enter_each_word
|
||||
else:
|
||||
description = subtext or ""
|
||||
# TODO: info_func should be changed to return data to be shown (and not show
|
||||
# them) so that individual models can implement showing logic on their own.
|
||||
# T3T1 should move the data to `flow_continue_recovery` and hide them
|
||||
# in the context menu
|
||||
|
||||
# NOTE: show_info can be understood as first screen before any shares
|
||||
homepage = RustLayout(
|
||||
trezorui2.confirm_recovery(
|
||||
title=text,
|
||||
description=description,
|
||||
button=button_label,
|
||||
info_button=info_func is not None,
|
||||
trezorui2.flow_continue_recovery(
|
||||
first_screen=show_info,
|
||||
recovery_type=recovery_type,
|
||||
text=text,
|
||||
subtext=subtext,
|
||||
)
|
||||
)
|
||||
|
||||
await button_request("recovery", ButtonRequestType.RecoveryHomepage)
|
||||
|
||||
if info_func is not None:
|
||||
return await _is_confirmed_info(homepage, info_func)
|
||||
else:
|
||||
result = await homepage
|
||||
return result is CONFIRMED
|
||||
# TODO: the button request might go to rust
|
||||
result = await interact(homepage, "recovery", ButtonRequestType.RecoveryHomepage)
|
||||
return result is CONFIRMED
|
||||
|
||||
|
||||
async def show_recovery_warning(
|
||||
|
@ -69,6 +69,30 @@ async def show_group_share_success(share_index: int, group_index: int) -> None:
|
||||
)
|
||||
|
||||
|
||||
async def _confirm_abort(dry_run: bool = False) -> None:
|
||||
from . import confirm_action
|
||||
|
||||
if dry_run:
|
||||
await confirm_action(
|
||||
"abort_recovery",
|
||||
TR.recovery__title_cancel_dry_run,
|
||||
TR.recovery__cancel_dry_run,
|
||||
description=TR.recovery__wanna_cancel_dry_run,
|
||||
verb=TR.buttons__cancel,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
else:
|
||||
await confirm_action(
|
||||
"abort_recovery",
|
||||
TR.recovery__title_cancel_recovery,
|
||||
TR.recovery__progress_will_be_lost,
|
||||
TR.recovery__wanna_cancel_recovery,
|
||||
verb=TR.buttons__cancel,
|
||||
reverse=True,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
|
||||
|
||||
async def continue_recovery(
|
||||
button_label: str,
|
||||
text: str,
|
||||
@ -81,6 +105,8 @@ async def continue_recovery(
|
||||
# There is very limited space on the screen
|
||||
# (and having middle button would mean shortening the right button text)
|
||||
|
||||
from trezor.wire import ActionCancelled
|
||||
|
||||
# Never showing info for dry-run, user already saw it and it is disturbing
|
||||
if recovery_type in (RecoveryType.DryRun, RecoveryType.UnlockRepeatedBackup):
|
||||
show_info = False
|
||||
@ -88,22 +114,32 @@ async def continue_recovery(
|
||||
if subtext:
|
||||
text += f"\n\n{subtext}"
|
||||
|
||||
homepage = RustLayout(
|
||||
trezorui2.confirm_recovery(
|
||||
title="",
|
||||
description=text,
|
||||
button=button_label,
|
||||
recovery_type=recovery_type,
|
||||
info_button=False,
|
||||
show_info=show_info, # type: ignore [No parameter named "show_info"]
|
||||
while True:
|
||||
homepage = RustLayout(
|
||||
trezorui2.confirm_recovery(
|
||||
title="",
|
||||
description=text,
|
||||
button=button_label,
|
||||
recovery_type=recovery_type,
|
||||
info_button=False,
|
||||
show_info=show_info, # type: ignore [No parameter named "show_info"]
|
||||
)
|
||||
)
|
||||
)
|
||||
result = await interact(
|
||||
homepage,
|
||||
"recovery",
|
||||
ButtonRequestType.RecoveryHomepage,
|
||||
)
|
||||
return result is trezorui2.CONFIRMED
|
||||
result = await interact(
|
||||
homepage,
|
||||
"recovery",
|
||||
ButtonRequestType.RecoveryHomepage,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
return True
|
||||
|
||||
# user has chosen to abort, confirm the choice
|
||||
try:
|
||||
await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
|
||||
except ActionCancelled:
|
||||
pass
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def show_recovery_warning(
|
||||
|
@ -11,18 +11,18 @@ CONFIRMED = trezorui2.CONFIRMED # global_import_cache
|
||||
INFO = trezorui2.INFO # global_import_cache
|
||||
|
||||
|
||||
async def _is_confirmed_info(
|
||||
async def _homepage_with_info(
|
||||
dialog: RustLayout,
|
||||
info_func: Callable,
|
||||
) -> bool:
|
||||
) -> trezorui2.UiResult:
|
||||
while True:
|
||||
result = await dialog
|
||||
|
||||
if result is trezorui2.INFO:
|
||||
if result is INFO:
|
||||
await info_func()
|
||||
dialog.request_complete_repaint()
|
||||
else:
|
||||
return result is CONFIRMED
|
||||
return result
|
||||
|
||||
|
||||
async def request_word_count(recovery_type: RecoveryType) -> int:
|
||||
@ -111,14 +111,40 @@ async def show_group_share_success(share_index: int, group_index: int) -> None:
|
||||
)
|
||||
|
||||
|
||||
async def _confirm_abort(dry_run: bool = False) -> None:
|
||||
from . import confirm_action
|
||||
|
||||
if dry_run:
|
||||
await confirm_action(
|
||||
"abort_recovery",
|
||||
TR.recovery__title_cancel_dry_run,
|
||||
TR.recovery__cancel_dry_run,
|
||||
description=TR.recovery__wanna_cancel_dry_run,
|
||||
verb=TR.buttons__cancel,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
else:
|
||||
await confirm_action(
|
||||
"abort_recovery",
|
||||
TR.recovery__title_cancel_recovery,
|
||||
TR.recovery__progress_will_be_lost,
|
||||
TR.recovery__wanna_cancel_recovery,
|
||||
verb=TR.buttons__cancel,
|
||||
reverse=True,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
|
||||
|
||||
async def continue_recovery(
|
||||
button_label: str,
|
||||
text: str,
|
||||
subtext: str | None,
|
||||
info_func: Callable | None,
|
||||
recovery_type: RecoveryType,
|
||||
show_info: bool = False, # unused on TT
|
||||
show_info: bool = False,
|
||||
) -> bool:
|
||||
from trezor.wire import ActionCancelled
|
||||
|
||||
from ..common import button_request
|
||||
|
||||
if show_info:
|
||||
@ -127,23 +153,32 @@ async def continue_recovery(
|
||||
else:
|
||||
description = subtext or ""
|
||||
|
||||
homepage = RustLayout(
|
||||
trezorui2.confirm_recovery(
|
||||
title=text,
|
||||
description=description,
|
||||
button=button_label,
|
||||
recovery_type=recovery_type,
|
||||
info_button=info_func is not None,
|
||||
while True:
|
||||
homepage = RustLayout(
|
||||
trezorui2.confirm_recovery(
|
||||
title=text,
|
||||
description=description,
|
||||
button=button_label,
|
||||
recovery_type=recovery_type,
|
||||
info_button=info_func is not None,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
await button_request("recovery", ButtonRequestType.RecoveryHomepage)
|
||||
await button_request("recovery", ButtonRequestType.RecoveryHomepage)
|
||||
|
||||
if info_func is not None:
|
||||
return await _is_confirmed_info(homepage, info_func)
|
||||
else:
|
||||
result = await homepage
|
||||
return result is CONFIRMED
|
||||
result = (
|
||||
await homepage
|
||||
if info_func is None
|
||||
else await _homepage_with_info(homepage, info_func)
|
||||
)
|
||||
if result is CONFIRMED:
|
||||
return True
|
||||
try:
|
||||
await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
|
||||
except ActionCancelled:
|
||||
pass
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def show_recovery_warning(
|
||||
|
Loading…
Reference in New Issue
Block a user