mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 01:18:28 +00:00
refactor(core): move and cleanup show_share_words
- model_t version was moved from using plain Paragraph to a dedicated component `ShareWords` so that it's consistent with other models. This allowed to move formatting to Rust and allowed the trait function to have `words` parameter of type `Vec<TString, 33>` - model_r ShareWords::render slightly refactored to be consistent with the new model_t version - mercury uses a unique version. The reason is that mercury SwipeFlow contains also the initial screen with instructions and prompt screen at the end.
This commit is contained in:
parent
a7a12cf898
commit
9bff86142d
@ -254,7 +254,6 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_flow_confirm_summary;
|
MP_QSTR_flow_confirm_summary;
|
||||||
MP_QSTR_flow_get_address;
|
MP_QSTR_flow_get_address;
|
||||||
MP_QSTR_flow_prompt_backup;
|
MP_QSTR_flow_prompt_backup;
|
||||||
MP_QSTR_flow_show_share_words;
|
|
||||||
MP_QSTR_flow_warning_hi_prio;
|
MP_QSTR_flow_warning_hi_prio;
|
||||||
MP_QSTR_get_language;
|
MP_QSTR_get_language;
|
||||||
MP_QSTR_get_transition_out;
|
MP_QSTR_get_transition_out;
|
||||||
@ -291,6 +290,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_inputs__return;
|
MP_QSTR_inputs__return;
|
||||||
MP_QSTR_inputs__show;
|
MP_QSTR_inputs__show;
|
||||||
MP_QSTR_inputs__space;
|
MP_QSTR_inputs__space;
|
||||||
|
MP_QSTR_instructions;
|
||||||
MP_QSTR_instructions__continue_holding;
|
MP_QSTR_instructions__continue_holding;
|
||||||
MP_QSTR_instructions__continue_in_app;
|
MP_QSTR_instructions__continue_in_app;
|
||||||
MP_QSTR_instructions__enter_next_share;
|
MP_QSTR_instructions__enter_next_share;
|
||||||
@ -641,7 +641,6 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_set_brightness;
|
MP_QSTR_set_brightness;
|
||||||
MP_QSTR_setting__adjust;
|
MP_QSTR_setting__adjust;
|
||||||
MP_QSTR_setting__apply;
|
MP_QSTR_setting__apply;
|
||||||
MP_QSTR_share_words;
|
|
||||||
MP_QSTR_share_words__words_in_order;
|
MP_QSTR_share_words__words_in_order;
|
||||||
MP_QSTR_share_words__wrote_down_all;
|
MP_QSTR_share_words__wrote_down_all;
|
||||||
MP_QSTR_show_address_details;
|
MP_QSTR_show_address_details;
|
||||||
@ -658,6 +657,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_show_progress_coinjoin;
|
MP_QSTR_show_progress_coinjoin;
|
||||||
MP_QSTR_show_remaining_shares;
|
MP_QSTR_show_remaining_shares;
|
||||||
MP_QSTR_show_share_words;
|
MP_QSTR_show_share_words;
|
||||||
|
MP_QSTR_show_share_words_mercury;
|
||||||
MP_QSTR_show_simple;
|
MP_QSTR_show_simple;
|
||||||
MP_QSTR_show_success;
|
MP_QSTR_show_success;
|
||||||
MP_QSTR_show_wait_text;
|
MP_QSTR_show_wait_text;
|
||||||
@ -683,7 +683,6 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_summary_title;
|
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_mono;
|
MP_QSTR_text_mono;
|
||||||
MP_QSTR_time_ms;
|
MP_QSTR_time_ms;
|
||||||
MP_QSTR_timer;
|
MP_QSTR_timer;
|
||||||
|
@ -2,6 +2,7 @@ use crate::{
|
|||||||
io::BinaryData,
|
io::BinaryData,
|
||||||
micropython::{
|
micropython::{
|
||||||
gc::Gc,
|
gc::Gc,
|
||||||
|
iter::IterBuf,
|
||||||
list::List,
|
list::List,
|
||||||
macros::{obj_fn_1, obj_fn_kw, obj_module},
|
macros::{obj_fn_1, obj_fn_kw, obj_module},
|
||||||
map::Map,
|
map::Map,
|
||||||
@ -25,6 +26,7 @@ use crate::{
|
|||||||
ui_features_fw::UIFeaturesFirmware,
|
ui_features_fw::UIFeaturesFirmware,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
/// Dummy implementation so that we can use `Empty` in a return type of unimplemented trait
|
/// Dummy implementation so that we can use `Empty` in a return type of unimplemented trait
|
||||||
/// function
|
/// function
|
||||||
@ -478,6 +480,54 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
|
|||||||
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_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let words: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
||||||
|
let title: Option<TString> = kwargs
|
||||||
|
.get(Qstr::MP_QSTR_title)
|
||||||
|
.and_then(Obj::try_into_option)
|
||||||
|
.unwrap_or(None);
|
||||||
|
|
||||||
|
let words: Vec<TString, 33> = util::iter_into_vec(words)?;
|
||||||
|
|
||||||
|
let layout = ModelUI::show_share_words(words, title)?;
|
||||||
|
Ok(LayoutObj::new_root(layout)?.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_show_share_words_mercury(
|
||||||
|
n_args: usize,
|
||||||
|
args: *const Obj,
|
||||||
|
kwargs: *mut Map,
|
||||||
|
) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let words: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
||||||
|
let subtitle: Option<TString> = kwargs
|
||||||
|
.get(Qstr::MP_QSTR_subtitle)
|
||||||
|
.and_then(Obj::try_into_option)
|
||||||
|
.unwrap_or(None);
|
||||||
|
let instructions: Obj = kwargs.get(Qstr::MP_QSTR_instructions)?;
|
||||||
|
let text_footer: Option<TString> = kwargs
|
||||||
|
.get(Qstr::MP_QSTR_description)
|
||||||
|
.and_then(Obj::try_into_option)
|
||||||
|
.unwrap_or(None);
|
||||||
|
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
||||||
|
|
||||||
|
let words: Vec<TString, 33> = util::iter_into_vec(words)?;
|
||||||
|
|
||||||
|
let layout = ModelUI::show_share_words_mercury(
|
||||||
|
words,
|
||||||
|
subtitle,
|
||||||
|
instructions,
|
||||||
|
text_footer,
|
||||||
|
text_confirm,
|
||||||
|
)?;
|
||||||
|
Ok(LayoutObj::new_root(layout)?.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_simple(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 text: TString = kwargs.get(Qstr::MP_QSTR_text)?.try_into()?;
|
let text: TString = kwargs.get(Qstr::MP_QSTR_text)?.try_into()?;
|
||||||
@ -923,6 +973,26 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
|||||||
/// """Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
|
/// """Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
|
||||||
Qstr::MP_QSTR_show_remaining_shares => obj_fn_kw!(0, new_show_remaining_shares).as_obj(),
|
Qstr::MP_QSTR_show_remaining_shares => obj_fn_kw!(0, new_show_remaining_shares).as_obj(),
|
||||||
|
|
||||||
|
/// def show_share_words(
|
||||||
|
/// *,
|
||||||
|
/// words: Iterable[str],
|
||||||
|
/// title: str | None = None,
|
||||||
|
/// ) -> LayoutObj[UiResult]:
|
||||||
|
/// """Show mnemonic for backup."""
|
||||||
|
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
|
||||||
|
|
||||||
|
/// def show_share_words_mercury(
|
||||||
|
/// *,
|
||||||
|
/// words: Iterable[str],
|
||||||
|
/// subtitle: str | None,
|
||||||
|
/// instructions: Iterable[str],
|
||||||
|
/// text_footer: str | None,
|
||||||
|
/// text_confirm: str,
|
||||||
|
/// ) -> LayoutObj[UiResult]:
|
||||||
|
/// """Show mnemonic for wallet backup preceded by an instruction screen and followed by a
|
||||||
|
/// confirmation screen."""
|
||||||
|
Qstr::MP_QSTR_show_share_words_mercury => obj_fn_kw!(0, new_show_share_words_mercury).as_obj(),
|
||||||
|
|
||||||
/// def show_simple(
|
/// def show_simple(
|
||||||
/// *,
|
/// *,
|
||||||
/// text: str,
|
/// text: str,
|
||||||
|
@ -78,15 +78,15 @@ fn footer_updating_func(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_show_share_words(
|
pub fn new_show_share_words(
|
||||||
title: TString<'static>,
|
|
||||||
subtitle: TString<'static>,
|
|
||||||
share_words_vec: Vec<TString<'static>, 33>,
|
share_words_vec: Vec<TString<'static>, 33>,
|
||||||
description: Option<TString<'static>>,
|
subtitle: TString<'static>,
|
||||||
instructions_paragraphs: ParagraphVecShort<'static>,
|
instructions_paragraphs: ParagraphVecShort<'static>,
|
||||||
|
text_footer: Option<TString<'static>>,
|
||||||
text_confirm: TString<'static>,
|
text_confirm: TString<'static>,
|
||||||
) -> Result<SwipeFlow, error::Error> {
|
) -> Result<SwipeFlow, error::Error> {
|
||||||
let nwords = share_words_vec.len();
|
let nwords = share_words_vec.len();
|
||||||
let paragraphs_spacing = 8;
|
let paragraphs_spacing = 8;
|
||||||
|
let title = TR::reset__recovery_wallet_backup_title.into();
|
||||||
|
|
||||||
let content_instruction = Frame::left_aligned(
|
let content_instruction = Frame::left_aligned(
|
||||||
title,
|
title,
|
||||||
@ -97,7 +97,7 @@ pub fn new_show_share_words(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_subtitle(TR::words__instructions.into())
|
.with_subtitle(TR::words__instructions.into())
|
||||||
.with_footer(TR::instructions__swipe_up.into(), description)
|
.with_footer(TR::instructions__swipe_up.into(), text_footer)
|
||||||
.with_swipe(Direction::Up, SwipeSettings::default())
|
.with_swipe(Direction::Up, SwipeSettings::default())
|
||||||
.map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed))
|
.map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed))
|
||||||
.one_button_request(ButtonRequestCode::ResetDevice.with_name("share_words"))
|
.one_button_request(ButtonRequestCode::ResetDevice.with_name("share_words"))
|
||||||
|
@ -658,38 +658,6 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
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_show_share_words(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 subtitle: TString = kwargs.get(Qstr::MP_QSTR_subtitle)?.try_into()?;
|
|
||||||
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
|
||||||
let share_words_vec: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
|
|
||||||
let description: Option<TString> = kwargs
|
|
||||||
.get(Qstr::MP_QSTR_description)?
|
|
||||||
.try_into_option()?
|
|
||||||
.and_then(|desc: TString| if desc.is_empty() { None } else { Some(desc) });
|
|
||||||
let text_info: Obj = kwargs.get(Qstr::MP_QSTR_text_info)?;
|
|
||||||
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
|
||||||
|
|
||||||
let mut instructions_paragraphs = ParagraphVecShort::new();
|
|
||||||
for item in IterBuf::new().try_iterate(text_info)? {
|
|
||||||
let text: TString = item.try_into()?;
|
|
||||||
instructions_paragraphs.add(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, text));
|
|
||||||
}
|
|
||||||
|
|
||||||
let flow = flow::show_share_words::new_show_share_words(
|
|
||||||
title,
|
|
||||||
subtitle,
|
|
||||||
share_words_vec,
|
|
||||||
description,
|
|
||||||
instructions_paragraphs,
|
|
||||||
text_confirm,
|
|
||||||
)?;
|
|
||||||
Ok(LayoutObj::new_root(flow)?.into())
|
|
||||||
};
|
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_with_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()?;
|
||||||
@ -932,19 +900,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """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(),
|
||||||
|
|
||||||
/// def flow_show_share_words(
|
|
||||||
/// *,
|
|
||||||
/// title: str,
|
|
||||||
/// subtitle: str,
|
|
||||||
/// words: Iterable[str],
|
|
||||||
/// description: str,
|
|
||||||
/// text_info: Iterable[str],
|
|
||||||
/// text_confirm: str,
|
|
||||||
/// ) -> LayoutObj[UiResult]:
|
|
||||||
/// """Show wallet backup words preceded by an instruction screen and followed by
|
|
||||||
/// confirmation."""
|
|
||||||
Qstr::MP_QSTR_flow_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
|
|
||||||
|
|
||||||
/// def flow_get_address(
|
/// def flow_get_address(
|
||||||
/// *,
|
/// *,
|
||||||
/// address: str | bytes,
|
/// address: str | bytes,
|
||||||
|
@ -540,6 +540,38 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
|
|||||||
Ok(obj)
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_share_words(
|
||||||
|
words: heapless::Vec<TString<'static>, 33>,
|
||||||
|
_title: Option<TString<'static>>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
Err::<RootComponent<Empty, ModelMercuryFeatures>, Error>(Error::ValueError(
|
||||||
|
c"use flow_share_words instead",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_share_words_mercury(
|
||||||
|
words: heapless::Vec<TString<'static>, 33>,
|
||||||
|
subtitle: Option<TString<'static>>,
|
||||||
|
instructions: crate::micropython::obj::Obj,
|
||||||
|
text_footer: Option<TString<'static>>,
|
||||||
|
text_confirm: TString<'static>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
let mut instructions_paragraphs = ParagraphVecShort::new();
|
||||||
|
for item in crate::micropython::iter::IterBuf::new().try_iterate(instructions)? {
|
||||||
|
let text: TString = item.try_into()?;
|
||||||
|
instructions_paragraphs.add(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, text));
|
||||||
|
}
|
||||||
|
|
||||||
|
let flow = flow::show_share_words::new_show_share_words(
|
||||||
|
words,
|
||||||
|
subtitle.unwrap_or(TString::empty()),
|
||||||
|
instructions_paragraphs,
|
||||||
|
text_footer,
|
||||||
|
text_confirm,
|
||||||
|
)?;
|
||||||
|
Ok(flow)
|
||||||
|
}
|
||||||
|
|
||||||
fn show_remaining_shares(
|
fn show_remaining_shares(
|
||||||
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
|
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
@ -92,23 +92,24 @@ impl<'a> ShareWords<'a> {
|
|||||||
fn render_words<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
fn render_words<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
let mut y_offset = 0;
|
let mut y_offset = 0;
|
||||||
// Showing the word index and the words itself
|
// Showing the word index and the words itself
|
||||||
for i in 0..WORDS_PER_PAGE {
|
for (word_idx, word) in self
|
||||||
|
.share_words
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.skip(self.page_index * WORDS_PER_PAGE)
|
||||||
|
.take(WORDS_PER_PAGE)
|
||||||
|
{
|
||||||
|
let ordinal = word_idx + 1;
|
||||||
y_offset += NUMBER_FONT.line_height() + EXTRA_LINE_HEIGHT;
|
y_offset += NUMBER_FONT.line_height() + EXTRA_LINE_HEIGHT;
|
||||||
let index = self.word_index() + i;
|
let base = self.area.top_left() + Offset::y(y_offset);
|
||||||
if index >= self.share_words.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let word = &self.share_words[index];
|
|
||||||
let baseline = self.area.top_left() + Offset::y(y_offset);
|
|
||||||
let ordinal = uformat!("{}.", index + 1);
|
|
||||||
|
|
||||||
shape::Text::new(baseline + Offset::x(NUMBER_X_OFFSET), &ordinal)
|
let ordinal_txt = uformat!("{}.", ordinal);
|
||||||
|
shape::Text::new(base + Offset::x(NUMBER_X_OFFSET), &ordinal_txt)
|
||||||
.with_font(NUMBER_FONT)
|
.with_font(NUMBER_FONT)
|
||||||
.with_fg(theme::FG)
|
.with_fg(theme::FG)
|
||||||
.render(target);
|
.render(target);
|
||||||
|
|
||||||
word.map(|w| {
|
word.map(|w| {
|
||||||
shape::Text::new(baseline + Offset::x(WORD_X_OFFSET), w)
|
shape::Text::new(base + Offset::x(WORD_X_OFFSET), w)
|
||||||
.with_font(WORD_FONT)
|
.with_font(WORD_FONT)
|
||||||
.with_fg(theme::FG)
|
.with_fg(theme::FG)
|
||||||
.render(target);
|
.render(target);
|
||||||
@ -171,13 +172,15 @@ impl<'a> crate::trace::Trace for ShareWords<'a> {
|
|||||||
self.get_final_text()
|
self.get_final_text()
|
||||||
} else {
|
} else {
|
||||||
let mut content = ShortString::new();
|
let mut content = ShortString::new();
|
||||||
for i in 0..WORDS_PER_PAGE {
|
for (word_idx, word) in self
|
||||||
let index = self.word_index() + i;
|
.share_words
|
||||||
if index >= self.share_words.len() {
|
.iter()
|
||||||
break;
|
.enumerate()
|
||||||
}
|
.skip(self.page_index * WORDS_PER_PAGE)
|
||||||
self.share_words[index]
|
.take(WORDS_PER_PAGE)
|
||||||
.map(|word| unwrap!(uwrite!(content, "{}. {}\n", index + 1, word)));
|
{
|
||||||
|
let ordinal = word_idx + 1;
|
||||||
|
word.map(|w| unwrap!(uwrite!(content, "{}. {}\n", ordinal, w)));
|
||||||
}
|
}
|
||||||
content
|
content
|
||||||
};
|
};
|
||||||
|
@ -839,25 +839,6 @@ extern "C" fn new_confirm_more(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_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
|
||||||
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
|
|
||||||
let share_words: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
|
|
||||||
|
|
||||||
let cancel_btn = Some(ButtonDetails::up_arrow_icon());
|
|
||||||
let confirm_btn =
|
|
||||||
Some(ButtonDetails::text(TR::buttons__hold_to_confirm.into()).with_default_duration());
|
|
||||||
|
|
||||||
let obj = LayoutObj::new(
|
|
||||||
ButtonPage::new(ShareWords::new(share_words), theme::BG)
|
|
||||||
.with_cancel_btn(cancel_btn)
|
|
||||||
.with_confirm_btn(confirm_btn),
|
|
||||||
)?;
|
|
||||||
Ok(obj.into())
|
|
||||||
};
|
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui2: Module = obj_module! {
|
pub static mp_module_trezorui2: Module = obj_module! {
|
||||||
/// from trezor import utils
|
/// from trezor import utils
|
||||||
@ -1016,11 +997,4 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Confirm long content with the possibility to go back from any page.
|
/// """Confirm long content with the possibility to go back from any page.
|
||||||
/// Meant to be used with confirm_with_info."""
|
/// Meant to be used with confirm_with_info."""
|
||||||
Qstr::MP_QSTR_confirm_more => obj_fn_kw!(0, new_confirm_more).as_obj(),
|
Qstr::MP_QSTR_confirm_more => obj_fn_kw!(0, new_confirm_more).as_obj(),
|
||||||
|
|
||||||
/// def show_share_words(
|
|
||||||
/// *,
|
|
||||||
/// share_words: Iterable[str],
|
|
||||||
/// ) -> LayoutObj[UiResult]:
|
|
||||||
/// """Shows a backup seed."""
|
|
||||||
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
|
|
||||||
};
|
};
|
||||||
|
@ -36,7 +36,7 @@ use super::{
|
|||||||
component::{
|
component::{
|
||||||
ButtonDetails, ButtonPage, CoinJoinProgress, ConfirmHomescreen, Flow, FlowPages, Frame,
|
ButtonDetails, ButtonPage, CoinJoinProgress, ConfirmHomescreen, Flow, FlowPages, Frame,
|
||||||
Homescreen, Lockscreen, NumberInput, PassphraseEntry, PinEntry, Progress, ScrollableFrame,
|
Homescreen, Lockscreen, NumberInput, PassphraseEntry, PinEntry, Progress, ScrollableFrame,
|
||||||
SimpleChoice, WordlistEntry, WordlistType,
|
ShareWords, SimpleChoice, WordlistEntry, WordlistType,
|
||||||
},
|
},
|
||||||
theme, ModelTRFeatures,
|
theme, ModelTRFeatures,
|
||||||
};
|
};
|
||||||
@ -603,6 +603,34 @@ impl UIFeaturesFirmware for ModelTRFeatures {
|
|||||||
Ok(obj)
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_share_words(
|
||||||
|
words: heapless::Vec<TString<'static>, 33>,
|
||||||
|
_title: Option<TString<'static>>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
let cancel_btn = Some(ButtonDetails::up_arrow_icon());
|
||||||
|
let confirm_btn =
|
||||||
|
Some(ButtonDetails::text(TR::buttons__hold_to_confirm.into()).with_default_duration());
|
||||||
|
|
||||||
|
let layout = RootComponent::new(
|
||||||
|
ButtonPage::new(ShareWords::new(words), theme::BG)
|
||||||
|
.with_cancel_btn(cancel_btn)
|
||||||
|
.with_confirm_btn(confirm_btn),
|
||||||
|
);
|
||||||
|
Ok(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_share_words_mercury(
|
||||||
|
_words: heapless::Vec<TString<'static>, 33>,
|
||||||
|
_subtitle: Option<TString<'static>>,
|
||||||
|
_instructions: crate::micropython::obj::Obj,
|
||||||
|
_text_footer: Option<TString<'static>>,
|
||||||
|
_text_confirm: TString<'static>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
Err::<RootComponent<Empty, ModelTRFeatures>, Error>(Error::ValueError(
|
||||||
|
c"use show_share_words",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn show_remaining_shares(
|
fn show_remaining_shares(
|
||||||
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
|
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
@ -23,6 +23,8 @@ mod progress;
|
|||||||
mod result;
|
mod result;
|
||||||
mod scroll;
|
mod scroll;
|
||||||
mod set_brightness;
|
mod set_brightness;
|
||||||
|
#[cfg(feature = "translations")]
|
||||||
|
mod share_words;
|
||||||
mod simple_page;
|
mod simple_page;
|
||||||
mod swipe;
|
mod swipe;
|
||||||
mod welcome_screen;
|
mod welcome_screen;
|
||||||
@ -59,6 +61,8 @@ pub use progress::Progress;
|
|||||||
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
||||||
pub use scroll::ScrollBar;
|
pub use scroll::ScrollBar;
|
||||||
pub use set_brightness::SetBrightnessDialog;
|
pub use set_brightness::SetBrightnessDialog;
|
||||||
|
#[cfg(feature = "translations")]
|
||||||
|
pub use share_words::ShareWords;
|
||||||
pub use simple_page::SimplePage;
|
pub use simple_page::SimplePage;
|
||||||
pub use swipe::{Swipe, SwipeDirection};
|
pub use swipe::{Swipe, SwipeDirection};
|
||||||
pub use welcome_screen::WelcomeScreen;
|
pub use welcome_screen::WelcomeScreen;
|
||||||
|
115
core/embed/rust/src/ui/model_tt/component/share_words.rs
Normal file
115
core/embed/rust/src/ui/model_tt/component/share_words.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Never, Paginate},
|
||||||
|
display::Font,
|
||||||
|
geometry::{Offset, Rect},
|
||||||
|
model_tt::theme,
|
||||||
|
shape::{self, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use heapless::Vec;
|
||||||
|
use ufmt::uwrite;
|
||||||
|
|
||||||
|
const WORDS_PER_PAGE: usize = 4;
|
||||||
|
const TOP_PADDING_OFFSET: i16 = 13;
|
||||||
|
const WORD_FONT: Font = Font::MONO;
|
||||||
|
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
|
||||||
|
|
||||||
|
/// Showing the given share words.
|
||||||
|
pub struct ShareWords<'a> {
|
||||||
|
area: Rect,
|
||||||
|
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||||
|
page_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ShareWords<'a> {
|
||||||
|
pub fn new(share_words: Vec<TString<'a>, MAX_WORDS>) -> Self {
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
share_words,
|
||||||
|
page_index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn total_page_count(&self) -> usize {
|
||||||
|
(self.share_words.len() + WORDS_PER_PAGE - 1) / WORDS_PER_PAGE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Component for ShareWords<'a> {
|
||||||
|
type Msg = Never;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.area = bounds;
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
let line_height = WORD_FONT.line_height();
|
||||||
|
let ordinal_largest_on_this_page =
|
||||||
|
(WORDS_PER_PAGE * (self.page_index + 1)).min(self.share_words.len());
|
||||||
|
let is_largest_double_digit = ordinal_largest_on_this_page >= 10;
|
||||||
|
let mut y_offset = self.area.top_left().y + TOP_PADDING_OFFSET;
|
||||||
|
|
||||||
|
for (word_idx, word) in self
|
||||||
|
.share_words
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.skip(self.page_index * WORDS_PER_PAGE)
|
||||||
|
.take(WORDS_PER_PAGE)
|
||||||
|
{
|
||||||
|
let ordinal = word_idx + 1;
|
||||||
|
let base = self.area.top_left() + Offset::y(y_offset);
|
||||||
|
word.map(|w| {
|
||||||
|
let double_digit = ordinal >= 10;
|
||||||
|
let text_fmt = if double_digit || !is_largest_double_digit {
|
||||||
|
uformat!("{}. {}", ordinal, w)
|
||||||
|
} else {
|
||||||
|
uformat!(" {}. {}", ordinal, w)
|
||||||
|
};
|
||||||
|
shape::Text::new(base, &text_fmt)
|
||||||
|
.with_font(WORD_FONT)
|
||||||
|
.with_fg(theme::FG)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
y_offset += line_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Paginate for ShareWords<'a> {
|
||||||
|
fn page_count(&mut self) -> usize {
|
||||||
|
// Not defining the logic here, as we do not want it to be `&mut`.
|
||||||
|
self.total_page_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_page(&mut self, active_page: usize) {
|
||||||
|
self.page_index = active_page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG-ONLY SECTION BELOW
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl<'a> crate::trace::Trace for ShareWords<'a> {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("ShareWords");
|
||||||
|
let mut content = heapless::String::<64>::new();
|
||||||
|
for (word_idx, word) in self
|
||||||
|
.share_words
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.skip(self.page_index * WORDS_PER_PAGE)
|
||||||
|
.take(WORDS_PER_PAGE)
|
||||||
|
{
|
||||||
|
let ordinal = word_idx + 1;
|
||||||
|
word.map(|w| unwrap!(uwrite!(content, "{}. {}\n", ordinal, w)));
|
||||||
|
}
|
||||||
|
t.string("screen_content", content.as_str().into());
|
||||||
|
}
|
||||||
|
}
|
@ -767,29 +767,6 @@ extern "C" fn new_confirm_more(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_show_share_words(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 pages: Obj = kwargs.get(Qstr::MP_QSTR_pages)?;
|
|
||||||
|
|
||||||
let mut paragraphs = ParagraphVecLong::new();
|
|
||||||
for page in IterBuf::new().try_iterate(pages)? {
|
|
||||||
let text: TString = page.try_into()?;
|
|
||||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, text).break_after());
|
|
||||||
}
|
|
||||||
|
|
||||||
let obj = LayoutObj::new(Frame::left_aligned(
|
|
||||||
theme::label_title(),
|
|
||||||
title,
|
|
||||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
|
||||||
.with_hold()?
|
|
||||||
.without_cancel(),
|
|
||||||
))?;
|
|
||||||
Ok(obj.into())
|
|
||||||
};
|
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui2: Module = obj_module! {
|
pub static mp_module_trezorui2: Module = obj_module! {
|
||||||
/// from trezor import utils
|
/// from trezor import utils
|
||||||
@ -919,14 +896,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Confirm long content with the possibility to go back from any page.
|
/// """Confirm long content with the possibility to go back from any page.
|
||||||
/// Meant to be used with confirm_with_info."""
|
/// Meant to be used with confirm_with_info."""
|
||||||
Qstr::MP_QSTR_confirm_more => obj_fn_kw!(0, new_confirm_more).as_obj(),
|
Qstr::MP_QSTR_confirm_more => obj_fn_kw!(0, new_confirm_more).as_obj(),
|
||||||
|
|
||||||
/// def show_share_words(
|
|
||||||
/// *,
|
|
||||||
/// title: str,
|
|
||||||
/// pages: Iterable[str],
|
|
||||||
/// ) -> LayoutObj[UiResult]:
|
|
||||||
/// """Show mnemonic for backup. Expects the words pre-divided into individual pages."""
|
|
||||||
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -29,7 +29,7 @@ use super::{
|
|||||||
check_homescreen_format, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet,
|
check_homescreen_format, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet,
|
||||||
CancelConfirmMsg, CoinJoinProgress, Dialog, FidoConfirm, Frame, Homescreen, IconDialog,
|
CancelConfirmMsg, CoinJoinProgress, Dialog, FidoConfirm, Frame, Homescreen, IconDialog,
|
||||||
Lockscreen, MnemonicKeyboard, NumberInputDialog, PassphraseKeyboard, PinKeyboard, Progress,
|
Lockscreen, MnemonicKeyboard, NumberInputDialog, PassphraseKeyboard, PinKeyboard, Progress,
|
||||||
SelectWordCount, SetBrightnessDialog, Slip39Input,
|
SelectWordCount, SetBrightnessDialog, ShareWords, Slip39Input,
|
||||||
},
|
},
|
||||||
theme, ModelTTFeatures,
|
theme, ModelTTFeatures,
|
||||||
};
|
};
|
||||||
@ -635,6 +635,32 @@ impl UIFeaturesFirmware for ModelTTFeatures {
|
|||||||
Ok(obj)
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_share_words(
|
||||||
|
words: heapless::Vec<TString<'static>, 33>,
|
||||||
|
title: Option<TString<'static>>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
let layout = RootComponent::new(Frame::left_aligned(
|
||||||
|
theme::label_title(),
|
||||||
|
title.unwrap_or(TString::empty()),
|
||||||
|
ButtonPage::new(ShareWords::new(words), theme::BG)
|
||||||
|
.with_hold()?
|
||||||
|
.without_cancel(),
|
||||||
|
));
|
||||||
|
Ok(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_share_words_mercury(
|
||||||
|
_words: heapless::Vec<TString<'static>, 33>,
|
||||||
|
_subtitle: Option<TString<'static>>,
|
||||||
|
_instructions: crate::micropython::obj::Obj,
|
||||||
|
_text_footer: Option<TString<'static>>,
|
||||||
|
_text_confirm: TString<'static>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
Err::<RootComponent<Empty, ModelTTFeatures>, Error>(Error::ValueError(
|
||||||
|
c"use show_share_words",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn show_remaining_shares(
|
fn show_remaining_shares(
|
||||||
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
|
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
@ -4,6 +4,7 @@ use crate::{
|
|||||||
micropython::{gc::Gc, list::List, obj::Obj},
|
micropython::{gc::Gc, list::List, obj::Obj},
|
||||||
strutil::TString,
|
strutil::TString,
|
||||||
};
|
};
|
||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
use super::layout::{
|
use super::layout::{
|
||||||
obj::{LayoutMaybeTrace, LayoutObj},
|
obj::{LayoutMaybeTrace, LayoutObj},
|
||||||
@ -175,6 +176,20 @@ pub trait UIFeaturesFirmware {
|
|||||||
pages_iterable: Obj, // TODO: replace Obj
|
pages_iterable: Obj, // TODO: replace Obj
|
||||||
) -> Result<impl LayoutMaybeTrace, Error>;
|
) -> Result<impl LayoutMaybeTrace, Error>;
|
||||||
|
|
||||||
|
fn show_share_words(
|
||||||
|
words: Vec<TString<'static>, 33>,
|
||||||
|
title: Option<TString<'static>>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error>;
|
||||||
|
|
||||||
|
// TODO: merge with `show_share_words` instead of having specific version for mercury
|
||||||
|
fn show_share_words_mercury(
|
||||||
|
words: Vec<TString<'static>, 33>,
|
||||||
|
subtitle: Option<TString<'static>>,
|
||||||
|
instructions: Obj, // TODO: replace Obj
|
||||||
|
text_footer: Option<TString<'static>>, // footer description at instruction screen
|
||||||
|
text_confirm: TString<'static>,
|
||||||
|
) -> Result<impl LayoutMaybeTrace, Error>;
|
||||||
|
|
||||||
fn show_simple(
|
fn show_simple(
|
||||||
text: TString<'static>,
|
text: TString<'static>,
|
||||||
title: Option<TString<'static>>,
|
title: Option<TString<'static>>,
|
||||||
|
@ -136,20 +136,6 @@ 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."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
|
||||||
def flow_show_share_words(
|
|
||||||
*,
|
|
||||||
title: str,
|
|
||||||
subtitle: str,
|
|
||||||
words: Iterable[str],
|
|
||||||
description: str,
|
|
||||||
text_info: Iterable[str],
|
|
||||||
text_confirm: str,
|
|
||||||
) -> LayoutObj[UiResult]:
|
|
||||||
"""Show wallet backup words preceded by an instruction screen and followed by
|
|
||||||
confirmation."""
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
def flow_get_address(
|
def flow_get_address(
|
||||||
*,
|
*,
|
||||||
@ -386,14 +372,6 @@ def confirm_more(
|
|||||||
) -> object:
|
) -> object:
|
||||||
"""Confirm long content with the possibility to go back from any page.
|
"""Confirm long content with the possibility to go back from any page.
|
||||||
Meant to be used with confirm_with_info."""
|
Meant to be used with confirm_with_info."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
|
||||||
def show_share_words(
|
|
||||||
*,
|
|
||||||
share_words: Iterable[str],
|
|
||||||
) -> LayoutObj[UiResult]:
|
|
||||||
"""Shows a backup seed."""
|
|
||||||
from trezor import utils
|
from trezor import utils
|
||||||
from trezorui_api import *
|
from trezorui_api import *
|
||||||
|
|
||||||
@ -530,12 +508,3 @@ def confirm_more(
|
|||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
"""Confirm long content with the possibility to go back from any page.
|
"""Confirm long content with the possibility to go back from any page.
|
||||||
Meant to be used with confirm_with_info."""
|
Meant to be used with confirm_with_info."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
|
||||||
def show_share_words(
|
|
||||||
*,
|
|
||||||
title: str,
|
|
||||||
pages: Iterable[str],
|
|
||||||
) -> LayoutObj[UiResult]:
|
|
||||||
"""Show mnemonic for backup. Expects the words pre-divided into individual pages."""
|
|
||||||
|
@ -361,6 +361,28 @@ def show_remaining_shares(
|
|||||||
"""Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
|
"""Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/api/firmware_upy.rs
|
||||||
|
def show_share_words(
|
||||||
|
*,
|
||||||
|
words: Iterable[str],
|
||||||
|
title: str | None = None,
|
||||||
|
) -> LayoutObj[UiResult]:
|
||||||
|
"""Show mnemonic for backup."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/api/firmware_upy.rs
|
||||||
|
def show_share_words_mercury(
|
||||||
|
*,
|
||||||
|
words: Iterable[str],
|
||||||
|
subtitle: str | None,
|
||||||
|
instructions: Iterable[str],
|
||||||
|
text_footer: str | None,
|
||||||
|
text_confirm: str,
|
||||||
|
) -> LayoutObj[UiResult]:
|
||||||
|
"""Show mnemonic for wallet backup preceded by an instruction screen and followed by a
|
||||||
|
confirmation screen."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/api/firmware_upy.rs
|
# rust/src/ui/api/firmware_upy.rs
|
||||||
def show_simple(
|
def show_simple(
|
||||||
*,
|
*,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from typing import Awaitable, Callable, Sequence
|
from typing import Awaitable, Callable, Sequence
|
||||||
|
|
||||||
import trezorui2
|
|
||||||
import trezorui_api
|
import trezorui_api
|
||||||
from trezor import TR, ui
|
from trezor import TR, ui
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
@ -17,9 +16,8 @@ def show_share_words(
|
|||||||
share_index: int | None = None,
|
share_index: int | None = None,
|
||||||
group_index: int | None = None,
|
group_index: int | None = None,
|
||||||
) -> Awaitable[None]:
|
) -> Awaitable[None]:
|
||||||
title = TR.reset__recovery_wallet_backup_title
|
|
||||||
if share_index is None:
|
if share_index is None:
|
||||||
subtitle = ""
|
subtitle = None
|
||||||
elif group_index is None:
|
elif group_index is None:
|
||||||
subtitle = TR.reset__recovery_share_title_template.format(share_index + 1)
|
subtitle = TR.reset__recovery_share_title_template.format(share_index + 1)
|
||||||
else:
|
else:
|
||||||
@ -27,24 +25,24 @@ def show_share_words(
|
|||||||
group_index + 1, share_index + 1
|
group_index + 1, share_index + 1
|
||||||
)
|
)
|
||||||
words_count = len(share_words)
|
words_count = len(share_words)
|
||||||
description = ""
|
description = None
|
||||||
text_info = [TR.reset__write_down_words_template.format(words_count)]
|
instructions = [TR.reset__write_down_words_template.format(words_count)]
|
||||||
if words_count == 20 and share_index is None:
|
if words_count == 20 and share_index is None:
|
||||||
# 1-of-1 SLIP39: inform the user about repeated words
|
# 1-of-1 SLIP39: inform the user about repeated words
|
||||||
text_info.append(TR.reset__words_may_repeat)
|
instructions.append(TR.reset__words_may_repeat)
|
||||||
if share_index == 0:
|
if share_index == 0:
|
||||||
# regular SLIP39, 1st share
|
# regular SLIP39, 1st share
|
||||||
description = TR.instructions__shares_start_with_1
|
description = TR.instructions__shares_start_with_1
|
||||||
text_info.append(TR.reset__repeat_for_all_shares)
|
instructions.append(TR.reset__repeat_for_all_shares)
|
||||||
|
assert len(instructions) < 3
|
||||||
text_confirm = TR.reset__words_written_down_template.format(words_count)
|
text_confirm = TR.reset__words_written_down_template.format(words_count)
|
||||||
|
|
||||||
return raise_if_not_confirmed(
|
return raise_if_not_confirmed(
|
||||||
trezorui2.flow_show_share_words(
|
trezorui_api.show_share_words_mercury(
|
||||||
title=title,
|
|
||||||
subtitle=subtitle,
|
|
||||||
words=share_words,
|
words=share_words,
|
||||||
description=description,
|
subtitle=subtitle,
|
||||||
text_info=text_info,
|
instructions=instructions,
|
||||||
|
text_footer=description,
|
||||||
text_confirm=text_confirm,
|
text_confirm=text_confirm,
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
import trezorui_api
|
import trezorui_api
|
||||||
@ -50,8 +49,9 @@ async def show_share_words(
|
|||||||
)
|
)
|
||||||
|
|
||||||
result = await interact(
|
result = await interact(
|
||||||
trezorui2.show_share_words( # type: ignore [Arguments missing for parameters]
|
trezorui_api.show_share_words(
|
||||||
share_words=share_words, # type: ignore [No parameter named "share_words"]
|
words=share_words,
|
||||||
|
title=None,
|
||||||
),
|
),
|
||||||
br_name,
|
br_name,
|
||||||
br_code,
|
br_code,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from typing import Awaitable, Callable, Sequence
|
from typing import Awaitable, Callable, Sequence
|
||||||
|
|
||||||
import trezorui2
|
|
||||||
import trezorui_api
|
import trezorui_api
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
@ -10,30 +9,6 @@ from ..common import interact, raise_if_not_confirmed
|
|||||||
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
|
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
|
||||||
|
|
||||||
|
|
||||||
def _split_share_into_pages(share_words: Sequence[str], per_page: int = 4) -> list[str]:
|
|
||||||
pages: list[str] = []
|
|
||||||
current = ""
|
|
||||||
fill = 2
|
|
||||||
|
|
||||||
for i, word in enumerate(share_words):
|
|
||||||
if i % per_page == 0:
|
|
||||||
if i != 0:
|
|
||||||
pages.append(current)
|
|
||||||
current = ""
|
|
||||||
|
|
||||||
# Align numbers to the right.
|
|
||||||
lastnum = i + per_page + 1
|
|
||||||
fill = 1 if lastnum < 10 else 2
|
|
||||||
else:
|
|
||||||
current += "\n"
|
|
||||||
current += f"{i + 1:>{fill}}. {word}"
|
|
||||||
|
|
||||||
if current:
|
|
||||||
pages.append(current)
|
|
||||||
|
|
||||||
return pages
|
|
||||||
|
|
||||||
|
|
||||||
def show_share_words(
|
def show_share_words(
|
||||||
share_words: Sequence[str],
|
share_words: Sequence[str],
|
||||||
share_index: int | None = None,
|
share_index: int | None = None,
|
||||||
@ -48,12 +23,10 @@ def show_share_words(
|
|||||||
group_index + 1, share_index + 1
|
group_index + 1, share_index + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
pages = _split_share_into_pages(share_words)
|
|
||||||
|
|
||||||
return raise_if_not_confirmed(
|
return raise_if_not_confirmed(
|
||||||
trezorui2.show_share_words(
|
trezorui_api.show_share_words(
|
||||||
|
words=share_words,
|
||||||
title=title,
|
title=title,
|
||||||
pages=pages,
|
|
||||||
),
|
),
|
||||||
"backup_words",
|
"backup_words",
|
||||||
ButtonRequestType.ResetDevice,
|
ButtonRequestType.ResetDevice,
|
||||||
|
Loading…
Reference in New Issue
Block a user