1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-23 14:58:09 +00:00

feat(core/ui): T3T1: tutorial flow

[no changelog]
This commit is contained in:
Martin Milata 2024-06-26 11:55:15 +02:00 committed by matejcik
parent b4200162ab
commit 106edd4309
20 changed files with 391 additions and 100 deletions

View File

@ -281,9 +281,9 @@ static void _librust_qstrs(void) {
MP_QSTR_instructions__continue_holding;
MP_QSTR_instructions__continue_in_app;
MP_QSTR_instructions__enter_next_share;
MP_QSTR_instructions__exit_tutorial;
MP_QSTR_instructions__hold_to_confirm;
MP_QSTR_instructions__hold_to_continue;
MP_QSTR_instructions__hold_to_exit_tutorial;
MP_QSTR_instructions__hold_to_finish_tutorial;
MP_QSTR_instructions__hold_to_sign;
MP_QSTR_instructions__learn_more;
@ -663,24 +663,33 @@ static void _librust_qstrs(void) {
MP_QSTR_trezorui2;
MP_QSTR_tutorial;
MP_QSTR_tutorial__continue;
MP_QSTR_tutorial__did_you_know;
MP_QSTR_tutorial__exit;
MP_QSTR_tutorial__first_transaction_finish;
MP_QSTR_tutorial__first_transaction_intro;
MP_QSTR_tutorial__first_wallet;
MP_QSTR_tutorial__get_started;
MP_QSTR_tutorial__lets_begin;
MP_QSTR_tutorial__menu;
MP_QSTR_tutorial__middle_click;
MP_QSTR_tutorial__one_more_step;
MP_QSTR_tutorial__press_and_hold;
MP_QSTR_tutorial__ready_to_use;
MP_QSTR_tutorial__ready_to_use_safe5;
MP_QSTR_tutorial__restart_tutorial;
MP_QSTR_tutorial__scroll_down;
MP_QSTR_tutorial__subtitle_safe5;
MP_QSTR_tutorial__sure_you_want_skip;
MP_QSTR_tutorial__swipe_up_and_down;
MP_QSTR_tutorial__title_easy_navigation;
MP_QSTR_tutorial__title_handy_menu;
MP_QSTR_tutorial__title_hello;
MP_QSTR_tutorial__title_navigation;
MP_QSTR_tutorial__title_hold;
MP_QSTR_tutorial__title_lets_begin;
MP_QSTR_tutorial__title_screen_scroll;
MP_QSTR_tutorial__title_skip;
MP_QSTR_tutorial__title_tutorial_complete;
MP_QSTR_tutorial__title_well_done;
MP_QSTR_tutorial__use_trezor;
MP_QSTR_tutorial__welcome_press_right;
MP_QSTR_tutorial__welcome_safe5;

View File

@ -1289,8 +1289,8 @@ pub enum TranslatedString {
instructions__continue_holding = 888, // "Continue\nholding"
instructions__enter_next_share = 889, // "Enter next share"
instructions__hold_to_continue = 890, // "Hold to continue"
instructions__hold_to_exit_tutorial = 891, // "Hold to exit tutorial"
instructions__hold_to_finish_tutorial = 892, // "Hold to finish tutorial"
instructions__exit_tutorial = 891, // "Exit tutorial"
instructions__hold_to_finish_tutorial = 892, // "\"\""
instructions__learn_more = 893, // "Learn more"
instructions__shares_continue_with_x_template = 894, // "Continue with Share #{0}"
instructions__shares_start_with_1 = 895, // "Start with share #1"
@ -1316,15 +1316,15 @@ pub enum TranslatedString {
send__transaction_signed = 915, // "Transaction signed"
tutorial__continue = 916, // "Continue tutorial"
tutorial__exit = 917, // "Exit tutorial"
tutorial__first_transaction_finish = 918, // "took place on 12 January 2009."
tutorial__first_transaction_intro = 919, // "The world's first bitcoin transaction"
tutorial__menu = 920, // "Menu includes context-specific actions and options."
tutorial__one_more_step = 921, // "One more step..."
tutorial__ready_to_use_safe5 = 922, // "Well done!\nNow you're ready to use your Trezor Safe 5."
tutorial__subtitle_safe5 = 923, // "Trezor Safe 5 tutorial"
tutorial__swipe_up_and_down = 924, // "Swipe up & down to move through screens."
tutorial__title_navigation = 925, // "Navigation"
tutorial__welcome_safe5 = 926, // "Welcome to Trezor Safe 5."
tutorial__first_transaction_finish = 918, // "\"\""
tutorial__first_transaction_intro = 919, // "\"\""
tutorial__menu = 920, // "Find context-specific actions and options in the menu."
tutorial__one_more_step = 921, // "\"\""
tutorial__ready_to_use_safe5 = 922, // "You're all set to start using your device!"
tutorial__subtitle_safe5 = 923, // "\"\""
tutorial__swipe_up_and_down = 924, // "Swipe up & down\nto move through screens."
tutorial__title_easy_navigation = 925, // "Easy navigation"
tutorial__welcome_safe5 = 926, // "Welcome to\nTrezor Safe 5"
words__good_to_know = 927, // "Good to know"
words__operation_cancelled = 928, // "Operation cancelled"
words__settings = 929, // "Settings"
@ -1340,6 +1340,15 @@ pub enum TranslatedString {
homescreen__settings_subtitle = 939, // "Settings"
homescreen__settings_title = 940, // "Homescreen"
reset__the_word_is_repeated = 941, // "The word is repeated"
tutorial__title_lets_begin = 942, // "Let's begin"
tutorial__did_you_know = 943, // "Did you know?"
tutorial__first_wallet = 944, // "The Trezor Model One, created in 2013,\nwas the world's first hardware wallet."
tutorial__restart_tutorial = 945, // "Restart tutorial"
tutorial__title_handy_menu = 946, // "Handy menu"
tutorial__title_hold = 947, // "Hold to confirm important actions"
tutorial__title_well_done = 948, // "Well done!"
tutorial__lets_begin = 949, // "Learn how to use and navigate this device with ease."
tutorial__get_started = 950, // "Get started!"
}
impl TranslatedString {
@ -2623,8 +2632,8 @@ impl TranslatedString {
Self::instructions__continue_holding => "Continue\nholding",
Self::instructions__enter_next_share => "Enter next share",
Self::instructions__hold_to_continue => "Hold to continue",
Self::instructions__hold_to_exit_tutorial => "Hold to exit tutorial",
Self::instructions__hold_to_finish_tutorial => "Hold to finish tutorial",
Self::instructions__exit_tutorial => "Exit tutorial",
Self::instructions__hold_to_finish_tutorial => "\"\"",
Self::instructions__learn_more => "Learn more",
Self::instructions__shares_continue_with_x_template => "Continue with Share #{0}",
Self::instructions__shares_start_with_1 => "Start with share #1",
@ -2650,15 +2659,15 @@ impl TranslatedString {
Self::send__transaction_signed => "Transaction signed",
Self::tutorial__continue => "Continue tutorial",
Self::tutorial__exit => "Exit tutorial",
Self::tutorial__first_transaction_finish => "took place on 12 January 2009.",
Self::tutorial__first_transaction_intro => "The world's first bitcoin transaction",
Self::tutorial__menu => "Menu includes context-specific actions and options.",
Self::tutorial__one_more_step => "One more step...",
Self::tutorial__ready_to_use_safe5 => "Well done!\nNow you're ready to use your Trezor Safe 5.",
Self::tutorial__subtitle_safe5 => "Trezor Safe 5 tutorial",
Self::tutorial__swipe_up_and_down => "Swipe up & down to move through screens.",
Self::tutorial__title_navigation => "Navigation",
Self::tutorial__welcome_safe5 => "Welcome to Trezor Safe 5.",
Self::tutorial__first_transaction_finish => "\"\"",
Self::tutorial__first_transaction_intro => "\"\"",
Self::tutorial__menu => "Find context-specific actions and options in the menu.",
Self::tutorial__one_more_step => "\"\"",
Self::tutorial__ready_to_use_safe5 => "You're all set to start using your device!",
Self::tutorial__subtitle_safe5 => "\"\"",
Self::tutorial__swipe_up_and_down => "Swipe up & down\nto move through screens.",
Self::tutorial__title_easy_navigation => "Easy navigation",
Self::tutorial__welcome_safe5 => "Welcome to\nTrezor Safe 5",
Self::words__good_to_know => "Good to know",
Self::words__operation_cancelled => "Operation cancelled",
Self::words__settings => "Settings",
@ -2674,6 +2683,15 @@ impl TranslatedString {
Self::homescreen__settings_subtitle => "Settings",
Self::homescreen__settings_title => "Homescreen",
Self::reset__the_word_is_repeated => "The word is repeated",
Self::tutorial__title_lets_begin => "Let's begin",
Self::tutorial__did_you_know => "Did you know?",
Self::tutorial__first_wallet => "The Trezor Model One, created in 2013,\nwas the world's first hardware wallet.",
Self::tutorial__restart_tutorial => "Restart tutorial",
Self::tutorial__title_handy_menu => "Handy menu",
Self::tutorial__title_hold => "Hold to confirm important actions",
Self::tutorial__title_well_done => "Well done!",
Self::tutorial__lets_begin => "Learn how to use and navigate this device with ease.",
Self::tutorial__get_started => "Get started!",
}
}
@ -3958,7 +3976,7 @@ impl TranslatedString {
Qstr::MP_QSTR_instructions__continue_holding => Some(Self::instructions__continue_holding),
Qstr::MP_QSTR_instructions__enter_next_share => Some(Self::instructions__enter_next_share),
Qstr::MP_QSTR_instructions__hold_to_continue => Some(Self::instructions__hold_to_continue),
Qstr::MP_QSTR_instructions__hold_to_exit_tutorial => Some(Self::instructions__hold_to_exit_tutorial),
Qstr::MP_QSTR_instructions__exit_tutorial => Some(Self::instructions__exit_tutorial),
Qstr::MP_QSTR_instructions__hold_to_finish_tutorial => Some(Self::instructions__hold_to_finish_tutorial),
Qstr::MP_QSTR_instructions__learn_more => Some(Self::instructions__learn_more),
Qstr::MP_QSTR_instructions__shares_continue_with_x_template => Some(Self::instructions__shares_continue_with_x_template),
@ -3992,7 +4010,7 @@ impl TranslatedString {
Qstr::MP_QSTR_tutorial__ready_to_use_safe5 => Some(Self::tutorial__ready_to_use_safe5),
Qstr::MP_QSTR_tutorial__subtitle_safe5 => Some(Self::tutorial__subtitle_safe5),
Qstr::MP_QSTR_tutorial__swipe_up_and_down => Some(Self::tutorial__swipe_up_and_down),
Qstr::MP_QSTR_tutorial__title_navigation => Some(Self::tutorial__title_navigation),
Qstr::MP_QSTR_tutorial__title_easy_navigation => Some(Self::tutorial__title_easy_navigation),
Qstr::MP_QSTR_tutorial__welcome_safe5 => Some(Self::tutorial__welcome_safe5),
Qstr::MP_QSTR_words__good_to_know => Some(Self::words__good_to_know),
Qstr::MP_QSTR_words__operation_cancelled => Some(Self::words__operation_cancelled),
@ -4009,6 +4027,15 @@ impl TranslatedString {
Qstr::MP_QSTR_homescreen__settings_subtitle => Some(Self::homescreen__settings_subtitle),
Qstr::MP_QSTR_homescreen__settings_title => Some(Self::homescreen__settings_title),
Qstr::MP_QSTR_reset__the_word_is_repeated => Some(Self::reset__the_word_is_repeated),
Qstr::MP_QSTR_tutorial__title_lets_begin => Some(Self::tutorial__title_lets_begin),
Qstr::MP_QSTR_tutorial__did_you_know => Some(Self::tutorial__did_you_know),
Qstr::MP_QSTR_tutorial__first_wallet => Some(Self::tutorial__first_wallet),
Qstr::MP_QSTR_tutorial__restart_tutorial => Some(Self::tutorial__restart_tutorial),
Qstr::MP_QSTR_tutorial__title_handy_menu => Some(Self::tutorial__title_handy_menu),
Qstr::MP_QSTR_tutorial__title_hold => Some(Self::tutorial__title_hold),
Qstr::MP_QSTR_tutorial__title_well_done => Some(Self::tutorial__title_well_done),
Qstr::MP_QSTR_tutorial__lets_begin => Some(Self::tutorial__lets_begin),
Qstr::MP_QSTR_tutorial__get_started => Some(Self::tutorial__get_started),
_ => None,
}
}

View File

@ -175,14 +175,8 @@ pub struct HoldToConfirm {
finalizing: bool,
}
#[derive(Clone)]
enum DismissType {
Tap,
Hold,
}
impl HoldToConfirm {
pub fn new() -> Self {
pub fn new(circle_color: Color, circle_inner_color: Color) -> Self {
let button = Button::new(ButtonContent::Empty)
.styled(theme::button_default())
.with_long_press(Duration::from_millis(2200))
@ -195,9 +189,9 @@ impl HoldToConfirm {
)
.vertically_centered(),
area: Rect::zero(),
circle_color: theme::GREEN,
circle_color,
circle_pad_color: theme::GREY_EXTRA_DARK,
circle_inner_color: theme::GREEN_LIGHT,
circle_inner_color,
button,
anim: HoldToConfirmAnim::default(),
finalizing: false,

View File

@ -17,7 +17,11 @@ pub enum PromptScreen {
impl PromptScreen {
pub fn new_hold_to_confirm() -> Self {
PromptScreen::Hold(HoldToConfirm::new())
PromptScreen::Hold(HoldToConfirm::new(theme::GREEN, theme::GREEN_LIGHT))
}
pub fn new_hold_to_confirm_danger() -> Self {
PromptScreen::Hold(HoldToConfirm::new(theme::ORANGE_LIGHT, theme::ORANGE_LIGHT))
}
pub fn new_tap_to_confirm() -> Self {
@ -26,6 +30,7 @@ impl PromptScreen {
theme::GREEN,
theme::GREY_EXTRA_DARK,
theme::GREEN_LIGHT,
theme::ICON_SIMPLE_CHECKMARK,
))
}
@ -35,6 +40,17 @@ impl PromptScreen {
theme::ORANGE_LIGHT,
theme::GREY_EXTRA_DARK,
theme::ORANGE_DIMMED,
theme::ICON_SIMPLE_CHECKMARK,
))
}
pub fn new_tap_to_start() -> Self {
PromptScreen::Tap(TapToConfirm::new(
theme::GREY,
theme::GREY,
theme::GREY_EXTRA_DARK,
theme::GREY_LIGHT,
theme::ICON_CHEVRON_RIGHT,
))
}
}

View File

@ -2,7 +2,7 @@ use crate::{
time::Duration,
ui::{
component::{Component, Event, EventCtx},
display::Color,
display::{toif::Icon, Color},
geometry::{Alignment2D, Offset, Rect},
lerp::Lerp,
shape,
@ -17,11 +17,11 @@ use crate::{time::Stopwatch, ui::constant::screen};
use pareen;
#[derive(Default, Clone)]
struct TapToConfirmAmin {
struct TapToConfirmAnim {
pub timer: Stopwatch,
}
impl TapToConfirmAmin {
impl TapToConfirmAnim {
const DURATION_MS: u32 = 600;
pub fn is_active(&self) -> bool {
@ -121,13 +121,8 @@ pub struct TapToConfirm {
circle_pad_color: Color,
circle_inner_color: Color,
mask_color: Color,
anim: TapToConfirmAmin,
}
#[derive(Clone)]
enum DismissType {
Tap,
Hold,
icon: Icon,
anim: TapToConfirmAnim,
}
impl TapToConfirm {
@ -136,6 +131,7 @@ impl TapToConfirm {
circle_inner_color: Color,
circle_pad_color: Color,
mask_color: Color,
icon: Icon,
) -> Self {
let button = Button::new(ButtonContent::Empty).styled(theme::button_default());
Self {
@ -145,7 +141,8 @@ impl TapToConfirm {
circle_pad_color,
mask_color,
button,
anim: TapToConfirmAmin::default(),
icon,
anim: TapToConfirmAnim::default(),
}
}
}
@ -235,7 +232,7 @@ impl Component for TapToConfirm {
.with_fg(theme::BLACK)
.render(target);
shape::ToifImage::new(center, theme::ICON_SIMPLE_CHECKMARK.toif)
shape::ToifImage::new(center, self.icon.toif)
.with_fg(theme::GREY)
.with_alpha(255 - self.anim.get_parent_cover_opacity(t))
.with_align(Alignment2D::CENTER)

View File

@ -8,6 +8,7 @@ pub mod get_address;
pub mod prompt_backup;
pub mod request_number;
pub mod show_share_words;
pub mod show_tutorial;
pub mod warning_hi_prio;
pub use confirm_action::{new_confirm_action, new_confirm_action_simple};
@ -22,4 +23,5 @@ pub use get_address::GetAddress;
pub use prompt_backup::PromptBackup;
pub use request_number::RequestNumber;
pub use show_share_words::ShowShareWords;
pub use show_tutorial::ShowTutorial;
pub use warning_hi_prio::WarningHiPrio;

View File

@ -0,0 +1,229 @@
use crate::{
error,
translations::TR,
ui::{
component::{
swipe_detect::SwipeSettings,
text::paragraphs::{Paragraph, Paragraphs},
ComponentExt, SwipeDirection,
},
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow},
layout::obj::LayoutObj,
model_mercury::component::SwipeContent,
},
};
use super::super::{
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
theme,
};
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
pub enum ShowTutorial {
StepWelcome,
StepBegin,
StepNavigation,
StepMenu,
StepHold,
StepDone,
Menu,
DidYouKnow,
HoldToExit,
}
impl FlowState for ShowTutorial {
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
match (self, direction) {
(ShowTutorial::StepBegin, SwipeDirection::Up) => {
Decision::Goto(ShowTutorial::StepNavigation, direction)
}
(ShowTutorial::StepNavigation, SwipeDirection::Up) => {
Decision::Goto(ShowTutorial::StepMenu, direction)
}
(ShowTutorial::StepNavigation, SwipeDirection::Down) => {
Decision::Goto(ShowTutorial::StepBegin, direction)
}
(ShowTutorial::StepMenu, SwipeDirection::Up) => {
Decision::Goto(ShowTutorial::StepHold, direction)
}
(ShowTutorial::StepMenu, SwipeDirection::Down) => {
Decision::Goto(ShowTutorial::StepNavigation, direction)
}
(ShowTutorial::StepMenu, SwipeDirection::Left) => {
Decision::Goto(ShowTutorial::Menu, direction)
}
(ShowTutorial::Menu, SwipeDirection::Left) => {
Decision::Goto(ShowTutorial::DidYouKnow, direction)
}
(ShowTutorial::Menu, SwipeDirection::Right) => {
Decision::Goto(ShowTutorial::StepBegin, direction)
}
(ShowTutorial::DidYouKnow, SwipeDirection::Right) => {
Decision::Goto(ShowTutorial::Menu, direction)
}
(ShowTutorial::StepDone, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
_ => Decision::Nothing,
}
}
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
match (self, msg) {
(ShowTutorial::StepWelcome, FlowMsg::Confirmed) => {
Decision::Goto(ShowTutorial::StepBegin, SwipeDirection::Up)
}
(ShowTutorial::StepMenu, FlowMsg::Info) => {
Decision::Goto(ShowTutorial::Menu, SwipeDirection::Left)
}
(ShowTutorial::Menu, FlowMsg::Choice(0)) => {
Decision::Goto(ShowTutorial::DidYouKnow, SwipeDirection::Left)
}
(ShowTutorial::Menu, FlowMsg::Choice(1)) => {
Decision::Goto(ShowTutorial::StepBegin, SwipeDirection::Right)
}
(ShowTutorial::Menu, FlowMsg::Choice(2)) => {
Decision::Goto(ShowTutorial::HoldToExit, SwipeDirection::Up)
}
(ShowTutorial::Menu, FlowMsg::Cancelled) => {
Decision::Goto(ShowTutorial::StepMenu, SwipeDirection::Right)
}
(ShowTutorial::DidYouKnow, FlowMsg::Cancelled) => {
Decision::Goto(ShowTutorial::Menu, SwipeDirection::Right)
}
(ShowTutorial::StepHold, FlowMsg::Confirmed) => {
Decision::Goto(ShowTutorial::StepDone, SwipeDirection::Up)
}
(ShowTutorial::HoldToExit, FlowMsg::Confirmed) => {
Decision::Goto(ShowTutorial::StepDone, SwipeDirection::Up)
}
_ => Decision::Nothing,
}
}
}
use crate::micropython::{map::Map, obj::Obj, util};
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn new_show_tutorial(_n_args: usize, _args: *const Obj, _kwargs: *mut Map) -> Obj {
unsafe { util::try_or_raise(ShowTutorial::new_obj) }
}
impl ShowTutorial {
fn new_obj() -> Result<Obj, error::Error> {
let content_step_welcome = Frame::left_aligned(
TR::tutorial__welcome_safe5.into(),
SwipeContent::new(PromptScreen::new_tap_to_start()),
)
.with_footer(TR::instructions__tap_to_start.into(), None)
.map(|msg| matches!(msg, FrameMsg::Content(())).then_some(FlowMsg::Confirmed));
let content_step_begin = Frame::left_aligned(
TR::tutorial__title_lets_begin.into(),
SwipeContent::new(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TR::tutorial__lets_begin,
))),
)
.with_footer(
TR::instructions__swipe_up.into(),
Some(TR::tutorial__get_started.into()),
)
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
.map(|_| None);
let content_step_navigation = Frame::left_aligned(
TR::tutorial__title_easy_navigation.into(),
SwipeContent::new(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TR::tutorial__swipe_up_and_down,
))),
)
.with_footer(
TR::instructions__swipe_up.into(),
Some(TR::tutorial__continue.into()),
)
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
.map(|_| None);
let content_step_menu = Frame::left_aligned(
TR::tutorial__title_handy_menu.into(),
SwipeContent::new(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TR::tutorial__menu,
))),
)
.with_menu_button()
.button_styled(theme::button_warning_low())
.with_footer(
TR::instructions__swipe_up.into(),
Some(TR::buttons__continue.into()),
)
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
let content_step_hold = Frame::left_aligned(
TR::tutorial__title_hold.into(),
SwipeContent::new(PromptScreen::new_hold_to_confirm()),
)
.with_footer(TR::instructions__exit_tutorial.into(), None)
.map(|msg| matches!(msg, FrameMsg::Content(())).then_some(FlowMsg::Confirmed));
let content_step_done = Frame::left_aligned(
TR::tutorial__title_well_done.into(),
SwipeContent::new(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TR::tutorial__ready_to_use_safe5,
))),
)
.with_footer(TR::instructions__swipe_up.into(), None)
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
.map(|_| None);
let content_menu = Frame::left_aligned(
"".into(),
VerticalMenu::empty()
.item(theme::ICON_CHEVRON_RIGHT, TR::tutorial__did_you_know.into())
.item(theme::ICON_REBOOT, TR::tutorial__restart_tutorial.into())
.danger(theme::ICON_CANCEL, TR::tutorial__exit.into()),
)
.with_cancel_button()
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
.with_swipe(SwipeDirection::Left, SwipeSettings::immediate())
.map(|msg| match msg {
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
});
let content_did_you_know = Frame::left_aligned(
"".into(),
SwipeContent::new(Paragraphs::new(Paragraph::new(
&theme::TEXT_MAIN_GREY_LIGHT,
TR::tutorial__first_wallet,
))),
)
.with_cancel_button()
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Cancelled));
let content_hold_to_exit = Frame::left_aligned(
TR::tutorial__title_hold.into(),
SwipeContent::new(PromptScreen::new_hold_to_confirm_danger()),
)
.with_footer(TR::instructions__exit_tutorial.into(), None)
.map(|msg| matches!(msg, FrameMsg::Content(())).then_some(FlowMsg::Confirmed));
let store = flow_store()
.add(content_step_welcome)?
.add(content_step_begin)?
.add(content_step_navigation)?
.add(content_step_menu)?
.add(content_step_hold)?
.add(content_step_done)?
.add(content_menu)?
.add(content_did_you_know)?
.add(content_hold_to_exit)?;
let res = SwipeFlow::new(ShowTutorial::StepWelcome, store)?;
Ok(LayoutObj::new(res)?.into())
}
}

View File

@ -1834,6 +1834,10 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Ask whether to update firmware, optionally show fingerprint. Shared with bootloader."""
Qstr::MP_QSTR_confirm_firmware_update => obj_fn_kw!(0, new_confirm_firmware_update).as_obj(),
/// def tutorial() -> LayoutObj[UiResult]:
/// """Show user how to interact with the device."""
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, flow::show_tutorial::new_show_tutorial).as_obj(), // FIXME turn this into obj_fn_0, T2B1 as well
/// def show_wait_text(message: str, /) -> LayoutObj[None]:
/// """Show single-line text in the middle of the screen."""
Qstr::MP_QSTR_show_wait_text => obj_fn_1!(new_show_wait_text).as_obj(),

View File

@ -546,6 +546,11 @@ def confirm_firmware_update(
"""Ask whether to update firmware, optionally show fingerprint. Shared with bootloader."""
# rust/src/ui/model_mercury/layout.rs
def tutorial() -> LayoutObj[UiResult]:
"""Show user how to interact with the device."""
# rust/src/ui/model_mercury/layout.rs
def show_wait_text(message: str, /) -> LayoutObj[None]:
"""Show single-line text in the middle of the screen."""

View File

@ -378,10 +378,9 @@ class TR:
instructions__continue_holding: str = "Continue\nholding"
instructions__continue_in_app: str = "Continue in the app"
instructions__enter_next_share: str = "Enter next share"
instructions__exit_tutorial: str = "Exit tutorial"
instructions__hold_to_confirm: str = "Hold to confirm"
instructions__hold_to_continue: str = "Hold to continue"
instructions__hold_to_exit_tutorial: str = "Hold to exit tutorial"
instructions__hold_to_finish_tutorial: str = "Hold to finish tutorial"
instructions__hold_to_sign: str = "Hold to sign"
instructions__learn_more: str = "Learn more"
instructions__shares_continue_with_x_template: str = "Continue with Share #{0}"
@ -848,27 +847,32 @@ class TR:
tezos__submit_proposal: str = "Submit proposal"
tezos__submit_proposals: str = "Submit proposals"
tutorial__continue: str = "Continue tutorial"
tutorial__did_you_know: str = "Did you know?"
tutorial__exit: str = "Exit tutorial"
tutorial__first_transaction_finish: str = "took place on 12 January 2009."
tutorial__first_transaction_intro: str = "The world's first bitcoin transaction"
tutorial__menu: str = "Menu includes context-specific actions and options."
tutorial__first_wallet: str = "The Trezor Model One, created in 2013,\nwas the world's first hardware wallet."
tutorial__get_started: str = "Get started!"
tutorial__lets_begin: str = "Learn how to use and navigate this device with ease."
tutorial__menu: str = "Find context-specific actions and options in the menu."
tutorial__middle_click: str = "Press both left and right at the same\ntime to confirm."
tutorial__one_more_step: str = "One more step..."
tutorial__press_and_hold: str = "Press and hold the right button to\napprove important operations."
tutorial__ready_to_use: str = "You're ready to\nuse Trezor."
tutorial__ready_to_use_safe5: str = "Well done!\nNow you're ready to use your Trezor Safe 5."
tutorial__ready_to_use_safe5: str = "You're all set to start using your device!"
tutorial__restart_tutorial: str = "Restart tutorial"
tutorial__scroll_down: str = "Press right to scroll down to read all content when text doesn't fit on one screen.\n\rPress left to scroll up."
tutorial__subtitle_safe5: str = "Trezor Safe 5 tutorial"
tutorial__sure_you_want_skip: str = "Are you sure you\nwant to skip the tutorial?"
tutorial__swipe_up_and_down: str = "Swipe up & down to move through screens."
tutorial__swipe_up_and_down: str = "Swipe up & down\nto move through screens."
tutorial__title_easy_navigation: str = "Easy navigation"
tutorial__title_handy_menu: str = "Handy menu"
tutorial__title_hello: str = "Hello"
tutorial__title_navigation: str = "Navigation"
tutorial__title_hold: str = "Hold to confirm important actions"
tutorial__title_lets_begin: str = "Let's begin"
tutorial__title_screen_scroll: str = "Screen scroll"
tutorial__title_skip: str = "Skip tutorial"
tutorial__title_tutorial_complete: str = "Tutorial complete"
tutorial__title_well_done: str = "Well done!"
tutorial__use_trezor: str = "Use Trezor by\nclicking the left and right buttons.\n\rContinue right."
tutorial__welcome_press_right: str = "Welcome to Trezor. Press right to continue."
tutorial__welcome_safe5: str = "Welcome to Trezor Safe 5."
tutorial__welcome_safe5: str = "Welcome to\nTrezor Safe 5"
u2f__get: str = "Increase and retrieve the U2F counter?"
u2f__set_template: str = "Set the U2F counter to {0}?"
u2f__title_get: str = "Get U2F counter"

View File

@ -6,9 +6,6 @@ if TYPE_CHECKING:
async def show_tutorial(msg: ShowDeviceTutorial) -> Success:
from trezor.messages import Success
# NOTE: tutorial is defined only for TR, and this function should
# also be called only in case of TR
from trezor.ui.layouts import tutorial
await tutorial()

View File

@ -55,7 +55,10 @@ def _find_message_handler_module(msg_type: int) -> str:
if msg_type == MessageType.RebootToBootloader:
return "apps.management.reboot_to_bootloader"
if utils.INTERNAL_MODEL in ("T2B1",) and msg_type == MessageType.ShowDeviceTutorial:
if (
utils.INTERNAL_MODEL in ("T2B1", "T3T1")
and msg_type == MessageType.ShowDeviceTutorial
):
return "apps.management.show_tutorial"
if utils.USE_BACKLIGHT and msg_type == MessageType.SetBrightness:

View File

@ -1491,3 +1491,14 @@ async def set_brightness(current: int | None = None) -> None:
"set_brightness",
BR_TYPE_OTHER,
)
def tutorial(br_code: ButtonRequestType = BR_TYPE_OTHER) -> Awaitable[None]:
"""Showing users how to interact with the device."""
return raise_if_not_confirmed(
interact(
RustLayout(trezorui2.tutorial()),
"tutorial",
br_code,
)
)

View File

@ -415,7 +415,6 @@
"instructions__hold_to_confirm": "Držením potvrďte",
"instructions__hold_to_continue": "Držením pokračujte",
"instructions__hold_to_exit_tutorial": "Podržením ukončíte tutoriál",
"instructions__hold_to_finish_tutorial": "Podržením dokončíte tutoriál",
"instructions__hold_to_sign": "Podržením podepíšete",
"instructions__learn_more": "Zjistit více",
"instructions__shares_continue_with_x_template": "Pokračujte částí č. {0}",
@ -883,16 +882,12 @@
"tezos__submit_proposals": "Odeslat návrhy",
"tutorial__continue": "Pokračovat v tutoriálu",
"tutorial__exit": "Ukončit tutoriál",
"tutorial__first_transaction_finish": "se uskutečnila 12. ledna 2009.",
"tutorial__first_transaction_intro": "První bitcoinová transakce na světě",
"tutorial__menu": "Nabídka obsahuje kontextové akce a možnosti.",
"tutorial__middle_click": "Stiskněte současně levé i pravé tlačítko\npro potvrzení.",
"tutorial__one_more_step": "Ještě jeden krok…",
"tutorial__press_and_hold": "Podržením pravého tlačítka\nschválíte důležité operace.",
"tutorial__ready_to_use": "Nyní můžete\npoužívat Trezor.",
"tutorial__ready_to_use_safe5": "Dobrá práce!\nNyní můžete začít používat svůj Trezor Safe 5.",
"tutorial__scroll_down": "Když se text nevejde na jednu obrazovku, pravým tlačítkem posunete text dolů.\n\rLevým tlačítkem se posunete nahoru.",
"tutorial__subtitle_safe5": "Tutoriál pro Trezor Safe 5",
"tutorial__sure_you_want_skip": "Opravdu chcete\npřeskočit tutoriál?",
"tutorial__swipe_up_and_down": "Přejetím prstem nahoru a dolů procházíte mezi obrazovkami.",
"tutorial__title_hello": "Dobrý den",

View File

@ -415,7 +415,6 @@
"instructions__hold_to_confirm": "Zum Bestätigen halten",
"instructions__hold_to_continue": "Zum Fortfahren halten",
"instructions__hold_to_exit_tutorial": "Zum Verlassen des Tutorials halten",
"instructions__hold_to_finish_tutorial": "Zum Abschließen des Tutorials halten",
"instructions__hold_to_sign": "Zum Signieren halten",
"instructions__learn_more": "Mehr erfahren",
"instructions__shares_continue_with_x_template": "Mit Share #{0} fortfahren",
@ -883,16 +882,12 @@
"tezos__submit_proposals": "Vorschläge senden",
"tutorial__continue": "Tutorial fortsetzen",
"tutorial__exit": "Tutorial schließen",
"tutorial__first_transaction_finish": "fand am 12. Januar 2009 statt.",
"tutorial__first_transaction_intro": "Die weltweit erste Bitcoin-Transaktion",
"tutorial__menu": "Das Menü enthält kontextspezifische Aktionen und Optionen.",
"tutorial__middle_click": "Drücke gleichzeitig rechts und links,\num zu bestätigen.",
"tutorial__one_more_step": "Ein Schritt noch ...",
"tutorial__press_and_hold": "Halte die rechte Taste gedrückt,\num wichtige Vorgänge zu genehmigen.",
"tutorial__ready_to_use": "Du kannst jetzt\nTrezor verwenden.",
"tutorial__ready_to_use_safe5": "Gut gemacht!\nJetzt kannst du deinen Trezor Safe 5 verwenden.",
"tutorial__scroll_down": "Drücke rechts, um nach unten zu scrollen und alles zu lesen, wenn der Text nicht auf einen Bildschirm passt.\n\rDrücke links, um nach oben zu scrollen.",
"tutorial__subtitle_safe5": "Tutorial zu Trezor Safe 5",
"tutorial__sure_you_want_skip": "Möchtest du das Tutorial\nwirklich überspringen?",
"tutorial__swipe_up_and_down": "Wische nach oben und unten, um Bildschirme zu wechseln.",
"tutorial__title_hello": "Hallo",

View File

@ -382,8 +382,7 @@
"instructions__enter_next_share": "Enter next share",
"instructions__hold_to_confirm": "Hold to confirm",
"instructions__hold_to_continue": "Hold to continue",
"instructions__hold_to_exit_tutorial": "Hold to exit tutorial",
"instructions__hold_to_finish_tutorial": "Hold to finish tutorial",
"instructions__exit_tutorial": "Exit tutorial",
"instructions__hold_to_sign": "Hold to sign",
"instructions__learn_more": "Learn more",
"instructions__shares_continue_with_x_template": "Continue with Share #{0}",
@ -850,27 +849,32 @@
"tezos__submit_proposal": "Submit proposal",
"tezos__submit_proposals": "Submit proposals",
"tutorial__continue": "Continue tutorial",
"tutorial__did_you_know": "Did you know?",
"tutorial__exit": "Exit tutorial",
"tutorial__first_transaction_finish": "took place on 12 January 2009.",
"tutorial__first_transaction_intro": "The world's first bitcoin transaction",
"tutorial__menu": "Menu includes context-specific actions and options.",
"tutorial__first_wallet": "The Trezor Model One, created in 2013,\nwas the world's first hardware wallet.",
"tutorial__lets_begin": "Learn how to use and navigate this device with ease.",
"tutorial__get_started": "Get started!",
"tutorial__menu": "Find context-specific actions and options in the menu.",
"tutorial__middle_click": "Press both left and right at the same\ntime to confirm.",
"tutorial__one_more_step": "One more step...",
"tutorial__press_and_hold": "Press and hold the right button to\napprove important operations.",
"tutorial__ready_to_use": "You're ready to\nuse Trezor.",
"tutorial__ready_to_use_safe5": "Well done!\nNow you're ready to use your Trezor Safe 5.",
"tutorial__ready_to_use_safe5": "You're all set to start using your device!",
"tutorial__restart_tutorial": "Restart tutorial",
"tutorial__scroll_down": "Press right to scroll down to read all content when text doesn't fit on one screen.\n\rPress left to scroll up.",
"tutorial__subtitle_safe5": "Trezor Safe 5 tutorial",
"tutorial__sure_you_want_skip": "Are you sure you\nwant to skip the tutorial?",
"tutorial__swipe_up_and_down": "Swipe up & down to move through screens.",
"tutorial__swipe_up_and_down": "Swipe up & down\nto move through screens.",
"tutorial__title_handy_menu": "Handy menu",
"tutorial__title_hello": "Hello",
"tutorial__title_navigation": "Navigation",
"tutorial__title_hold": "Hold to confirm important actions",
"tutorial__title_lets_begin": "Let's begin",
"tutorial__title_easy_navigation": "Easy navigation",
"tutorial__title_screen_scroll": "Screen scroll",
"tutorial__title_skip": "Skip tutorial",
"tutorial__title_tutorial_complete": "Tutorial complete",
"tutorial__title_well_done": "Well done!",
"tutorial__use_trezor": "Use Trezor by\nclicking the left and right buttons.\n\rContinue right.",
"tutorial__welcome_press_right": "Welcome to Trezor. Press right to continue.",
"tutorial__welcome_safe5": "Welcome to Trezor Safe 5.",
"tutorial__welcome_safe5": "Welcome to\nTrezor Safe 5",
"u2f__get": "Increase and retrieve the U2F counter?",
"u2f__set_template": "Set the U2F counter to {0}?",
"u2f__title_get": "Get U2F counter",

View File

@ -415,7 +415,6 @@
"instructions__hold_to_confirm": "Pulsa para confirmar",
"instructions__hold_to_continue": "Pulsa para continuar",
"instructions__hold_to_exit_tutorial": "Pulsa para salir del tutorial",
"instructions__hold_to_finish_tutorial": "Pulsa para finalizar el tutorial",
"instructions__hold_to_sign": "Pulsa para firmar",
"instructions__learn_more": "Más información",
"instructions__shares_continue_with_x_template": "Continuar con el recurso n.º {0}",
@ -883,16 +882,12 @@
"tezos__submit_proposals": "Enviar propuestas",
"tutorial__continue": "Continuar con el tutorial",
"tutorial__exit": "Salir del tutorial",
"tutorial__first_transaction_finish": "tuvo lugar el 12 de enero de 2009.",
"tutorial__first_transaction_intro": "La primera transacción en bitcoins del mundo",
"tutorial__menu": "El menú incluye funciones y opciones específicas del contexto.",
"tutorial__middle_click": "Pulsa ambos botones a la vez\npara confirmar.",
"tutorial__one_more_step": "Un paso más...",
"tutorial__press_and_hold": "Mantén pulsado el botón derecho para\naprobar operaciones importantes.",
"tutorial__ready_to_use": "Ya puedes\nusar Trezor.",
"tutorial__ready_to_use_safe5": "¡Conseguido!\nYa puedes usar Trezor Safe 5.",
"tutorial__scroll_down": "Pulsa el botón derecho para ir bajando y leer todo cuando el texto no quepa en una pantalla.\n\rPulsa el botón izquierdo para ir hacia arriba.",
"tutorial__subtitle_safe5": "Tutorial de Trezor Safe 5",
"tutorial__sure_you_want_skip": "¿Seguro que quieres\nomitir el tutorial?",
"tutorial__swipe_up_and_down": "Desliza hacia arriba y hacia abajo para moverte por las pantallas.",
"tutorial__title_hello": "Hola",

View File

@ -415,7 +415,6 @@
"instructions__hold_to_confirm": "Appui pour confirmer",
"instructions__hold_to_continue": "Appui pour continuer",
"instructions__hold_to_exit_tutorial": "Appui pour quitter le tutoriel",
"instructions__hold_to_finish_tutorial": "Appui pour finir le tutoriel",
"instructions__hold_to_sign": "Appui pour signer",
"instructions__learn_more": "En savoir plus",
"instructions__shares_continue_with_x_template": "Continuez avec le fragment #{0}",
@ -883,16 +882,12 @@
"tezos__submit_proposals": "Soumettre des propositions",
"tutorial__continue": "Continuer le tutoriel",
"tutorial__exit": "Quitter le tutoriel",
"tutorial__first_transaction_finish": "a eu lieu le 12 janvier 2009.",
"tutorial__first_transaction_intro": "La première transaction bitcoin au monde",
"tutorial__menu": "Le menu comprend des actions et des options spécifiques au contexte.",
"tutorial__middle_click": "App. simultanément sur les boutons gauche et droit\npour conf.",
"tutorial__one_more_step": "Encore une étape...",
"tutorial__press_and_hold": "Maintenez enfoncé le bouton droit\npour accepter les choix importants.",
"tutorial__ready_to_use": "Vous êtes prêt à\nutiliser Trezor.",
"tutorial__ready_to_use_safe5": "Bravo !\nVous êtes prêt à utiliser votre Trezor Safe 5.",
"tutorial__scroll_down": "Utilisez le bouton droit pour lire le contenu lorsqu'il dépasse.\n\rBouton gauche pour faire défiler l'écran vers le haut.",
"tutorial__subtitle_safe5": "Tutoriel Trezor Safe 5",
"tutorial__sure_you_want_skip": "Voulez-vous vraiment\nignorer le tutoriel ?",
"tutorial__swipe_up_and_down": "Faites glisser vers le haut ou vers le bas pour naviguer entre les écrans.",
"tutorial__title_hello": "Bonjour",

View File

@ -890,7 +890,7 @@
"888": "instructions__continue_holding",
"889": "instructions__enter_next_share",
"890": "instructions__hold_to_continue",
"891": "instructions__hold_to_exit_tutorial",
"891": "instructions__exit_tutorial",
"892": "instructions__hold_to_finish_tutorial",
"893": "instructions__learn_more",
"894": "instructions__shares_continue_with_x_template",
@ -924,7 +924,7 @@
"922": "tutorial__ready_to_use_safe5",
"923": "tutorial__subtitle_safe5",
"924": "tutorial__swipe_up_and_down",
"925": "tutorial__title_navigation",
"925": "tutorial__title_easy_navigation",
"926": "tutorial__welcome_safe5",
"927": "words__good_to_know",
"928": "words__operation_cancelled",
@ -940,5 +940,14 @@
"938": "reset__repeat_for_all_shares",
"939": "homescreen__settings_subtitle",
"940": "homescreen__settings_title",
"941": "reset__the_word_is_repeated"
"941": "reset__the_word_is_repeated",
"942": "tutorial__title_lets_begin",
"943": "tutorial__did_you_know",
"944": "tutorial__first_wallet",
"945": "tutorial__restart_tutorial",
"946": "tutorial__title_handy_menu",
"947": "tutorial__title_hold",
"948": "tutorial__title_well_done",
"949": "tutorial__lets_begin",
"950": "tutorial__get_started"
}

View File

@ -1,8 +1,8 @@
{
"current": {
"merkle_root": "21b44a01ac264efd5336977e2da46f69e223dd8c80071599cbf4836a53f39953",
"datetime": "2024-06-27T14:58:08.073537",
"commit": "3bf052f81a87afd9a2ac94499f095acd3971133b"
"merkle_root": "90d6cff1d798ae305bcc046a548fb6dd1e81dd1ba417c28115bc7bf8fd73bbae",
"datetime": "2024-06-30T13:54:36.644531",
"commit": "2ef7f7074a9436e1a632c90f5b721580e51269bb"
},
"history": [
{