mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 20:38:10 +00:00
feat(core/ui): add T3T1 Status/PromptScreen
StatusScreen serves to show a result of operation, typically a checkmark with a circle around dismissed by swipe up gesture. PromptScreen serves to confirm action, typically by holding a button. Designs based on Figma. So far without animation.
This commit is contained in:
parent
1882cc3a80
commit
6b76ec0413
@ -204,6 +204,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_fingerprint;
|
MP_QSTR_fingerprint;
|
||||||
MP_QSTR_firmware_update__title;
|
MP_QSTR_firmware_update__title;
|
||||||
MP_QSTR_firmware_update__title_fingerprint;
|
MP_QSTR_firmware_update__title_fingerprint;
|
||||||
|
MP_QSTR_flow_get_address;
|
||||||
MP_QSTR_get_language;
|
MP_QSTR_get_language;
|
||||||
MP_QSTR_hold;
|
MP_QSTR_hold;
|
||||||
MP_QSTR_hold_danger;
|
MP_QSTR_hold_danger;
|
||||||
@ -231,7 +232,6 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_inputs__return;
|
MP_QSTR_inputs__return;
|
||||||
MP_QSTR_inputs__show;
|
MP_QSTR_inputs__show;
|
||||||
MP_QSTR_inputs__space;
|
MP_QSTR_inputs__space;
|
||||||
MP_QSTR_instructions__swipe_up;
|
|
||||||
MP_QSTR_is_type_of;
|
MP_QSTR_is_type_of;
|
||||||
MP_QSTR_items;
|
MP_QSTR_items;
|
||||||
MP_QSTR_joint__title;
|
MP_QSTR_joint__title;
|
||||||
@ -531,6 +531,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_show_share_words;
|
MP_QSTR_show_share_words;
|
||||||
MP_QSTR_show_simple;
|
MP_QSTR_show_simple;
|
||||||
MP_QSTR_show_success;
|
MP_QSTR_show_success;
|
||||||
|
MP_QSTR_show_tx_context_menu;
|
||||||
MP_QSTR_show_wait_text;
|
MP_QSTR_show_wait_text;
|
||||||
MP_QSTR_show_warning;
|
MP_QSTR_show_warning;
|
||||||
MP_QSTR_sign;
|
MP_QSTR_sign;
|
||||||
|
@ -1251,6 +1251,9 @@ pub enum TranslatedString {
|
|||||||
cardano__deposit = 851, // "Deposit:"
|
cardano__deposit = 851, // "Deposit:"
|
||||||
#[cfg(feature = "universal_fw")]
|
#[cfg(feature = "universal_fw")]
|
||||||
cardano__vote_delegation = 852, // "Vote delegation"
|
cardano__vote_delegation = 852, // "Vote delegation"
|
||||||
|
instructions__swipe_up = 853, // "Swipe up"
|
||||||
|
instructions__tap_to_confirm = 854, // "Tap to confirm"
|
||||||
|
instructions__hold_to_confirm = 855, // "Hold to confirm"
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TranslatedString {
|
impl TranslatedString {
|
||||||
@ -2497,6 +2500,9 @@ impl TranslatedString {
|
|||||||
Self::cardano__deposit => "Deposit:",
|
Self::cardano__deposit => "Deposit:",
|
||||||
#[cfg(feature = "universal_fw")]
|
#[cfg(feature = "universal_fw")]
|
||||||
Self::cardano__vote_delegation => "Vote delegation",
|
Self::cardano__vote_delegation => "Vote delegation",
|
||||||
|
Self::instructions__swipe_up => "Swipe up",
|
||||||
|
Self::instructions__tap_to_confirm => "Tap to confirm",
|
||||||
|
Self::instructions__hold_to_confirm => "Hold to confirm",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3744,6 +3750,9 @@ impl TranslatedString {
|
|||||||
Qstr::MP_QSTR_cardano__deposit => Some(Self::cardano__deposit),
|
Qstr::MP_QSTR_cardano__deposit => Some(Self::cardano__deposit),
|
||||||
#[cfg(feature = "universal_fw")]
|
#[cfg(feature = "universal_fw")]
|
||||||
Qstr::MP_QSTR_cardano__vote_delegation => Some(Self::cardano__vote_delegation),
|
Qstr::MP_QSTR_cardano__vote_delegation => Some(Self::cardano__vote_delegation),
|
||||||
|
Qstr::MP_QSTR_instructions__swipe_up => Some(Self::instructions__swipe_up),
|
||||||
|
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),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,12 @@ mod number_input;
|
|||||||
#[cfg(feature = "translations")]
|
#[cfg(feature = "translations")]
|
||||||
mod page;
|
mod page;
|
||||||
mod progress;
|
mod progress;
|
||||||
|
mod prompt_screen;
|
||||||
mod result;
|
mod result;
|
||||||
mod scroll;
|
mod scroll;
|
||||||
mod share_words;
|
mod share_words;
|
||||||
mod simple_page;
|
mod simple_page;
|
||||||
|
mod status_screen;
|
||||||
mod swipe;
|
mod swipe;
|
||||||
mod welcome_screen;
|
mod welcome_screen;
|
||||||
|
|
||||||
@ -57,10 +59,12 @@ pub use number_input::{NumberInputDialog, NumberInputDialogMsg};
|
|||||||
#[cfg(feature = "translations")]
|
#[cfg(feature = "translations")]
|
||||||
pub use page::ButtonPage;
|
pub use page::ButtonPage;
|
||||||
pub use progress::Progress;
|
pub use progress::Progress;
|
||||||
|
pub use prompt_screen::PromptScreen;
|
||||||
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
||||||
pub use scroll::ScrollBar;
|
pub use scroll::ScrollBar;
|
||||||
pub use share_words::ShareWords;
|
pub use share_words::ShareWords;
|
||||||
pub use simple_page::SimplePage;
|
pub use simple_page::SimplePage;
|
||||||
|
pub use status_screen::StatusScreen;
|
||||||
pub use swipe::{Swipe, SwipeDirection};
|
pub use swipe::{Swipe, SwipeDirection};
|
||||||
pub use vertical_menu::{VerticalMenu, VerticalMenuChoiceMsg};
|
pub use vertical_menu::{VerticalMenu, VerticalMenuChoiceMsg};
|
||||||
pub use welcome_screen::WelcomeScreen;
|
pub use welcome_screen::WelcomeScreen;
|
||||||
|
141
core/embed/rust/src/ui/model_mercury/component/prompt_screen.rs
Normal file
141
core/embed/rust/src/ui/model_mercury/component/prompt_screen.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
use crate::{
|
||||||
|
time::Duration,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx},
|
||||||
|
display::Color,
|
||||||
|
geometry::{Alignment2D, Offset, Rect},
|
||||||
|
shape,
|
||||||
|
shape::Renderer,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{theme, Button, ButtonContent, ButtonMsg};
|
||||||
|
|
||||||
|
/// Component requesting an action from a user. Most typically embedded as a
|
||||||
|
/// content of a Frame and promptin "Tap to confirm" or "Hold to XYZ".
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PromptScreen {
|
||||||
|
area: Rect,
|
||||||
|
button: Button,
|
||||||
|
circle_color: Color,
|
||||||
|
circle_pad_color: Color,
|
||||||
|
circle_inner_color: Color,
|
||||||
|
dismiss_type: DismissType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum DismissType {
|
||||||
|
Tap,
|
||||||
|
Hold,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PromptScreen {
|
||||||
|
pub fn new_hold_to_confirm() -> Self {
|
||||||
|
let icon = theme::ICON_SIGN;
|
||||||
|
let button = Button::new(ButtonContent::Icon(icon))
|
||||||
|
.styled(theme::button_default())
|
||||||
|
.with_long_press(Duration::from_secs(2));
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
circle_color: theme::GREEN,
|
||||||
|
circle_pad_color: theme::GREY_EXTRA_DARK,
|
||||||
|
circle_inner_color: theme::GREEN_LIGHT,
|
||||||
|
dismiss_type: DismissType::Hold,
|
||||||
|
button,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_tap_to_confirm() -> Self {
|
||||||
|
let icon = theme::ICON_SIMPLE_CHECKMARK;
|
||||||
|
let button = Button::new(ButtonContent::Icon(icon)).styled(theme::button_default());
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
circle_color: theme::GREEN,
|
||||||
|
circle_inner_color: theme::GREEN,
|
||||||
|
circle_pad_color: theme::GREY_EXTRA_DARK,
|
||||||
|
dismiss_type: DismissType::Tap,
|
||||||
|
button,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_tap_to_cancel() -> Self {
|
||||||
|
let icon = theme::ICON_SIMPLE_CHECKMARK;
|
||||||
|
let button = Button::new(ButtonContent::Icon(icon)).styled(theme::button_default());
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
circle_color: theme::ORANGE_LIGHT,
|
||||||
|
circle_inner_color: theme::ORANGE_LIGHT,
|
||||||
|
circle_pad_color: theme::GREY_EXTRA_DARK,
|
||||||
|
dismiss_type: DismissType::Tap,
|
||||||
|
button,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for PromptScreen {
|
||||||
|
type Msg = ();
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.area = bounds;
|
||||||
|
self.button.place(Rect::snap(
|
||||||
|
self.area.center(),
|
||||||
|
Offset::uniform(55),
|
||||||
|
Alignment2D::CENTER,
|
||||||
|
));
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
let btn_msg = self.button.event(ctx, event);
|
||||||
|
match (&self.dismiss_type, btn_msg) {
|
||||||
|
(DismissType::Tap, Some(ButtonMsg::Clicked)) => {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
(DismissType::Hold, Some(ButtonMsg::LongPressed)) => {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
shape::Circle::new(self.area.center(), 70)
|
||||||
|
.with_fg(self.circle_pad_color)
|
||||||
|
.with_bg(theme::BLACK)
|
||||||
|
.with_thickness(20)
|
||||||
|
.render(target);
|
||||||
|
shape::Circle::new(self.area.center(), 50)
|
||||||
|
.with_fg(self.circle_color)
|
||||||
|
.with_bg(theme::BLACK)
|
||||||
|
.with_thickness(2)
|
||||||
|
.render(target);
|
||||||
|
shape::Circle::new(self.area.center(), 48)
|
||||||
|
.with_fg(self.circle_pad_color)
|
||||||
|
.with_bg(theme::BLACK)
|
||||||
|
.with_thickness(8)
|
||||||
|
.render(target);
|
||||||
|
matches!(self.dismiss_type, DismissType::Hold).then(|| {
|
||||||
|
shape::Circle::new(self.area.center(), 40)
|
||||||
|
.with_fg(self.circle_inner_color)
|
||||||
|
.with_bg(theme::BLACK)
|
||||||
|
.with_thickness(2)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
self.button.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::ui::flow::Swipable for PromptScreen {}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for PromptScreen {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("StatusScreen");
|
||||||
|
t.child("button", &self.button);
|
||||||
|
}
|
||||||
|
}
|
119
core/embed/rust/src/ui/model_mercury/component/status_screen.rs
Normal file
119
core/embed/rust/src/ui/model_mercury/component/status_screen.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
use crate::ui::{
|
||||||
|
component::{Component, Event, EventCtx, Timeout},
|
||||||
|
display::{Color, Icon},
|
||||||
|
geometry::{Alignment2D, Rect},
|
||||||
|
shape,
|
||||||
|
shape::Renderer,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{theme, Swipe, SwipeDirection};
|
||||||
|
|
||||||
|
/// Component showing status of an operation. Most typically embedded as a
|
||||||
|
/// content of a Frame and showing success (checkmark with a circle around).
|
||||||
|
pub struct StatusScreen {
|
||||||
|
area: Rect,
|
||||||
|
icon: Icon,
|
||||||
|
icon_color: Color,
|
||||||
|
circle_color: Color,
|
||||||
|
dismiss_type: DismissType,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DismissType {
|
||||||
|
SwipeUp(Swipe),
|
||||||
|
Timeout(Timeout),
|
||||||
|
}
|
||||||
|
|
||||||
|
const TIMEOUT_MS: u32 = 2000;
|
||||||
|
|
||||||
|
impl StatusScreen {
|
||||||
|
fn new(icon: Icon, icon_color: Color, circle_color: Color, dismiss_style: DismissType) -> Self {
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
icon,
|
||||||
|
icon_color,
|
||||||
|
circle_color,
|
||||||
|
dismiss_type: dismiss_style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_success() -> Self {
|
||||||
|
Self::new(
|
||||||
|
theme::ICON_SIMPLE_CHECKMARK,
|
||||||
|
theme::GREEN_LIME,
|
||||||
|
theme::GREEN_LIGHT,
|
||||||
|
DismissType::SwipeUp(Swipe::new().up()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_success_timeout() -> Self {
|
||||||
|
Self::new(
|
||||||
|
theme::ICON_SIMPLE_CHECKMARK,
|
||||||
|
theme::GREEN_LIME,
|
||||||
|
theme::GREEN_LIGHT,
|
||||||
|
DismissType::Timeout(Timeout::new(TIMEOUT_MS)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_neutral() -> Self {
|
||||||
|
Self::new(
|
||||||
|
theme::ICON_SIMPLE_CHECKMARK,
|
||||||
|
theme::GREY_EXTRA_LIGHT,
|
||||||
|
theme::GREY_DARK,
|
||||||
|
DismissType::SwipeUp(Swipe::new().up()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for StatusScreen {
|
||||||
|
type Msg = ();
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.area = bounds;
|
||||||
|
if let DismissType::SwipeUp(swipe) = &mut self.dismiss_type {
|
||||||
|
swipe.place(bounds);
|
||||||
|
}
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
match self.dismiss_type {
|
||||||
|
DismissType::SwipeUp(ref mut swipe) => {
|
||||||
|
let swipe_dir = swipe.event(ctx, event);
|
||||||
|
match swipe_dir {
|
||||||
|
Some(SwipeDirection::Up) => return Some(()),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DismissType::Timeout(ref mut timeout) => {
|
||||||
|
if let Some(_) = timeout.event(ctx, event) {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
shape::Circle::new(self.area.center(), 40)
|
||||||
|
.with_fg(self.circle_color)
|
||||||
|
.with_bg(theme::BLACK)
|
||||||
|
.with_thickness(2)
|
||||||
|
.render(target);
|
||||||
|
shape::ToifImage::new(self.area.center(), self.icon.toif)
|
||||||
|
.with_align(Alignment2D::CENTER)
|
||||||
|
.with_fg(self.icon_color)
|
||||||
|
.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for StatusScreen {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("StatusScreen");
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ use crate::{
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use super::super::{
|
use super::super::{
|
||||||
component::{Frame, FrameMsg, VerticalMenu, VerticalMenuChoiceMsg},
|
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ use super::super::{
|
|||||||
pub enum ConfirmResetDevice {
|
pub enum ConfirmResetDevice {
|
||||||
Intro,
|
Intro,
|
||||||
Menu,
|
Menu,
|
||||||
|
Confirm,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowState for ConfirmResetDevice {
|
impl FlowState for ConfirmResetDevice {
|
||||||
@ -33,7 +34,12 @@ impl FlowState for ConfirmResetDevice {
|
|||||||
(ConfirmResetDevice::Menu, SwipeDirection::Right) => {
|
(ConfirmResetDevice::Menu, SwipeDirection::Right) => {
|
||||||
Decision::Goto(ConfirmResetDevice::Intro, direction)
|
Decision::Goto(ConfirmResetDevice::Intro, direction)
|
||||||
}
|
}
|
||||||
(ConfirmResetDevice::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
|
(ConfirmResetDevice::Intro, SwipeDirection::Up) => {
|
||||||
|
Decision::Goto(ConfirmResetDevice::Confirm, direction)
|
||||||
|
}
|
||||||
|
(ConfirmResetDevice::Confirm, SwipeDirection::Down) => {
|
||||||
|
Decision::Goto(ConfirmResetDevice::Intro, direction)
|
||||||
|
}
|
||||||
_ => Decision::Nothing,
|
_ => Decision::Nothing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,13 +53,16 @@ impl FlowState for ConfirmResetDevice {
|
|||||||
Decision::Goto(ConfirmResetDevice::Intro, SwipeDirection::Right)
|
Decision::Goto(ConfirmResetDevice::Intro, SwipeDirection::Right)
|
||||||
}
|
}
|
||||||
(ConfirmResetDevice::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled),
|
(ConfirmResetDevice::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled),
|
||||||
|
(ConfirmResetDevice::Confirm, FlowMsg::Confirmed) => {
|
||||||
|
Decision::Return(FlowMsg::Confirmed)
|
||||||
|
}
|
||||||
_ => Decision::Nothing,
|
_ => Decision::Nothing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
micropython::{buffer::StrBuffer, map::Map, obj::Obj, util},
|
micropython::{map::Map, obj::Obj, util},
|
||||||
ui::layout::obj::LayoutObj,
|
ui::layout::obj::LayoutObj,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,29 +105,19 @@ impl ConfirmResetDevice {
|
|||||||
|
|
||||||
let store = flow_store()
|
let store = flow_store()
|
||||||
// Intro,
|
// Intro,
|
||||||
.add(
|
.add(content_intro, |msg| {
|
||||||
Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info)
|
||||||
.with_info_button()
|
})?
|
||||||
.with_footer(TR::instructions__swipe_up.into(), None),
|
// Context Menu,
|
||||||
|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info),
|
.add(content_menu, |msg| match msg {
|
||||||
)?
|
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||||
// Menu,
|
|
||||||
.add(
|
|
||||||
Frame::left_aligned(
|
|
||||||
"".into(),
|
|
||||||
VerticalMenu::context_menu(unwrap!(Vec::from_slice(&[(
|
|
||||||
"Cancel", // FIXME: use TString
|
|
||||||
theme::ICON_CANCEL
|
|
||||||
)]))),
|
|
||||||
)
|
|
||||||
.with_cancel_button(),
|
|
||||||
|msg| match msg {
|
|
||||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => {
|
|
||||||
Some(FlowMsg::Choice(i))
|
|
||||||
}
|
|
||||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||||
},
|
})?
|
||||||
)?;
|
// Confirm prompt
|
||||||
|
.add(content_confirm, |msg| match msg {
|
||||||
|
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||||
|
_ => Some(FlowMsg::Cancelled),
|
||||||
|
})?;
|
||||||
|
|
||||||
let res = SwipeFlow::new(ConfirmResetDevice::Intro, store)?;
|
let res = SwipeFlow::new(ConfirmResetDevice::Intro, store)?;
|
||||||
Ok(LayoutObj::new(res)?.into())
|
Ok(LayoutObj::new(res)?.into())
|
||||||
|
147
core/embed/rust/src/ui/model_mercury/flow/create_backup.rs
Normal file
147
core/embed/rust/src/ui/model_mercury/flow/create_backup.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use crate::{
|
||||||
|
error,
|
||||||
|
strutil::TString,
|
||||||
|
translations::TR,
|
||||||
|
ui::{
|
||||||
|
component::text::paragraphs::{Paragraph, Paragraphs},
|
||||||
|
flow::{
|
||||||
|
base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeDirection, SwipeFlow,
|
||||||
|
SwipePage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
component::{
|
||||||
|
CancelInfoConfirmMsg, Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg,
|
||||||
|
},
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||||
|
pub enum CreateBackup {
|
||||||
|
Intro,
|
||||||
|
Menu,
|
||||||
|
SkipBackupIntro,
|
||||||
|
SkipBackupConfirm,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowState for CreateBackup {
|
||||||
|
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||||
|
match (self, direction) {
|
||||||
|
(CreateBackup::Intro, SwipeDirection::Left) => {
|
||||||
|
Decision::Goto(CreateBackup::Menu, direction)
|
||||||
|
}
|
||||||
|
(CreateBackup::SkipBackupIntro, SwipeDirection::Up) => {
|
||||||
|
Decision::Goto(CreateBackup::SkipBackupConfirm, direction)
|
||||||
|
}
|
||||||
|
(CreateBackup::SkipBackupConfirm, SwipeDirection::Down) => {
|
||||||
|
Decision::Goto(CreateBackup::SkipBackupIntro, direction)
|
||||||
|
}
|
||||||
|
(CreateBackup::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)
|
||||||
|
}
|
||||||
|
(CreateBackup::Menu, FlowMsg::Choice(0)) => {
|
||||||
|
Decision::Goto(CreateBackup::SkipBackupIntro, SwipeDirection::Left)
|
||||||
|
}
|
||||||
|
(CreateBackup::Menu, FlowMsg::Cancelled) => {
|
||||||
|
Decision::Goto(CreateBackup::Intro, SwipeDirection::Right)
|
||||||
|
}
|
||||||
|
(CreateBackup::SkipBackupIntro, FlowMsg::Cancelled) => {
|
||||||
|
Decision::Goto(CreateBackup::Menu, SwipeDirection::Right)
|
||||||
|
}
|
||||||
|
(CreateBackup::SkipBackupConfirm, FlowMsg::Cancelled) => {
|
||||||
|
Decision::Goto(CreateBackup::SkipBackupIntro, SwipeDirection::Right)
|
||||||
|
}
|
||||||
|
(CreateBackup::SkipBackupConfirm, FlowMsg::Confirmed) => {
|
||||||
|
Decision::Return(FlowMsg::Cancelled)
|
||||||
|
}
|
||||||
|
_ => Decision::Nothing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
micropython::{map::Map, obj::Obj, util},
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateBackup {
|
||||||
|
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."),
|
||||||
|
)];
|
||||||
|
let paragraphs = Paragraphs::new(par_array);
|
||||||
|
let content_intro = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
||||||
|
.with_menu_button()
|
||||||
|
.with_footer(TR::instructions__swipe_up.into(), None);
|
||||||
|
|
||||||
|
let content_menu = Frame::left_aligned(
|
||||||
|
"".into(),
|
||||||
|
VerticalMenu::context_menu(unwrap!(Vec::from_slice(&[(
|
||||||
|
"Skip backup", // FIXME: use TString
|
||||||
|
theme::ICON_CANCEL
|
||||||
|
)]))),
|
||||||
|
)
|
||||||
|
.with_cancel_button();
|
||||||
|
|
||||||
|
let par_array_skip_intro: [Paragraph<'static>; 2] = [
|
||||||
|
Paragraph::new(&theme::TEXT_WARNING, TString::from_str("Not recommended!")),
|
||||||
|
Paragraph::new(
|
||||||
|
&theme::TEXT_MAIN_GREY_LIGHT,
|
||||||
|
TString::from_str("Create a backup to avoid losing access to your funds"),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let paragraphs_skip_intro = Paragraphs::new(par_array_skip_intro);
|
||||||
|
let content_skip_intro = Frame::left_aligned(
|
||||||
|
TR::backup__title_skip.into(),
|
||||||
|
SwipePage::vertical(paragraphs_skip_intro),
|
||||||
|
)
|
||||||
|
.with_cancel_button()
|
||||||
|
.with_footer(
|
||||||
|
TR::instructions__swipe_up.into(),
|
||||||
|
Some(TR::words__continue_anyway.into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let content_skip_confirm = Frame::left_aligned(
|
||||||
|
TR::backup__title_skip.into(),
|
||||||
|
PromptScreen::new_tap_to_cancel(),
|
||||||
|
)
|
||||||
|
.with_footer(TR::instructions__tap_to_confirm.into(), None);
|
||||||
|
|
||||||
|
let store = flow_store()
|
||||||
|
.add(content_intro, |msg| {
|
||||||
|
matches!(msg, FrameMsg::Button(CancelInfoConfirmMsg::Info)).then_some(FlowMsg::Info)
|
||||||
|
})?
|
||||||
|
.add(content_menu, |msg| match msg {
|
||||||
|
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||||
|
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||||
|
FrameMsg::Button(_) => None,
|
||||||
|
})?
|
||||||
|
.add(content_skip_intro, |msg| match msg {
|
||||||
|
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
})?
|
||||||
|
.add(content_skip_confirm, |msg| match msg {
|
||||||
|
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||||
|
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
})?;
|
||||||
|
let res = SwipeFlow::new(CreateBackup::Intro, store)?;
|
||||||
|
Ok(LayoutObj::new(res)?.into())
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
Border, Component, Empty, FormattedText, Label, Never, Qr, Timeout,
|
Border, Component, Empty, FormattedText, Label, Never, Qr, Timeout,
|
||||||
},
|
},
|
||||||
display::{tjpgd::jpeg_info, Icon},
|
display::tjpgd::jpeg_info,
|
||||||
geometry,
|
geometry,
|
||||||
layout::{
|
layout::{
|
||||||
obj::{ComponentMsgObj, LayoutObj},
|
obj::{ComponentMsgObj, LayoutObj},
|
||||||
@ -53,8 +53,8 @@ use super::{
|
|||||||
FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput,
|
FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput,
|
||||||
MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputDialog, NumberInputDialogMsg,
|
MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputDialog, NumberInputDialogMsg,
|
||||||
PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress,
|
PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress,
|
||||||
SelectWordCount, SelectWordCountMsg, ShareWords, SimplePage, Slip39Input, VerticalMenu,
|
PromptScreen, SelectWordCount, SelectWordCountMsg, ShareWords, SimplePage, Slip39Input,
|
||||||
VerticalMenuChoiceMsg,
|
StatusScreen, VerticalMenu, VerticalMenuChoiceMsg,
|
||||||
},
|
},
|
||||||
flow, theme,
|
flow, theme,
|
||||||
};
|
};
|
||||||
@ -207,6 +207,34 @@ impl ComponentMsgObj for VerticalMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentMsgObj for StatusScreen {
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
() => Ok(CONFIRMED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentMsgObj for PromptScreen {
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
() => Ok(CONFIRMED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ComponentMsgObj for SwipeUpScreen<T>
|
||||||
|
where
|
||||||
|
T: Component,
|
||||||
|
{
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
SwipeUpScreenMsg::Content(_) => Err(Error::TypeError),
|
||||||
|
SwipeUpScreenMsg::Swiped => Ok(CONFIRMED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ComponentMsgObj for ButtonPage<T>
|
impl<T> ComponentMsgObj for ButtonPage<T>
|
||||||
where
|
where
|
||||||
T: Component + Paginate,
|
T: Component + Paginate,
|
||||||
@ -1297,6 +1325,22 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
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();
|
||||||
|
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 {
|
extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
@ -355,8 +355,10 @@ class TR:
|
|||||||
inputs__return: str = "RETURN"
|
inputs__return: str = "RETURN"
|
||||||
inputs__show: str = "SHOW"
|
inputs__show: str = "SHOW"
|
||||||
inputs__space: str = "SPACE"
|
inputs__space: str = "SPACE"
|
||||||
|
instructions__hold_to_confirm: str = "Hold to confirm"
|
||||||
instructions__swipe_up: str = "Swipe up"
|
instructions__swipe_up: str = "Swipe up"
|
||||||
joint__title: str = "JOINT TRANSACTION"
|
instructions__tap_to_confirm: str = "Tap to confirm"
|
||||||
|
joint__title: str = "Joint transaction"
|
||||||
joint__to_the_total_amount: str = "To the total amount:"
|
joint__to_the_total_amount: str = "To the total amount:"
|
||||||
joint__you_are_contributing: str = "You are contributing:"
|
joint__you_are_contributing: str = "You are contributing:"
|
||||||
language__change_to_template: str = "Change language to {0}?"
|
language__change_to_template: str = "Change language to {0}?"
|
||||||
|
@ -358,6 +358,8 @@
|
|||||||
"inputs__show": "SHOW",
|
"inputs__show": "SHOW",
|
||||||
"inputs__space": "SPACE",
|
"inputs__space": "SPACE",
|
||||||
"instructions__swipe_up": "Swipe up",
|
"instructions__swipe_up": "Swipe up",
|
||||||
|
"instructions__tap_to_confirm": "Tap to confirm",
|
||||||
|
"instructions__hold_to_confirm": "Hold to confirm",
|
||||||
"joint__title": "Joint transaction",
|
"joint__title": "Joint transaction",
|
||||||
"joint__to_the_total_amount": "To the total amount:",
|
"joint__to_the_total_amount": "To the total amount:",
|
||||||
"joint__you_are_contributing": "You are contributing:",
|
"joint__you_are_contributing": "You are contributing:",
|
||||||
|
@ -852,5 +852,7 @@
|
|||||||
"850": "cardano__delegating_to_script",
|
"850": "cardano__delegating_to_script",
|
||||||
"851": "cardano__deposit",
|
"851": "cardano__deposit",
|
||||||
"852": "cardano__vote_delegation",
|
"852": "cardano__vote_delegation",
|
||||||
"853": "instructions__swipe_up"
|
"853": "instructions__swipe_up",
|
||||||
|
"854": "instructions__tap_to_confirm",
|
||||||
|
"855": "instructions__hold_to_confirm"
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"merkle_root": "4b19a878ad4d4daf9941ca0a7e024bcb5aa7e456c292821ef9ed5144c72e2531",
|
"merkle_root": "ba39f116679b466d0b1192964d3037d54f5e2486c1ba6e2fc6de522f984f6f0c",
|
||||||
"datetime": "2024-05-17T10:01:56.790696",
|
"datetime": "2024-05-17T10:09:38.714090",
|
||||||
"commit": "93fca0189d3622a316657465d1b96b642e8665a1"
|
"commit": "e797e871c50530f1c1058e588e3ec0ead9f5b13f"
|
||||||
},
|
},
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user