diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 612c5a869e..e4b40810d1 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -204,6 +204,7 @@ static void _librust_qstrs(void) { MP_QSTR_fingerprint; MP_QSTR_firmware_update__title; MP_QSTR_firmware_update__title_fingerprint; + MP_QSTR_flow_get_address; MP_QSTR_get_language; MP_QSTR_hold; MP_QSTR_hold_danger; @@ -231,7 +232,6 @@ static void _librust_qstrs(void) { MP_QSTR_inputs__return; MP_QSTR_inputs__show; MP_QSTR_inputs__space; - MP_QSTR_instructions__swipe_up; MP_QSTR_is_type_of; MP_QSTR_items; MP_QSTR_joint__title; @@ -531,6 +531,7 @@ static void _librust_qstrs(void) { MP_QSTR_show_share_words; MP_QSTR_show_simple; MP_QSTR_show_success; + MP_QSTR_show_tx_context_menu; MP_QSTR_show_wait_text; MP_QSTR_show_warning; MP_QSTR_sign; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index 70f9baab90..7e774c70f4 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -1251,6 +1251,9 @@ pub enum TranslatedString { cardano__deposit = 851, // "Deposit:" #[cfg(feature = "universal_fw")] 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 { @@ -2497,6 +2500,9 @@ impl TranslatedString { Self::cardano__deposit => "Deposit:", #[cfg(feature = "universal_fw")] 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), #[cfg(feature = "universal_fw")] 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, } } diff --git a/core/embed/rust/src/ui/model_mercury/component/mod.rs b/core/embed/rust/src/ui/model_mercury/component/mod.rs index 68bd86ce98..fd9053a01a 100644 --- a/core/embed/rust/src/ui/model_mercury/component/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/component/mod.rs @@ -21,10 +21,12 @@ mod number_input; #[cfg(feature = "translations")] mod page; mod progress; +mod prompt_screen; mod result; mod scroll; mod share_words; mod simple_page; +mod status_screen; mod swipe; mod welcome_screen; @@ -57,10 +59,12 @@ pub use number_input::{NumberInputDialog, NumberInputDialogMsg}; #[cfg(feature = "translations")] pub use page::ButtonPage; pub use progress::Progress; +pub use prompt_screen::PromptScreen; pub use result::{ResultFooter, ResultScreen, ResultStyle}; pub use scroll::ScrollBar; pub use share_words::ShareWords; pub use simple_page::SimplePage; +pub use status_screen::StatusScreen; pub use swipe::{Swipe, SwipeDirection}; pub use vertical_menu::{VerticalMenu, VerticalMenuChoiceMsg}; pub use welcome_screen::WelcomeScreen; diff --git a/core/embed/rust/src/ui/model_mercury/component/prompt_screen.rs b/core/embed/rust/src/ui/model_mercury/component/prompt_screen.rs new file mode 100644 index 0000000000..73f0370a5f --- /dev/null +++ b/core/embed/rust/src/ui/model_mercury/component/prompt_screen.rs @@ -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 { + 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); + } +} diff --git a/core/embed/rust/src/ui/model_mercury/component/status_screen.rs b/core/embed/rust/src/ui/model_mercury/component/status_screen.rs new file mode 100644 index 0000000000..a4d0554acf --- /dev/null +++ b/core/embed/rust/src/ui/model_mercury/component/status_screen.rs @@ -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 { + 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"); + } +} diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs index 32475935af..227eb8dc35 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs @@ -14,7 +14,7 @@ use crate::{ use heapless::Vec; use super::super::{ - component::{Frame, FrameMsg, VerticalMenu, VerticalMenuChoiceMsg}, + component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg}, theme, }; @@ -22,6 +22,7 @@ use super::super::{ pub enum ConfirmResetDevice { Intro, Menu, + Confirm, } impl FlowState for ConfirmResetDevice { @@ -33,7 +34,12 @@ impl FlowState for ConfirmResetDevice { (ConfirmResetDevice::Menu, SwipeDirection::Right) => { 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, } } @@ -47,13 +53,16 @@ impl FlowState for ConfirmResetDevice { Decision::Goto(ConfirmResetDevice::Intro, SwipeDirection::Right) } (ConfirmResetDevice::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled), + (ConfirmResetDevice::Confirm, FlowMsg::Confirmed) => { + Decision::Return(FlowMsg::Confirmed) + } _ => Decision::Nothing, } } } use crate::{ - micropython::{buffer::StrBuffer, map::Map, obj::Obj, util}, + micropython::{map::Map, obj::Obj, util}, ui::layout::obj::LayoutObj, }; @@ -96,29 +105,19 @@ impl ConfirmResetDevice { let store = flow_store() // Intro, - .add( - Frame::left_aligned(title, SwipePage::vertical(paragraphs)) - .with_info_button() - .with_footer(TR::instructions__swipe_up.into(), None), - |msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info), - )? - // 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), - }, - )?; + .add(content_intro, |msg| { + matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info) + })? + // Context Menu, + .add(content_menu, |msg| match msg { + FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)), + 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)?; Ok(LayoutObj::new(res)?.into()) diff --git a/core/embed/rust/src/ui/model_mercury/flow/create_backup.rs b/core/embed/rust/src/ui/model_mercury/flow/create_backup.rs new file mode 100644 index 0000000000..95504a2ae7 --- /dev/null +++ b/core/embed/rust/src/ui/model_mercury/flow/create_backup.rs @@ -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 { + 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 { + 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 { + 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()) + } +} diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index 7b0eb7bf61..cbdb00622c 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -35,7 +35,7 @@ use crate::{ }, Border, Component, Empty, FormattedText, Label, Never, Qr, Timeout, }, - display::{tjpgd::jpeg_info, Icon}, + display::tjpgd::jpeg_info, geometry, layout::{ obj::{ComponentMsgObj, LayoutObj}, @@ -53,8 +53,8 @@ use super::{ FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputDialog, NumberInputDialogMsg, PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress, - SelectWordCount, SelectWordCountMsg, ShareWords, SimplePage, Slip39Input, VerticalMenu, - VerticalMenuChoiceMsg, + PromptScreen, SelectWordCount, SelectWordCountMsg, ShareWords, SimplePage, Slip39Input, + StatusScreen, VerticalMenu, VerticalMenuChoiceMsg, }, flow, theme, }; @@ -207,6 +207,34 @@ impl ComponentMsgObj for VerticalMenu { } } +impl ComponentMsgObj for StatusScreen { + fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { + match msg { + () => Ok(CONFIRMED.as_obj()), + } + } +} + +impl ComponentMsgObj for PromptScreen { + fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { + match msg { + () => Ok(CONFIRMED.as_obj()), + } + } +} + +impl ComponentMsgObj for SwipeUpScreen +where + T: Component, +{ + fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { + match msg { + SwipeUpScreenMsg::Content(_) => Err(Error::TypeError), + SwipeUpScreenMsg::Swiped => Ok(CONFIRMED.as_obj()), + } + } +} + impl ComponentMsgObj for ButtonPage where 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) } } +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 { let block = move |_args: &[Obj], kwargs: &Map| { let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index f2cfe8838a..f43c313da5 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -355,8 +355,10 @@ class TR: inputs__return: str = "RETURN" inputs__show: str = "SHOW" inputs__space: str = "SPACE" + instructions__hold_to_confirm: str = "Hold to confirm" 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__you_are_contributing: str = "You are contributing:" language__change_to_template: str = "Change language to {0}?" diff --git a/core/translations/en.json b/core/translations/en.json index 05645cc3f4..bcd31e85e2 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -358,6 +358,8 @@ "inputs__show": "SHOW", "inputs__space": "SPACE", "instructions__swipe_up": "Swipe up", + "instructions__tap_to_confirm": "Tap to confirm", + "instructions__hold_to_confirm": "Hold to confirm", "joint__title": "Joint transaction", "joint__to_the_total_amount": "To the total amount:", "joint__you_are_contributing": "You are contributing:", diff --git a/core/translations/order.json b/core/translations/order.json index 97c9086a63..444bcec4f5 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -852,5 +852,7 @@ "850": "cardano__delegating_to_script", "851": "cardano__deposit", "852": "cardano__vote_delegation", - "853": "instructions__swipe_up" + "853": "instructions__swipe_up", + "854": "instructions__tap_to_confirm", + "855": "instructions__hold_to_confirm" } diff --git a/core/translations/signatures.json b/core/translations/signatures.json index 6e303bad2e..566126774e 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "4b19a878ad4d4daf9941ca0a7e024bcb5aa7e456c292821ef9ed5144c72e2531", - "datetime": "2024-05-17T10:01:56.790696", - "commit": "93fca0189d3622a316657465d1b96b642e8665a1" + "merkle_root": "ba39f116679b466d0b1192964d3037d54f5e2486c1ba6e2fc6de522f984f6f0c", + "datetime": "2024-05-17T10:09:38.714090", + "commit": "e797e871c50530f1c1058e588e3ec0ead9f5b13f" }, "history": [ {