diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index d04460c4a2..f6561f69f3 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/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..e47763eed3 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -763,28 +763,52 @@ 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 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 _extra_title: Option = kwargs + .get(Qstr::MP_QSTR_extra_title) + .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 = 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 +1892,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, + /// account_items: Iterable[tuple[str, str]] | None = None, + /// extra_items: Iterable[tuple[str, str]] | None = None, + /// extra_title: 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..e63e381097 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -1290,14 +1290,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, + account_items: Iterable[tuple[str, str]] | None = None, + extra_items: Iterable[tuple[str, str]] | None = None, + extra_title: 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/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index 153565e335..aabd663f85 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -750,43 +750,61 @@ 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 = [] + 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)) + extra_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, + account_items=account_info_items, + extra_items=extra_info_items, + extra_title=TR.words__title_information, 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, + 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), + 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 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 +829,16 @@ 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, + extra_items=fee_info_items, + extra_title=TR.confirm_total__title_fee, + verb_cancel="^", ) info_layout = trezorui2.show_info_with_cancel( title=TR.confirm_total__title_fee, @@ -879,17 +899,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 +934,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 +951,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 +963,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,