1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-04 13:38:28 +00:00
This commit is contained in:
Vít Obrusník 2024-12-03 11:02:20 +01:00 committed by GitHub
commit 6c84fdf56f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 503 additions and 458 deletions

View File

@ -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;

View File

@ -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};

View File

@ -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<Button, MAX_ITEMS>;
type VerticalMenuButtons = Vec<Button, MENU_MAX_ITEMS>;
#[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(),
}
}

View File

@ -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<Msg = FlowMsg> + 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<TString<'static>>,
fee_params: Option<ShowInfoParams>,
account_params: Option<ShowInfoParams>,
extra_params: Option<ShowInfoParams>,
verb_cancel: Option<TString<'static>>,
) -> Result<SwipeFlow, error::Error> {
// 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::<usize, 3>::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)
}

View File

@ -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<TString> = 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<TString> = kwargs
.get(Qstr::MP_QSTR_title)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let fee_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_fee_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let account_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_account_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let extra_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_extra_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let verb_cancel: Option<TString> = 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<TString> =
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."""

View File

@ -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(

View File

@ -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<TString> = kwargs
.get(Qstr::MP_QSTR_fee_rate_amount)?
.try_into_option()?;
let account_label: Option<TString> =
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<TString> = kwargs
.get(Qstr::MP_QSTR_title)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let fee_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_fee_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let account_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_account_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let extra_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_extra_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let verb_cancel: Option<TString<'static>> = 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."""

View File

@ -93,20 +93,12 @@ where
left: Option<TString<'static>>,
right: Option<TString<'static>>,
) -> 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<TString<'static>>) -> 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
}

View File

@ -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<TString> = kwargs
.get(Qstr::MP_QSTR_title)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let fee_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_fee_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let account_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_account_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let extra_items: Option<Obj> = kwargs
.get(Qstr::MP_QSTR_extra_items)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let verb_cancel: Option<TString<'static>> = 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(
/// *,

View File

@ -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

View File

@ -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,

View File

@ -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:

View File

@ -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,

View File

@ -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()