1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 15:38:11 +00:00

feat(core/ui): add cancel button to paginated blobs

[no changelog]
This commit is contained in:
Ioan Bizău 2024-11-14 12:35:31 +01:00
parent 8313c30d5a
commit 2bc787a652
9 changed files with 251 additions and 142 deletions

View File

@ -174,6 +174,7 @@ static void _librust_qstrs(void) {
MP_QSTR_buttons__turn_on; MP_QSTR_buttons__turn_on;
MP_QSTR_buttons__view_all_data; MP_QSTR_buttons__view_all_data;
MP_QSTR_can_go_back; MP_QSTR_can_go_back;
MP_QSTR_cancel;
MP_QSTR_cancel_arrow; MP_QSTR_cancel_arrow;
MP_QSTR_cancel_cross; MP_QSTR_cancel_cross;
MP_QSTR_cancel_text; MP_QSTR_cancel_text;

View File

@ -25,94 +25,20 @@ use super::super::{
theme, theme,
}; };
#[derive(Copy, Clone, PartialEq, Eq)] const MENU_ITEM_CANCEL: usize = 0;
pub enum ConfirmAction { const MENU_ITEM_INFO: usize = 1;
Intro,
Menu,
Confirm,
}
impl FlowController for ConfirmAction { // Extra button at the top-right corner of the Action screen
fn index(&'static self) -> usize { #[derive(PartialEq)]
*self as usize pub enum ConfirmActionExtra {
} // Opens a menu which can (optionally) lead to an extra Info screen, or cancel the action
Menu {
fn handle_swipe(&'static self, direction: Direction) -> Decision {
match (self, direction) {
(Self::Intro, Direction::Left) => Self::Menu.swipe(direction),
(Self::Menu, Direction::Right) => Self::Intro.swipe(direction),
(Self::Intro, Direction::Up) => Self::Confirm.swipe(direction),
(Self::Confirm, Direction::Down) => Self::Intro.swipe(direction),
(Self::Confirm, Direction::Left) => Self::Menu.swipe(direction),
_ => self.do_nothing(),
}
}
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Intro, FlowMsg::Info) => Self::Menu.goto(),
(Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(),
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled),
(Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Info),
(Self::Confirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
(Self::Confirm, FlowMsg::Info) => Self::Menu.goto(),
_ => self.do_nothing(),
}
}
}
/// ConfirmAction flow without a separate "Tap to confirm" or "Hold to confirm"
/// screen. Swiping up directly from the intro screen confirms action.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ConfirmActionSimple {
Intro,
Menu,
}
impl FlowController for ConfirmActionSimple {
#[inline]
fn index(&'static self) -> usize {
*self as usize
}
fn handle_swipe(&'static self, direction: Direction) -> Decision {
match (self, direction) {
(Self::Intro, Direction::Left) => Self::Menu.swipe(direction),
(Self::Menu, Direction::Right) => Self::Intro.swipe(direction),
(Self::Intro, Direction::Up) => self.return_msg(FlowMsg::Confirmed),
_ => self.do_nothing(),
}
}
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Intro, FlowMsg::Info) => Self::Menu.goto(),
(Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(),
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled),
(Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Info),
_ => self.do_nothing(),
}
}
}
pub struct ConfirmActionMenu {
verb_cancel: Option<TString<'static>>, verb_cancel: Option<TString<'static>>,
info: bool, has_info: bool,
verb_info: Option<TString<'static>>, verb_info: Option<TString<'static>>,
} },
// Shows a cancel button directly
impl ConfirmActionMenu { Cancel,
pub fn new(
verb_cancel: Option<TString<'static>>,
info: bool,
verb_info: Option<TString<'static>>,
) -> Self {
Self {
verb_cancel,
info,
verb_info,
}
}
} }
pub struct ConfirmActionStrings { pub struct ConfirmActionStrings {
@ -138,6 +64,106 @@ impl ConfirmActionStrings {
} }
} }
/// The simplest form of the ConfirmAction flow:
/// no menu, nor a separate "Tap to confirm" or "Hold to confirm".
#[derive(Copy, Clone, PartialEq, Eq)]
enum ConfirmAction {
Action,
}
impl FlowController for ConfirmAction {
#[inline]
fn index(&'static self) -> usize {
*self as usize
}
fn handle_swipe(&'static self, direction: Direction) -> Decision {
match (self, direction) {
(Self::Action, Direction::Up) => self.return_msg(FlowMsg::Confirmed),
_ => self.do_nothing(),
}
}
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled),
_ => self.do_nothing(),
}
}
}
/// A ConfirmAction flow with a menu which can contain various items
/// as defined by ConfirmActionExtra::Menu.
#[derive(Copy, Clone, PartialEq, Eq)]
enum ConfirmActionWithMenu {
Action,
Menu,
}
impl FlowController for ConfirmActionWithMenu {
#[inline]
fn index(&'static self) -> usize {
*self as usize
}
fn handle_swipe(&'static self, direction: Direction) -> Decision {
match (self, direction) {
(Self::Action, Direction::Left) => Self::Menu.swipe(direction),
(Self::Menu, Direction::Right) => Self::Action.swipe(direction),
(Self::Action, Direction::Up) => self.return_msg(FlowMsg::Confirmed),
_ => self.do_nothing(),
}
}
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Action, FlowMsg::Info) => Self::Menu.goto(),
(Self::Menu, FlowMsg::Cancelled) => Self::Action.swipe_right(),
(Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => self.return_msg(FlowMsg::Cancelled),
(Self::Menu, FlowMsg::Choice(MENU_ITEM_INFO)) => self.return_msg(FlowMsg::Info),
_ => self.do_nothing(),
}
}
}
// A ConfirmAction flow with a menu and
// a separate "Tap to confirm" or "Hold to confirm" screen.
#[derive(Copy, Clone, PartialEq, Eq)]
enum ConfirmActionWithMenuAndConfirmation {
Action,
Menu,
Confirmation,
}
impl FlowController for ConfirmActionWithMenuAndConfirmation {
fn index(&'static self) -> usize {
*self as usize
}
fn handle_swipe(&'static self, direction: Direction) -> Decision {
match (self, direction) {
(Self::Action, Direction::Left) => Self::Menu.swipe(direction),
(Self::Menu, Direction::Right) => Self::Action.swipe(direction),
(Self::Action, Direction::Up) => Self::Confirmation.swipe(direction),
(Self::Confirmation, Direction::Down) => Self::Action.swipe(direction),
(Self::Confirmation, Direction::Left) => Self::Menu.swipe(direction),
_ => self.do_nothing(),
}
}
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Action, FlowMsg::Info) => Self::Menu.goto(),
(Self::Menu, FlowMsg::Cancelled) => Self::Action.swipe_right(),
(Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => self.return_msg(FlowMsg::Cancelled),
(Self::Menu, FlowMsg::Choice(MENU_ITEM_INFO)) => self.return_msg(FlowMsg::Info),
(Self::Confirmation, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
(Self::Confirmation, FlowMsg::Info) => Self::Menu.goto(),
_ => self.do_nothing(),
}
}
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new_confirm_action( pub fn new_confirm_action(
title: TString<'static>, title: TString<'static>,
@ -168,7 +194,11 @@ pub fn new_confirm_action(
new_confirm_action_simple( new_confirm_action_simple(
paragraphs, paragraphs,
ConfirmActionMenu::new(verb_cancel, false, None), ConfirmActionExtra::Menu {
verb_cancel,
has_info: false,
verb_info: None,
},
ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)), ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)),
hold, hold,
None, None,
@ -179,21 +209,29 @@ pub fn new_confirm_action(
#[inline(never)] #[inline(never)]
fn new_confirm_action_uni<T: Component + Paginate + MaybeTrace + 'static>( fn new_confirm_action_uni<T: Component + Paginate + MaybeTrace + 'static>(
content: SwipeContent<SwipePage<T>>, content: SwipeContent<SwipePage<T>>,
menu: ConfirmActionMenu, extra: ConfirmActionExtra,
strings: ConfirmActionStrings, strings: ConfirmActionStrings,
hold: bool, hold: bool,
show_page_counter: bool, show_page_counter: bool,
) -> Result<SwipeFlow, error::Error> { ) -> Result<SwipeFlow, error::Error> {
let (prompt_screen, prompt_pages, flow, page) = let (prompt_screen, prompt_pages, flow, page) =
create_flow(strings.title, strings.prompt_screen, hold); create_flow(strings.title, strings.prompt_screen, hold, &extra);
let mut content_intro = Frame::left_aligned(strings.title, content) let mut content = Frame::left_aligned(strings.title, content)
.with_swipe(Direction::Up, SwipeSettings::default()) .with_swipe(Direction::Up, SwipeSettings::default())
.with_swipe(Direction::Left, SwipeSettings::default()) .with_swipe(Direction::Left, SwipeSettings::default())
.with_vertical_pages() .with_vertical_pages()
.with_menu_button()
.with_footer(TR::instructions__swipe_up.into(), None); .with_footer(TR::instructions__swipe_up.into(), None);
match extra {
ConfirmActionExtra::Menu { .. } => {
content = content.with_menu_button();
}
ConfirmActionExtra::Cancel => {
content = content.with_cancel_button();
}
}
if show_page_counter { if show_page_counter {
fn footer_update_fn<T: Component + Paginate>( fn footer_update_fn<T: Component + Paginate>(
content: &SwipeContent<SwipePage<T>>, content: &SwipeContent<SwipePage<T>>,
@ -205,26 +243,25 @@ fn new_confirm_action_uni<T: Component + Paginate + MaybeTrace + 'static>(
footer.update_page_counter(ctx, current_page, page_count); footer.update_page_counter(ctx, current_page, page_count);
} }
content_intro = content_intro content = content
.with_footer_counter(TR::instructions__swipe_up.into()) .with_footer_counter(TR::instructions__swipe_up.into())
.register_footer_update_fn(footer_update_fn::<T>); .register_footer_update_fn(footer_update_fn::<T>);
} }
if let Some(subtitle) = strings.subtitle { if let Some(subtitle) = strings.subtitle {
content_intro = content_intro.with_subtitle(subtitle); content = content.with_subtitle(subtitle);
} }
let content_intro = content_intro let content = content
.map(move |msg| match msg { .map(move |msg| match msg {
FrameMsg::Button(_) => Some(FlowMsg::Info), FrameMsg::Button(FlowMsg::Info) => Some(FlowMsg::Info),
FrameMsg::Button(FlowMsg::Cancelled) => Some(FlowMsg::Cancelled),
_ => None, _ => None,
}) })
.with_pages(move |intro_pages| intro_pages + prompt_pages); .with_pages(move |intro_pages| intro_pages + prompt_pages);
let flow = flow?.with_page(page, content_intro)?; let flow = flow?.with_page(page, content)?;
let flow = create_menu(flow, extra, prompt_screen)?;
let flow = create_menu(flow, menu, prompt_screen)?;
let flow = create_confirm(flow, strings.subtitle, hold, prompt_screen)?; let flow = create_confirm(flow, strings.subtitle, hold, prompt_screen)?;
Ok(flow) Ok(flow)
@ -234,6 +271,7 @@ fn create_flow(
title: TString<'static>, title: TString<'static>,
prompt_screen: Option<TString<'static>>, prompt_screen: Option<TString<'static>>,
hold: bool, hold: bool,
extra: &ConfirmActionExtra,
) -> ( ) -> (
Option<TString<'static>>, Option<TString<'static>>,
usize, usize,
@ -242,34 +280,46 @@ fn create_flow(
) { ) {
let prompt_screen = prompt_screen.or_else(|| hold.then_some(title)); let prompt_screen = prompt_screen.or_else(|| hold.then_some(title));
let prompt_pages: usize = prompt_screen.is_some().into(); let prompt_pages: usize = prompt_screen.is_some().into();
let initial_page: &dyn FlowController = match (extra, prompt_screen.is_some()) {
let (flow, page): (Result<SwipeFlow, Error>, &dyn FlowController) = if prompt_screen.is_some() { (ConfirmActionExtra::Menu { .. }, false) => &ConfirmActionWithMenu::Action,
(SwipeFlow::new(&ConfirmAction::Intro), &ConfirmAction::Intro) (ConfirmActionExtra::Menu { .. }, true) => &ConfirmActionWithMenuAndConfirmation::Action,
} else { _ => &ConfirmAction::Action,
(
SwipeFlow::new(&ConfirmActionSimple::Intro),
&ConfirmActionSimple::Intro,
)
}; };
(prompt_screen, prompt_pages, flow, page) (
prompt_screen,
prompt_pages,
SwipeFlow::new(initial_page),
initial_page,
)
} }
fn create_menu( fn create_menu(
flow: SwipeFlow, flow: SwipeFlow,
menu: ConfirmActionMenu, extra: ConfirmActionExtra,
prompt_screen: Option<TString<'static>>, prompt_screen: Option<TString<'static>>,
) -> Result<SwipeFlow, Error> { ) -> Result<SwipeFlow, Error> {
if let ConfirmActionExtra::Menu {
verb_cancel,
has_info,
verb_info,
} = extra
{
// NB: The Cancel menu item is always the first,
// because of the MENU_ITEM_CANCEL = 0.
// If we want the cancel item to be somewhere else,
// we would need to account for that and we could not use a constant.
let mut menu_choices = VerticalMenu::empty().danger( let mut menu_choices = VerticalMenu::empty().danger(
theme::ICON_CANCEL, theme::ICON_CANCEL,
menu.verb_cancel.unwrap_or(TR::buttons__cancel.into()), verb_cancel.unwrap_or(TR::buttons__cancel.into()),
); );
if menu.info { if has_info {
// The Info menu item (if present) has to be the 2nd,
// because of MENU_ITEM_INFO = 1!
menu_choices = menu_choices.item( menu_choices = menu_choices.item(
theme::ICON_CHEVRON_RIGHT, theme::ICON_CHEVRON_RIGHT,
menu.verb_info verb_info.unwrap_or(TR::words__title_information.into()),
.unwrap_or(TR::words__title_information.into()),
); );
} }
@ -283,12 +333,16 @@ fn create_menu(
}); });
if prompt_screen.is_some() { if prompt_screen.is_some() {
flow.with_page(&ConfirmAction::Menu, content_menu) flow.with_page(&ConfirmActionWithMenuAndConfirmation::Menu, content_menu)
} else { } else {
flow.with_page(&ConfirmActionSimple::Menu, content_menu) flow.with_page(&ConfirmActionWithMenu::Menu, content_menu)
}
} else {
Ok(flow) // no menu being added
} }
} }
// Create the extra confirmation screen (optional).
fn create_confirm( fn create_confirm(
flow: SwipeFlow, flow: SwipeFlow,
subtitle: Option<TString<'static>>, subtitle: Option<TString<'static>>,
@ -324,7 +378,10 @@ fn create_confirm(
_ => None, _ => None,
}); });
flow.with_page(&ConfirmAction::Confirm, content_confirm) flow.with_page(
&ConfirmActionWithMenuAndConfirmation::Confirmation,
content_confirm,
)
} else { } else {
Ok(flow) Ok(flow)
} }
@ -333,7 +390,7 @@ fn create_confirm(
#[inline(never)] #[inline(never)]
pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>( pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>(
content: T, content: T,
menu: ConfirmActionMenu, extra: ConfirmActionExtra,
strings: ConfirmActionStrings, strings: ConfirmActionStrings,
hold: bool, hold: bool,
page_limit: Option<usize>, page_limit: Option<usize>,
@ -341,7 +398,7 @@ pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>
) -> Result<SwipeFlow, error::Error> { ) -> Result<SwipeFlow, error::Error> {
new_confirm_action_uni( new_confirm_action_uni(
SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)), SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)),
menu, extra,
strings, strings,
hold, hold,
show_page_counter, show_page_counter,

View File

@ -19,7 +19,7 @@ pub mod util;
pub mod warning_hi_prio; pub mod warning_hi_prio;
pub use confirm_action::{ pub use confirm_action::{
new_confirm_action, new_confirm_action_simple, ConfirmActionMenu, ConfirmActionStrings, new_confirm_action, new_confirm_action_simple, ConfirmActionExtra, ConfirmActionStrings,
}; };
#[cfg(feature = "universal_fw")] #[cfg(feature = "universal_fw")]
pub use confirm_fido::new_confirm_fido; pub use confirm_fido::new_confirm_fido;

View File

@ -3,7 +3,7 @@ use super::{
component::{Frame, FrameMsg}, component::{Frame, FrameMsg},
theme, theme,
}, },
ConfirmActionMenu, ConfirmActionStrings, ConfirmActionExtra, ConfirmActionStrings,
}; };
use crate::{ use crate::{
error::Error, error::Error,
@ -52,6 +52,7 @@ pub struct ConfirmBlobParams {
swipe_up: bool, swipe_up: bool,
swipe_down: bool, swipe_down: bool,
swipe_right: bool, swipe_right: bool,
cancel: bool,
} }
impl ConfirmBlobParams { impl ConfirmBlobParams {
@ -84,6 +85,7 @@ impl ConfirmBlobParams {
swipe_up: false, swipe_up: false,
swipe_down: false, swipe_down: false,
swipe_right: false, swipe_right: false,
cancel: false,
} }
} }
@ -152,6 +154,11 @@ impl ConfirmBlobParams {
self self
} }
pub const fn with_cancel(mut self, cancel: bool) -> Self {
self.cancel = cancel;
self
}
pub const fn with_footer( pub const fn with_footer(
mut self, mut self,
instruction: TString<'static>, instruction: TString<'static>,
@ -262,9 +269,19 @@ impl ConfirmBlobParams {
} }
.into_paragraphs(); .into_paragraphs();
let confirm_extra = if self.cancel {
ConfirmActionExtra::Cancel
} else {
ConfirmActionExtra::Menu {
verb_cancel: self.verb_cancel,
has_info: self.info_button,
verb_info: self.verb_info,
}
};
flow::new_confirm_action_simple( flow::new_confirm_action_simple(
paragraphs, paragraphs,
ConfirmActionMenu::new(self.verb_cancel, self.info_button, self.verb_info), confirm_extra,
ConfirmActionStrings::new( ConfirmActionStrings::new(
self.title, self.title,
self.subtitle, self.subtitle,

View File

@ -55,7 +55,7 @@ use crate::{
flow::{ flow::{
new_confirm_action_simple, new_confirm_action_simple,
util::{ConfirmBlobParams, ShowInfoParams}, util::{ConfirmBlobParams, ShowInfoParams},
ConfirmActionMenu, ConfirmActionStrings, ConfirmActionExtra, ConfirmActionStrings,
}, },
theme::ICON_BULLET_CHECKMARK, theme::ICON_BULLET_CHECKMARK,
}, },
@ -246,7 +246,11 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
new_confirm_action_simple( new_confirm_action_simple(
FormattedText::new(ops).vertically_centered(), FormattedText::new(ops).vertically_centered(),
ConfirmActionMenu::new(None, false, None), ConfirmActionExtra::Menu {
verb_cancel: None,
has_info: false,
verb_info: None,
},
ConfirmActionStrings::new(title, None, None, Some(title)), ConfirmActionStrings::new(title, None, None, Some(title)),
false, false,
None, None,
@ -296,6 +300,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
.get(Qstr::MP_QSTR_page_limit) .get(Qstr::MP_QSTR_page_limit)
.unwrap_or_else(|_| Obj::const_none()) .unwrap_or_else(|_| Obj::const_none())
.try_into_option()?; .try_into_option()?;
let cancel: bool = kwargs.get_or(Qstr::MP_QSTR_cancel, false)?;
let (description, description_font) = if page_limit == Some(1) { let (description, description_font) = if page_limit == Some(1) {
( (
@ -318,6 +323,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
.with_chunkify(chunkify) .with_chunkify(chunkify)
.with_page_counter(page_counter) .with_page_counter(page_counter)
.with_page_limit(page_limit) .with_page_limit(page_limit)
.with_cancel(cancel)
.with_prompt(prompt_screen) .with_prompt(prompt_screen)
.with_hold(hold) .with_hold(hold)
.into_flow() .into_flow()
@ -389,7 +395,11 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
flow::new_confirm_action_simple( flow::new_confirm_action_simple(
paragraphs, paragraphs,
ConfirmActionMenu::new(None, false, None), ConfirmActionExtra::Menu {
verb_cancel: None,
has_info: false,
verb_info: None,
},
ConfirmActionStrings::new(title, None, None, None), ConfirmActionStrings::new(title, None, None, None),
false, false,
None, None,
@ -432,7 +442,11 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
new_confirm_action_simple( new_confirm_action_simple(
paragraphs.into_paragraphs(), paragraphs.into_paragraphs(),
ConfirmActionMenu::new(None, false, None), ConfirmActionExtra::Menu {
verb_cancel: None,
has_info: false,
verb_info: None,
},
ConfirmActionStrings::new(title, None, None, hold.then_some(title)), ConfirmActionStrings::new(title, None, None, hold.then_some(title)),
hold, hold,
None, None,
@ -462,7 +476,11 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
new_confirm_action_simple( new_confirm_action_simple(
paragraphs, paragraphs,
ConfirmActionMenu::new(None, false, None), ConfirmActionExtra::Menu {
verb_cancel: None,
has_info: false,
verb_info: None,
},
ConfirmActionStrings::new( ConfirmActionStrings::new(
TR::homescreen__settings_title.into(), TR::homescreen__settings_title.into(),
Some(TR::homescreen__settings_subtitle.into()), Some(TR::homescreen__settings_subtitle.into()),
@ -774,7 +792,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
new_confirm_action_simple( new_confirm_action_simple(
paragraphs.into_paragraphs(), paragraphs.into_paragraphs(),
ConfirmActionMenu::new(None, true, None), ConfirmActionExtra::Menu {
verb_cancel: None,
has_info: true,
verb_info: None,
},
ConfirmActionStrings::new(title, None, None, Some(title)), ConfirmActionStrings::new(title, None, None, Some(title)),
true, true,
None, None,
@ -1091,7 +1113,11 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
new_confirm_action_simple( new_confirm_action_simple(
paragraphs, paragraphs,
ConfirmActionMenu::new(None, false, None), ConfirmActionExtra::Menu {
verb_cancel: None,
has_info: false,
verb_info: None,
},
ConfirmActionStrings::new( ConfirmActionStrings::new(
TR::coinjoin__title.into(), TR::coinjoin__title.into(),
None, None,
@ -1587,6 +1613,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// page_counter: bool = False, /// page_counter: bool = False,
/// prompt_screen: bool = False, /// prompt_screen: bool = False,
/// page_limit: int | None = None, /// page_limit: int | None = None,
/// cancel: bool = False,
/// ) -> LayoutObj[UiResult]: /// ) -> LayoutObj[UiResult]:
/// """Confirm byte sequence data.""" /// """Confirm byte sequence data."""
Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(),

View File

@ -1712,6 +1712,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// page_counter: bool = False, /// page_counter: bool = False,
/// prompt_screen: bool = False, /// prompt_screen: bool = False,
/// page_limit: int | None = None, /// page_limit: int | None = None,
/// cancel: bool = False,
/// ) -> LayoutObj[UiResult]: /// ) -> LayoutObj[UiResult]:
/// """Confirm byte sequence data.""" /// """Confirm byte sequence data."""
Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(),

View File

@ -1806,6 +1806,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// page_counter: bool = False, /// page_counter: bool = False,
/// prompt_screen: bool = False, /// prompt_screen: bool = False,
/// page_limit: int | None = None, /// page_limit: int | None = None,
/// cancel: bool = False,
/// ) -> LayoutObj[UiResult]: /// ) -> LayoutObj[UiResult]:
/// """Confirm byte sequence data.""" /// """Confirm byte sequence data."""
Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(),

View File

@ -71,6 +71,7 @@ def confirm_blob(
page_counter: bool = False, page_counter: bool = False,
prompt_screen: bool = False, prompt_screen: bool = False,
page_limit: int | None = None, page_limit: int | None = None,
cancel: bool = False,
) -> LayoutObj[UiResult]: ) -> LayoutObj[UiResult]:
"""Confirm byte sequence data.""" """Confirm byte sequence data."""
@ -644,6 +645,7 @@ def confirm_blob(
page_counter: bool = False, page_counter: bool = False,
prompt_screen: bool = False, prompt_screen: bool = False,
page_limit: int | None = None, page_limit: int | None = None,
cancel: bool = False,
) -> LayoutObj[UiResult]: ) -> LayoutObj[UiResult]:
"""Confirm byte sequence data.""" """Confirm byte sequence data."""
@ -1216,6 +1218,7 @@ def confirm_blob(
page_counter: bool = False, page_counter: bool = False,
prompt_screen: bool = False, prompt_screen: bool = False,
page_limit: int | None = None, page_limit: int | None = None,
cancel: bool = False,
) -> LayoutObj[UiResult]: ) -> LayoutObj[UiResult]:
"""Confirm byte sequence data.""" """Confirm byte sequence data."""

View File

@ -480,6 +480,7 @@ def confirm_blob(
info_layout = trezorui2.confirm_blob( info_layout = trezorui2.confirm_blob(
title=title, title=title,
data=data, data=data,
subtitle=description,
description=None, description=None,
verb=None, verb=None,
verb_cancel=verb_cancel, verb_cancel=verb_cancel,
@ -488,6 +489,7 @@ def confirm_blob(
chunkify=chunkify, chunkify=chunkify,
page_counter=True, page_counter=True,
prompt_screen=False, prompt_screen=False,
cancel=True,
) )
return with_info( return with_info(