Skip to content

Commit

Permalink
refactor(core): move and cleanup show_share_words
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
obrusvit committed Nov 19, 2024
1 parent 479e262 commit 6bffb8e
Show file tree
Hide file tree
Showing 18 changed files with 356 additions and 204 deletions.
5 changes: 2 additions & 3 deletions core/embed/rust/librust_qstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ static void _librust_qstrs(void) {
MP_QSTR_flow_confirm_summary;
MP_QSTR_flow_get_address;
MP_QSTR_flow_prompt_backup;
MP_QSTR_flow_show_share_words;
MP_QSTR_flow_warning_hi_prio;
MP_QSTR_get_language;
MP_QSTR_get_transition_out;
Expand Down Expand Up @@ -291,6 +290,7 @@ static void _librust_qstrs(void) {
MP_QSTR_inputs__return;
MP_QSTR_inputs__show;
MP_QSTR_inputs__space;
MP_QSTR_instructions;
MP_QSTR_instructions__continue_holding;
MP_QSTR_instructions__continue_in_app;
MP_QSTR_instructions__enter_next_share;
Expand Down Expand Up @@ -641,7 +641,6 @@ static void _librust_qstrs(void) {
MP_QSTR_set_brightness;
MP_QSTR_setting__adjust;
MP_QSTR_setting__apply;
MP_QSTR_share_words;
MP_QSTR_share_words__words_in_order;
MP_QSTR_share_words__wrote_down_all;
MP_QSTR_show_address_details;
Expand All @@ -658,6 +657,7 @@ static void _librust_qstrs(void) {
MP_QSTR_show_progress_coinjoin;
MP_QSTR_show_remaining_shares;
MP_QSTR_show_share_words;
MP_QSTR_show_share_words_mercury;
MP_QSTR_show_simple;
MP_QSTR_show_success;
MP_QSTR_show_wait_text;
Expand All @@ -683,7 +683,6 @@ static void _librust_qstrs(void) {
MP_QSTR_summary_title;
MP_QSTR_text;
MP_QSTR_text_confirm;
MP_QSTR_text_info;
MP_QSTR_text_mono;
MP_QSTR_time_ms;
MP_QSTR_timer;
Expand Down
70 changes: 70 additions & 0 deletions core/embed/rust/src/ui/api/firmware_upy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
io::BinaryData,
micropython::{
gc::Gc,
iter::IterBuf,
list::List,
macros::{obj_fn_1, obj_fn_kw, obj_module},
map::Map,
Expand All @@ -25,6 +26,7 @@ use crate::{
ui_features_fw::UIFeaturesFirmware,
},
};
use heapless::Vec;

/// Dummy implementation so that we can use `Empty` in a return type of unimplemented trait
/// function
Expand Down Expand Up @@ -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) }
}

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 {
let block = move |_args: &[Obj], kwargs: &Map| {
let text: TString = kwargs.get(Qstr::MP_QSTR_text)?.try_into()?;
Expand Down Expand Up @@ -923,6 +973,26 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """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(),

/// 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(
/// *,
/// text: str,
Expand Down
8 changes: 4 additions & 4 deletions core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ fn footer_updating_func(
}

pub fn new_show_share_words(
title: TString<'static>,
subtitle: TString<'static>,
share_words_vec: Vec<TString<'static>, 33>,
description: Option<TString<'static>>,
subtitle: TString<'static>,
instructions_paragraphs: ParagraphVecShort<'static>,
text_footer: Option<TString<'static>>,
text_confirm: TString<'static>,
) -> Result<SwipeFlow, error::Error> {
let nwords = share_words_vec.len();
let paragraphs_spacing = 8;
let title = TR::reset__recovery_wallet_backup_title.into();

let content_instruction = Frame::left_aligned(
title,
Expand All @@ -97,7 +97,7 @@ pub fn new_show_share_words(
),
)
.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())
.map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed))
.one_button_request(ButtonRequestCode::ResetDevice.with_name("share_words"))
Expand Down
45 changes: 0 additions & 45 deletions core/embed/rust/src/ui/model_mercury/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}

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 {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
Expand Down Expand Up @@ -932,19 +900,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """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(),

/// 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(
/// *,
/// address: str | bytes,
Expand Down
32 changes: 32 additions & 0 deletions core/embed/rust/src/ui/model_mercury/ui_features_fw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,38 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
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(
pages_iterable: crate::micropython::obj::Obj, // TODO: replace Obj
) -> Result<impl LayoutMaybeTrace, Error> {
Expand Down
39 changes: 21 additions & 18 deletions core/embed/rust/src/ui/model_tr/component/share_words.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,24 @@ impl<'a> ShareWords<'a> {
fn render_words<'s>(&'s self, target: &mut impl Renderer<'s>) {
let mut y_offset = 0;
// 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;
let index = self.word_index() + i;
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);
let base = self.area.top_left() + Offset::y(y_offset);

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_fg(theme::FG)
.render(target);

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_fg(theme::FG)
.render(target);
Expand Down Expand Up @@ -171,13 +172,15 @@ impl<'a> crate::trace::Trace for ShareWords<'a> {
self.get_final_text()
} else {
let mut content = ShortString::new();
for i in 0..WORDS_PER_PAGE {
let index = self.word_index() + i;
if index >= self.share_words.len() {
break;
}
self.share_words[index]
.map(|word| unwrap!(uwrite!(content, "{}. {}\n", index + 1, word)));
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)));
}
content
};
Expand Down
26 changes: 0 additions & 26 deletions core/embed/rust/src/ui/model_tr/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}

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]
pub static mp_module_trezorui2: Module = obj_module! {
/// from trezor import utils
Expand Down Expand Up @@ -1016,11 +997,4 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Confirm long content with the possibility to go back from any page.
/// Meant to be used with confirm_with_info."""
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(),
};
Loading

0 comments on commit 6bffb8e

Please sign in to comment.