diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index a26dc21659..ae99c4fe50 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -50,7 +50,8 @@ static void _librust_qstrs(void) { MP_QSTR_confirm_modify_fee; MP_QSTR_confirm_modify_output; MP_QSTR_confirm_more; - MP_QSTR_confirm_output; + MP_QSTR_confirm_output_address; + MP_QSTR_confirm_output_amount; MP_QSTR_confirm_properties; MP_QSTR_confirm_recovery; MP_QSTR_confirm_reset_device; diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 59f4f4393a..ee1b015388 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -550,54 +550,61 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { +extern "C" fn new_confirm_output_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = |_args: &[Obj], kwargs: &Map| { let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?; let address_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_label)?.try_into()?; - let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?; let address_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_title)?.try_into()?; - let amount_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?; let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?; let get_page = move |page_index| { - // Showing two screens - the recipient address and summary confirmation - match page_index { - 0 => { - // RECIPIENT + address - let btn_layout = ButtonLayout::cancel_none_text("CONTINUE".into()); - let btn_actions = ButtonActions::cancel_none_next(); - // Not putting hyphens in the address. - // Potentially adding address label in different font. - let mut ops = OpTextLayout::new(theme::TEXT_MONO_DATA); - if !address_label.is_empty() { - // NOTE: need to explicitly turn off the chunkification before rendering the - // address label (for some reason it does not help to turn it off after - // rendering the chunks) - if chunkify { - ops = ops.chunkify_text(None); - } - ops = ops.text_normal(address_label.clone()).newline(); - } - if chunkify { - // Chunkifying the address into smaller pieces when requested - ops = ops.chunkify_text(Some((theme::MONO_CHUNKS, 2))); - } - ops = ops.text_mono(address.clone()); - let formatted = FormattedText::new(ops).vertically_centered(); - Page::new(btn_layout, btn_actions, formatted).with_title(address_title.clone()) + assert!(page_index == 0); + // RECIPIENT + address + let btn_layout = ButtonLayout::cancel_none_text("CONTINUE".into()); + let btn_actions = ButtonActions::cancel_none_confirm(); + // Not putting hyphens in the address. + // Potentially adding address label in different font. + let mut ops = OpTextLayout::new(theme::TEXT_MONO_DATA); + if !address_label.is_empty() { + // NOTE: need to explicitly turn off the chunkification before rendering the + // address label (for some reason it does not help to turn it off after + // rendering the chunks) + if chunkify { + ops = ops.chunkify_text(None); } - 1 => { - // AMOUNT + amount - let btn_layout = ButtonLayout::up_arrow_none_text("CONFIRM".into()); - let btn_actions = ButtonActions::prev_none_confirm(); - let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount.clone()); - let formatted = FormattedText::new(ops).vertically_centered(); - Page::new(btn_layout, btn_actions, formatted).with_title(amount_title.clone()) - } - _ => unreachable!(), + ops = ops.text_normal(address_label.clone()).newline(); } + if chunkify { + // Chunkifying the address into smaller pieces when requested + ops = ops.chunkify_text(Some((theme::MONO_CHUNKS, 2))); + } + ops = ops.text_mono(address.clone()); + let formatted = FormattedText::new(ops).vertically_centered(); + Page::new(btn_layout, btn_actions, formatted).with_title(address_title.clone()) }; - let pages = FlowPages::new(get_page, 2); + let pages = FlowPages::new(get_page, 1); + + 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_confirm_output_amount(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { + let block = |_args: &[Obj], kwargs: &Map| { + let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?; + let amount_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?; + + let get_page = move |page_index| { + assert!(page_index == 0); + // AMOUNT + amount + let btn_layout = ButtonLayout::up_arrow_none_text("CONFIRM".into()); + let btn_actions = ButtonActions::cancel_none_confirm(); + let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount.clone()); + let formatted = FormattedText::new(ops).vertically_centered(); + Page::new(btn_layout, btn_actions, formatted).with_title(amount_title.clone()) + }; + let pages = FlowPages::new(get_page, 1); let obj = LayoutObj::new(Flow::new(pages))?; Ok(obj.into()) @@ -1668,17 +1675,23 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Decrease or increase amount for given address.""" Qstr::MP_QSTR_confirm_modify_output => obj_fn_kw!(0, new_confirm_modify_output).as_obj(), - /// def confirm_output( + /// def confirm_output_address( /// *, /// address: str, /// address_label: str, - /// amount: str, /// address_title: str, - /// amount_title: str, /// chunkify: bool = False, /// ) -> object: - /// """Confirm output.""" - Qstr::MP_QSTR_confirm_output => obj_fn_kw!(0, new_confirm_output).as_obj(), + /// """Confirm output address.""" + Qstr::MP_QSTR_confirm_output_address => obj_fn_kw!(0, new_confirm_output_address).as_obj(), + + /// def confirm_output_amount( + /// *, + /// amount: str, + /// amount_title: str, + /// ) -> object: + /// """Confirm output amount.""" + Qstr::MP_QSTR_confirm_output_amount => obj_fn_kw!(0, new_confirm_output_amount).as_obj(), /// def confirm_total( /// *, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index a6c89546ad..fbd9789f11 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -126,16 +126,23 @@ def confirm_modify_output( # rust/src/ui/model_tr/layout.rs -def confirm_output( +def confirm_output_address( *, address: str, address_label: str, - amount: str, address_title: str, - amount_title: str, chunkify: bool = False, ) -> object: - """Confirm output.""" + """Confirm output address.""" + + +# rust/src/ui/model_tr/layout.rs +def confirm_output_amount( + *, + amount: str, + amount_title: str, +) -> object: + """Confirm output amount.""" # rust/src/ui/model_tr/layout.rs diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 4b0135b976..f7e445cb5b 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -662,22 +662,34 @@ async def confirm_output( ) amount_title = "AMOUNT" if output_index is None else f"AMOUNT #{output_index + 1}" - await raise_if_not_confirmed( - interact( + while True: + result = await interact( RustLayout( - trezorui2.confirm_output( + trezorui2.confirm_output_address( address=address, address_label=address_label or "", address_title=address_title, - amount_title=amount_title, - amount=amount, chunkify=chunkify, ) ), "confirm_output", br_code, ) - ) + if result is not CONFIRMED: + raise ActionCancelled + + result = await interact( + RustLayout( + trezorui2.confirm_output_amount( + amount_title=amount_title, + amount=amount, + ) + ), + "confirm_output", + br_code, + ) + if result is CONFIRMED: + return async def tutorial(