1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-29 19:08:12 +00:00

refactor(core): move selectors to UiFeatures

This commit is contained in:
obrusvit 2024-10-28 10:07:11 +01:00
parent 34c374b3bc
commit 5aeab55429
16 changed files with 205 additions and 257 deletions

View File

@ -14,7 +14,7 @@ use crate::{
base::LAYOUT_STATE, base::LAYOUT_STATE,
obj::{LayoutObj, ATTACH_TYPE_OBJ}, obj::{LayoutObj, ATTACH_TYPE_OBJ},
result::{CANCELLED, CONFIRMED, INFO}, result::{CANCELLED, CONFIRMED, INFO},
util::upy_disable_animation, util::{upy_disable_animation, RecoveryType},
}, },
ui_features::ModelUI, ui_features::ModelUI,
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
@ -118,6 +118,29 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m
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_select_word(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 description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let words: [TString<'static>; 3] = util::iter_into_array(words_iterable)?;
let layout = ModelUI::select_word(title, description, words)?;
Ok(LayoutObj::new_root(layout)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let recovery_type: RecoveryType = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
let layout = ModelUI::select_word_count(recovery_type)?;
Ok(LayoutObj::new_root(layout)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn show_info(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()?;
@ -299,6 +322,24 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Passphrase input keyboard.""" /// """Passphrase input keyboard."""
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(), Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(),
/// def select_word(
/// *,
/// title: str,
/// description: str,
/// words: Iterable[str],
/// ) -> LayoutObj[int]:
/// """Select mnemonic word from three possibilities - seed check after backup. The
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
/// def select_word_count(
/// *,
/// recovery_type: RecoveryType,
/// ) -> LayoutObj[int | str]: # TR returns str
/// """Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
/// For unlocking a repeated backup, select from 20 or 33."""
Qstr::MP_QSTR_select_word_count => obj_fn_kw!(0, new_select_word_count).as_obj(),
/// def show_info( /// def show_info(
/// *, /// *,
/// title: str, /// title: str,

View File

@ -1162,20 +1162,6 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
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_select_word(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 description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let words: [TString; 3] = util::iter_into_array(words_iterable)?;
let content = VerticalMenu::select_word(words);
let frame_with_menu = Frame::left_aligned(title, content).with_subtitle(description);
Ok(LayoutObj::new(frame_with_menu)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_show_checklist(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()?;
@ -1226,23 +1212,6 @@ extern "C" fn new_show_tutorial() -> Obj {
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
} }
extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let recovery_type: RecoveryType = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
let content = if matches!(recovery_type, RecoveryType::UnlockRepeatedBackup) {
SelectWordCount::new_multishare()
} else {
SelectWordCount::new_all()
};
let obj = LayoutObj::new(Frame::left_aligned(
TR::recovery__num_of_words.into(),
content,
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_group_share_success( extern "C" fn new_show_group_share_success(
n_args: usize, n_args: usize,
args: *const Obj, args: *const Obj,
@ -1617,16 +1586,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Confirm coinjoin authorization.""" /// """Confirm coinjoin authorization."""
Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(), Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(),
/// def select_word(
/// *,
/// title: str,
/// description: str,
/// words: Iterable[str],
/// ) -> LayoutObj[int]:
/// """Select mnemonic word from three possibilities - seed check after backup. The
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
/// def flow_prompt_backup() -> LayoutObj[UiResult]: /// def flow_prompt_backup() -> LayoutObj[UiResult]:
/// """Prompt a user to create backup with an option to skip.""" /// """Prompt a user to create backup with an option to skip."""
Qstr::MP_QSTR_flow_prompt_backup => obj_fn_0!(new_prompt_backup).as_obj(), Qstr::MP_QSTR_flow_prompt_backup => obj_fn_0!(new_prompt_backup).as_obj(),
@ -1688,14 +1647,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Device recovery homescreen.""" /// """Device recovery homescreen."""
Qstr::MP_QSTR_flow_continue_recovery => obj_fn_kw!(0, new_continue_recovery).as_obj(), Qstr::MP_QSTR_flow_continue_recovery => obj_fn_kw!(0, new_continue_recovery).as_obj(),
/// def select_word_count(
/// *,
/// recovery_type: RecoveryType,
/// ) -> LayoutObj[int | str]: # merucry returns int
/// """Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
/// For unlocking a repeated backup, select from 20 or 33."""
Qstr::MP_QSTR_select_word_count => obj_fn_kw!(0, new_select_word_count).as_obj(),
/// def show_group_share_success( /// def show_group_share_success(
/// *, /// *,
/// lines: Iterable[str] /// lines: Iterable[str]

View File

@ -10,14 +10,18 @@ use crate::{
text::paragraphs::{Paragraph, Paragraphs}, text::paragraphs::{Paragraph, Paragraphs},
}, },
geometry::Direction, geometry::Direction,
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, layout::{
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
util::RecoveryType,
},
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
}, },
}; };
use super::{ use super::{
component::{ component::{
Bip39Input, Frame, MnemonicKeyboard, PinKeyboard, Slip39Input, SwipeContent, SwipeUpScreen, Bip39Input, Frame, MnemonicKeyboard, PinKeyboard, SelectWordCount, Slip39Input,
SwipeContent, SwipeUpScreen, VerticalMenu,
}, },
flow, theme, ModelMercuryFeatures, flow, theme, ModelMercuryFeatures,
}; };
@ -105,6 +109,30 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
Ok(flow) Ok(flow)
} }
fn select_word(
title: TString<'static>,
description: TString<'static>,
words: [TString<'static>; 3],
) -> Result<impl LayoutMaybeTrace, Error> {
let content = VerticalMenu::select_word(words);
let layout =
RootComponent::new(Frame::left_aligned(title, content).with_subtitle(description));
Ok(layout)
}
fn select_word_count(recovery_type: RecoveryType) -> Result<impl LayoutMaybeTrace, Error> {
let content = if matches!(recovery_type, RecoveryType::UnlockRepeatedBackup) {
SelectWordCount::new_multishare()
} else {
SelectWordCount::new_all()
};
let layout = RootComponent::new(Frame::left_aligned(
TR::recovery__num_of_words.into(),
content,
));
Ok(layout)
}
fn show_info( fn show_info(
title: TString<'static>, title: TString<'static>,
description: TString<'static>, description: TString<'static>,

View File

@ -1197,29 +1197,6 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
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_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| {
// we ignore passed in `title` and use `description` in its place
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
// There are only 3 words, but SimpleChoice requires 5 elements
let words: Vec<TString<'static>, 5> = util::iter_into_vec(words_iterable)?;
// Returning the index of the selected word, not the word itself
let obj = LayoutObj::new(
Frame::new(
description,
SimpleChoice::new(words, false)
.with_show_incomplete()
.with_return_index(),
)
.with_title_centered(),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| { let block = |_args: &[Obj], kwargs: &Map| {
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?; let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
@ -1332,29 +1309,6 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
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_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| {
let title: TString = TR::word_count__title.into();
let recovery_type: RecoveryType = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
let choices: Vec<TString<'static>, 5> = {
let nums: &[&str] = if matches!(recovery_type, RecoveryType::UnlockRepeatedBackup) {
&["20", "33"]
} else {
&["12", "18", "20", "24", "33"]
};
nums.iter().map(|&num| num.into()).collect()
};
let obj = LayoutObj::new(
Frame::new(title, SimpleChoice::new(choices, false)).with_title_centered(),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_group_share_success( extern "C" fn new_show_group_share_success(
n_args: usize, n_args: usize,
args: *const Obj, args: *const Obj,
@ -1753,16 +1707,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Confirm coinjoin authorization.""" /// """Confirm coinjoin authorization."""
Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(), Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(),
/// def select_word(
/// *,
/// title: str, # unused on TR
/// description: str,
/// words: Iterable[str],
/// ) -> LayoutObj[int]:
/// """Select mnemonic word from three possibilities - seed check after backup. The
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
/// def show_share_words( /// def show_share_words(
/// *, /// *,
/// share_words: Iterable[str], /// share_words: Iterable[str],
@ -1804,14 +1748,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Device recovery homescreen.""" /// """Device recovery homescreen."""
Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(), Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(),
/// def select_word_count(
/// *,
/// recovery_type: RecoveryType,
/// ) -> LayoutObj[int | str]: # TR returns str
/// """Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
/// For unlocking a repeated backup, select from 20 or 33."""
Qstr::MP_QSTR_select_word_count => obj_fn_kw!(0, new_select_word_count).as_obj(),
/// def show_group_share_success( /// def show_group_share_success(
/// *, /// *,
/// lines: Iterable[str], /// lines: Iterable[str],

View File

@ -4,24 +4,30 @@ use crate::{
maybe_trace::MaybeTrace, maybe_trace::MaybeTrace,
micropython::gc::Gc, micropython::gc::Gc,
strutil::TString, strutil::TString,
translations::TR,
ui::{ ui::{
component::{ component::{
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt}, text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
Component, ComponentExt, Paginate, Timeout, Component, ComponentExt, Paginate, Timeout,
}, },
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, layout::{
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
util::RecoveryType,
},
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
}, },
}; };
use super::{ use super::{
component::{ component::{
ButtonDetails, ButtonPage, Frame, PassphraseEntry, PinEntry, ScrollableFrame, ButtonDetails, ButtonPage, Frame, PassphraseEntry, PinEntry, ScrollableFrame, SimpleChoice,
WordlistEntry, WordlistType, WordlistEntry, WordlistType,
}, },
theme, ModelTRFeatures, theme, ModelTRFeatures,
}; };
use heapless::Vec;
impl UIFeaturesFirmware for ModelTRFeatures { impl UIFeaturesFirmware for ModelTRFeatures {
fn confirm_action( fn confirm_action(
title: TString<'static>, title: TString<'static>,
@ -116,6 +122,43 @@ impl UIFeaturesFirmware for ModelTRFeatures {
Ok(layout) Ok(layout)
} }
fn select_word(
title: TString<'static>,
description: TString<'static>,
words: [TString<'static>; 3],
) -> Result<impl LayoutMaybeTrace, Error> {
let words: Vec<TString<'static>, 5> = Vec::from_iter(words);
// Returning the index of the selected word, not the word itself
let layout = RootComponent::new(
Frame::new(
description,
SimpleChoice::new(words, false)
.with_show_incomplete()
.with_return_index(),
)
.with_title_centered(),
);
Ok(layout)
}
fn select_word_count(recovery_type: RecoveryType) -> Result<impl LayoutMaybeTrace, Error> {
let title: TString = TR::word_count__title.into();
let choices: Vec<TString<'static>, 5> = {
let nums: &[&str] = if matches!(recovery_type, RecoveryType::UnlockRepeatedBackup) {
&["20", "33"]
} else {
&["12", "18", "20", "24", "33"]
};
nums.iter().map(|&num| num.into()).collect()
};
let layout = RootComponent::new(
Frame::new(title, SimpleChoice::new(choices, false)).with_title_centered(),
);
Ok(layout)
}
fn show_info( fn show_info(
title: TString<'static>, title: TString<'static>,
description: TString<'static>, description: TString<'static>,

View File

@ -1150,24 +1150,6 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
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_select_word(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 description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let words: [TString<'static>; 3] = util::iter_into_array(words_iterable)?;
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_DEMIBOLD, description)]);
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, Button::select_word(words)),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_show_share_words(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()?;
@ -1319,36 +1301,6 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
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_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let recovery_type: RecoveryType = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
let title: TString = match recovery_type {
RecoveryType::DryRun => TR::recovery__title_dry_run.into(),
RecoveryType::UnlockRepeatedBackup => TR::recovery__title_dry_run.into(),
_ => TR::recovery__title.into(),
};
let paragraphs = Paragraphs::new(Paragraph::new(
&theme::TEXT_DEMIBOLD,
TR::recovery__num_of_words,
));
let content = if matches!(recovery_type, RecoveryType::UnlockRepeatedBackup) {
SelectWordCount::new_multishare()
} else {
SelectWordCount::new_all()
};
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, content),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_group_share_success( extern "C" fn new_show_group_share_success(
n_args: usize, n_args: usize,
args: *const Obj, args: *const Obj,
@ -1755,16 +1707,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Confirm coinjoin authorization.""" /// """Confirm coinjoin authorization."""
Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(), Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(),
/// def select_word(
/// *,
/// title: str,
/// description: str,
/// words: Iterable[str],
/// ) -> LayoutObj[int]:
/// """Select mnemonic word from three possibilities - seed check after backup. The
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
/// def show_share_words( /// def show_share_words(
/// *, /// *,
/// title: str, /// title: str,
@ -1814,14 +1756,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Device recovery homescreen.""" /// """Device recovery homescreen."""
Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(), Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(),
/// def select_word_count(
/// *,
/// recovery_type: RecoveryType,
/// ) -> LayoutObj[int | str]: # TT returns int
/// """Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
/// For unlocking a repeated backup, select from 20 or 33."""
Qstr::MP_QSTR_select_word_count => obj_fn_kw!(0, new_select_word_count).as_obj(),
/// def show_group_share_success( /// def show_group_share_success(
/// *, /// *,
/// lines: Iterable[str] /// lines: Iterable[str]

View File

@ -7,18 +7,22 @@ use crate::{
ui::{ ui::{
component::{ component::{
image::BlendedImage, image::BlendedImage,
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt}, text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
ComponentExt, Empty, Timeout, ComponentExt, Empty, Timeout,
}, },
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, layout::{
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
util::RecoveryType,
},
ui_features_fw::UIFeaturesFirmware, ui_features_fw::UIFeaturesFirmware,
}, },
}; };
use super::{ use super::{
component::{ component::{
Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet, CancelConfirmMsg, Frame, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet, CancelConfirmMsg, Dialog,
IconDialog, MnemonicKeyboard, PassphraseKeyboard, PinKeyboard, Slip39Input, Frame, IconDialog, MnemonicKeyboard, PassphraseKeyboard, PinKeyboard, SelectWordCount,
Slip39Input,
}, },
theme, ModelTTFeatures, theme, ModelTTFeatures,
}; };
@ -119,6 +123,46 @@ impl UIFeaturesFirmware for ModelTTFeatures {
Ok(layout) Ok(layout)
} }
fn select_word(
title: TString<'static>,
description: TString<'static>,
words: [TString<'static>; 3],
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_DEMIBOLD, description)]);
let layout = RootComponent::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, Button::select_word(words)),
));
Ok(layout)
}
fn select_word_count(recovery_type: RecoveryType) -> Result<impl LayoutMaybeTrace, Error> {
let title: TString = match recovery_type {
RecoveryType::DryRun => TR::recovery__title_dry_run.into(),
RecoveryType::UnlockRepeatedBackup => TR::recovery__title_dry_run.into(),
_ => TR::recovery__title.into(),
};
let paragraphs = Paragraphs::new(Paragraph::new(
&theme::TEXT_DEMIBOLD,
TR::recovery__num_of_words,
));
let content = if matches!(recovery_type, RecoveryType::UnlockRepeatedBackup) {
SelectWordCount::new_multishare()
} else {
SelectWordCount::new_all()
};
let layout = RootComponent::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, content),
));
Ok(layout)
}
fn show_info( fn show_info(
title: TString<'static>, title: TString<'static>,
description: TString<'static>, description: TString<'static>,

View File

@ -1,7 +1,9 @@
use crate::{error::Error, io::BinaryData, micropython::gc::Gc, strutil::TString}; use crate::{error::Error, io::BinaryData, micropython::gc::Gc, strutil::TString};
use super::layout::obj::{LayoutMaybeTrace, LayoutObj}; use super::layout::{
obj::{LayoutMaybeTrace, LayoutObj},
util::RecoveryType,
};
pub trait UIFeaturesFirmware { pub trait UIFeaturesFirmware {
fn confirm_action( fn confirm_action(
@ -44,6 +46,14 @@ pub trait UIFeaturesFirmware {
max_len: u32, max_len: u32,
) -> Result<impl LayoutMaybeTrace, Error>; ) -> Result<impl LayoutMaybeTrace, Error>;
fn select_word(
title: TString<'static>,
description: TString<'static>,
words: [TString<'static>; 3],
) -> Result<impl LayoutMaybeTrace, Error>;
fn select_word_count(recovery_type: RecoveryType) -> Result<impl LayoutMaybeTrace, Error>;
fn show_info( fn show_info(
title: TString<'static>, title: TString<'static>,
description: TString<'static>, description: TString<'static>,

View File

@ -242,17 +242,6 @@ def confirm_coinjoin(
"""Confirm coinjoin authorization.""" """Confirm coinjoin authorization."""
# rust/src/ui/model_mercury/layout.rs
def select_word(
*,
title: str,
description: str,
words: Iterable[str],
) -> LayoutObj[int]:
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/model_mercury/layout.rs # rust/src/ui/model_mercury/layout.rs
def flow_prompt_backup() -> LayoutObj[UiResult]: def flow_prompt_backup() -> LayoutObj[UiResult]:
"""Prompt a user to create backup with an option to skip.""" """Prompt a user to create backup with an option to skip."""
@ -320,15 +309,6 @@ def flow_continue_recovery(
"""Device recovery homescreen.""" """Device recovery homescreen."""
# rust/src/ui/model_mercury/layout.rs
def select_word_count(
*,
recovery_type: RecoveryType,
) -> LayoutObj[int | str]: # merucry returns int
"""Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
For unlocking a repeated backup, select from 20 or 33."""
# rust/src/ui/model_mercury/layout.rs # rust/src/ui/model_mercury/layout.rs
def show_group_share_success( def show_group_share_success(
*, *,
@ -728,17 +708,6 @@ def confirm_coinjoin(
"""Confirm coinjoin authorization.""" """Confirm coinjoin authorization."""
# rust/src/ui/model_tr/layout.rs
def select_word(
*,
title: str, # unused on TR
description: str,
words: Iterable[str],
) -> LayoutObj[int]:
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/model_tr/layout.rs # rust/src/ui/model_tr/layout.rs
def show_share_words( def show_share_words(
*, *,
@ -784,15 +753,6 @@ def confirm_recovery(
"""Device recovery homescreen.""" """Device recovery homescreen."""
# rust/src/ui/model_tr/layout.rs
def select_word_count(
*,
recovery_type: RecoveryType,
) -> LayoutObj[int | str]: # TR returns str
"""Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
For unlocking a repeated backup, select from 20 or 33."""
# rust/src/ui/model_tr/layout.rs # rust/src/ui/model_tr/layout.rs
def show_group_share_success( def show_group_share_success(
*, *,
@ -1113,17 +1073,6 @@ def confirm_coinjoin(
"""Confirm coinjoin authorization.""" """Confirm coinjoin authorization."""
# rust/src/ui/model_tt/layout.rs
def select_word(
*,
title: str,
description: str,
words: Iterable[str],
) -> LayoutObj[int]:
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/model_tt/layout.rs # rust/src/ui/model_tt/layout.rs
def show_share_words( def show_share_words(
*, *,
@ -1178,15 +1127,6 @@ def confirm_recovery(
"""Device recovery homescreen.""" """Device recovery homescreen."""
# rust/src/ui/model_tt/layout.rs
def select_word_count(
*,
recovery_type: RecoveryType,
) -> LayoutObj[int | str]: # TT returns int
"""Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
For unlocking a repeated backup, select from 20 or 33."""
# rust/src/ui/model_tt/layout.rs # rust/src/ui/model_tt/layout.rs
def show_group_share_success( def show_group_share_success(
*, *,

View File

@ -136,6 +136,26 @@ def request_passphrase(
"""Passphrase input keyboard.""" """Passphrase input keyboard."""
# rust/src/ui/api/firmware_upy.rs
def select_word(
*,
title: str,
description: str,
words: Iterable[str],
) -> LayoutObj[int]:
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/api/firmware_upy.rs
def select_word_count(
*,
recovery_type: RecoveryType,
) -> LayoutObj[int | str]: # TR returns str
"""Select a mnemonic word count from the options: 12, 18, 20, 24, or 33.
For unlocking a repeated backup, select from 20 or 33."""
# rust/src/ui/api/firmware_upy.rs # rust/src/ui/api/firmware_upy.rs
def show_info( def show_info(
*, *,

View File

@ -17,9 +17,10 @@ if TYPE_CHECKING:
async def request_word_count(recovery_type: RecoveryType) -> int: async def request_word_count(recovery_type: RecoveryType) -> int:
selector = trezorui2.select_word_count(recovery_type=recovery_type)
count = await interact( count = await interact(
selector, "recovery_word_count", ButtonRequestType.MnemonicWordCount trezorui_api.select_word_count(recovery_type=recovery_type),
"recovery_word_count",
ButtonRequestType.MnemonicWordCount,
) )
return int(count) return int(count)

View File

@ -75,7 +75,7 @@ async def select_word(
words.append(words[-1]) words.append(words[-1])
result = await interact( result = await interact(
trezorui2.select_word( trezorui_api.select_word(
title=title, title=title,
description=TR.reset__select_word_x_of_y_template.format( description=TR.reset__select_word_x_of_y_template.format(
checked_index + 1, count checked_index + 1, count

View File

@ -16,7 +16,7 @@ if TYPE_CHECKING:
async def request_word_count(recovery_type: RecoveryType) -> int: async def request_word_count(recovery_type: RecoveryType) -> int:
count = await interact( count = await interact(
trezorui2.select_word_count(recovery_type=recovery_type), trezorui_api.select_word_count(recovery_type=recovery_type),
"recovery_word_count", "recovery_word_count",
ButtonRequestType.MnemonicWordCount, ButtonRequestType.MnemonicWordCount,
) )

View File

@ -88,7 +88,7 @@ async def select_word(
word_ordinal = format_ordinal(checked_index + 1) word_ordinal = format_ordinal(checked_index + 1)
result = await interact( result = await interact(
trezorui2.select_word( trezorui_api.select_word(
title="", title="",
description=TR.reset__select_word_template.format(word_ordinal), description=TR.reset__select_word_template.format(word_ordinal),
words=(words[0].lower(), words[1].lower(), words[2].lower()), words=(words[0].lower(), words[1].lower(), words[2].lower()),

View File

@ -17,7 +17,7 @@ if TYPE_CHECKING:
async def request_word_count(recovery_type: RecoveryType) -> int: async def request_word_count(recovery_type: RecoveryType) -> int:
count = await interact( count = await interact(
trezorui2.select_word_count(recovery_type=recovery_type), trezorui_api.select_word_count(recovery_type=recovery_type),
"recovery_word_count", "recovery_word_count",
ButtonRequestType.MnemonicWordCount, ButtonRequestType.MnemonicWordCount,
) )

View File

@ -84,7 +84,7 @@ async def select_word(
words.append(words[-1]) words.append(words[-1])
result = await interact( result = await interact(
trezorui2.select_word( trezorui_api.select_word(
title=title, title=title,
description=TR.reset__select_word_x_of_y_template.format( description=TR.reset__select_word_x_of_y_template.format(
checked_index + 1, count checked_index + 1, count