From c08e495c9b72d39a19a538dd952a1947c0aa9a00 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Thu, 28 Nov 2024 15:06:08 +0100 Subject: [PATCH 01/11] refactor(core): model_t confirm_summary - model_t confirm_total refactored to confirm_summary - parameter set changed to pave the way for unification across models [no changelog] --- core/embed/rust/librust_qstr.h | 6 ++ core/embed/rust/src/ui/model_tt/layout.rs | 60 ++++++++--- core/mocks/generated/trezorui2.pyi | 13 ++- core/src/trezor/ui/layouts/tt/__init__.py | 119 ++++++++++++++-------- 4 files changed, 137 insertions(+), 61 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 78b6bfeabb..3c3837c30c 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -37,6 +37,7 @@ static void _librust_qstrs(void) { MP_QSTR___dict__; MP_QSTR___name__; MP_QSTR_account; + MP_QSTR_account_info; MP_QSTR_account_items; MP_QSTR_account_items_title; MP_QSTR_account_label; @@ -72,6 +73,7 @@ static void _librust_qstrs(void) { 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; @@ -209,6 +211,7 @@ static void _librust_qstrs(void) { MP_QSTR_confirm_properties; MP_QSTR_confirm_recovery; MP_QSTR_confirm_reset_device; + MP_QSTR_confirm_summary; MP_QSTR_confirm_total; MP_QSTR_confirm_total__fee_rate; MP_QSTR_confirm_total__fee_rate_colon; @@ -242,7 +245,10 @@ static void _librust_qstrs(void) { MP_QSTR_experimental_mode__only_for_dev; MP_QSTR_experimental_mode__title; MP_QSTR_extra; + MP_QSTR_extra_info; + MP_QSTR_fee; MP_QSTR_fee_amount; + MP_QSTR_fee_info; MP_QSTR_fee_items; MP_QSTR_fee_label; MP_QSTR_fee_rate_amount; diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 46b645d8aa..a9807795f2 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -763,20 +763,39 @@ 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 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 cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; - 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() @@ -784,7 +803,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma 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(); } @@ -1867,15 +1890,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, + /// 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, /// 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(), + 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 a8d7004cb1..88bcc7044d 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -1301,11 +1301,16 @@ 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, + 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, cancel_arrow: bool = False, ) -> LayoutObj[UiResult]: """Transaction summary. Always hold to confirm.""" diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index aecd69d7ee..cd9f67bf23 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -726,43 +726,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) @@ -789,13 +812,14 @@ 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, + fee_items=fee_info_items, cancel_arrow=True, ) info_layout = trezorui2.show_info_with_cancel( @@ -857,17 +881,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, ) @@ -886,8 +916,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, ) @@ -900,8 +933,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, ) @@ -909,12 +945,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, From 2b559fc22fa324dc9b65b79da0ffd0c63d05544e Mon Sep 17 00:00:00 2001 From: obrusvit Date: Thu, 28 Nov 2024 16:05:50 +0100 Subject: [PATCH 02/11] refactor(core): model_r confirm_summary - altcoin_tx_summary removed and replaced with confirm_summary [no changelog] --- core/embed/rust/librust_qstr.h | 10 - .../rust/src/ui/model_tr/component/button.rs | 9 + core/embed/rust/src/ui/model_tr/layout.rs | 227 ++++++++---------- core/mocks/generated/trezorui2.pyi | 29 +-- core/src/trezor/ui/layouts/tr/__init__.py | 80 +++--- tests/input_flows.py | 4 +- 6 files changed, 159 insertions(+), 200 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 3c3837c30c..0855dc3d06 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -40,7 +40,6 @@ static void _librust_qstrs(void) { MP_QSTR_account_info; MP_QSTR_account_items; MP_QSTR_account_items_title; - MP_QSTR_account_label; MP_QSTR_account_path; MP_QSTR_accounts; MP_QSTR_action; @@ -70,13 +69,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; @@ -178,7 +175,6 @@ static void _librust_qstrs(void) { 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; @@ -212,7 +208,6 @@ static void _librust_qstrs(void) { MP_QSTR_confirm_recovery; MP_QSTR_confirm_reset_device; MP_QSTR_confirm_summary; - MP_QSTR_confirm_total; MP_QSTR_confirm_total__fee_rate; MP_QSTR_confirm_total__fee_rate_colon; MP_QSTR_confirm_total__sending_from_account; @@ -247,13 +242,10 @@ static void _librust_qstrs(void) { MP_QSTR_extra; MP_QSTR_extra_info; MP_QSTR_fee; - MP_QSTR_fee_amount; MP_QSTR_fee_info; 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; @@ -324,7 +316,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; @@ -703,7 +694,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_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 9c32929da5..5cea1aa87a 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,112 @@ 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 cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; + // 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 btns_summary_page = + |cancel_arrow: bool, has_pages_after: bool| -> (ButtonLayout, ButtonActions) { + // if there are no info pages, the right button is not needed + // if cancel_arrow is true, the left button is an arrow pointing up + let left_button = Some(if cancel_arrow { + ButtonDetails::up_arrow_icon() + } else { + ButtonDetails::cancel_icon() + }); + let right_button = has_pages_after.then(|| { + ButtonDetails::text("i".into()) + .with_fixed_width(theme::BUTTON_ICON_WIDTH) + .with_font(Font::NORMAL) + }); + let middle_button = Some(ButtonDetails::armed_text(TR::buttons__confirm.into())); + + ( + ButtonLayout::new(left_button, middle_button, right_button), + 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(cancel_arrow, !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 +758,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()) @@ -1809,30 +1790,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, + /// cancel_arrow: bool = False, /// ) -> 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/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 88bcc7044d..163b600196 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -755,32 +755,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, + cancel_arrow: bool = False, ) -> 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.""" diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 4470823307..dd878f6af5 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -789,15 +789,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, @@ -849,14 +856,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, @@ -876,14 +881,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, @@ -900,14 +903,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, @@ -925,13 +926,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/tests/input_flows.py b/tests/input_flows.py index ff1782a1fe..2c3c995fa1 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() From ff7f6ea44616fe03a1159d306e3e8ba21696c60f Mon Sep 17 00:00:00 2001 From: obrusvit Date: Fri, 29 Nov 2024 15:32:09 +0100 Subject: [PATCH 03/11] refactor(core): mercury confirm_summary - old confirm_total removed - flow_confirm_summary refactored to confirm_summary [no changelog] --- core/embed/rust/librust_qstr.h | 6 +- .../ui/model_mercury/flow/confirm_summary.rs | 86 ++++++---- .../embed/rust/src/ui/model_mercury/layout.rs | 158 ++++++++---------- core/mocks/generated/trezorui2.pyi | 32 ++-- .../src/trezor/ui/layouts/mercury/__init__.py | 77 +++++---- 5 files changed, 179 insertions(+), 180 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 0855dc3d06..e0d69f9385 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -37,9 +37,7 @@ static void _librust_qstrs(void) { MP_QSTR___dict__; MP_QSTR___name__; MP_QSTR_account; - MP_QSTR_account_info; MP_QSTR_account_items; - MP_QSTR_account_items_title; MP_QSTR_account_path; MP_QSTR_accounts; MP_QSTR_action; @@ -240,9 +238,8 @@ static void _librust_qstrs(void) { MP_QSTR_experimental_mode__only_for_dev; MP_QSTR_experimental_mode__title; MP_QSTR_extra; - MP_QSTR_extra_info; + MP_QSTR_extra_items; MP_QSTR_fee; - MP_QSTR_fee_info; MP_QSTR_fee_items; MP_QSTR_fee_label; MP_QSTR_fee_rate_amount; @@ -253,7 +250,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; 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..5554a57bea 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, }, @@ -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,19 @@ 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, ) -> 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,34 +110,42 @@ 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)); } - menu = menu.danger( - theme::ICON_CANCEL, - cancel_text.unwrap_or(TR::send__cancel_sign.into()), - ); + if content_extra.is_some() && menu_items.len() < 2 { + // NOTE: extra is shown only if VerticalMenu has space (max 2 items + 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, TR::send__cancel_sign.into()); unwrap!(menu_items.push(MENU_ITEM_CANCEL)); let content_menu = Frame::left_aligned(TString::empty(), menu) .with_cancel_button() @@ -159,13 +172,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 03b7a4e73e..2e4e4eac54 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -657,51 +657,72 @@ 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: 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 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 _cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; - 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)); - } + // 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 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)); - } - - let flow = flow::new_confirm_summary( - summary_params, - account_params, - fee_params, - br_name, - br_code, - cancel_text, - )?; + let flow = + flow::new_confirm_summary(summary_params, fee_params, account_params, extra_params)?; Ok(LayoutObj::new_root(flow)?.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -790,34 +811,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()?; @@ -1674,16 +1667,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, @@ -2029,19 +2012,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, + /// cancel_arrow: bool = False, /// ) -> 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/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 163b600196..70e8f16bc1 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -157,17 +157,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( *, @@ -547,18 +536,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, + cancel_arrow: bool = False, ) -> LayoutObj[UiResult]: - """Total summary and hold to confirm.""" + """Confirm summary of a transaction.""" # rust/src/ui/model_mercury/layout.rs diff --git a/core/src/trezor/ui/layouts/mercury/__init__.py b/core/src/trezor/ui/layouts/mercury/__init__.py index ca61bd9be2..3a1a34dd65 100644 --- a/core/src/trezor/ui/layouts/mercury/__init__.py +++ b/core/src/trezor/ui/layouts/mercury/__init__.py @@ -655,10 +655,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: @@ -669,44 +665,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, ) @@ -826,8 +826,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, ) @@ -839,13 +842,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, ) @@ -853,10 +856,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, From 29cf6d3557b4c6f877c38fe39c959148df6e4e21 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 13:48:12 +0100 Subject: [PATCH 04/11] fixup! refactor(core): mercury confirm_summary --- core/embed/rust/src/ui/model_mercury/component/mod.rs | 2 +- .../rust/src/ui/model_mercury/component/vertical_menu.rs | 6 +++--- .../embed/rust/src/ui/model_mercury/flow/confirm_summary.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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 5554a57bea..98f894d69f 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 @@ -19,7 +19,7 @@ use super::{ super::{ component::{ Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent, VerticalMenu, - VerticalMenuChoiceMsg, + VerticalMenuChoiceMsg, MENU_MAX_ITEMS, }, theme, }, @@ -137,7 +137,7 @@ pub fn new_confirm_summary( ); unwrap!(menu_items.push(MENU_ITEM_ACCOUNT_INFO)); } - if content_extra.is_some() && menu_items.len() < 2 { + if content_extra.is_some() && menu_items.len() < MENU_MAX_ITEMS - 1 { // NOTE: extra is shown only if VerticalMenu has space (max 2 items + Cancel) menu = menu.item( theme::ICON_CHEVRON_RIGHT, From e7c5bc2632b7752cf00f7afbe06fec8edfe37f02 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 14:46:23 +0100 Subject: [PATCH 05/11] fixup! refactor(core): model_t confirm_summary --- .../rust/src/ui/model_tt/component/page.rs | 22 +++++++++---------- core/embed/rust/src/ui/model_tt/layout.rs | 14 +++++++----- core/src/trezor/ui/layouts/tt/__init__.py | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) 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 a9807795f2..e3ac28f876 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -785,7 +785,10 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut .get(Qstr::MP_QSTR_extra_items) .unwrap_or_else(|_| Obj::const_none()) .try_into_option()?; - let cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; + let verb_cancel: Option> = kwargs + .get(Qstr::MP_QSTR_verb_cancel) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; let info_button: bool = fee_items.is_some() || account_items.is_some() || extra_items.is_some(); @@ -796,10 +799,9 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Paragraph::new(&theme::TEXT_MONO, fee), ]); - 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(); } @@ -1900,7 +1902,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// fee_items: Iterable[tuple[str, str]] | None = None, /// account_items: Iterable[tuple[str, str]] | None = None, /// extra_items: Iterable[tuple[str, str]] | None = None, - /// cancel_arrow: bool = False, + /// verb_cancel: str | None = None, /// ) -> LayoutObj[UiResult]: /// """Transaction summary. Always hold to confirm.""" Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index cd9f67bf23..37cfb22034 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -820,7 +820,7 @@ if not utils.BITCOIN_ONLY: fee_label=f"{TR.send__maximum_fee}:", title=TR.words__title_summary, fee_items=fee_info_items, - cancel_arrow=True, + verb_cancel="^", ) info_layout = trezorui2.show_info_with_cancel( title=TR.confirm_total__title_fee, From f9c897d2df2a396ea4dfe668ae93b1d74a5ad4f2 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 14:46:35 +0100 Subject: [PATCH 06/11] fixup! refactor(core): model_r confirm_summary --- core/embed/rust/src/ui/model_tr/layout.rs | 53 +++++++++++------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 5cea1aa87a..070ba7b3f5 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -661,7 +661,10 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut .get(Qstr::MP_QSTR_extra_items) .unwrap_or_else(|_| Obj::const_none()) .try_into_option()?; - let cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; + 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(); @@ -676,31 +679,26 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut } // button layouts and actions - let btns_summary_page = - |cancel_arrow: bool, has_pages_after: bool| -> (ButtonLayout, ButtonActions) { - // if there are no info pages, the right button is not needed - // if cancel_arrow is true, the left button is an arrow pointing up - let left_button = Some(if cancel_arrow { - ButtonDetails::up_arrow_icon() - } else { - ButtonDetails::cancel_icon() - }); - let right_button = has_pages_after.then(|| { - ButtonDetails::text("i".into()) - .with_fixed_width(theme::BUTTON_ICON_WIDTH) - .with_font(Font::NORMAL) - }); - let middle_button = Some(ButtonDetails::armed_text(TR::buttons__confirm.into())); + 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 = verb_cancel.map(ButtonDetails::from_text_possible_icon); + 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_button, middle_button, right_button), - if has_pages_after { - ButtonActions::cancel_confirm_next() - } else { - ButtonActions::cancel_confirm_none() - }, - ) - }; + ( + 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 { @@ -721,8 +719,7 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut match page_index { 0 => { // Total amount + fee - let (btn_layout, btn_actions) = - btns_summary_page(cancel_arrow, !info_pages.is_empty()); + let (btn_layout, btn_actions) = btns_summary_page(!info_pages.is_empty()); let ops = OpTextLayout::new(theme::TEXT_MONO) .text_bold(amount_label) @@ -1800,7 +1797,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// fee_items: Iterable[tuple[str, str]] | None = None, /// account_items: Iterable[tuple[str, str]] | None = None, /// extra_items: Iterable[tuple[str, str]] | None = None, - /// cancel_arrow: bool = False, + /// verb_cancel: str | None = None, /// ) -> LayoutObj[UiResult]: /// """Confirm summary of a transaction.""" Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), From b1850b904a04c466ba324f03f9e4f896cec699e9 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 14:46:43 +0100 Subject: [PATCH 07/11] fixup! refactor(core): mercury confirm_summary --- core/embed/rust/librust_qstr.h | 1 - .../src/ui/model_mercury/flow/confirm_summary.rs | 8 ++++++-- core/embed/rust/src/ui/model_mercury/layout.rs | 14 +++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index e0d69f9385..c8e28516b0 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -172,7 +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_text; MP_QSTR_case_sensitive; MP_QSTR_check_homescreen_format; 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 98f894d69f..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 @@ -87,6 +87,7 @@ pub fn new_confirm_summary( fee_params: Option, account_params: Option, extra_params: Option, + verb_cancel: Option>, ) -> Result { // Summary let content_summary = summary_params @@ -138,14 +139,17 @@ pub fn new_confirm_summary( 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 (max 2 items + Cancel) + // 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, TR::send__cancel_sign.into()); + menu = menu.danger( + theme::ICON_CANCEL, + 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) .with_cancel_button() diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index 2e4e4eac54..d68d4bf596 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -677,7 +677,10 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut .get(Qstr::MP_QSTR_extra_items) .unwrap_or_else(|_| Obj::const_none()) .try_into_option()?; - let _cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?; + let verb_cancel: Option = kwargs + .get(Qstr::MP_QSTR_verb_cancel) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; let mut summary_params = ShowInfoParams::new(title.unwrap_or(TString::empty())) .with_menu_button() @@ -721,8 +724,13 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut None }; - let flow = - flow::new_confirm_summary(summary_params, fee_params, account_params, extra_params)?; + let flow = flow::new_confirm_summary( + summary_params, + fee_params, + account_params, + extra_params, + verb_cancel, + )?; Ok(LayoutObj::new_root(flow)?.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } From 61448aad34b21198d00d80507fc3321b923be27a Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 18:28:00 +0100 Subject: [PATCH 08/11] fixup! refactor(core): model_r confirm_summary --- core/embed/rust/src/ui/model_tr/layout.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 070ba7b3f5..d68b1fe481 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -679,10 +679,11 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut } // 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 = verb_cancel.map(ButtonDetails::from_text_possible_icon); + 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) From 0dcc125c4d34ad973987d26844aa80b8eb9c290a Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 19:20:09 +0100 Subject: [PATCH 09/11] fixup! refactor(core): mercury confirm_summary --- core/embed/rust/src/ui/model_mercury/layout.rs | 2 +- core/mocks/generated/trezorui2.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index d68d4bf596..567b3aa256 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -2030,7 +2030,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// fee_items: Iterable[tuple[str, str]] | None = None, /// account_items: Iterable[tuple[str, str]] | None = None, /// extra_items: Iterable[tuple[str, str]] | None = None, - /// cancel_arrow: bool = False, + /// verb_cancel: str | None = None, /// ) -> LayoutObj[UiResult]: /// """Confirm summary of a transaction.""" Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(), diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 70e8f16bc1..317adabc4b 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -546,7 +546,7 @@ def confirm_summary( fee_items: Iterable[tuple[str, str]] | None = None, account_items: Iterable[tuple[str, str]] | None = None, extra_items: Iterable[tuple[str, str]] | None = None, - cancel_arrow: bool = False, + verb_cancel: str | None = None, ) -> LayoutObj[UiResult]: """Confirm summary of a transaction.""" From acee4b605ee059090bba4299e78c0d66681692c1 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 19:20:33 +0100 Subject: [PATCH 10/11] fixup! refactor(core): model_t confirm_summary --- core/embed/rust/src/ui/model_tt/layout.rs | 2 +- core/mocks/generated/trezorui2.pyi | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index e3ac28f876..e71b83380b 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1904,7 +1904,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// 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.""" 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 317adabc4b..82769e29f3 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -1290,9 +1290,9 @@ def confirm_summary( fee_items: Iterable[tuple[str, str]] | None = None, account_items: Iterable[tuple[str, str]] | None = None, extra_items: Iterable[tuple[str, str]] | None = None, - cancel_arrow: bool = False, + 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 From 5c2bacd39a8535063ec7b25b65fdf2b8da4dff9a Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 2 Dec 2024 19:20:42 +0100 Subject: [PATCH 11/11] fixup! refactor(core): model_r confirm_summary --- core/mocks/generated/trezorui2.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 82769e29f3..1652dd40c8 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -755,7 +755,7 @@ def confirm_summary( fee_items: Iterable[tuple[str, str]] | None = None, account_items: Iterable[tuple[str, str]] | None = None, extra_items: Iterable[tuple[str, str]] | None = None, - cancel_arrow: bool = False, + verb_cancel: str | None = None, ) -> LayoutObj[UiResult]: """Confirm summary of a transaction."""