mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-25 18:28:47 +00:00
refactor(core): merge confirm_summary flow into confirm_output flow
This commit is contained in:
parent
692eee3e07
commit
b747a5bcc9
@ -666,6 +666,10 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_subprompt;
|
MP_QSTR_subprompt;
|
||||||
MP_QSTR_subtext;
|
MP_QSTR_subtext;
|
||||||
MP_QSTR_subtitle;
|
MP_QSTR_subtitle;
|
||||||
|
MP_QSTR_summary_br_code;
|
||||||
|
MP_QSTR_summary_br_name;
|
||||||
|
MP_QSTR_summary_items;
|
||||||
|
MP_QSTR_summary_title;
|
||||||
MP_QSTR_text;
|
MP_QSTR_text;
|
||||||
MP_QSTR_text_confirm;
|
MP_QSTR_text_confirm;
|
||||||
MP_QSTR_text_info;
|
MP_QSTR_text_info;
|
||||||
|
@ -98,7 +98,7 @@ pub struct SwipeFlow {
|
|||||||
/// Current state of the flow.
|
/// Current state of the flow.
|
||||||
state: &'static dyn FlowState,
|
state: &'static dyn FlowState,
|
||||||
/// Store of all screens which are part of the flow.
|
/// Store of all screens which are part of the flow.
|
||||||
store: Vec<GcBox<dyn FlowComponentDynTrait>, 10>,
|
store: Vec<GcBox<dyn FlowComponentDynTrait>, 12>,
|
||||||
/// Swipe detector.
|
/// Swipe detector.
|
||||||
swipe: SwipeDetect,
|
swipe: SwipeDetect,
|
||||||
/// Swipe allowed
|
/// Swipe allowed
|
||||||
|
@ -2,12 +2,14 @@ use heapless::Vec;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error,
|
error,
|
||||||
micropython::{map::Map, obj::Obj, qstr::Qstr, util},
|
micropython::{iter::IterBuf, map::Map, obj::Obj, qstr::Qstr, util},
|
||||||
strutil::TString,
|
strutil::TString,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
button_request::ButtonRequest,
|
button_request::ButtonRequest,
|
||||||
component::{swipe_detect::SwipeSettings, ButtonRequestExt, ComponentExt, SwipeDirection},
|
component::{
|
||||||
|
swipe_detect::SwipeSettings, ButtonRequestExt, ComponentExt, MsgMap, SwipeDirection,
|
||||||
|
},
|
||||||
flow::{
|
flow::{
|
||||||
base::{DecisionBuilder as _, StateChange},
|
base::{DecisionBuilder as _, StateChange},
|
||||||
FlowMsg, FlowState, SwipeFlow,
|
FlowMsg, FlowState, SwipeFlow,
|
||||||
@ -24,11 +26,12 @@ use super::{
|
|||||||
},
|
},
|
||||||
theme,
|
theme,
|
||||||
},
|
},
|
||||||
util::ConfirmBlobParams,
|
util::{ConfirmBlobParams, ShowInfoParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
const MENU_ITEM_CANCEL: usize = 0;
|
const MENU_ITEM_CANCEL: usize = 0;
|
||||||
const MENU_ITEM_ACCOUNT_INFO: usize = 1;
|
const MENU_ITEM_FEE_INFO: usize = 1;
|
||||||
|
const MENU_ITEM_ACCOUNT_INFO: usize = 2;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum ConfirmOutput {
|
pub enum ConfirmOutput {
|
||||||
@ -102,8 +105,8 @@ impl FlowState for ConfirmOutputWithAmount {
|
|||||||
fn handle_event(&'static self, msg: FlowMsg) -> StateChange {
|
fn handle_event(&'static self, msg: FlowMsg) -> StateChange {
|
||||||
match (self, msg) {
|
match (self, msg) {
|
||||||
(_, FlowMsg::Info) => Self::Menu.transit(),
|
(_, FlowMsg::Info) => Self::Menu.transit(),
|
||||||
(Self::Menu, FlowMsg::Choice(0)) => Self::AccountInfo.transit(),
|
(Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::CancelTap.swipe_left(),
|
||||||
(Self::Menu, FlowMsg::Choice(1)) => Self::CancelTap.swipe_left(),
|
(Self::Menu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => Self::AccountInfo.transit(),
|
||||||
(Self::Menu, FlowMsg::Cancelled) => Self::Address.swipe_right(),
|
(Self::Menu, FlowMsg::Cancelled) => Self::Address.swipe_right(),
|
||||||
(Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
(Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
||||||
(_, FlowMsg::Cancelled) => Self::Menu.transit(),
|
(_, FlowMsg::Cancelled) => Self::Menu.transit(),
|
||||||
@ -112,6 +115,97 @@ impl FlowState for ConfirmOutputWithAmount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ConfirmOutputWithSummary {
|
||||||
|
Address,
|
||||||
|
AddressMenu,
|
||||||
|
AddressMenuCancel,
|
||||||
|
Summary,
|
||||||
|
SummaryMenu,
|
||||||
|
SummaryMenuCancel,
|
||||||
|
FeeInfo,
|
||||||
|
Hold,
|
||||||
|
HoldMenu,
|
||||||
|
HoldMenuCancel,
|
||||||
|
AccountInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowState for ConfirmOutputWithSummary {
|
||||||
|
#[inline]
|
||||||
|
fn index(&'static self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange {
|
||||||
|
match (self, direction) {
|
||||||
|
(Self::Address, SwipeDirection::Left) => Self::AddressMenu.swipe(direction),
|
||||||
|
(Self::Address, SwipeDirection::Up) => Self::Summary.swipe(direction),
|
||||||
|
(Self::AccountInfo, SwipeDirection::Right) => Self::AddressMenu.swipe(direction),
|
||||||
|
(Self::Summary, SwipeDirection::Left) => Self::SummaryMenu.swipe(direction),
|
||||||
|
(Self::Summary, SwipeDirection::Up) => Self::Hold.swipe(direction),
|
||||||
|
(Self::Summary, SwipeDirection::Down) => Self::Address.swipe(direction),
|
||||||
|
(Self::FeeInfo, SwipeDirection::Right) => Self::SummaryMenu.swipe(direction),
|
||||||
|
(Self::Hold, SwipeDirection::Left) => Self::HoldMenu.swipe(direction),
|
||||||
|
(Self::Hold, SwipeDirection::Down) => Self::Summary.swipe(direction),
|
||||||
|
_ => self.do_nothing(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&'static self, msg: FlowMsg) -> StateChange {
|
||||||
|
match (self, msg) {
|
||||||
|
(Self::Address, FlowMsg::Info) => Self::AddressMenu.transit(),
|
||||||
|
(Self::AddressMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => {
|
||||||
|
Self::AddressMenuCancel.swipe_left()
|
||||||
|
}
|
||||||
|
(Self::AddressMenuCancel, FlowMsg::Cancelled) => Self::AddressMenu.swipe_right(),
|
||||||
|
(Self::Summary, FlowMsg::Info) => Self::SummaryMenu.transit(),
|
||||||
|
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => {
|
||||||
|
Self::SummaryMenuCancel.swipe_left()
|
||||||
|
}
|
||||||
|
(Self::SummaryMenuCancel, FlowMsg::Cancelled) => Self::SummaryMenu.swipe_right(),
|
||||||
|
(Self::Hold, FlowMsg::Info) => Self::HoldMenu.transit(),
|
||||||
|
(Self::HoldMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => {
|
||||||
|
Self::HoldMenuCancel.swipe_left()
|
||||||
|
}
|
||||||
|
(Self::HoldMenuCancel, FlowMsg::Cancelled) => Self::HoldMenu.swipe_right(),
|
||||||
|
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => Self::FeeInfo.swipe_left(),
|
||||||
|
(Self::AddressMenu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => {
|
||||||
|
Self::AccountInfo.swipe_left()
|
||||||
|
}
|
||||||
|
(Self::AddressMenu, FlowMsg::Cancelled) => Self::Address.swipe_right(),
|
||||||
|
(Self::SummaryMenu, FlowMsg::Cancelled) => Self::Summary.swipe_right(),
|
||||||
|
(Self::FeeInfo, FlowMsg::Cancelled) => Self::SummaryMenu.swipe_right(),
|
||||||
|
(Self::HoldMenu, FlowMsg::Cancelled) => Self::Hold.swipe_right(),
|
||||||
|
(
|
||||||
|
Self::AddressMenuCancel | Self::SummaryMenuCancel | Self::HoldMenuCancel,
|
||||||
|
FlowMsg::Confirmed,
|
||||||
|
) => self.return_msg(FlowMsg::Cancelled),
|
||||||
|
(Self::Address, FlowMsg::Cancelled) => Self::AddressMenu.transit(),
|
||||||
|
(Self::Summary, FlowMsg::Cancelled) => Self::SummaryMenu.transit(),
|
||||||
|
(Self::Hold, FlowMsg::Cancelled) => Self::HoldMenu.transit(),
|
||||||
|
(Self::Hold, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||||
|
_ => self.do_nothing(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cancel_page(
|
||||||
|
) -> MsgMap<Frame<SwipeContent<PromptScreen>>, impl Fn(FrameMsg<PromptMsg>) -> Option<FlowMsg>> {
|
||||||
|
Frame::left_aligned(
|
||||||
|
TR::send__cancel_sign.into(),
|
||||||
|
SwipeContent::new(PromptScreen::new_tap_to_cancel()),
|
||||||
|
)
|
||||||
|
.with_cancel_button()
|
||||||
|
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||||
|
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||||
|
.with_swipe(SwipeDirection::Left, SwipeSettings::default())
|
||||||
|
.map(|msg| match msg {
|
||||||
|
FrameMsg::Content(PromptMsg::Confirmed) => Some(FlowMsg::Confirmed),
|
||||||
|
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
pub extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
pub extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, new_confirm_output_obj) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, new_confirm_output_obj) }
|
||||||
@ -119,9 +213,11 @@ pub extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
|
|
||||||
fn new_confirm_output_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
fn new_confirm_output_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||||
let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
|
let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
|
||||||
|
|
||||||
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
||||||
let account_path: Option<TString> =
|
let account_path: Option<TString> =
|
||||||
kwargs.get(Qstr::MP_QSTR_account_path)?.try_into_option()?;
|
kwargs.get(Qstr::MP_QSTR_account_path)?.try_into_option()?;
|
||||||
|
|
||||||
let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?;
|
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 br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
|
||||||
|
|
||||||
@ -131,6 +227,18 @@ fn new_confirm_output_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
|
|||||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||||
let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
|
let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
|
||||||
|
|
||||||
|
let summary_items: Obj = kwargs.get(Qstr::MP_QSTR_summary_items)?;
|
||||||
|
let fee_items: Obj = kwargs.get(Qstr::MP_QSTR_fee_items)?;
|
||||||
|
|
||||||
|
let summary_title: Option<TString> =
|
||||||
|
kwargs.get(Qstr::MP_QSTR_summary_title)?.try_into_option()?;
|
||||||
|
let summary_br_name: Option<TString> = kwargs
|
||||||
|
.get(Qstr::MP_QSTR_summary_br_name)?
|
||||||
|
.try_into_option()?;
|
||||||
|
let summary_br_code: Option<u16> = kwargs
|
||||||
|
.get(Qstr::MP_QSTR_summary_br_code)?
|
||||||
|
.try_into_option()?;
|
||||||
|
|
||||||
let cancel_text: Option<TString> = kwargs.get(Qstr::MP_QSTR_cancel_text)?.try_into_option()?;
|
let cancel_text: Option<TString> = kwargs.get(Qstr::MP_QSTR_cancel_text)?.try_into_option()?;
|
||||||
|
|
||||||
// Address
|
// Address
|
||||||
@ -143,27 +251,27 @@ fn new_confirm_output_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
|
|||||||
.into_layout()?
|
.into_layout()?
|
||||||
.one_button_request(ButtonRequest::from_num(br_code, br_name));
|
.one_button_request(ButtonRequest::from_num(br_code, br_name));
|
||||||
|
|
||||||
// Menu
|
// AddressMenu
|
||||||
let mut menu = VerticalMenu::empty();
|
let mut address_menu = VerticalMenu::empty();
|
||||||
let mut menu_items = Vec::<usize, 3>::new();
|
let mut address_menu_items = Vec::<usize, 2>::new();
|
||||||
if account.is_some() && account_path.is_some() {
|
if account.is_some() && account_path.is_some() {
|
||||||
menu = menu.item(
|
address_menu = address_menu.item(
|
||||||
theme::ICON_CHEVRON_RIGHT,
|
theme::ICON_CHEVRON_RIGHT,
|
||||||
TR::address_details__account_info.into(),
|
TR::address_details__account_info.into(),
|
||||||
);
|
);
|
||||||
unwrap!(menu_items.push(MENU_ITEM_ACCOUNT_INFO));
|
unwrap!(address_menu_items.push(MENU_ITEM_ACCOUNT_INFO));
|
||||||
}
|
}
|
||||||
menu = menu.danger(
|
address_menu = address_menu.danger(
|
||||||
theme::ICON_CANCEL,
|
theme::ICON_CANCEL,
|
||||||
cancel_text.unwrap_or(TR::send__cancel_sign.into()),
|
cancel_text.unwrap_or(TR::send__cancel_sign.into()),
|
||||||
);
|
);
|
||||||
unwrap!(menu_items.push(MENU_ITEM_CANCEL));
|
unwrap!(address_menu_items.push(MENU_ITEM_CANCEL));
|
||||||
let content_menu = Frame::left_aligned(TString::empty(), menu)
|
let content_address_menu = Frame::left_aligned(TString::empty(), address_menu)
|
||||||
.with_cancel_button()
|
.with_cancel_button()
|
||||||
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||||
.map(move |msg| match msg {
|
.map(move |msg| match msg {
|
||||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => {
|
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => {
|
||||||
let selected_item = menu_items[i];
|
let selected_item = address_menu_items[i];
|
||||||
Some(FlowMsg::Choice(selected_item))
|
Some(FlowMsg::Choice(selected_item))
|
||||||
}
|
}
|
||||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||||
@ -173,21 +281,6 @@ fn new_confirm_output_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
|
|||||||
let ad = AddressDetails::new(TR::send__send_from.into(), account, account_path)?;
|
let ad = AddressDetails::new(TR::send__send_from.into(), account, account_path)?;
|
||||||
let content_account = ad.map(|_| Some(FlowMsg::Cancelled));
|
let content_account = ad.map(|_| Some(FlowMsg::Cancelled));
|
||||||
|
|
||||||
// CancelTap
|
|
||||||
let content_cancel_tap = Frame::left_aligned(
|
|
||||||
TR::send__cancel_sign.into(),
|
|
||||||
SwipeContent::new(PromptScreen::new_tap_to_cancel()),
|
|
||||||
)
|
|
||||||
.with_cancel_button()
|
|
||||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
|
||||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
|
||||||
.with_swipe(SwipeDirection::Left, SwipeSettings::default())
|
|
||||||
.map(|msg| match msg {
|
|
||||||
FrameMsg::Content(PromptMsg::Confirmed) => Some(FlowMsg::Confirmed),
|
|
||||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let res = if amount.is_some() {
|
let res = if amount.is_some() {
|
||||||
let content_amount = ConfirmBlobParams::new(
|
let content_amount = ConfirmBlobParams::new(
|
||||||
TR::words__amount.into(),
|
TR::words__amount.into(),
|
||||||
@ -205,15 +298,121 @@ fn new_confirm_output_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
|
|||||||
SwipeFlow::new(&ConfirmOutputWithAmount::Address)?
|
SwipeFlow::new(&ConfirmOutputWithAmount::Address)?
|
||||||
.with_page(&ConfirmOutputWithAmount::Address, content_address)?
|
.with_page(&ConfirmOutputWithAmount::Address, content_address)?
|
||||||
.with_page(&ConfirmOutputWithAmount::Amount, content_amount)?
|
.with_page(&ConfirmOutputWithAmount::Amount, content_amount)?
|
||||||
.with_page(&ConfirmOutputWithAmount::Menu, content_menu)?
|
.with_page(&ConfirmOutputWithAmount::Menu, content_address_menu)?
|
||||||
.with_page(&ConfirmOutputWithAmount::AccountInfo, content_account)?
|
.with_page(&ConfirmOutputWithAmount::AccountInfo, content_account)?
|
||||||
.with_page(&ConfirmOutputWithAmount::CancelTap, content_cancel_tap)?
|
.with_page(&ConfirmOutputWithAmount::CancelTap, get_cancel_page())?
|
||||||
|
} else if summary_items != Obj::const_none() {
|
||||||
|
// Summary
|
||||||
|
let mut summary =
|
||||||
|
ShowInfoParams::new(summary_title.unwrap_or(TR::words__title_summary.into()))
|
||||||
|
.with_menu_button()
|
||||||
|
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||||
|
.with_swipe_up()
|
||||||
|
.with_swipe_down();
|
||||||
|
for pair in IterBuf::new().try_iterate(summary_items)? {
|
||||||
|
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||||
|
summary = unwrap!(summary.add(label, value));
|
||||||
|
}
|
||||||
|
let content_summary = summary
|
||||||
|
.into_layout()?
|
||||||
|
.one_button_request(ButtonRequest::from_num(
|
||||||
|
summary_br_code.unwrap(),
|
||||||
|
summary_br_name.unwrap(),
|
||||||
|
))
|
||||||
|
.with_pages(|summary_pages| summary_pages + 1);
|
||||||
|
|
||||||
|
// Hold
|
||||||
|
let content_hold = Frame::left_aligned(
|
||||||
|
TR::send__sign_transaction.into(),
|
||||||
|
SwipeContent::new(PromptScreen::new_hold_to_confirm()),
|
||||||
|
)
|
||||||
|
.with_menu_button()
|
||||||
|
.with_footer(TR::instructions__hold_to_sign.into(), None)
|
||||||
|
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||||
|
.with_swipe(SwipeDirection::Left, SwipeSettings::default())
|
||||||
|
.map(|msg| match msg {
|
||||||
|
FrameMsg::Content(PromptMsg::Confirmed) => Some(FlowMsg::Confirmed),
|
||||||
|
FrameMsg::Button(_) => Some(FlowMsg::Info),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// FeeInfo
|
||||||
|
let mut has_fee_info = false;
|
||||||
|
let mut fee = ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button();
|
||||||
|
if fee_items != Obj::const_none() {
|
||||||
|
for pair in IterBuf::new().try_iterate(fee_items)? {
|
||||||
|
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||||
|
fee = unwrap!(fee.add(label, value));
|
||||||
|
has_fee_info = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let content_fee = fee.into_layout()?;
|
||||||
|
|
||||||
|
// SummaryMenu
|
||||||
|
let mut summary_menu = VerticalMenu::empty();
|
||||||
|
let mut summary_menu_items = Vec::<usize, 2>::new();
|
||||||
|
if has_fee_info {
|
||||||
|
summary_menu = summary_menu.item(
|
||||||
|
theme::ICON_CHEVRON_RIGHT,
|
||||||
|
TR::confirm_total__title_fee.into(),
|
||||||
|
);
|
||||||
|
unwrap!(summary_menu_items.push(MENU_ITEM_FEE_INFO));
|
||||||
|
}
|
||||||
|
summary_menu = summary_menu.danger(
|
||||||
|
theme::ICON_CANCEL,
|
||||||
|
cancel_text.unwrap_or(TR::send__cancel_sign.into()),
|
||||||
|
);
|
||||||
|
unwrap!(summary_menu_items.push(MENU_ITEM_CANCEL));
|
||||||
|
let content_summary_menu = Frame::left_aligned(TString::empty(), summary_menu)
|
||||||
|
.with_cancel_button()
|
||||||
|
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||||
|
.map(move |msg| match msg {
|
||||||
|
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => {
|
||||||
|
let selected_item = summary_menu_items[i];
|
||||||
|
Some(FlowMsg::Choice(selected_item))
|
||||||
|
}
|
||||||
|
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||||
|
});
|
||||||
|
|
||||||
|
// HoldMenu
|
||||||
|
let hold_menu = VerticalMenu::empty().danger(
|
||||||
|
theme::ICON_CANCEL,
|
||||||
|
cancel_text.unwrap_or(TR::send__cancel_sign.into()),
|
||||||
|
);
|
||||||
|
let content_hold_menu = Frame::left_aligned(TString::empty(), hold_menu)
|
||||||
|
.with_cancel_button()
|
||||||
|
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||||
|
.map(move |msg| match msg {
|
||||||
|
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(_)) => {
|
||||||
|
Some(FlowMsg::Choice(MENU_ITEM_CANCEL))
|
||||||
|
}
|
||||||
|
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||||
|
});
|
||||||
|
|
||||||
|
SwipeFlow::new(&ConfirmOutputWithSummary::Address)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::Address, content_address)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::AddressMenu, content_address_menu)?
|
||||||
|
.with_page(
|
||||||
|
&ConfirmOutputWithSummary::AddressMenuCancel,
|
||||||
|
get_cancel_page(),
|
||||||
|
)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::Summary, content_summary)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::SummaryMenu, content_summary_menu)?
|
||||||
|
.with_page(
|
||||||
|
&ConfirmOutputWithSummary::SummaryMenuCancel,
|
||||||
|
get_cancel_page(),
|
||||||
|
)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::FeeInfo, content_fee)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::Hold, content_hold)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::HoldMenu, content_hold_menu)?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::HoldMenuCancel, get_cancel_page())?
|
||||||
|
.with_page(&ConfirmOutputWithSummary::AccountInfo, content_account)?
|
||||||
} else {
|
} else {
|
||||||
SwipeFlow::new(&ConfirmOutput::Address)?
|
SwipeFlow::new(&ConfirmOutput::Address)?
|
||||||
.with_page(&ConfirmOutput::Address, content_address)?
|
.with_page(&ConfirmOutput::Address, content_address)?
|
||||||
.with_page(&ConfirmOutput::Menu, content_menu)?
|
.with_page(&ConfirmOutput::Menu, content_address_menu)?
|
||||||
.with_page(&ConfirmOutput::AccountInfo, content_account)?
|
.with_page(&ConfirmOutput::AccountInfo, content_account)?
|
||||||
.with_page(&ConfirmOutput::CancelTap, content_cancel_tap)?
|
.with_page(&ConfirmOutput::CancelTap, get_cancel_page())?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(LayoutObj::new(res)?.into())
|
Ok(LayoutObj::new(res)?.into())
|
||||||
|
@ -53,7 +53,6 @@ impl FlowState for ConfirmSummary {
|
|||||||
(Self::Summary, SwipeDirection::Up) => Self::Hold.swipe(direction),
|
(Self::Summary, SwipeDirection::Up) => Self::Hold.swipe(direction),
|
||||||
(Self::Hold, SwipeDirection::Down) => Self::Summary.swipe(direction),
|
(Self::Hold, SwipeDirection::Down) => Self::Summary.swipe(direction),
|
||||||
(Self::Menu, SwipeDirection::Right) => Self::Summary.swipe(direction),
|
(Self::Menu, SwipeDirection::Right) => Self::Summary.swipe(direction),
|
||||||
(Self::Menu, SwipeDirection::Left) => Self::FeeInfo.swipe(direction),
|
|
||||||
(Self::AccountInfo | Self::FeeInfo | Self::CancelTap, SwipeDirection::Right) => {
|
(Self::AccountInfo | Self::FeeInfo | Self::CancelTap, SwipeDirection::Right) => {
|
||||||
Self::Menu.swipe(direction)
|
Self::Menu.swipe(direction)
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,7 @@ pub struct ShowInfoParams {
|
|||||||
footer_description: Option<TString<'static>>,
|
footer_description: Option<TString<'static>>,
|
||||||
chunkify: bool,
|
chunkify: bool,
|
||||||
swipe_up: bool,
|
swipe_up: bool,
|
||||||
|
swipe_down: bool,
|
||||||
items: Vec<(TString<'static>, TString<'static>), 4>,
|
items: Vec<(TString<'static>, TString<'static>), 4>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +168,7 @@ impl ShowInfoParams {
|
|||||||
footer_description: None,
|
footer_description: None,
|
||||||
chunkify: false,
|
chunkify: false,
|
||||||
swipe_up: false,
|
swipe_up: false,
|
||||||
|
swipe_down: false,
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,6 +220,11 @@ impl ShowInfoParams {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn with_swipe_down(mut self) -> Self {
|
||||||
|
self.swipe_down = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn into_layout(
|
pub fn into_layout(
|
||||||
self,
|
self,
|
||||||
@ -268,6 +275,10 @@ impl ShowInfoParams {
|
|||||||
frame = frame.with_swipe(SwipeDirection::Up, SwipeSettings::default());
|
frame = frame.with_swipe(SwipeDirection::Up, SwipeSettings::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.swipe_down {
|
||||||
|
frame = frame.with_swipe(SwipeDirection::Down, SwipeSettings::default());
|
||||||
|
}
|
||||||
|
|
||||||
frame = frame.with_vertical_pages();
|
frame = frame.with_vertical_pages();
|
||||||
|
|
||||||
Ok(frame.map(move |msg| {
|
Ok(frame.map(move |msg| {
|
||||||
|
@ -1764,9 +1764,14 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// account_path: str | None,
|
/// account_path: str | None,
|
||||||
/// br_code: ButtonRequestType,
|
/// br_code: ButtonRequestType,
|
||||||
/// br_name: str,
|
/// br_name: str,
|
||||||
|
/// summary_items: Iterable[tuple[str, str]] | None = None,
|
||||||
|
/// fee_items: Iterable[tuple[str, str]] | None = None,
|
||||||
|
/// summary_title: str | None = None,
|
||||||
|
/// summary_br_code: ButtonRequestType | None = None,
|
||||||
|
/// summary_br_name: str | None = None,
|
||||||
/// cancel_text: str | None = None,
|
/// cancel_text: str | None = None,
|
||||||
/// ) -> LayoutObj[UiResult]:
|
/// ) -> LayoutObj[UiResult]:
|
||||||
/// """Confirm recipient."""
|
/// """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, flow::new_confirm_output).as_obj(),
|
Qstr::MP_QSTR_flow_confirm_output => obj_fn_kw!(0, flow::new_confirm_output).as_obj(),
|
||||||
|
|
||||||
/// def flow_confirm_summary(
|
/// def flow_confirm_summary(
|
||||||
|
@ -585,9 +585,14 @@ def flow_confirm_output(
|
|||||||
account_path: str | None,
|
account_path: str | None,
|
||||||
br_code: ButtonRequestType,
|
br_code: ButtonRequestType,
|
||||||
br_name: str,
|
br_name: str,
|
||||||
|
summary_items: Iterable[tuple[str, str]] | None = None,
|
||||||
|
fee_items: Iterable[tuple[str, str]] | None = None,
|
||||||
|
summary_title: str | None = None,
|
||||||
|
summary_br_code: ButtonRequestType | None = None,
|
||||||
|
summary_br_name: str | None = None,
|
||||||
cancel_text: str | None = None,
|
cancel_text: str | None = None,
|
||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
"""Confirm recipient."""
|
"""Confirm the recipient, (optionally) confirm the amount and (optionally) confirm the summary and present a Hold to Sign page."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
|
@ -648,14 +648,19 @@ async def confirm_output(
|
|||||||
await raise_if_not_confirmed(
|
await raise_if_not_confirmed(
|
||||||
RustLayout(
|
RustLayout(
|
||||||
trezorui2.flow_confirm_output(
|
trezorui2.flow_confirm_output(
|
||||||
|
title=title,
|
||||||
address=address,
|
address=address,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
title=title,
|
|
||||||
chunkify=chunkify,
|
chunkify=chunkify,
|
||||||
account=source_account,
|
account=source_account,
|
||||||
account_path=source_account_path,
|
account_path=source_account_path,
|
||||||
br_code=br_code,
|
br_code=br_code,
|
||||||
br_name="confirm_output",
|
br_name="confirm_output",
|
||||||
|
summary_items=None,
|
||||||
|
fee_items=None,
|
||||||
|
summary_title=None,
|
||||||
|
summary_br_name=None,
|
||||||
|
summary_br_code=None,
|
||||||
cancel_text=cancel_text,
|
cancel_text=cancel_text,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1023,21 +1028,28 @@ if not utils.BITCOIN_ONLY:
|
|||||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||||
chunkify: bool = False,
|
chunkify: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_output(
|
await raise_if_not_confirmed(
|
||||||
recipient,
|
RustLayout(
|
||||||
title=TR.words__recipient,
|
trezorui2.flow_confirm_output(
|
||||||
chunkify=chunkify,
|
title=TR.words__recipient,
|
||||||
cancel_text=TR.buttons__cancel,
|
address=recipient,
|
||||||
br_code=ButtonRequestType.Other,
|
amount=None,
|
||||||
)
|
chunkify=chunkify,
|
||||||
|
account=None,
|
||||||
await _confirm_summary(
|
account_path=None,
|
||||||
items=(
|
br_code=ButtonRequestType.Other,
|
||||||
(TR.words__amount, total_amount),
|
br_name="confirm_output",
|
||||||
(TR.send__maximum_fee, maximum_fee),
|
summary_items=(
|
||||||
),
|
(TR.words__amount, total_amount),
|
||||||
fee_items=fee_info_items,
|
(TR.send__maximum_fee, maximum_fee),
|
||||||
cancel_text=TR.buttons__cancel,
|
),
|
||||||
|
fee_items=fee_info_items,
|
||||||
|
summary_title=TR.words__title_summary,
|
||||||
|
summary_br_name="confirm_total",
|
||||||
|
summary_br_code=ButtonRequestType.SignTx,
|
||||||
|
cancel_text=TR.buttons__cancel,
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def confirm_ethereum_staking_tx(
|
async def confirm_ethereum_staking_tx(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"merkle_root": "14c1d561c00b83c685a89d37be68d94dae0fb8b88835d346c877e6246fe022e3",
|
"merkle_root": "ec4c2be9904443c49bf659e69670ffb2d979fa9e4335fb3cda666a08ce27c16c",
|
||||||
"datetime": "2024-08-09T07:44:08.162423",
|
"datetime": "2024-08-22T07:03:54.917145",
|
||||||
"commit": "89a3ce360514eff165fe0fcb0c2a218b88d85e07"
|
"commit": "bfa6f2c3e44156eea17c08bb09ce12f2307bcc0f"
|
||||||
},
|
},
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
|
@ -454,7 +454,7 @@ class EthereumFlow:
|
|||||||
if cancel:
|
if cancel:
|
||||||
self.debug.press_no()
|
self.debug.press_no()
|
||||||
else:
|
else:
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
yield
|
yield
|
||||||
TR.assert_equals(
|
TR.assert_equals(
|
||||||
self.debug.wait_layout().title(), "words__title_summary"
|
self.debug.wait_layout().title(), "words__title_summary"
|
||||||
|
Loading…
Reference in New Issue
Block a user