mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 14:16:06 +00:00
feat(eckhart): show danger flow
This commit is contained in:
parent
5e8a75619e
commit
2b06ba21bd
@ -323,6 +323,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_max_feerate;
|
MP_QSTR_max_feerate;
|
||||||
MP_QSTR_max_len;
|
MP_QSTR_max_len;
|
||||||
MP_QSTR_max_rounds;
|
MP_QSTR_max_rounds;
|
||||||
|
MP_QSTR_menu_title;
|
||||||
MP_QSTR_message;
|
MP_QSTR_message;
|
||||||
MP_QSTR_min_count;
|
MP_QSTR_min_count;
|
||||||
MP_QSTR_misc__decrypt_value;
|
MP_QSTR_misc__decrypt_value;
|
||||||
|
@ -736,12 +736,16 @@ extern "C" fn new_show_danger(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
|||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||||
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
|
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
|
||||||
|
let menu_title: Option<TString> = kwargs
|
||||||
|
.get(Qstr::MP_QSTR_menu_title)
|
||||||
|
.unwrap_or_else(|_| Obj::const_none())
|
||||||
|
.try_into_option()?;
|
||||||
let verb_cancel: Option<TString> = kwargs
|
let verb_cancel: Option<TString> = kwargs
|
||||||
.get(Qstr::MP_QSTR_verb_cancel)
|
.get(Qstr::MP_QSTR_verb_cancel)
|
||||||
.unwrap_or_else(|_| Obj::const_none())
|
.unwrap_or_else(|_| Obj::const_none())
|
||||||
.try_into_option()?;
|
.try_into_option()?;
|
||||||
|
|
||||||
let layout = ModelUI::show_danger(title, description, value, verb_cancel)?;
|
let layout = ModelUI::show_danger(title, description, value, menu_title, verb_cancel)?;
|
||||||
Ok(LayoutObj::new_root(layout)?.into())
|
Ok(LayoutObj::new_root(layout)?.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
@ -1518,6 +1522,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
|||||||
/// title: str,
|
/// title: str,
|
||||||
/// description: str,
|
/// description: str,
|
||||||
/// value: str = "",
|
/// value: str = "",
|
||||||
|
/// menu_title: str | None = None,
|
||||||
/// verb_cancel: str | None = None,
|
/// verb_cancel: str | None = None,
|
||||||
/// ) -> LayoutObj[UiResult]:
|
/// ) -> LayoutObj[UiResult]:
|
||||||
/// """Warning modal that makes it easier to cancel than to continue."""
|
/// """Warning modal that makes it easier to cancel than to continue."""
|
||||||
|
@ -815,6 +815,7 @@ impl FirmwareUI for UIBolt {
|
|||||||
_title: TString<'static>,
|
_title: TString<'static>,
|
||||||
_description: TString<'static>,
|
_description: TString<'static>,
|
||||||
_value: TString<'static>,
|
_value: TString<'static>,
|
||||||
|
_menu_title: Option<TString<'static>>,
|
||||||
_verb_cancel: Option<TString<'static>>,
|
_verb_cancel: Option<TString<'static>>,
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"show_danger not supported"))
|
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"show_danger not supported"))
|
||||||
|
@ -986,6 +986,7 @@ impl FirmwareUI for UICaesar {
|
|||||||
_title: TString<'static>,
|
_title: TString<'static>,
|
||||||
_description: TString<'static>,
|
_description: TString<'static>,
|
||||||
_value: TString<'static>,
|
_value: TString<'static>,
|
||||||
|
_menu_title: Option<TString<'static>>,
|
||||||
_verb_cancel: Option<TString<'static>>,
|
_verb_cancel: Option<TString<'static>>,
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"show_danger not supported"))
|
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"show_danger not supported"))
|
||||||
|
@ -829,6 +829,7 @@ impl FirmwareUI for UIDelizia {
|
|||||||
title: TString<'static>,
|
title: TString<'static>,
|
||||||
description: TString<'static>,
|
description: TString<'static>,
|
||||||
value: TString<'static>,
|
value: TString<'static>,
|
||||||
|
_menu_title: Option<TString<'static>>,
|
||||||
verb_cancel: Option<TString<'static>>,
|
verb_cancel: Option<TString<'static>>,
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
let flow = flow::show_danger::new_show_danger(title, description, value, verb_cancel)?;
|
let flow = flow::show_danger::new_show_danger(title, description, value, verb_cancel)?;
|
||||||
|
@ -2,10 +2,12 @@ pub mod confirm_reset;
|
|||||||
pub mod get_address;
|
pub mod get_address;
|
||||||
pub mod prompt_backup;
|
pub mod prompt_backup;
|
||||||
pub mod request_passphrase;
|
pub mod request_passphrase;
|
||||||
|
pub mod show_danger;
|
||||||
pub mod show_share_words;
|
pub mod show_share_words;
|
||||||
|
|
||||||
pub use confirm_reset::new_confirm_reset;
|
pub use confirm_reset::new_confirm_reset;
|
||||||
pub use get_address::GetAddress;
|
pub use get_address::GetAddress;
|
||||||
pub use prompt_backup::PromptBackup;
|
pub use prompt_backup::PromptBackup;
|
||||||
pub use request_passphrase::RequestPassphrase;
|
pub use request_passphrase::RequestPassphrase;
|
||||||
|
pub use show_danger::ShowDanger;
|
||||||
pub use show_share_words::new_show_share_words_flow;
|
pub use show_share_words::new_show_share_words_flow;
|
||||||
|
137
core/embed/rust/src/ui/layout_eckhart/flow/show_danger.rs
Normal file
137
core/embed/rust/src/ui/layout_eckhart/flow/show_danger.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use crate::{
|
||||||
|
error,
|
||||||
|
strutil::TString,
|
||||||
|
translations::TR,
|
||||||
|
ui::{
|
||||||
|
component::{
|
||||||
|
text::paragraphs::{Paragraph, ParagraphSource},
|
||||||
|
ComponentExt,
|
||||||
|
},
|
||||||
|
flow::{
|
||||||
|
base::{Decision, DecisionBuilder as _},
|
||||||
|
FlowController, FlowMsg, SwipeFlow,
|
||||||
|
},
|
||||||
|
geometry::{Alignment, Direction, LinearPlacement, Offset},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
component::Button,
|
||||||
|
firmware::{
|
||||||
|
ActionBar, Header, HeaderMsg, TextScreen, TextScreenMsg, VerticalMenu, VerticalMenuScreen,
|
||||||
|
VerticalMenuScreenMsg,
|
||||||
|
},
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TIMEOUT_MS: u32 = 2000;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ShowDanger {
|
||||||
|
Message,
|
||||||
|
Menu,
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowController for ShowDanger {
|
||||||
|
#[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::Message, FlowMsg::Info) => Self::Menu.goto(),
|
||||||
|
(Self::Message, FlowMsg::Cancelled) => Self::Cancelled.goto(),
|
||||||
|
(Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Confirmed),
|
||||||
|
(Self::Menu, FlowMsg::Choice(_)) => Self::Cancelled.goto(),
|
||||||
|
(Self::Menu, FlowMsg::Cancelled) => Self::Message.goto(),
|
||||||
|
(Self::Cancelled, _) => self.return_msg(FlowMsg::Cancelled),
|
||||||
|
_ => self.do_nothing(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_show_danger(
|
||||||
|
title: TString<'static>,
|
||||||
|
description: TString<'static>,
|
||||||
|
value: TString<'static>,
|
||||||
|
menu_title: Option<TString<'static>>,
|
||||||
|
verb_cancel: Option<TString<'static>>,
|
||||||
|
) -> Result<SwipeFlow, error::Error> {
|
||||||
|
let verb_cancel = verb_cancel.unwrap_or(TR::words__cancel_and_exit.into());
|
||||||
|
|
||||||
|
// Message
|
||||||
|
let paragraphs = [
|
||||||
|
Paragraph::new(&theme::TEXT_REGULAR, description),
|
||||||
|
Paragraph::new(&theme::TEXT_MONO_EXTRA_LIGHT, value),
|
||||||
|
]
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical().with_spacing(35));
|
||||||
|
|
||||||
|
let content_message = TextScreen::new(paragraphs)
|
||||||
|
.with_header(
|
||||||
|
Header::new(title)
|
||||||
|
.with_menu_button()
|
||||||
|
.with_icon(theme::ICON_INFO, theme::ORANGE)
|
||||||
|
.with_text_style(theme::label_title_danger()),
|
||||||
|
)
|
||||||
|
.with_action_bar(ActionBar::new_single(Button::with_text(verb_cancel)))
|
||||||
|
.map(|msg| match msg {
|
||||||
|
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||||
|
TextScreenMsg::Confirmed => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
let content_menu = VerticalMenuScreen::new(
|
||||||
|
VerticalMenu::empty()
|
||||||
|
.with_separators()
|
||||||
|
.item(
|
||||||
|
Button::with_text(verb_cancel)
|
||||||
|
.styled(theme::menu_item_title())
|
||||||
|
.with_text_align(Alignment::Start)
|
||||||
|
.with_content_offset(Offset::x(12)),
|
||||||
|
)
|
||||||
|
.item(
|
||||||
|
Button::with_text(TR::words__continue_anyway.into())
|
||||||
|
.styled(theme::menu_item_title_orange())
|
||||||
|
.with_text_align(Alignment::Start)
|
||||||
|
.with_content_offset(Offset::x(12)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_header(
|
||||||
|
Header::new(menu_title.unwrap_or("".into()))
|
||||||
|
.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,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancelled
|
||||||
|
let content_cancelled = TextScreen::new(
|
||||||
|
Paragraph::new(&theme::TEXT_REGULAR, TR::words__operation_cancelled)
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical()),
|
||||||
|
)
|
||||||
|
.with_header(Header::new(TR::words__title_done.into()).with_icon(theme::ICON_DONE, theme::GREY))
|
||||||
|
.with_action_bar(ActionBar::new_timeout(
|
||||||
|
Button::with_text(TR::instructions__continue_in_app.into()),
|
||||||
|
TIMEOUT_MS,
|
||||||
|
))
|
||||||
|
.map(|_| Some(FlowMsg::Confirmed));
|
||||||
|
|
||||||
|
let mut res = SwipeFlow::new(&ShowDanger::Message)?;
|
||||||
|
res.add_page(&ShowDanger::Message, content_message)?
|
||||||
|
.add_page(&ShowDanger::Menu, content_menu)?
|
||||||
|
.add_page(&ShowDanger::Cancelled, content_cancelled)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
@ -39,10 +39,10 @@ pub const TEXT_BIG: TextStyle = TextStyle::new(
|
|||||||
/// TT Satoshi Regular - 38 (Screen text, Menu item label)
|
/// TT Satoshi Regular - 38 (Screen text, Menu item label)
|
||||||
pub const TEXT_REGULAR: TextStyle = TextStyle::new(
|
pub const TEXT_REGULAR: TextStyle = TextStyle::new(
|
||||||
fonts::FONT_SATOSHI_REGULAR_38,
|
fonts::FONT_SATOSHI_REGULAR_38,
|
||||||
GREY_EXTRA_LIGHT,
|
GREY_LIGHT,
|
||||||
BG,
|
BG,
|
||||||
GREY_EXTRA_LIGHT,
|
GREY_LIGHT,
|
||||||
GREY_EXTRA_LIGHT,
|
GREY_LIGHT,
|
||||||
);
|
);
|
||||||
/// TT Satoshi Medium - 26 (Screen text, Button label, Input value)
|
/// TT Satoshi Medium - 26 (Screen text, Button label, Input value)
|
||||||
pub const TEXT_MEDIUM: TextStyle = TextStyle::new(
|
pub const TEXT_MEDIUM: TextStyle = TextStyle::new(
|
||||||
@ -105,6 +105,14 @@ pub fn get_chunkified_text_style(_character_length: usize) -> &'static TextStyle
|
|||||||
&TEXT_MONO_ADDRESS_CHUNKS
|
&TEXT_MONO_ADDRESS_CHUNKS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const TEXT_MONO_EXTRA_LIGHT: TextStyle = TextStyle::new(
|
||||||
|
fonts::FONT_MONO_LIGHT_30,
|
||||||
|
GREY_EXTRA_LIGHT,
|
||||||
|
BG,
|
||||||
|
GREY_EXTRA_LIGHT,
|
||||||
|
GREY_EXTRA_LIGHT,
|
||||||
|
);
|
||||||
|
|
||||||
// Macro for styles differing only in text color
|
// Macro for styles differing only in text color
|
||||||
macro_rules! label_title {
|
macro_rules! label_title {
|
||||||
($color:expr) => {
|
($color:expr) => {
|
||||||
|
@ -498,12 +498,15 @@ impl FirmwareUI for UIEckhart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show_danger(
|
fn show_danger(
|
||||||
_title: TString<'static>,
|
title: TString<'static>,
|
||||||
_description: TString<'static>,
|
description: TString<'static>,
|
||||||
_value: TString<'static>,
|
value: TString<'static>,
|
||||||
_verb_cancel: Option<TString<'static>>,
|
menu_title: Option<TString<'static>>,
|
||||||
|
verb_cancel: Option<TString<'static>>,
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
let flow =
|
||||||
|
flow::show_danger::new_show_danger(title, description, value, menu_title, verb_cancel)?;
|
||||||
|
Ok(flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_error(
|
fn show_error(
|
||||||
|
@ -278,6 +278,7 @@ pub trait FirmwareUI {
|
|||||||
title: TString<'static>,
|
title: TString<'static>,
|
||||||
description: TString<'static>,
|
description: TString<'static>,
|
||||||
value: TString<'static>,
|
value: TString<'static>,
|
||||||
|
menu_title: Option<TString<'static>>,
|
||||||
verb_cancel: Option<TString<'static>>,
|
verb_cancel: Option<TString<'static>>,
|
||||||
) -> Result<impl LayoutMaybeTrace, Error>;
|
) -> Result<impl LayoutMaybeTrace, Error>;
|
||||||
|
|
||||||
|
@ -491,6 +491,7 @@ def show_danger(
|
|||||||
title: str,
|
title: str,
|
||||||
description: str,
|
description: str,
|
||||||
value: str = "",
|
value: str = "",
|
||||||
|
menu_title: str | None = None,
|
||||||
verb_cancel: str | None = None,
|
verb_cancel: str | None = None,
|
||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
"""Warning modal that makes it easier to cancel than to continue."""
|
"""Warning modal that makes it easier to cancel than to continue."""
|
||||||
|
Loading…
Reference in New Issue
Block a user