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

refactor(core): move confirm_action to UiFeatures

This commit is contained in:
obrusvit 2024-10-27 16:33:16 +01:00
parent af4c3dc69c
commit 34c374b3bc
15 changed files with 145 additions and 153 deletions

View File

@ -13,7 +13,8 @@ use crate::{
layout::{ layout::{
base::LAYOUT_STATE, base::LAYOUT_STATE,
obj::{LayoutObj, ATTACH_TYPE_OBJ}, obj::{LayoutObj, ATTACH_TYPE_OBJ},
result::{CANCELLED, CONFIRMED, INFO}, util::upy_disable_animation, result::{CANCELLED, CONFIRMED, INFO},
util::upy_disable_animation,
}, },
ui_features::ModelUI, ui_features::ModelUI,
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
@ -24,6 +25,51 @@ use crate::{
// UIFeaturesFirmware` // UIFeaturesFirmware`
// NOTE: `disable_animation` not a part of trait UiFeaturesFirmware // NOTE: `disable_animation` not a part of trait UiFeaturesFirmware
extern "C" fn new_confirm_action(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 action: Option<TString> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<TString> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let subtitle: Option<TString> = kwargs
.get(Qstr::MP_QSTR_subtitle)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let verb: Option<TString> = kwargs
.get(Qstr::MP_QSTR_verb)
.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 hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let hold_danger: bool = kwargs.get_or(Qstr::MP_QSTR_hold_danger, false)?;
let reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
let prompt_screen: bool = kwargs.get_or(Qstr::MP_QSTR_prompt_screen, false)?;
let prompt_title: Option<TString> = kwargs
.get(Qstr::MP_QSTR_prompt_title)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let layout = ModelUI::confirm_action(
title,
action,
description,
subtitle,
verb,
verb_cancel,
hold,
hold_danger,
reverse,
prompt_screen,
prompt_title,
)?;
Ok(LayoutObj::new_root(layout)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: TString = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let prompt: TString = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
@ -200,6 +246,23 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Disable animations, debug builds only.""" /// """Disable animations, debug builds only."""
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(), Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
/// def confirm_action(
/// *,
/// title: str,
/// action: str | None,
/// description: str | None,
/// subtitle: str | None = None,
/// verb: str | None = None,
/// verb_cancel: str | None = None,
/// hold: bool = False,
/// hold_danger: bool = False,
/// reverse: bool = False,
/// prompt_screen: bool = False,
/// prompt_title: str | None = None,
/// ) -> LayoutObj[UiResult]:
/// """Confirm action."""
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
/// def request_bip39( /// def request_bip39(
/// *, /// *,
/// prompt: str, /// prompt: str,

View File

@ -324,40 +324,6 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
extern "C" fn new_confirm_action(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 action: Option<TString> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<TString> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let subtitle: Option<TString> = kwargs
.get(Qstr::MP_QSTR_subtitle)
.unwrap_or(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 reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let prompt_screen: bool = kwargs.get_or(Qstr::MP_QSTR_prompt_screen, false)?;
let prompt_title: TString = kwargs.get_or(Qstr::MP_QSTR_prompt_title, title)?;
let flow = flow::confirm_action::new_confirm_action(
title,
action,
description,
subtitle,
verb_cancel,
reverse,
hold,
prompt_screen,
prompt_title,
)?;
Ok(LayoutObj::new_root(flow)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
@ -1861,5 +1827,5 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// cancel_text: str | None = None, /// cancel_text: str | None = None,
/// ) -> LayoutObj[UiResult]: /// ) -> LayoutObj[UiResult]:
/// """Total summary and hold to confirm.""" /// """Total summary and hold to confirm."""
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, flow::new_confirm_summary).as_obj(), Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
}; };

View File

@ -18,7 +18,8 @@ use crate::{
use super::{ use super::{
component::{ component::{
Bip39Input, Frame, MnemonicKeyboard, PinKeyboard, Slip39Input, SwipeContent, SwipeUpScreen, Bip39Input, Frame, MnemonicKeyboard, PinKeyboard, Slip39Input, SwipeContent, SwipeUpScreen,
}, flow, theme, ModelMercuryFeatures },
flow, theme, ModelMercuryFeatures,
}; };
impl UIFeaturesFirmware for ModelMercuryFeatures { impl UIFeaturesFirmware for ModelMercuryFeatures {

View File

@ -270,42 +270,6 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
Ok(obj.into()) Ok(obj.into())
} }
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<TString> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<TString> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: TString<'static> =
kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.into())?;
let verb_cancel: Option<TString<'static>> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let paragraphs = {
let action = action.unwrap_or("".into());
let description = description.unwrap_or("".into());
let mut paragraphs = ParagraphVecShort::new();
if !reverse {
paragraphs
.add(Paragraph::new(&theme::TEXT_BOLD, action))
.add(Paragraph::new(&theme::TEXT_NORMAL, description));
} else {
paragraphs
.add(Paragraph::new(&theme::TEXT_NORMAL, description))
.add(Paragraph::new(&theme::TEXT_BOLD, action));
}
paragraphs.into_paragraphs()
};
content_in_button_page(title, paragraphs, verb, verb_cancel, hold)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;

View File

@ -6,8 +6,8 @@ use crate::{
strutil::TString, strutil::TString,
ui::{ ui::{
component::{ component::{
text::paragraphs::{Paragraph, Paragraphs}, text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
ComponentExt, Timeout, Component, ComponentExt, Paginate, Timeout,
}, },
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
@ -15,7 +15,10 @@ use crate::{
}; };
use super::{ use super::{
component::{Frame, PassphraseEntry, PinEntry, WordlistEntry, WordlistType}, component::{
ButtonDetails, ButtonPage, Frame, PassphraseEntry, PinEntry, ScrollableFrame,
WordlistEntry, WordlistType,
},
theme, ModelTRFeatures, theme, ModelTRFeatures,
}; };
@ -135,3 +138,40 @@ impl UIFeaturesFirmware for ModelTRFeatures {
Ok(obj) Ok(obj)
} }
} }
/// Function to create and call a `ButtonPage` dialog based on paginable content
/// (e.g. `Paragraphs` or `FormattedText`).
/// Has optional title (supply empty `TString` for that) and hold-to-confirm
/// functionality.
fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
title: TString<'static>,
content: T,
verb: TString<'static>,
verb_cancel: Option<TString<'static>>,
hold: bool,
) -> Result<impl LayoutMaybeTrace, Error> {
// Left button - icon, text or nothing.
let cancel_btn = verb_cancel.map(ButtonDetails::from_text_possible_icon);
// Right button - text or nothing.
// Optional HoldToConfirm
let mut confirm_btn = if !verb.is_empty() {
Some(ButtonDetails::text(verb))
} else {
None
};
if hold {
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
}
let content = ButtonPage::new(content, theme::BG)
.with_cancel_btn(cancel_btn)
.with_confirm_btn(confirm_btn);
let mut frame = ScrollableFrame::new(content);
if !title.is_empty() {
frame = frame.with_title(title);
}
Ok(RootComponent::new(frame))
}

View File

@ -317,54 +317,6 @@ impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
} }
} }
extern "C" fn new_confirm_action(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 action: Option<TString> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<TString> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: Option<TString> = kwargs
.get(Qstr::MP_QSTR_verb)
.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 reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let hold_danger: bool = kwargs.get_or(Qstr::MP_QSTR_hold_danger, false)?;
let paragraphs = {
let action = action.unwrap_or("".into());
let description = description.unwrap_or("".into());
let mut paragraphs = ParagraphVecShort::new();
if !reverse {
paragraphs
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, action))
.add(Paragraph::new(&theme::TEXT_NORMAL, description));
} else {
paragraphs
.add(Paragraph::new(&theme::TEXT_NORMAL, description))
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, action));
}
paragraphs.into_paragraphs()
};
let mut page = if hold {
ButtonPage::new(paragraphs, theme::BG).with_hold()?
} else {
ButtonPage::new(paragraphs, theme::BG).with_cancel_confirm(verb_cancel, verb)
};
if hold && hold_danger {
page = page.with_confirm_style(theme::button_danger())
}
let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
@ -1574,23 +1526,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// from trezorui_api import * /// from trezorui_api import *
/// ///
/// def confirm_action(
/// *,
/// title: str,
/// action: str | None,
/// description: str | None,
/// subtitle: str | None = None,
/// verb: str | None = None,
/// verb_cancel: str | None = None,
/// hold: bool = False,
/// hold_danger: bool = False,
/// reverse: bool = False,
/// prompt_screen: bool = False,
/// prompt_title: str | None = None,
/// ) -> LayoutObj[UiResult]:
/// """Confirm action."""
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
/// def confirm_emphasized( /// def confirm_emphasized(
/// *, /// *,
/// title: str, /// title: str,

View File

@ -5,7 +5,11 @@ use crate::{
strutil::TString, strutil::TString,
translations::TR, translations::TR,
ui::{ ui::{
component::{image::BlendedImage, ComponentExt, Empty, Timeout}, component::{
image::BlendedImage,
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt},
ComponentExt, Empty, Timeout,
},
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
}, },
@ -13,8 +17,8 @@ use crate::{
use super::{ use super::{
component::{ component::{
Bip39Input, Button, ButtonMsg, ButtonStyleSheet, CancelConfirmMsg, IconDialog, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet, CancelConfirmMsg, Frame,
MnemonicKeyboard, PassphraseKeyboard, PinKeyboard, Slip39Input, IconDialog, MnemonicKeyboard, PassphraseKeyboard, PinKeyboard, Slip39Input,
}, },
theme, ModelTTFeatures, theme, ModelTTFeatures,
}; };

View File

@ -2,6 +2,7 @@ use crate::{error::Error, io::BinaryData, micropython::gc::Gc, strutil::TString}
use super::layout::obj::{LayoutMaybeTrace, LayoutObj}; use super::layout::obj::{LayoutMaybeTrace, LayoutObj};
pub trait UIFeaturesFirmware { pub trait UIFeaturesFirmware {
fn confirm_action( fn confirm_action(
title: TString<'static>, title: TString<'static>,

View File

@ -78,6 +78,24 @@ def disable_animation(disable: bool) -> None:
"""Disable animations, debug builds only.""" """Disable animations, debug builds only."""
# rust/src/ui/api/firmware_upy.rs
def confirm_action(
*,
title: str,
action: str | None,
description: str | None,
subtitle: str | None = None,
verb: str | None = None,
verb_cancel: str | None = None,
hold: bool = False,
hold_danger: bool = False,
reverse: bool = False,
prompt_screen: bool = False,
prompt_title: str | None = None,
) -> LayoutObj[UiResult]:
"""Confirm action."""
# rust/src/ui/api/firmware_upy.rs # rust/src/ui/api/firmware_upy.rs
def request_bip39( def request_bip39(
*, *,

View File

@ -44,7 +44,7 @@ def confirm_action(
description = description.format(description_param) description = description.format(description_param)
return raise_if_not_confirmed( return raise_if_not_confirmed(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=title, title=title,
action=action, action=action,
description=description, description=description,

View File

@ -44,7 +44,7 @@ async def confirm_fido_reset() -> bool:
from trezor import TR from trezor import TR
confirm = ui.Layout( confirm = ui.Layout(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=TR.fido__title_reset, title=TR.fido__title_reset,
action=TR.fido__erase_credentials, action=TR.fido__erase_credentials,
description=TR.words__really_wanna, description=TR.words__really_wanna,

View File

@ -70,7 +70,7 @@ def confirm_action(
description = description.format(description_param) description = description.format(description_param)
return raise_if_not_confirmed( return raise_if_not_confirmed(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=title, title=title,
action=action, action=action,
description=description, description=description,
@ -146,7 +146,7 @@ async def prompt_backup() -> bool:
return True return True
result = await interact( result = await interact(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=TR.backup__title_skip, title=TR.backup__title_skip,
action=None, action=None,
description=TR.backup__want_to_skip, description=TR.backup__want_to_skip,

View File

@ -34,7 +34,7 @@ async def confirm_fido(
async def confirm_fido_reset() -> bool: async def confirm_fido_reset() -> bool:
from trezor import TR from trezor import TR
confirm = trezorui2.confirm_action( confirm = trezorui_api.confirm_action(
title=TR.fido__title_reset, title=TR.fido__title_reset,
description=TR.fido__wanna_erase_credentials, description=TR.fido__wanna_erase_credentials,
action=None, action=None,

View File

@ -42,7 +42,7 @@ def confirm_action(
description = description.format(description_param) description = description.format(description_param)
return raise_if_not_confirmed( return raise_if_not_confirmed(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=title, title=title,
action=action, action=action,
description=description, description=description,
@ -108,7 +108,7 @@ async def show_wallet_created_success() -> None:
# TODO cleanup @ redesign # TODO cleanup @ redesign
async def prompt_backup() -> bool: async def prompt_backup() -> bool:
result = await interact( result = await interact(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=TR.words__title_success, title=TR.words__title_success,
action=TR.backup__new_wallet_successfully_created, action=TR.backup__new_wallet_successfully_created,
description=TR.backup__it_should_be_backed_up, description=TR.backup__it_should_be_backed_up,
@ -123,7 +123,7 @@ async def prompt_backup() -> bool:
return True return True
result = await interact( result = await interact(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=TR.words__warning, title=TR.words__warning,
action=TR.backup__want_to_skip, action=TR.backup__want_to_skip,
description=TR.backup__can_back_up_anytime, description=TR.backup__can_back_up_anytime,

View File

@ -53,7 +53,7 @@ async def confirm_fido_reset() -> bool:
from trezor import TR from trezor import TR
confirm = ui.Layout( confirm = ui.Layout(
trezorui2.confirm_action( trezorui_api.confirm_action(
title=TR.fido__title_reset, title=TR.fido__title_reset,
action=TR.fido__erase_credentials, action=TR.fido__erase_credentials,
description=TR.words__really_wanna, description=TR.words__really_wanna,