diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index d04460c4a2..1950ac0713 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -38,8 +38,6 @@ static void _librust_qstrs(void) { MP_QSTR___name__; MP_QSTR_account; MP_QSTR_account_items; - MP_QSTR_account_items_title; - MP_QSTR_account_label; MP_QSTR_account_path; MP_QSTR_accounts; MP_QSTR_action; @@ -69,12 +67,11 @@ static void _librust_qstrs(void) { MP_QSTR_address_qr; MP_QSTR_address_title; MP_QSTR_allow_cancel; - MP_QSTR_altcoin_tx_summary; MP_QSTR_amount; MP_QSTR_amount_change; + MP_QSTR_amount_label; MP_QSTR_amount_new; MP_QSTR_amount_title; - MP_QSTR_amount_value; MP_QSTR_app_name; MP_QSTR_area_bytesize; MP_QSTR_attach_timer_fn; @@ -175,8 +172,6 @@ static void _librust_qstrs(void) { MP_QSTR_buttons__view_all_data; MP_QSTR_can_go_back; MP_QSTR_cancel; - MP_QSTR_cancel_arrow; - MP_QSTR_cancel_cross; MP_QSTR_cancel_text; MP_QSTR_case_sensitive; MP_QSTR_check_homescreen_format; @@ -209,7 +204,7 @@ static void _librust_qstrs(void) { MP_QSTR_confirm_properties; MP_QSTR_confirm_recovery; MP_QSTR_confirm_reset_device; - MP_QSTR_confirm_total; + MP_QSTR_confirm_summary; MP_QSTR_confirm_total__fee_rate; MP_QSTR_confirm_total__fee_rate_colon; MP_QSTR_confirm_total__sending_from_account; @@ -242,12 +237,11 @@ static void _librust_qstrs(void) { MP_QSTR_experimental_mode__only_for_dev; MP_QSTR_experimental_mode__title; MP_QSTR_extra; - MP_QSTR_fee_amount; + MP_QSTR_extra_items; + MP_QSTR_fee; MP_QSTR_fee_items; MP_QSTR_fee_label; MP_QSTR_fee_rate_amount; - MP_QSTR_fee_title; - MP_QSTR_fee_value; MP_QSTR_fingerprint; MP_QSTR_firmware_update__title; MP_QSTR_firmware_update__title_fingerprint; @@ -255,7 +249,6 @@ static void _librust_qstrs(void) { MP_QSTR_flow_confirm_output; MP_QSTR_flow_confirm_reset; MP_QSTR_flow_confirm_set_new_pin; - MP_QSTR_flow_confirm_summary; MP_QSTR_flow_continue_recovery; MP_QSTR_flow_get_address; MP_QSTR_flow_prompt_backup; @@ -317,7 +310,6 @@ static void _librust_qstrs(void) { MP_QSTR_instructions__view_all_data; MP_QSTR_is_type_of; MP_QSTR_items; - MP_QSTR_items_title; MP_QSTR_joint__title; MP_QSTR_joint__to_the_total_amount; MP_QSTR_joint__you_are_contributing; @@ -697,7 +689,6 @@ static void _librust_qstrs(void) { MP_QSTR_title; MP_QSTR_total_amount; MP_QSTR_total_fee_new; - MP_QSTR_total_label; MP_QSTR_total_len; MP_QSTR_touch_event; MP_QSTR_trace; 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 113ab70d77..bfce219bf1 100644 --- a/core/embed/rust/src/ui/model_mercury/component/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/component/mod.rs @@ -85,7 +85,7 @@ pub use swipe_up_screen::{SwipeUpScreen, SwipeUpScreenMsg}; #[cfg(feature = "translations")] pub use tap_to_confirm::TapToConfirm; pub use updatable_more_info::UpdatableMoreInfo; -pub use vertical_menu::{PagedVerticalMenu, VerticalMenu, VerticalMenuChoiceMsg}; +pub use vertical_menu::{PagedVerticalMenu, VerticalMenu, VerticalMenuChoiceMsg, MENU_MAX_ITEMS}; pub use welcome_screen::WelcomeScreen; use super::{constant, theme}; diff --git a/core/embed/rust/src/ui/model_mercury/component/vertical_menu.rs b/core/embed/rust/src/ui/model_mercury/component/vertical_menu.rs index a4a9db7ebe..6425b8a74c 100644 --- a/core/embed/rust/src/ui/model_mercury/component/vertical_menu.rs +++ b/core/embed/rust/src/ui/model_mercury/component/vertical_menu.rs @@ -25,7 +25,7 @@ pub enum VerticalMenuChoiceMsg { /// Number of buttons. /// Presently, VerticalMenu holds only fixed number of buttons. -const MAX_ITEMS: usize = 3; +pub const MENU_MAX_ITEMS: usize = 3; /// Fixed height of each menu button. const MENU_BUTTON_HEIGHT: i16 = 64; @@ -33,7 +33,7 @@ const MENU_BUTTON_HEIGHT: i16 = 64; /// Fixed height of a separator. const MENU_SEP_HEIGHT: i16 = 2; -type VerticalMenuButtons = Vec; +type VerticalMenuButtons = Vec; #[derive(Default, Clone)] struct AttachAnimation { @@ -180,7 +180,7 @@ impl VerticalMenu { fn new(buttons: VerticalMenuButtons) -> Self { Self { buttons, - n_items: MAX_ITEMS, + n_items: MENU_MAX_ITEMS, attach_animation: AttachAnimation::default(), } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs index f7adb903cd..3095f3e91f 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs @@ -1,15 +1,15 @@ use heapless::Vec; use crate::{ - error, + error::{self}, + maybe_trace::MaybeTrace, strutil::TString, translations::TR, ui::{ - button_request::ButtonRequest, - component::{swipe_detect::SwipeSettings, ButtonRequestExt, ComponentExt}, + component::{swipe_detect::SwipeSettings, Component, ComponentExt}, flow::{ base::{Decision, DecisionBuilder as _}, - FlowController, FlowMsg, SwipeFlow, + FlowController, FlowMsg, Swipable, SwipeFlow, }, geometry::Direction, }, @@ -19,7 +19,7 @@ use super::{ super::{ component::{ Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent, VerticalMenu, - VerticalMenuChoiceMsg, + VerticalMenuChoiceMsg, MENU_MAX_ITEMS, }, theme, }, @@ -29,6 +29,7 @@ use super::{ const MENU_ITEM_CANCEL: usize = 0; const MENU_ITEM_FEE_INFO: usize = 1; const MENU_ITEM_ACCOUNT_INFO: usize = 2; +const MENU_ITEM_EXTRA_INFO: usize = 3; #[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmSummary { @@ -37,6 +38,7 @@ pub enum ConfirmSummary { Menu, FeeInfo, AccountInfo, + ExtraInfo, CancelTap, } @@ -52,9 +54,10 @@ impl FlowController for ConfirmSummary { (Self::Summary, Direction::Up) => Self::Hold.swipe(direction), (Self::Hold, Direction::Down) => Self::Summary.swipe(direction), (Self::Menu, Direction::Right) => Self::Summary.swipe(direction), - (Self::AccountInfo | Self::FeeInfo | Self::CancelTap, Direction::Right) => { - Self::Menu.swipe(direction) - } + ( + Self::AccountInfo | Self::FeeInfo | Self::ExtraInfo | Self::CancelTap, + Direction::Right, + ) => Self::Menu.swipe(direction), _ => self.do_nothing(), } } @@ -66,6 +69,7 @@ impl FlowController for ConfirmSummary { (Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::CancelTap.swipe_left(), (Self::Menu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => Self::FeeInfo.swipe_left(), (Self::Menu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => Self::AccountInfo.swipe_left(), + (Self::Menu, FlowMsg::Choice(MENU_ITEM_EXTRA_INFO)) => Self::ExtraInfo.swipe_left(), (Self::Menu, FlowMsg::Cancelled) => Self::Summary.swipe_right(), (Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), (_, FlowMsg::Cancelled) => Self::Menu.goto(), @@ -74,18 +78,20 @@ impl FlowController for ConfirmSummary { } } +fn dummy_page() -> impl Component + Swipable + MaybeTrace { + Frame::left_aligned(TString::empty(), VerticalMenu::empty()).map(|_| Some(FlowMsg::Cancelled)) +} + pub fn new_confirm_summary( summary_params: ShowInfoParams, - account_params: ShowInfoParams, - fee_params: ShowInfoParams, - br_name: TString<'static>, - br_code: u16, - cancel_text: Option>, + fee_params: Option, + account_params: Option, + extra_params: Option, + verb_cancel: Option>, ) -> Result { // Summary let content_summary = summary_params .into_layout()? - .one_button_request(ButtonRequest::from_num(br_code, br_name)) // Summary(1) + Hold(1) .with_pages(|summary_pages| summary_pages + 1); @@ -105,33 +111,44 @@ pub fn new_confirm_summary( }); // FeeInfo - let has_fee_info = !fee_params.is_empty(); - let content_fee = fee_params.into_layout()?; - + let content_fee = fee_params.map(|params| params.into_layout()).transpose()?; // AccountInfo - let has_account_info = !account_params.is_empty(); - let content_account = account_params.into_layout()?; + let content_account = account_params + .map(|params| params.into_layout()) + .transpose()?; + // ExtraInfo + let content_extra = extra_params + .map(|params| params.into_layout()) + .transpose()?; - // Menu + // Menu with provided info and cancel let mut menu = VerticalMenu::empty(); let mut menu_items = Vec::::new(); - if has_fee_info { + if content_fee.is_some() { menu = menu.item( theme::ICON_CHEVRON_RIGHT, TR::confirm_total__title_fee.into(), ); unwrap!(menu_items.push(MENU_ITEM_FEE_INFO)); } - if has_account_info { + if content_account.is_some() { menu = menu.item( theme::ICON_CHEVRON_RIGHT, TR::address_details__account_info.into(), ); unwrap!(menu_items.push(MENU_ITEM_ACCOUNT_INFO)); } + if content_extra.is_some() && menu_items.len() < MENU_MAX_ITEMS - 1 { + // NOTE: extra is shown only if VerticalMenu has space considering mandatory "Cancel" + menu = menu.item( + theme::ICON_CHEVRON_RIGHT, + TR::words__title_information.into(), + ); + unwrap!(menu_items.push(MENU_ITEM_EXTRA_INFO)); + } menu = menu.danger( theme::ICON_CANCEL, - cancel_text.unwrap_or(TR::send__cancel_sign.into()), + verb_cancel.unwrap_or(TR::send__cancel_sign.into()), ); unwrap!(menu_items.push(MENU_ITEM_CANCEL)); let content_menu = Frame::left_aligned(TString::empty(), menu) @@ -159,13 +176,26 @@ pub fn new_confirm_summary( _ => None, }); - let res = SwipeFlow::new(&ConfirmSummary::Summary)? + let mut res = SwipeFlow::new(&ConfirmSummary::Summary)? .with_page(&ConfirmSummary::Summary, content_summary)? .with_page(&ConfirmSummary::Hold, content_hold)? - .with_page(&ConfirmSummary::Menu, content_menu)? - .with_page(&ConfirmSummary::FeeInfo, content_fee)? - .with_page(&ConfirmSummary::AccountInfo, content_account)? - .with_page(&ConfirmSummary::CancelTap, content_cancel_tap)?; + .with_page(&ConfirmSummary::Menu, content_menu)?; + if let Some(content_fee) = content_fee { + res = res.with_page(&ConfirmSummary::FeeInfo, content_fee)?; + } else { + res = res.with_page(&ConfirmSummary::FeeInfo, dummy_page())?; + }; + if let Some(content_account) = content_account { + res = res.with_page(&ConfirmSummary::AccountInfo, content_account)? + } else { + res = res.with_page(&ConfirmSummary::AccountInfo, dummy_page())? + }; + if let Some(content_extra) = content_extra { + res = res.with_page(&ConfirmSummary::ExtraInfo, content_extra)? + } else { + res = res.with_page(&ConfirmSummary::ExtraInfo, dummy_page())? + }; + res = res.with_page(&ConfirmSummary::CancelTap, content_cancel_tap)?; Ok(res) } diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index af8f06e736..d577957656 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -624,50 +624,79 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - let account_items: Obj = kwargs.get(Qstr::MP_QSTR_account_items)?; - let account_items_title: Option = kwargs - .get(Qstr::MP_QSTR_account_items_title) - .unwrap_or(Obj::const_none()) + let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?; + let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?; + let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?; + let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?; + let title: Option = kwargs + .get(Qstr::MP_QSTR_title) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let fee_items: Option = kwargs + .get(Qstr::MP_QSTR_fee_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let account_items: Option = kwargs + .get(Qstr::MP_QSTR_account_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let extra_items: Option = kwargs + .get(Qstr::MP_QSTR_extra_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let verb_cancel: Option = kwargs + .get(Qstr::MP_QSTR_verb_cancel) + .unwrap_or_else(|_| Obj::const_none()) .try_into_option()?; - let fee_items: Obj = kwargs.get(Qstr::MP_QSTR_fee_items)?; - let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?; - let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?; - let cancel_text: Option = - kwargs.get(Qstr::MP_QSTR_cancel_text)?.try_into_option()?; - let mut summary_params = ShowInfoParams::new(title) + let mut summary_params = ShowInfoParams::new(title.unwrap_or(TString::empty())) .with_menu_button() .with_footer(TR::instructions__swipe_up.into(), None) .with_swipe_up(); - for pair in IterBuf::new().try_iterate(items)? { - let [label, value]: [TString; 2] = util::iter_into_array(pair)?; - summary_params = unwrap!(summary_params.add(label, value)); - } + summary_params = unwrap!(summary_params.add(amount_label, amount)); + summary_params = unwrap!(summary_params.add(fee_label, fee)); - let mut account_params = - ShowInfoParams::new(account_items_title.unwrap_or(TR::send__send_from.into())) - .with_cancel_button(); - for pair in IterBuf::new().try_iterate(account_items)? { - let [label, value]: [TString; 2] = util::iter_into_array(pair)?; - account_params = unwrap!(account_params.add(label, value)); - } - - let mut fee_params = - ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button(); - for pair in IterBuf::new().try_iterate(fee_items)? { - let [label, value]: [TString; 2] = util::iter_into_array(pair)?; - fee_params = unwrap!(fee_params.add(label, value)); - } + // collect available info + let fee_params = if let Some(items) = fee_items { + let mut fee_params = + ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button(); + for pair in IterBuf::new().try_iterate(items)? { + let [label, value]: [TString; 2] = util::iter_into_array(pair)?; + fee_params = unwrap!(fee_params.add(label, value)); + } + Some(fee_params) + } else { + None + }; + let account_params = if let Some(items) = account_items { + let mut account_params = + ShowInfoParams::new(TR::send__send_from.into()).with_cancel_button(); + for pair in IterBuf::new().try_iterate(items)? { + let [label, value]: [TString; 2] = util::iter_into_array(pair)?; + account_params = unwrap!(account_params.add(label, value)); + } + Some(account_params) + } else { + None + }; + let extra_params = if let Some(items) = extra_items { + let mut extra_params = + ShowInfoParams::new(TR::words__title_information.into()).with_cancel_button(); + for pair in IterBuf::new().try_iterate(items)? { + let [label, value]: [TString; 2] = util::iter_into_array(pair)?; + extra_params = unwrap!(extra_params.add(label, value)); + } + Some(extra_params) + } else { + None + }; let flow = flow::new_confirm_summary( summary_params, - account_params, fee_params, - br_name, - br_code, - cancel_text, + account_params, + extra_params, + verb_cancel, )?; Ok(LayoutObj::new_root(flow)?.into()) }; @@ -761,34 +790,6 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - - let mut paragraphs = ParagraphVecShort::new(); - - for pair in IterBuf::new().try_iterate(items)? { - let [label, value]: [TString; 2] = util::iter_into_array(pair)?; - paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break()); - paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value)); - } - - new_confirm_action_simple( - paragraphs.into_paragraphs(), - ConfirmActionExtra::Menu(ConfirmActionMenuStrings::new()), - ConfirmActionStrings::new(title, None, None, Some(title)), - true, - None, - 0, - false, - ) - .and_then(LayoutObj::new_root) - .map(Into::into) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?; @@ -1649,16 +1650,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm value. Merge of confirm_total and confirm_output.""" Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(), - /// def confirm_total( - /// *, - /// title: str, - /// items: Iterable[tuple[str, str]], - /// info_button: bool = False, - /// cancel_arrow: bool = False, - /// ) -> LayoutObj[UiResult]: - /// """Transaction summary. Always hold to confirm.""" - Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(), - /// def confirm_modify_output( /// *, /// sign: int, @@ -2004,19 +1995,20 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm the recipient, (optionally) confirm the amount and (optionally) confirm the summary and present a Hold to Sign page.""" Qstr::MP_QSTR_flow_confirm_output => obj_fn_kw!(0, new_confirm_output).as_obj(), - /// def flow_confirm_summary( + /// def confirm_summary( /// *, - /// title: str, - /// items: Iterable[tuple[str, str]], - /// account_items: Iterable[tuple[str, str]], - /// account_items_title: str | None, - /// fee_items: Iterable[tuple[str, str]], - /// br_code: ButtonRequestType, - /// br_name: str, - /// cancel_text: str | None = None, + /// amount: str, + /// amount_label: str, + /// fee: str, + /// fee_label: str, + /// title: str | None = None, + /// fee_items: Iterable[tuple[str, str]] | None = None, + /// account_items: Iterable[tuple[str, str]] | None = None, + /// extra_items: Iterable[tuple[str, str]] | None = None, + /// verb_cancel: str | None = None, /// ) -> LayoutObj[UiResult]: - /// """Total summary and hold to confirm.""" - Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), + /// """Confirm summary of a transaction.""" + Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), /// class BacklightLevels: /// """Backlight levels. Values dynamically update based on user settings.""" diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index 1f4aaacc15..8218ea73cc 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -915,6 +915,15 @@ impl ButtonActions { ) } + /// Cancelling with left and confirming with middle + pub fn cancel_confirm_none() -> Self { + Self::new( + Some(ButtonAction::Cancel), + Some(ButtonAction::Confirm), + None, + ) + } + /// Cancelling with left, confirming with middle and info with right pub fn cancel_confirm_info() -> Self { Self::new( diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index a75b683a2d..bc61353d37 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -45,6 +45,7 @@ use crate::{ }, ComponentExt, FormattedText, Label, LineBreaking, Never, Timeout, }, + display::Font, geometry, layout::{ base::LAYOUT_STATE, @@ -638,131 +639,110 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs: unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { +extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = |_args: &[Obj], kwargs: &Map| { - let total_amount: TString = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?; - let fee_amount: TString = kwargs.get(Qstr::MP_QSTR_fee_amount)?.try_into()?; - let fee_rate_amount: Option = kwargs - .get(Qstr::MP_QSTR_fee_rate_amount)? - .try_into_option()?; - let account_label: Option = - kwargs.get(Qstr::MP_QSTR_account_label)?.try_into_option()?; - let total_label: TString = kwargs.get(Qstr::MP_QSTR_total_label)?.try_into()?; + let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?; + let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?; + let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?; let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?; + let _title: Option = kwargs + .get(Qstr::MP_QSTR_title) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let fee_items: Option = kwargs + .get(Qstr::MP_QSTR_fee_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let account_items: Option = kwargs + .get(Qstr::MP_QSTR_account_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let extra_items: Option = kwargs + .get(Qstr::MP_QSTR_extra_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let verb_cancel: Option> = kwargs + .get(Qstr::MP_QSTR_verb_cancel) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + // collect available info pages + let mut info_pages: Vec<(TString, Obj), 3> = Vec::new(); + if let Some(info) = fee_items { + unwrap!(info_pages.push((TR::confirm_total__title_fee.into(), info))); + } + if let Some(info) = account_items { + unwrap!(info_pages.push((TR::confirm_total__title_sending_from.into(), info))); + } + if let Some(info) = extra_items { + unwrap!(info_pages.push((TR::words__title_information.into(), info))); + } + + // button layouts and actions + let verb_cancel: TString = verb_cancel.unwrap_or(TString::empty()); + let btns_summary_page = move |has_pages_after: bool| -> (ButtonLayout, ButtonActions) { + // if there are no info pages, the right button is not needed + // if verb_cancel is "^", the left button is an arrow pointing up + let left_btn = Some(ButtonDetails::from_text_possible_icon(verb_cancel)); + let right_btn = has_pages_after.then(|| { + ButtonDetails::text("i".into()) + .with_fixed_width(theme::BUTTON_ICON_WIDTH) + .with_font(Font::NORMAL) + }); + let middle_btn = Some(ButtonDetails::armed_text(TR::buttons__confirm.into())); + + ( + ButtonLayout::new(left_btn, middle_btn, right_btn), + if has_pages_after { + ButtonActions::cancel_confirm_next() + } else { + ButtonActions::cancel_confirm_none() + }, + ) + }; + let btns_info_page = |is_last: bool| -> (ButtonLayout, ButtonActions) { + // on the last info page, the right button is not needed + if is_last { + ( + ButtonLayout::arrow_none_none(), + ButtonActions::prev_none_none(), + ) + } else { + ( + ButtonLayout::arrow_none_arrow(), + ButtonActions::prev_none_next(), + ) + } + }; + + let total_pages = 1 + info_pages.len(); let get_page = move |page_index| { match page_index { 0 => { // Total amount + fee - let btn_layout = ButtonLayout::cancel_armed_info(TR::buttons__confirm.into()); - let btn_actions = ButtonActions::cancel_confirm_next(); + let (btn_layout, btn_actions) = btns_summary_page(!info_pages.is_empty()); let ops = OpTextLayout::new(theme::TEXT_MONO) - .text_bold(total_label) + .text_bold(amount_label) .newline() - .text_mono(total_amount) + .text_mono(amount) .newline() .newline() .text_bold(fee_label) .newline() - .text_mono(fee_amount); + .text_mono(fee); let formatted = FormattedText::new(ops); Page::new(btn_layout, btn_actions, formatted) } - 1 => { - // Fee rate info - let btn_layout = ButtonLayout::arrow_none_arrow(); - let btn_actions = ButtonActions::prev_none_next(); - - let fee_rate_amount = fee_rate_amount.unwrap_or("".into()); - - let ops = OpTextLayout::new(theme::TEXT_MONO) - .text_bold_upper(TR::confirm_total__title_fee) - .newline() - .newline() - .newline_half() - .text_bold(TR::confirm_total__fee_rate_colon) - .newline() - .text_mono(fee_rate_amount); - - let formatted = FormattedText::new(ops); - Page::new(btn_layout, btn_actions, formatted) - } - 2 => { - // Wallet and account info - let btn_layout = ButtonLayout::arrow_none_none(); - let btn_actions = ButtonActions::prev_none_none(); - - let account_label = account_label.unwrap_or("".into()); - - // TODO: include wallet info when available - - let ops = OpTextLayout::new(theme::TEXT_MONO) - .text_bold_upper(TR::confirm_total__title_sending_from) - .newline() - .newline() - .newline_half() - .text_bold(TR::words__account_colon) - .newline() - .text_mono(account_label); - - let formatted = FormattedText::new(ops); - Page::new(btn_layout, btn_actions, formatted) - } - _ => unreachable!(), - } - }; - let pages = FlowPages::new(get_page, 3); - - let obj = LayoutObj::new(Flow::new(pages))?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - -extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = |_args: &[Obj], kwargs: &Map| { - let amount_title: TString = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?; - let amount_value: TString = kwargs.get(Qstr::MP_QSTR_amount_value)?.try_into()?; - let fee_title: TString = kwargs.get(Qstr::MP_QSTR_fee_title)?.try_into()?; - let fee_value: TString = kwargs.get(Qstr::MP_QSTR_fee_value)?.try_into()?; - let items_title: TString = kwargs.get(Qstr::MP_QSTR_items_title)?.try_into()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - let cancel_cross: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_cross, false)?; - - let get_page = move |page_index| { - match page_index { - 0 => { - // Amount + fee - let btn_layout = if cancel_cross { - ButtonLayout::cancel_armed_info(TR::buttons__confirm.into()) - } else { - ButtonLayout::up_arrow_armed_info(TR::buttons__confirm.into()) - }; - let btn_actions = ButtonActions::cancel_confirm_next(); - - let ops = OpTextLayout::new(theme::TEXT_MONO) - .text_bold(amount_title) - .newline() - .text_mono(amount_value) - .newline() - .newline_half() - .text_bold(fee_title) - .newline() - .text_mono(fee_value); - - let formatted = FormattedText::new(ops); - Page::new(btn_layout, btn_actions, formatted) - } - 1 => { - // Other information - let btn_layout = ButtonLayout::arrow_none_none(); - let btn_actions = ButtonActions::prev_none_none(); + i => { + // Other info pages as provided + let (title, info_obj) = &info_pages[i - 1]; + let is_last = i == total_pages - 1; + let (btn_layout, btn_actions) = btns_info_page(is_last); let mut ops = OpTextLayout::new(theme::TEXT_MONO); - - for item in unwrap!(IterBuf::new().try_iterate(items)) { + for item in unwrap!(IterBuf::new().try_iterate(*info_obj)) { let [key, value]: [Obj; 2] = unwrap!(util::iter_into_array(item)); if !ops.is_empty() { // Each key-value pair is on its own page @@ -776,13 +756,12 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m let formatted = FormattedText::new(ops).vertically_centered(); Page::new(btn_layout, btn_actions, formatted) - .with_title(items_title) .with_slim_arrows() + .with_title(*title) } - _ => unreachable!(), } }; - let pages = FlowPages::new(get_page, 2); + let pages = FlowPages::new(get_page, total_pages); let obj = LayoutObj::new(Flow::new(pages).with_scrollbar(false))?; Ok(obj.into()) @@ -1810,30 +1789,20 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm output amount.""" Qstr::MP_QSTR_confirm_output_amount => obj_fn_kw!(0, new_confirm_output_amount).as_obj(), - /// def confirm_total( + /// def confirm_summary( /// *, - /// total_amount: str, - /// fee_amount: str, - /// fee_rate_amount: str | None, - /// account_label: str | None, - /// total_label: str, + /// amount: str, + /// amount_label: str, + /// fee: str, /// fee_label: str, + /// title: str | None = None, + /// fee_items: Iterable[tuple[str, str]] | None = None, + /// account_items: Iterable[tuple[str, str]] | None = None, + /// extra_items: Iterable[tuple[str, str]] | None = None, + /// verb_cancel: str | None = None, /// ) -> LayoutObj[UiResult]: /// """Confirm summary of a transaction.""" - Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(), - - /// def altcoin_tx_summary( - /// *, - /// amount_title: str, - /// amount_value: str, - /// fee_title: str, - /// fee_value: str, - /// items_title: str, - /// items: Iterable[Tuple[str, str]], - /// cancel_cross: bool = False, - /// ) -> LayoutObj[UiResult]: - /// """Confirm details about altcoin transaction.""" - Qstr::MP_QSTR_altcoin_tx_summary => obj_fn_kw!(0, new_altcoin_tx_summary).as_obj(), + Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), /// def tutorial() -> LayoutObj[UiResult]: /// """Show user how to interact with the device.""" diff --git a/core/embed/rust/src/ui/model_tt/component/page.rs b/core/embed/rust/src/ui/model_tt/component/page.rs index 9dbe9376be..b1e623e85e 100644 --- a/core/embed/rust/src/ui/model_tt/component/page.rs +++ b/core/embed/rust/src/ui/model_tt/component/page.rs @@ -93,20 +93,12 @@ where left: Option>, right: Option>, ) -> Self { - let cancel = match left { - Some(verb) => verb.map(|s| match s { - "^" => Button::with_icon(theme::ICON_UP), - "<" => Button::with_icon(theme::ICON_BACK), - _ => Button::with_text(verb), - }), - _ => Button::with_icon(theme::ICON_CANCEL), - }; let confirm = match right { Some(verb) => Button::with_text(verb).styled(theme::button_confirm()), _ => Button::with_icon(theme::ICON_CONFIRM).styled(theme::button_confirm()), }; - self.button_cancel = Some(cancel); self.button_confirm = confirm; + self = self.with_cancel_button(left); self } @@ -117,8 +109,16 @@ where self } - pub fn with_cancel_arrow(mut self) -> Self { - self.button_cancel = Some(Button::with_icon(theme::ICON_UP)); + pub fn with_cancel_button(mut self, left: Option>) -> Self { + let cancel = match left { + Some(verb) => verb.map(|s| match s { + "^" => Button::with_icon(theme::ICON_UP), + "<" => Button::with_icon(theme::ICON_BACK), + _ => Button::with_text(verb), + }), + _ => Button::with_icon(theme::ICON_CANCEL), + }; + self.button_cancel = Some(cancel); self } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index dc12782bae..1c554b1899 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -763,28 +763,53 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { +extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?; - let cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; + let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?; + let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?; + let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?; + let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?; + let title: Option = kwargs + .get(Qstr::MP_QSTR_title) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let fee_items: Option = kwargs + .get(Qstr::MP_QSTR_fee_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let account_items: Option = kwargs + .get(Qstr::MP_QSTR_account_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let extra_items: Option = kwargs + .get(Qstr::MP_QSTR_extra_items) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let verb_cancel: Option> = kwargs + .get(Qstr::MP_QSTR_verb_cancel) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; - let mut paragraphs = ParagraphVecShort::new(); + let info_button: bool = + fee_items.is_some() || account_items.is_some() || extra_items.is_some(); + let paragraphs = ParagraphVecShort::from_iter([ + Paragraph::new(&theme::TEXT_NORMAL, amount_label).no_break(), + Paragraph::new(&theme::TEXT_MONO, amount), + Paragraph::new(&theme::TEXT_NORMAL, fee_label).no_break(), + Paragraph::new(&theme::TEXT_MONO, fee), + ]); - for pair in IterBuf::new().try_iterate(items)? { - let [label, value]: [TString; 2] = util::iter_into_array(pair)?; - paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break()); - paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value)); - } - let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?; - if cancel_arrow { - page = page.with_cancel_arrow() - } + let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG) + .with_hold()? + .with_cancel_button(verb_cancel); if info_button { page = page.with_swipe_left(); } - let mut frame = Frame::left_aligned(theme::label_title(), title, page); + let mut frame = Frame::left_aligned( + theme::label_title(), + title.unwrap_or(TString::empty()), + page, + ); if info_button { frame = frame.with_info_button(); } @@ -1868,15 +1893,20 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm value. Merge of confirm_total and confirm_output.""" Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(), - /// def confirm_total( + /// def confirm_summary( /// *, - /// title: str, - /// items: Iterable[tuple[str, str]], - /// info_button: bool = False, - /// cancel_arrow: bool = False, + /// amount: str, + /// amount_label: str, + /// fee: str, + /// fee_label: str, + /// title: str | None = None, + /// fee_items: Iterable[tuple[str, str]] | None = None, + /// account_items: Iterable[tuple[str, str]] | None = None, + /// extra_items: Iterable[tuple[str, str]] | None = None, + /// verb_cancel: str | None = None, /// ) -> LayoutObj[UiResult]: - /// """Transaction summary. Always hold to confirm.""" - Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(), + /// """Confirm summary of a transaction.""" + Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), /// def confirm_modify_output( /// *, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 5291aef006..ab913a03fb 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -144,17 +144,6 @@ def confirm_value( """Confirm value. Merge of confirm_total and confirm_output.""" -# rust/src/ui/model_mercury/layout.rs -def confirm_total( - *, - title: str, - items: Iterable[tuple[str, str]], - info_button: bool = False, - cancel_arrow: bool = False, -) -> LayoutObj[UiResult]: - """Transaction summary. Always hold to confirm.""" - - # rust/src/ui/model_mercury/layout.rs def confirm_modify_output( *, @@ -534,18 +523,19 @@ def flow_confirm_output( # rust/src/ui/model_mercury/layout.rs -def flow_confirm_summary( +def confirm_summary( *, - title: str, - items: Iterable[tuple[str, str]], - account_items: Iterable[tuple[str, str]], - account_items_title: str | None, - fee_items: Iterable[tuple[str, str]], - br_code: ButtonRequestType, - br_name: str, - cancel_text: str | None = None, + amount: str, + amount_label: str, + fee: str, + fee_label: str, + title: str | None = None, + fee_items: Iterable[tuple[str, str]] | None = None, + account_items: Iterable[tuple[str, str]] | None = None, + extra_items: Iterable[tuple[str, str]] | None = None, + verb_cancel: str | None = None, ) -> LayoutObj[UiResult]: - """Total summary and hold to confirm.""" + """Confirm summary of a transaction.""" # rust/src/ui/model_mercury/layout.rs @@ -743,32 +733,21 @@ def confirm_output_amount( # rust/src/ui/model_tr/layout.rs -def confirm_total( +def confirm_summary( *, - total_amount: str, - fee_amount: str, - fee_rate_amount: str | None, - account_label: str | None, - total_label: str, + amount: str, + amount_label: str, + fee: str, fee_label: str, + title: str | None = None, + fee_items: Iterable[tuple[str, str]] | None = None, + account_items: Iterable[tuple[str, str]] | None = None, + extra_items: Iterable[tuple[str, str]] | None = None, + verb_cancel: str | None = None, ) -> LayoutObj[UiResult]: """Confirm summary of a transaction.""" -# rust/src/ui/model_tr/layout.rs -def altcoin_tx_summary( - *, - amount_title: str, - amount_value: str, - fee_title: str, - fee_value: str, - items_title: str, - items: Iterable[Tuple[str, str]], - cancel_cross: bool = False, -) -> LayoutObj[UiResult]: - """Confirm details about altcoin transaction.""" - - # rust/src/ui/model_tr/layout.rs def tutorial() -> LayoutObj[UiResult]: """Show user how to interact with the device.""" @@ -1290,14 +1269,19 @@ def confirm_value( # rust/src/ui/model_tt/layout.rs -def confirm_total( +def confirm_summary( *, - title: str, - items: Iterable[tuple[str, str]], - info_button: bool = False, - cancel_arrow: bool = False, + amount: str, + amount_label: str, + fee: str, + fee_label: str, + title: str | None = None, + fee_items: Iterable[tuple[str, str]] | None = None, + account_items: Iterable[tuple[str, str]] | None = None, + extra_items: Iterable[tuple[str, str]] | None = None, + verb_cancel: str | None = None, ) -> LayoutObj[UiResult]: - """Transaction summary. Always hold to confirm.""" + """Confirm summary of a transaction.""" # rust/src/ui/model_tt/layout.rs diff --git a/core/src/trezor/ui/layouts/mercury/__init__.py b/core/src/trezor/ui/layouts/mercury/__init__.py index e5d63f54a8..ce3e94a9df 100644 --- a/core/src/trezor/ui/layouts/mercury/__init__.py +++ b/core/src/trezor/ui/layouts/mercury/__init__.py @@ -686,10 +686,6 @@ def confirm_total( total_label = total_label or TR.send__total_amount # def_arg fee_label = fee_label or TR.send__incl_transaction_fee # def_arg - items = [ - (total_label, total_amount), - (fee_label, fee_amount), - ] fee_items = [] account_items = [] if source_account: @@ -700,44 +696,48 @@ def confirm_total( fee_items.append((TR.confirm_total__fee_rate, fee_rate_amount)) return raise_if_not_confirmed( - trezorui2.flow_confirm_summary( + trezorui2.confirm_summary( + amount=total_amount, + amount_label=total_label, + fee=fee_amount, + fee_label=fee_label, title=title, - items=items, - fee_items=fee_items, - account_items=account_items, - account_items_title=None, - br_name=br_name, - br_code=br_code, - cancel_text=TR.send__cancel_sign, + fee_items=fee_items or None, + account_items=account_items or None, ), - None, + br_name, + br_code, ) def _confirm_summary( - items: Iterable[tuple[str, str]] | None = None, + amount: str, + amount_label: str, + fee: str, + fee_label: str, title: str | None = None, - info_items: Iterable[tuple[str, str]] | None = None, - info_title: str | None = None, fee_items: Iterable[tuple[str, str]] | None = None, + account_items: Iterable[tuple[str, str]] | None = None, + extra_items: Iterable[tuple[str, str]] | None = None, + extra_title: str | None = None, br_name: str = "confirm_total", br_code: ButtonRequestType = ButtonRequestType.SignTx, - cancel_text: str | None = None, ) -> Awaitable[None]: title = title or TR.words__title_summary # def_arg return raise_if_not_confirmed( - trezorui2.flow_confirm_summary( + trezorui2.confirm_summary( + amount=amount, + amount_label=amount_label, + fee=fee, + fee_label=fee_label, title=title, - items=items or (), - fee_items=fee_items or (), - account_items=info_items or (), - account_items_title=info_title, - br_name=br_name, - br_code=br_code, - cancel_text=cancel_text, + fee_items=fee_items or None, + account_items=account_items or None, + extra_items=extra_items or None, ), - None, + br_name, + br_code, ) @@ -853,8 +853,11 @@ if not utils.BITCOIN_ONLY: ) # def_arg fee_title = fee_title or TR.words__fee # def_arg return _confirm_summary( - items=((amount_title, amount), (fee_title, fee)), - info_items=items, + amount, + amount_title, + fee, + fee_title, + extra_items=items, br_name=br_name, br_code=br_code, ) @@ -866,13 +869,13 @@ if not utils.BITCOIN_ONLY: ) -> Awaitable[None]: amount_title = TR.send__total_amount fee_title = TR.send__incl_transaction_fee - more_info_title = TR.buttons__more_info return _confirm_summary( - items=((amount_title, amount), (fee_title, fee)), - info_items=items, - info_title=more_info_title, - fee_items=None, + amount, + amount_title, + fee, + fee_title, + extra_items=items, br_name="confirm_cardano_tx", br_code=ButtonRequestType.SignTx, ) @@ -880,10 +883,10 @@ if not utils.BITCOIN_ONLY: def confirm_joint_total(spending_amount: str, total_amount: str) -> Awaitable[None]: return _confirm_summary( - items=( - (TR.send__you_are_contributing, spending_amount), - (TR.send__to_the_total_amount, total_amount), - ), + spending_amount, + TR.send__you_are_contributing, + total_amount, + TR.send__to_the_total_amount, title=TR.send__title_joint_transaction, br_name="confirm_joint_total", br_code=ButtonRequestType.SignTx, diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index f65efda5ba..ad9477e6c4 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -814,15 +814,22 @@ def confirm_total( ) -> Awaitable[None]: total_label = total_label or f"{TR.send__total_amount}:" # def_arg fee_label = fee_label or TR.send__including_fee # def_arg - return interact( - # TODO: resolve these differences in TT's and TR's confirm_total - trezorui2.confirm_total( # type: ignore [Arguments missing] - total_amount=total_amount, # type: ignore [No parameter named] - fee_amount=fee_amount, # type: ignore [No parameter named] - fee_rate_amount=fee_rate_amount, # type: ignore [No parameter named] - account_label=source_account, # type: ignore [No parameter named] - total_label=total_label, # type: ignore [No parameter named] - fee_label=fee_label, # type: ignore [No parameter named] + + fee_info_items = [] + if fee_rate_amount: + fee_info_items.append((TR.confirm_total__fee_rate_colon, fee_rate_amount)) + account_info_items = [] + if source_account: + account_info_items.append((TR.words__account_colon, source_account)) + + return raise_if_not_confirmed( + trezorui2.confirm_summary( + amount=total_amount, + amount_label=total_label, + fee=fee_amount, + fee_label=fee_label, + fee_items=fee_info_items or None, + account_items=account_info_items or None, ), br_name, br_code, @@ -871,14 +878,12 @@ if not utils.BITCOIN_ONLY: amount_title = f"{TR.words__amount}:" amount_value = total_amount await raise_if_not_confirmed( - trezorui2.altcoin_tx_summary( - amount_title=amount_title, - amount_value=amount_value, - fee_title=f"{TR.send__maximum_fee}:", - fee_value=maximum_fee, - items_title=TR.confirm_total__title_fee, - items=[(f"{k}:", v) for (k, v) in info_items], - cancel_cross=True, + trezorui2.confirm_summary( + amount=amount_value, + amount_label=amount_title, + fee=maximum_fee, + fee_label=f"{TR.send__maximum_fee}:", + fee_items=[(f"{k}:", v) for (k, v) in info_items], ), br_name=br_name, br_code=br_code, @@ -898,14 +903,12 @@ if not utils.BITCOIN_ONLY: ) # def_arg fee_title = fee_title or TR.words__fee # def_arg return raise_if_not_confirmed( - trezorui2.altcoin_tx_summary( - amount_title=amount_title, - amount_value=amount, - fee_title=fee_title, - fee_value=fee, - items_title=TR.confirm_total__title_fee, - items=items, - cancel_cross=True, + trezorui2.confirm_summary( + amount=amount, + amount_label=amount_title, + fee=fee, + fee_label=fee_title, + fee_items=items, ), br_name=br_name, br_code=br_code, @@ -922,14 +925,12 @@ if not utils.BITCOIN_ONLY: fee_title = TR.send__including_fee return raise_if_not_confirmed( - trezorui2.altcoin_tx_summary( - amount_title=amount_title, - amount_value=amount, - fee_title=fee_title, - fee_value=fee, - items_title=TR.words__title_information, - items=items, - cancel_cross=True, + trezorui2.confirm_summary( + amount=amount, + amount_label=amount_title, + fee=fee, + fee_label=fee_title, + extra_items=items, ), br_name="confirm_cardano_tx", br_code=ButtonRequestType.SignTx, @@ -947,13 +948,12 @@ if not utils.BITCOIN_ONLY: br_code: ButtonRequestType = ButtonRequestType.SignTx, chunkify: bool = False, ) -> None: - summary_layout = trezorui2.altcoin_tx_summary( - amount_title=f"{TR.words__amount}:", - amount_value=total_amount, - fee_title=f"{TR.send__maximum_fee}:", - fee_value=maximum_fee, - items_title=TR.confirm_total__title_fee, - items=[(f"{k}:", v) for (k, v) in fee_info_items], + summary_layout = trezorui2.confirm_summary( + amount=total_amount, + amount_label=f"{TR.words__amount}:", + fee=maximum_fee, + fee_label=f"{TR.send__maximum_fee}:", + fee_items=[(f"{k}:", v) for (k, v) in fee_info_items], ) if not is_contract_interaction: diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index 153565e335..5d020beb87 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -750,43 +750,66 @@ def confirm_total( total_label = total_label or f"{TR.send__total_amount}:" # def_arg fee_label = fee_label or TR.send__including_fee # def_arg - items = [ - (total_label, total_amount), - (fee_label, fee_amount), - ] - info_items = [] + account_info_items = [] + fee_info_items = [] + extra_info_items = [] if source_account: - info_items.append((TR.confirm_total__sending_from_account, source_account)) + account_info_items.append( + (TR.confirm_total__sending_from_account, source_account) + ) if fee_rate_amount: - info_items.append((f"{TR.confirm_total__fee_rate}:", fee_rate_amount)) + fee_info_items.append((f"{TR.confirm_total__fee_rate}:", fee_rate_amount)) return _confirm_summary( - items, - TR.words__title_summary, - info_items=info_items, + total_amount, + total_label, + fee_amount, + fee_label, + title=title, + fee_items=fee_info_items, + account_items=account_info_items, + extra_items=extra_info_items, br_name=br_name, br_code=br_code, ) def _confirm_summary( - items: Iterable[tuple[str, str]], + amount: str, + amount_label: str, + fee: str, + fee_label: str, title: str | None = None, - info_items: Iterable[tuple[str, str]] | None = None, - info_title: str | None = None, + fee_items: Iterable[tuple[str, str]] | None = None, + account_items: Iterable[tuple[str, str]] | None = None, + extra_items: Iterable[tuple[str, str]] | None = None, + extra_title: str | None = None, br_name: str = "confirm_total", br_code: ButtonRequestType = ButtonRequestType.SignTx, ) -> Awaitable[None]: title = title or TR.words__title_summary # def_arg - total_layout = trezorui2.confirm_total( + total_layout = trezorui2.confirm_summary( + amount=amount, + amount_label=amount_label, + fee=fee, + fee_label=fee_label, title=title, - items=items, - info_button=bool(info_items), + fee_items=fee_items or None, + account_items=account_items or None, + extra_items=extra_items or None, ) - info_items = info_items or [] + + # TODO: use `_info` params directly in this^ layout instead of using `with_info` + info_items = [] + if fee_items: + info_items.extend(fee_items) + if account_items: + info_items.extend(account_items) + if extra_items: + info_items.extend(extra_items) info_layout = trezorui2.show_info_with_cancel( - title=info_title if info_title else TR.words__title_information, + title=extra_title if extra_title else TR.words__title_information, items=info_items, ) return with_info(total_layout, info_layout, br_name, br_code) @@ -811,14 +834,15 @@ if not utils.BITCOIN_ONLY: br_code: ButtonRequestType = ButtonRequestType.SignTx, chunkify: bool = False, ) -> None: - total_layout = trezorui2.confirm_total( + # NOTE: fee_info used so that info button is shown + total_layout = trezorui2.confirm_summary( + amount=total_amount, + amount_label=f"{TR.words__amount}:", + fee=maximum_fee, + fee_label=f"{TR.send__maximum_fee}:", title=TR.words__title_summary, - items=[ - (f"{TR.words__amount}:", total_amount), - (f"{TR.send__maximum_fee}:", maximum_fee), - ], - info_button=True, - cancel_arrow=True, + fee_items=fee_info_items, + verb_cancel="^", ) info_layout = trezorui2.show_info_with_cancel( title=TR.confirm_total__title_fee, @@ -879,17 +903,23 @@ if not utils.BITCOIN_ONLY: # confirmation if verb == TR.ethereum__staking_claim: - items = ((f"{TR.send__maximum_fee}:", maximum_fee),) + amount = "" + amount_label = "" + fee_label = f"{TR.send__maximum_fee}:" + fee = maximum_fee else: - items = ( - (f"{TR.words__amount}:", total_amount), - (f"{TR.send__maximum_fee}:", maximum_fee), - ) + amount_label = f"{TR.words__amount}:" + amount = total_amount + fee_label = f"{TR.send__maximum_fee}:" + fee = maximum_fee await _confirm_summary( - items, # items + amount, + amount_label, + fee, + fee_label, title=title, - info_title=TR.confirm_total__title_fee, - info_items=[(f"{k}:", v) for (k, v) in info_items], + extra_items=[(f"{k}:", v) for (k, v) in info_items], + extra_title=TR.confirm_total__title_fee, br_name=br_name, br_code=br_code, ) @@ -908,8 +938,11 @@ if not utils.BITCOIN_ONLY: ) # def_arg fee_title = fee_title or TR.words__fee # def_arg return _confirm_summary( - ((amount_title, amount), (fee_title, fee)), - info_items=items, + amount, + amount_title, + fee, + fee_title, + extra_items=items, br_name=br_name, br_code=br_code, ) @@ -922,8 +955,11 @@ if not utils.BITCOIN_ONLY: amount_title = f"{TR.send__total_amount}:" fee_title = TR.send__including_fee return _confirm_summary( - ((amount_title, amount), (fee_title, fee)), - info_items=items, + amount, + amount_title, + fee, + fee_title, + extra_items=items, br_name="confirm_cardano_tx", br_code=ButtonRequestType.SignTx, ) @@ -931,12 +967,13 @@ if not utils.BITCOIN_ONLY: def confirm_joint_total(spending_amount: str, total_amount: str) -> Awaitable[None]: return raise_if_not_confirmed( - trezorui2.confirm_total( + # FIXME: arguments for amount/fee are misused here + trezorui2.confirm_summary( + amount=spending_amount, + amount_label=TR.send__you_are_contributing, + fee=total_amount, + fee_label=TR.send__to_the_total_amount, title=TR.send__title_joint_transaction, - items=[ - (TR.send__you_are_contributing, spending_amount), - (TR.send__to_the_total_amount, total_amount), - ], ), "confirm_joint_total", ButtonRequestType.SignTx, diff --git a/tests/input_flows.py b/tests/input_flows.py index 24cf6a5093..70c3bdc4f5 100644 --- a/tests/input_flows.py +++ b/tests/input_flows.py @@ -835,10 +835,10 @@ def sign_tx_go_to_info_tr( yield layout = client.debug.press_right() - screen_texts.append(layout.text_content()) + screen_texts.append(layout.visible_screen()) layout = client.debug.press_right() - screen_texts.append(layout.text_content()) + screen_texts.append(layout.visible_screen()) client.debug.press_left() client.debug.press_left()