mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-24 23:38:09 +00:00
feat(core/rust): new design of wallet creation dialogs
[no changelog]
This commit is contained in:
parent
672d6b7d13
commit
03f77c50e9
@ -37,6 +37,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_case_sensitive;
|
MP_QSTR_case_sensitive;
|
||||||
MP_QSTR_confirm_action;
|
MP_QSTR_confirm_action;
|
||||||
MP_QSTR_confirm_address;
|
MP_QSTR_confirm_address;
|
||||||
|
MP_QSTR_confirm_backup;
|
||||||
MP_QSTR_confirm_blob;
|
MP_QSTR_confirm_blob;
|
||||||
MP_QSTR_confirm_coinjoin;
|
MP_QSTR_confirm_coinjoin;
|
||||||
MP_QSTR_confirm_emphasized;
|
MP_QSTR_confirm_emphasized;
|
||||||
|
@ -97,14 +97,13 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Drawing text
|
// Drawing text
|
||||||
Op::Text(text) => {
|
Op::Text(text, continued) => {
|
||||||
// Try to fit text on the current page and if they do not fit,
|
// Try to fit text on the current page and if they do not fit,
|
||||||
// return the appropriate OutOfBounds message
|
// return the appropriate OutOfBounds message
|
||||||
|
|
||||||
// Inserting the ellipsis at the very beginning of the text if needed
|
// Inserting the ellipsis at the very beginning of the text if needed
|
||||||
// (just once for the first Op::Text on the non-first page).
|
// (just for incomplete texts that were separated)
|
||||||
self.layout.continues_from_prev_page =
|
self.layout.continues_from_prev_page = continued;
|
||||||
skip_bytes > 0 && total_processed_chars == 0;
|
|
||||||
|
|
||||||
let fit = self.layout.layout_text(text.as_ref(), cursor, sink);
|
let fit = self.layout.layout_text(text.as_ref(), cursor, sink);
|
||||||
|
|
||||||
@ -146,14 +145,16 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
let mut skipped = 0;
|
let mut skipped = 0;
|
||||||
ops_iter.filter_map(move |op| {
|
ops_iter.filter_map(move |op| {
|
||||||
match op {
|
match op {
|
||||||
Op::Text(text) if skipped < skip_bytes => {
|
Op::Text(text, _continued) if skipped < skip_bytes => {
|
||||||
let skip_text_bytes_if_fits_partially = skip_bytes - skipped;
|
let skip_text_bytes_if_fits_partially = skip_bytes - skipped;
|
||||||
skipped = skipped.saturating_add(text.as_ref().len());
|
skipped = skipped.saturating_add(text.as_ref().len());
|
||||||
if skipped > skip_bytes {
|
if skipped > skip_bytes {
|
||||||
// Fits partially
|
// Fits partially
|
||||||
// Skipping some bytes at the beginning, leaving rest
|
// Skipping some bytes at the beginning, leaving rest
|
||||||
|
// Signifying that the text continues from previous page
|
||||||
Some(Op::Text(
|
Some(Op::Text(
|
||||||
text.skip_prefix(skip_text_bytes_if_fits_partially),
|
text.skip_prefix(skip_text_bytes_if_fits_partially),
|
||||||
|
true,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// Does not fit at all
|
// Does not fit at all
|
||||||
@ -184,15 +185,15 @@ impl<T: StringType + Clone> OpTextLayout<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(self, text: T) -> Self {
|
pub fn text(self, text: T) -> Self {
|
||||||
self.with_new_item(Op::Text(text))
|
self.with_new_item(Op::Text(text, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newline(self) -> Self {
|
pub fn newline(self) -> Self {
|
||||||
self.with_new_item(Op::Text("\n".into()))
|
self.text("\n".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newline_half(self) -> Self {
|
pub fn newline_half(self) -> Self {
|
||||||
self.with_new_item(Op::Text("\r".into()))
|
self.text("\r".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_page(self) -> Self {
|
pub fn next_page(self) -> Self {
|
||||||
@ -238,7 +239,9 @@ impl<T: StringType + Clone> OpTextLayout<T> {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Op<T: StringType> {
|
pub enum Op<T: StringType> {
|
||||||
/// Render text with current color and font.
|
/// Render text with current color and font.
|
||||||
Text(T),
|
/// Bool signifies whether this is a split Text Op continued from previous
|
||||||
|
/// page. If true, a leading ellipsis will be rendered.
|
||||||
|
Text(T, bool),
|
||||||
/// Set current text color.
|
/// Set current text color.
|
||||||
Color(Color),
|
Color(Color),
|
||||||
/// Set currently used font.
|
/// Set currently used font.
|
||||||
|
@ -567,6 +567,15 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Left text and WIDE right arrow.
|
||||||
|
pub fn text_none_arrow_wide(text: T) -> Self {
|
||||||
|
Self::new(
|
||||||
|
Some(ButtonDetails::text(text)),
|
||||||
|
None,
|
||||||
|
Some(ButtonDetails::down_arrow_icon_wide()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Only right text.
|
/// Only right text.
|
||||||
pub fn none_none_text(text: T) -> Self {
|
pub fn none_none_text(text: T) -> Self {
|
||||||
Self::new(None, None, Some(ButtonDetails::text(text)))
|
Self::new(None, None, Some(ButtonDetails::text(text)))
|
||||||
|
@ -39,10 +39,11 @@ where
|
|||||||
{
|
{
|
||||||
pub fn new(pages: FlowPages<F, T>) -> Self {
|
pub fn new(pages: FlowPages<F, T>) -> Self {
|
||||||
let current_page = pages.get(0);
|
let current_page = pages.get(0);
|
||||||
|
let title = current_page.title().map(Title::new);
|
||||||
Self {
|
Self {
|
||||||
pages,
|
pages,
|
||||||
current_page,
|
current_page,
|
||||||
title: None,
|
title,
|
||||||
content_area: Rect::zero(),
|
content_area: Rect::zero(),
|
||||||
title_area: Rect::zero(),
|
title_area: Rect::zero(),
|
||||||
scrollbar: Child::new(ScrollBar::to_be_filled_later()),
|
scrollbar: Child::new(ScrollBar::to_be_filled_later()),
|
||||||
@ -85,11 +86,11 @@ where
|
|||||||
/// position.
|
/// position.
|
||||||
fn change_current_page(&mut self, ctx: &mut EventCtx) {
|
fn change_current_page(&mut self, ctx: &mut EventCtx) {
|
||||||
self.current_page = self.pages.get(self.page_counter);
|
self.current_page = self.pages.get(self.page_counter);
|
||||||
if self.title.is_some() {
|
if let Some(title) = self.current_page.title() {
|
||||||
if let Some(title) = self.current_page.title() {
|
self.title = Some(Title::new(title));
|
||||||
self.title = Some(Title::new(title));
|
self.title.place(self.title_area);
|
||||||
self.title.place(self.title_area);
|
} else {
|
||||||
}
|
self.title = None;
|
||||||
}
|
}
|
||||||
let scrollbar_active_index = self
|
let scrollbar_active_index = self
|
||||||
.pages
|
.pages
|
||||||
|
@ -25,9 +25,7 @@ where
|
|||||||
cancel_btn_details: Option<ButtonDetails<U>>,
|
cancel_btn_details: Option<ButtonDetails<U>>,
|
||||||
/// Right button of the last screen
|
/// Right button of the last screen
|
||||||
confirm_btn_details: Option<ButtonDetails<U>>,
|
confirm_btn_details: Option<ButtonDetails<U>>,
|
||||||
/// Left button of the last page
|
/// Left button of every screen
|
||||||
last_back_btn_details: Option<ButtonDetails<U>>,
|
|
||||||
/// Left button of every screen in the middle
|
|
||||||
back_btn_details: Option<ButtonDetails<U>>,
|
back_btn_details: Option<ButtonDetails<U>>,
|
||||||
/// Right button of every screen apart the last one
|
/// Right button of every screen apart the last one
|
||||||
next_btn_details: Option<ButtonDetails<U>>,
|
next_btn_details: Option<ButtonDetails<U>>,
|
||||||
@ -47,8 +45,7 @@ where
|
|||||||
pad: Pad::with_background(background).with_clear(),
|
pad: Pad::with_background(background).with_clear(),
|
||||||
cancel_btn_details: Some(ButtonDetails::cancel_icon()),
|
cancel_btn_details: Some(ButtonDetails::cancel_icon()),
|
||||||
confirm_btn_details: Some(ButtonDetails::text("CONFIRM".into())),
|
confirm_btn_details: Some(ButtonDetails::text("CONFIRM".into())),
|
||||||
back_btn_details: Some(ButtonDetails::up_arrow_icon_wide()),
|
back_btn_details: Some(ButtonDetails::up_arrow_icon()),
|
||||||
last_back_btn_details: Some(ButtonDetails::up_arrow_icon()),
|
|
||||||
next_btn_details: Some(ButtonDetails::down_arrow_icon_wide()),
|
next_btn_details: Some(ButtonDetails::down_arrow_icon_wide()),
|
||||||
// Setting empty layout for now, we do not yet know the page count.
|
// Setting empty layout for now, we do not yet know the page count.
|
||||||
// Initial button layout will be set in `place()` after we can call
|
// Initial button layout will be set in `place()` after we can call
|
||||||
@ -122,18 +119,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout<U> {
|
fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout<U> {
|
||||||
let btn_left = self.get_left_button_details(!has_prev, !has_next);
|
let btn_left = self.get_left_button_details(!has_prev);
|
||||||
let btn_right = self.get_right_button_details(has_next);
|
let btn_right = self.get_right_button_details(has_next);
|
||||||
ButtonLayout::new(btn_left, None, btn_right)
|
ButtonLayout::new(btn_left, None, btn_right)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the left button details, depending whether the page is first, last,
|
/// Get the left button details, depending whether the page is first or not.
|
||||||
/// or in the middle.
|
fn get_left_button_details(&self, is_first: bool) -> Option<ButtonDetails<U>> {
|
||||||
fn get_left_button_details(&self, is_first: bool, is_last: bool) -> Option<ButtonDetails<U>> {
|
|
||||||
if is_first {
|
if is_first {
|
||||||
self.cancel_btn_details.clone()
|
self.cancel_btn_details.clone()
|
||||||
} else if is_last {
|
|
||||||
self.last_back_btn_details.clone()
|
|
||||||
} else {
|
} else {
|
||||||
self.back_btn_details.clone()
|
self.back_btn_details.clone()
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,15 @@ use crate::{
|
|||||||
|
|
||||||
use heapless::{String, Vec};
|
use heapless::{String, Vec};
|
||||||
|
|
||||||
use super::{common::display_left, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar};
|
use super::{common::display_left, scrollbar::SCROLLBAR_SPACE, theme, ScrollBar};
|
||||||
|
|
||||||
const WORDS_PER_PAGE: usize = 3;
|
const WORDS_PER_PAGE: usize = 3;
|
||||||
const EXTRA_LINE_HEIGHT: i16 = 2;
|
const EXTRA_LINE_HEIGHT: i16 = 2;
|
||||||
const NUMBER_X_OFFSET: i16 = 5;
|
const NUMBER_X_OFFSET: i16 = 0;
|
||||||
const NUMBER_WORD_OFFSET: i16 = 20;
|
const WORD_X_OFFSET: i16 = 25;
|
||||||
const NUMBER_FONT: Font = Font::DEMIBOLD;
|
const NUMBER_FONT: Font = Font::DEMIBOLD;
|
||||||
const WORD_FONT: Font = Font::BIG;
|
const WORD_FONT: Font = Font::BIG;
|
||||||
const INFO_TOP_OFFSET: i16 = 15;
|
const INFO_TOP_OFFSET: i16 = 20;
|
||||||
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
|
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
|
||||||
|
|
||||||
/// Showing the given share words.
|
/// Showing the given share words.
|
||||||
@ -28,7 +28,6 @@ where
|
|||||||
T: StringType,
|
T: StringType,
|
||||||
{
|
{
|
||||||
area: Rect,
|
area: Rect,
|
||||||
title: Child<Title<T>>,
|
|
||||||
scrollbar: Child<ScrollBar>,
|
scrollbar: Child<ScrollBar>,
|
||||||
share_words: Vec<T, MAX_WORDS>,
|
share_words: Vec<T, MAX_WORDS>,
|
||||||
page_index: usize,
|
page_index: usize,
|
||||||
@ -38,10 +37,9 @@ impl<T> ShareWords<T>
|
|||||||
where
|
where
|
||||||
T: StringType + Clone,
|
T: StringType + Clone,
|
||||||
{
|
{
|
||||||
pub fn new(title: T, share_words: Vec<T, MAX_WORDS>) -> Self {
|
pub fn new(share_words: Vec<T, MAX_WORDS>) -> Self {
|
||||||
let mut instance = Self {
|
let mut instance = Self {
|
||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
title: Child::new(Title::new(title)),
|
|
||||||
scrollbar: Child::new(ScrollBar::to_be_filled_later()),
|
scrollbar: Child::new(ScrollBar::to_be_filled_later()),
|
||||||
share_words,
|
share_words,
|
||||||
page_index: 0,
|
page_index: 0,
|
||||||
@ -53,15 +51,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn word_index(&self) -> usize {
|
fn word_index(&self) -> usize {
|
||||||
(self.page_index - 2) * WORDS_PER_PAGE
|
self.page_index * WORDS_PER_PAGE
|
||||||
}
|
|
||||||
|
|
||||||
fn is_entry_page(&self) -> bool {
|
|
||||||
self.page_index == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_second_page(&self) -> bool {
|
|
||||||
self.page_index == 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_final_page(&self) -> bool {
|
fn is_final_page(&self) -> bool {
|
||||||
@ -74,36 +64,13 @@ where
|
|||||||
} else {
|
} else {
|
||||||
self.share_words.len() / WORDS_PER_PAGE + 1
|
self.share_words.len() / WORDS_PER_PAGE + 1
|
||||||
};
|
};
|
||||||
// Two pages before the words, one after it
|
// One page after the words
|
||||||
2 + word_screens + 1
|
word_screens + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_first_text(&self) -> String<100> {
|
fn get_final_text(&self) -> String<50> {
|
||||||
build_string!(
|
build_string!(
|
||||||
100,
|
50,
|
||||||
"Write down all ",
|
|
||||||
inttostr!(self.share_words.len() as u8),
|
|
||||||
" words in order."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Display the first page with user information.
|
|
||||||
fn paint_entry_page(&mut self) {
|
|
||||||
self.render_text_on_screen(&self.get_first_text(), Font::MONO);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_second_text(&self) -> String<100> {
|
|
||||||
build_string!(100, "Do NOT make digital copies!")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Display the second page with user information.
|
|
||||||
fn paint_second_page(&mut self) {
|
|
||||||
self.render_text_on_screen(&self.get_second_text(), Font::MONO);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_final_text(&self) -> String<100> {
|
|
||||||
build_string!(
|
|
||||||
100,
|
|
||||||
"I wrote down all ",
|
"I wrote down all ",
|
||||||
inttostr!(self.share_words.len() as u8),
|
inttostr!(self.share_words.len() as u8),
|
||||||
" words in order."
|
" words in order."
|
||||||
@ -112,15 +79,10 @@ where
|
|||||||
|
|
||||||
/// Display the final page with user confirmation.
|
/// Display the final page with user confirmation.
|
||||||
fn paint_final_page(&mut self) {
|
fn paint_final_page(&mut self) {
|
||||||
self.render_text_on_screen(&self.get_final_text(), Font::MONO);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shows text in the main screen area.
|
|
||||||
fn render_text_on_screen(&self, text: &str, font: Font) {
|
|
||||||
text_multiline(
|
text_multiline(
|
||||||
self.area.split_top(INFO_TOP_OFFSET).1,
|
self.area.split_top(INFO_TOP_OFFSET).1,
|
||||||
text,
|
&self.get_final_text(),
|
||||||
font,
|
Font::NORMAL,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
Alignment::Start,
|
Alignment::Start,
|
||||||
@ -138,9 +100,10 @@ where
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let word = &self.share_words[index];
|
let word = &self.share_words[index];
|
||||||
let baseline = self.area.top_left() + Offset::new(NUMBER_X_OFFSET, y_offset);
|
let baseline = self.area.top_left() + Offset::y(y_offset);
|
||||||
display_left(baseline, &inttostr!(index as u8 + 1), NUMBER_FONT);
|
let ordinal = build_string!(5, inttostr!(index as u8 + 1), ".");
|
||||||
display_left(baseline + Offset::x(NUMBER_WORD_OFFSET), &word, WORD_FONT);
|
display_left(baseline + Offset::x(NUMBER_X_OFFSET), &ordinal, NUMBER_FONT);
|
||||||
|
display_left(baseline + Offset::x(WORD_X_OFFSET), &word, WORD_FONT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,12 +115,11 @@ where
|
|||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
let (title_area, _) = bounds.split_top(theme::FONT_HEADER.line_height());
|
let (top_area, _) = bounds.split_top(theme::FONT_HEADER.line_height());
|
||||||
|
|
||||||
let (title_area, scrollbar_area) =
|
let (_, scrollbar_area) =
|
||||||
title_area.split_right(self.scrollbar.inner().overall_width() + SCROLLBAR_SPACE);
|
top_area.split_right(self.scrollbar.inner().overall_width() + SCROLLBAR_SPACE);
|
||||||
|
|
||||||
self.title.place(title_area);
|
|
||||||
self.scrollbar.place(scrollbar_area);
|
self.scrollbar.place(scrollbar_area);
|
||||||
|
|
||||||
self.area = bounds;
|
self.area = bounds;
|
||||||
@ -165,7 +127,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
self.title.event(ctx, event);
|
|
||||||
self.scrollbar.event(ctx, event);
|
self.scrollbar.event(ctx, event);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -174,12 +135,7 @@ where
|
|||||||
// Showing scrollbar in all cases
|
// Showing scrollbar in all cases
|
||||||
// Individual pages are responsible for not colliding with it
|
// Individual pages are responsible for not colliding with it
|
||||||
self.scrollbar.paint();
|
self.scrollbar.paint();
|
||||||
if self.is_entry_page() {
|
if self.is_final_page() {
|
||||||
self.title.paint();
|
|
||||||
self.paint_entry_page();
|
|
||||||
} else if self.is_second_page() {
|
|
||||||
self.paint_second_page();
|
|
||||||
} else if self.is_final_page() {
|
|
||||||
self.paint_final_page();
|
self.paint_final_page();
|
||||||
} else {
|
} else {
|
||||||
self.paint_words();
|
self.paint_words();
|
||||||
@ -211,19 +167,10 @@ where
|
|||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("ShareWords");
|
t.component("ShareWords");
|
||||||
let content = if self.is_entry_page() {
|
let content = if self.is_final_page() {
|
||||||
build_string!(
|
|
||||||
100,
|
|
||||||
self.title.inner().get_text(),
|
|
||||||
"\n",
|
|
||||||
&self.get_first_text()
|
|
||||||
)
|
|
||||||
} else if self.is_second_page() {
|
|
||||||
self.get_second_text()
|
|
||||||
} else if self.is_final_page() {
|
|
||||||
self.get_final_text()
|
self.get_final_text()
|
||||||
} else {
|
} else {
|
||||||
let mut content = String::<100>::new();
|
let mut content = String::<50>::new();
|
||||||
for i in 0..WORDS_PER_PAGE {
|
for i in 0..WORDS_PER_PAGE {
|
||||||
let index = self.word_index() + i;
|
let index = self.word_index() + i;
|
||||||
if index >= self.share_words.len() {
|
if index >= self.share_words.len() {
|
||||||
@ -231,7 +178,7 @@ where
|
|||||||
}
|
}
|
||||||
let word = &self.share_words[index];
|
let word = &self.share_words[index];
|
||||||
let current_line =
|
let current_line =
|
||||||
build_string!(20, inttostr!(index as u8 + 1), " ", word.as_ref(), "\n");
|
build_string!(50, inttostr!(index as u8 + 1), ". ", word.as_ref(), "\n");
|
||||||
unwrap!(content.push_str(¤t_line));
|
unwrap!(content.push_str(¤t_line));
|
||||||
}
|
}
|
||||||
content
|
content
|
||||||
|
@ -413,9 +413,43 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
|||||||
.text_normal("More info at".into())
|
.text_normal("More info at".into())
|
||||||
.newline()
|
.newline()
|
||||||
.text_bold("trezor.io/tos".into());
|
.text_bold("trezor.io/tos".into());
|
||||||
let formatted = FormattedText::new(ops);
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
|
||||||
content_in_button_page(title, formatted, button, None, false)
|
content_in_button_page(title, formatted, button, Some("".into()), false)
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], _kwargs: &Map| {
|
||||||
|
let get_page = move |page_index| match page_index {
|
||||||
|
0 => {
|
||||||
|
let btn_layout = ButtonLayout::text_none_arrow_wide("SKIP".into());
|
||||||
|
let btn_actions = ButtonActions::cancel_none_next();
|
||||||
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
||||||
|
.text_normal("New wallet created.".into())
|
||||||
|
.newline()
|
||||||
|
.newline()
|
||||||
|
.text_normal("It should be backed up now!".into());
|
||||||
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
Page::new(btn_layout, btn_actions, formatted).with_title("SUCCESS".into())
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let btn_layout = ButtonLayout::up_arrow_none_text("BACK UP".into());
|
||||||
|
let btn_actions = ButtonActions::prev_none_confirm();
|
||||||
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(
|
||||||
|
"You can use your backup to recover your wallet at any time.".into(),
|
||||||
|
);
|
||||||
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
|
.with_title("BACK UP WALLET".into())
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let pages = FlowPages::new(get_page, 2);
|
||||||
|
|
||||||
|
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||||
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
@ -532,7 +566,6 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
let address_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_label)?.try_into()?;
|
let address_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_label)?.try_into()?;
|
||||||
let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
||||||
let address_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_title)?.try_into()?;
|
let address_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_title)?.try_into()?;
|
||||||
let address_title_clone = address_title.clone();
|
|
||||||
let amount_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?;
|
let amount_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?;
|
||||||
|
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
@ -566,7 +599,7 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
};
|
};
|
||||||
let pages = FlowPages::new(get_page, 2);
|
let pages = FlowPages::new(get_page, 2);
|
||||||
|
|
||||||
let obj = LayoutObj::new(Flow::new(pages).with_common_title(address_title_clone))?;
|
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
@ -669,11 +702,11 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
.line_breaking(LineBreaking::BreakWordsNoHyphen)
|
.line_breaking(LineBreaking::BreakWordsNoHyphen)
|
||||||
.text_mono(address.clone());
|
.text_mono(address.clone());
|
||||||
let formatted = FormattedText::new(ops);
|
let formatted = FormattedText::new(ops);
|
||||||
Page::new(btn_layout, btn_actions, formatted)
|
Page::new(btn_layout, btn_actions, formatted).with_title(title.clone())
|
||||||
};
|
};
|
||||||
let pages = FlowPages::new(get_page, 1);
|
let pages = FlowPages::new(get_page, 1);
|
||||||
|
|
||||||
let obj = LayoutObj::new(Flow::new(pages).with_common_title(title))?;
|
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
@ -688,7 +721,7 @@ fn tutorial_screen(
|
|||||||
btn_actions: ButtonActions,
|
btn_actions: ButtonActions,
|
||||||
) -> Page<StrBuffer> {
|
) -> Page<StrBuffer> {
|
||||||
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL).text_normal(text.into());
|
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL).text_normal(text.into());
|
||||||
let formatted = FormattedText::new(ops).vertically_aligned(geometry::Alignment::Center);
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::new(btn_layout, btn_actions, formatted).with_title(title.into())
|
Page::new(btn_layout, btn_actions, formatted).with_title(title.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,11 +799,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
|||||||
|
|
||||||
let pages = FlowPages::new(get_page, PAGE_COUNT);
|
let pages = FlowPages::new(get_page, PAGE_COUNT);
|
||||||
|
|
||||||
let obj = LayoutObj::new(
|
let obj = LayoutObj::new(Flow::new(pages).with_scrollbar(false))?;
|
||||||
Flow::new(pages)
|
|
||||||
.with_scrollbar(false)
|
|
||||||
.with_common_title("HELLO".into()),
|
|
||||||
)?;
|
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
@ -1156,16 +1185,17 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
|||||||
|
|
||||||
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 title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
|
||||||
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
|
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
|
||||||
let share_words: Vec<StrBuffer, 33> = iter_into_vec(share_words_obj)?;
|
let share_words: Vec<StrBuffer, 33> = iter_into_vec(share_words_obj)?;
|
||||||
|
|
||||||
|
let cancel_btn = Some(ButtonDetails::up_arrow_icon());
|
||||||
let confirm_btn = Some(
|
let confirm_btn = Some(
|
||||||
ButtonDetails::<StrBuffer>::text("HOLD TO CONFIRM".into()).with_default_duration(),
|
ButtonDetails::<StrBuffer>::text("HOLD TO CONFIRM".into()).with_default_duration(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let obj = LayoutObj::new(
|
let obj = LayoutObj::new(
|
||||||
ButtonPage::new(ShareWords::new(title, share_words), theme::BG)
|
ButtonPage::new(ShareWords::new(share_words), theme::BG)
|
||||||
|
.with_cancel_btn(cancel_btn)
|
||||||
.with_confirm_btn(confirm_btn),
|
.with_confirm_btn(confirm_btn),
|
||||||
)?;
|
)?;
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
@ -1460,6 +1490,10 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Confirm TOS before device setup."""
|
/// """Confirm TOS before device setup."""
|
||||||
Qstr::MP_QSTR_confirm_reset_device => obj_fn_kw!(0, new_confirm_reset_device).as_obj(),
|
Qstr::MP_QSTR_confirm_reset_device => obj_fn_kw!(0, new_confirm_reset_device).as_obj(),
|
||||||
|
|
||||||
|
/// def confirm_backup() -> object:
|
||||||
|
/// """Strongly recommend user to do backup."""
|
||||||
|
Qstr::MP_QSTR_confirm_backup => obj_fn_kw!(0, new_confirm_backup).as_obj(),
|
||||||
|
|
||||||
/// def show_address_details(
|
/// def show_address_details(
|
||||||
/// *,
|
/// *,
|
||||||
/// address: str,
|
/// address: str,
|
||||||
@ -1649,7 +1683,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
|
|
||||||
/// def show_share_words(
|
/// def show_share_words(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
|
||||||
/// share_words: Iterable[str],
|
/// share_words: Iterable[str],
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Shows a backup seed."""
|
/// """Shows a backup seed."""
|
||||||
|
@ -75,6 +75,11 @@ def confirm_reset_device(
|
|||||||
"""Confirm TOS before device setup."""
|
"""Confirm TOS before device setup."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_tr/layout.rs
|
||||||
|
def confirm_backup() -> object:
|
||||||
|
"""Strongly recommend user to do backup."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def show_address_details(
|
def show_address_details(
|
||||||
*,
|
*,
|
||||||
@ -285,7 +290,6 @@ def select_word(
|
|||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def show_share_words(
|
def show_share_words(
|
||||||
*,
|
*,
|
||||||
title: str,
|
|
||||||
share_words: Iterable[str],
|
share_words: Iterable[str],
|
||||||
) -> object:
|
) -> object:
|
||||||
"""Shows a backup seed."""
|
"""Shows a backup seed."""
|
||||||
|
@ -132,12 +132,12 @@ async def _show_confirmation_success(
|
|||||||
|
|
||||||
|
|
||||||
async def _show_confirmation_failure() -> None:
|
async def _show_confirmation_failure() -> None:
|
||||||
from trezor.ui.layouts.recovery import show_recovery_warning
|
from trezor.ui.layouts.reset import show_reset_warning
|
||||||
|
|
||||||
await show_recovery_warning(
|
await show_reset_warning(
|
||||||
"warning_backup_check",
|
"warning_backup_check",
|
||||||
"Please check again.",
|
"Please check again",
|
||||||
"That is the wrong word.",
|
"Wrong word selected!",
|
||||||
"Check again",
|
"Check again",
|
||||||
ButtonRequestType.ResetDevice,
|
ButtonRequestType.ResetDevice,
|
||||||
)
|
)
|
||||||
@ -171,7 +171,7 @@ async def bip39_show_and_confirm_mnemonic(mnemonic: str) -> None:
|
|||||||
|
|
||||||
# make the user confirm some words from the mnemonic
|
# make the user confirm some words from the mnemonic
|
||||||
if await _share_words_confirmed(None, words):
|
if await _share_words_confirmed(None, words):
|
||||||
break # this share is confirmed, go to next one
|
break # mnemonic is confirmed, go next
|
||||||
|
|
||||||
|
|
||||||
# SLIP39
|
# SLIP39
|
||||||
|
@ -400,26 +400,25 @@ async def confirm_reset_device(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO cleanup @ redesign
|
|
||||||
async def confirm_backup() -> bool:
|
async def confirm_backup() -> bool:
|
||||||
if await get_bool(
|
br_type = "backup_device"
|
||||||
"backup_device",
|
br_code = ButtonRequestType.ResetDevice
|
||||||
"SUCCESS",
|
|
||||||
description="New wallet has been created.\nIt should be backed up now!",
|
result = await interact(
|
||||||
verb="BACK UP",
|
RustLayout(trezorui2.confirm_backup()),
|
||||||
verb_cancel="SKIP",
|
br_type,
|
||||||
br_code=ButtonRequestType.ResetDevice,
|
br_code,
|
||||||
):
|
)
|
||||||
|
if result is CONFIRMED:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return await get_bool(
|
return await get_bool(
|
||||||
"backup_device",
|
br_type,
|
||||||
"WARNING",
|
"SKIP BACKUP",
|
||||||
"Are you sure you want to skip the backup?\n",
|
description="Are you sure you want to skip the backup?",
|
||||||
"You can back up your Trezor once, at any time.",
|
|
||||||
verb="BACK UP",
|
verb="BACK UP",
|
||||||
verb_cancel="SKIP",
|
verb_cancel="SKIP",
|
||||||
br_code=ButtonRequestType.ResetDevice,
|
br_code=br_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from trezor.wire import ActionCancelled
|
|||||||
import trezorui2
|
import trezorui2
|
||||||
|
|
||||||
from ..common import interact
|
from ..common import interact
|
||||||
from . import RustLayout, confirm_action, raise_if_not_confirmed
|
from . import RustLayout, confirm_action, show_error
|
||||||
|
|
||||||
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
|
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
|
||||||
|
|
||||||
@ -21,9 +21,11 @@ async def show_share_words(
|
|||||||
group_index: int | None = None,
|
group_index: int | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Showing words, asking for write down confirmation and preparing for check
|
# Showing words, asking for write down confirmation and preparing for check
|
||||||
|
br_type = "backup_words"
|
||||||
|
br_code = ButtonRequestType.ResetDevice
|
||||||
|
|
||||||
if share_index is None:
|
if share_index is None:
|
||||||
title = "RECOVERY SEED"
|
title = "STANDARD BACKUP"
|
||||||
check_title = "CHECK BACKUP"
|
check_title = "CHECK BACKUP"
|
||||||
elif group_index is None:
|
elif group_index is None:
|
||||||
title = f"SHARE #{share_index + 1}"
|
title = f"SHARE #{share_index + 1}"
|
||||||
@ -32,26 +34,37 @@ async def show_share_words(
|
|||||||
title = f"G{group_index + 1} - SHARE {share_index + 1}"
|
title = f"G{group_index + 1} - SHARE {share_index + 1}"
|
||||||
check_title = f"GROUP {group_index + 1} - SHARE {share_index + 1}"
|
check_title = f"GROUP {group_index + 1} - SHARE {share_index + 1}"
|
||||||
|
|
||||||
await raise_if_not_confirmed(
|
# We want the option to go back from words to the previous screen
|
||||||
interact(
|
# (by sending CANCELLED)
|
||||||
|
while True:
|
||||||
|
await confirm_action(
|
||||||
|
br_type,
|
||||||
|
title,
|
||||||
|
description=f"Write down all {len(share_words)} words in order.",
|
||||||
|
verb="SHOW WORDS",
|
||||||
|
verb_cancel=None,
|
||||||
|
br_code=br_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await interact(
|
||||||
RustLayout(
|
RustLayout(
|
||||||
trezorui2.show_share_words( # type: ignore [Argument missing for parameter "pages"]
|
trezorui2.show_share_words( # type: ignore [Arguments missing for parameters]
|
||||||
title=title,
|
|
||||||
share_words=share_words, # type: ignore [No parameter named "share_words"]
|
share_words=share_words, # type: ignore [No parameter named "share_words"]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"backup_words",
|
br_type,
|
||||||
ButtonRequestType.ResetDevice,
|
br_code,
|
||||||
)
|
)
|
||||||
)
|
if result is CONFIRMED:
|
||||||
|
break
|
||||||
|
|
||||||
await confirm_action(
|
await confirm_action(
|
||||||
"backup_words",
|
br_type,
|
||||||
check_title,
|
check_title,
|
||||||
description="Select the correct word for each position.",
|
description="Select the correct word for each position.",
|
||||||
verb="CONTINUE",
|
verb="CONTINUE",
|
||||||
verb_cancel=None,
|
verb_cancel=None,
|
||||||
br_code=ButtonRequestType.ResetDevice,
|
br_code=br_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -234,13 +247,12 @@ async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int:
|
|||||||
|
|
||||||
|
|
||||||
async def show_warning_backup(slip39: bool) -> None:
|
async def show_warning_backup(slip39: bool) -> None:
|
||||||
await confirm_action(
|
await show_error(
|
||||||
"backup_warning",
|
"backup_warning",
|
||||||
"SHAMIR BACKUP" if slip39 else "WALLET BACKUP",
|
"REMEMBER",
|
||||||
description="You can use your backup to recover your wallet at any time.",
|
"Never make a digital copy of your backup or upload it online!",
|
||||||
verb="HOLD TO BEGIN",
|
"OK, I UNDERSTAND",
|
||||||
hold=True,
|
ButtonRequestType.ResetDevice,
|
||||||
br_code=ButtonRequestType.ResetDevice,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -253,3 +265,21 @@ async def show_success_backup() -> None:
|
|||||||
verb_cancel=None,
|
verb_cancel=None,
|
||||||
br_code=ButtonRequestType.Success,
|
br_code=ButtonRequestType.Success,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def show_reset_warning(
|
||||||
|
ctx: GenericContext,
|
||||||
|
br_type: str,
|
||||||
|
content: str,
|
||||||
|
subheader: str | None = None,
|
||||||
|
button: str = "TRY AGAIN",
|
||||||
|
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||||
|
) -> None:
|
||||||
|
await show_error(
|
||||||
|
ctx,
|
||||||
|
br_type,
|
||||||
|
button.upper(),
|
||||||
|
subheader or "",
|
||||||
|
content,
|
||||||
|
br_code=br_code,
|
||||||
|
)
|
||||||
|
@ -7,7 +7,7 @@ from trezor.wire.context import wait as ctx_wait
|
|||||||
import trezorui2
|
import trezorui2
|
||||||
|
|
||||||
from ..common import interact
|
from ..common import interact
|
||||||
from . import RustLayout
|
from . import RustLayout, raise_if_not_confirmed
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Sequence
|
from typing import Callable, Sequence
|
||||||
@ -335,3 +335,26 @@ async def show_success_backup() -> None:
|
|||||||
|
|
||||||
text = "Use your backup when you need to recover your wallet."
|
text = "Use your backup when you need to recover your wallet."
|
||||||
await show_success("success_backup", text, "Your backup is done.")
|
await show_success("success_backup", text, "Your backup is done.")
|
||||||
|
|
||||||
|
|
||||||
|
async def show_reset_warning(
|
||||||
|
br_type: str,
|
||||||
|
content: str,
|
||||||
|
subheader: str | None = None,
|
||||||
|
button: str = "TRY AGAIN",
|
||||||
|
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||||
|
) -> None:
|
||||||
|
await raise_if_not_confirmed(
|
||||||
|
interact(
|
||||||
|
RustLayout(
|
||||||
|
trezorui2.show_warning(
|
||||||
|
title=subheader or "",
|
||||||
|
description=content,
|
||||||
|
button=button.upper(),
|
||||||
|
allow_cancel=False,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
br_type,
|
||||||
|
br_code,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -581,20 +581,20 @@ class DebugLink:
|
|||||||
x, y = click
|
x, y = click
|
||||||
return self.input(x=x, y=y, hold_ms=hold_ms, wait=True)
|
return self.input(x=x, y=y, hold_ms=hold_ms, wait=True)
|
||||||
|
|
||||||
def press_yes(self, wait: bool = False) -> None:
|
def press_yes(self, wait: bool = False) -> Optional[LayoutContent]:
|
||||||
self.input(button=messages.DebugButton.YES, wait=wait)
|
return self.input(button=messages.DebugButton.YES, wait=wait)
|
||||||
|
|
||||||
def press_no(self, wait: bool = False) -> None:
|
def press_no(self, wait: bool = False) -> Optional[LayoutContent]:
|
||||||
self.input(button=messages.DebugButton.NO, wait=wait)
|
return self.input(button=messages.DebugButton.NO, wait=wait)
|
||||||
|
|
||||||
def press_info(self, wait: bool = False) -> None:
|
def press_info(self, wait: bool = False) -> Optional[LayoutContent]:
|
||||||
self.input(button=messages.DebugButton.INFO, wait=wait)
|
return self.input(button=messages.DebugButton.INFO, wait=wait)
|
||||||
|
|
||||||
def swipe_up(self, wait: bool = False) -> None:
|
def swipe_up(self, wait: bool = False) -> Optional[LayoutContent]:
|
||||||
self.input(swipe=messages.DebugSwipeDirection.UP, wait=wait)
|
return self.input(swipe=messages.DebugSwipeDirection.UP, wait=wait)
|
||||||
|
|
||||||
def swipe_down(self, wait: bool = False) -> None:
|
def swipe_down(self, wait: bool = False) -> Optional[LayoutContent]:
|
||||||
self.input(swipe=messages.DebugSwipeDirection.DOWN, wait=wait)
|
return self.input(swipe=messages.DebugSwipeDirection.DOWN, wait=wait)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def swipe_right(self) -> None:
|
def swipe_right(self) -> None:
|
||||||
|
@ -20,14 +20,10 @@ def confirm_new_wallet(debug: "DebugLink") -> None:
|
|||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def confirm_read(debug: "DebugLink", title: str, hold: bool = False) -> None:
|
def confirm_read(debug: "DebugLink", title: str, middle_r: bool = False) -> None:
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
if title == "Caution":
|
if title == "Caution":
|
||||||
if debug.model == "T":
|
assert "Never make a digital copy" in layout.text_content()
|
||||||
# TODO: could look into button texts
|
|
||||||
assert "OK, I UNDERSTAND" in layout.json_str
|
|
||||||
elif debug.model == "R":
|
|
||||||
assert "use your backup to recover" in layout.text_content()
|
|
||||||
elif title == "Success":
|
elif title == "Success":
|
||||||
# TODO: improve this
|
# TODO: improve this
|
||||||
assert any(
|
assert any(
|
||||||
@ -36,7 +32,7 @@ def confirm_read(debug: "DebugLink", title: str, hold: bool = False) -> None:
|
|||||||
"success",
|
"success",
|
||||||
"finished",
|
"finished",
|
||||||
"done",
|
"done",
|
||||||
"has been created",
|
"created",
|
||||||
"Keep it safe",
|
"Keep it safe",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -50,12 +46,10 @@ def confirm_read(debug: "DebugLink", title: str, hold: bool = False) -> None:
|
|||||||
elif debug.model == "R":
|
elif debug.model == "R":
|
||||||
if layout.page_count() > 1:
|
if layout.page_count() > 1:
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
if hold:
|
if middle_r:
|
||||||
# TODO: create debug.hold_right()?
|
debug.press_middle(wait=True)
|
||||||
debug.press_yes()
|
|
||||||
else:
|
else:
|
||||||
debug.press_right()
|
debug.press_right(wait=True)
|
||||||
debug.wait_layout()
|
|
||||||
|
|
||||||
|
|
||||||
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
|
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
|
||||||
@ -94,18 +88,19 @@ def read_words(
|
|||||||
assert layout.title() == "RECOVERY SEED"
|
assert layout.title() == "RECOVERY SEED"
|
||||||
elif debug.model == "R":
|
elif debug.model == "R":
|
||||||
if backup_type == messages.BackupType.Slip39_Advanced:
|
if backup_type == messages.BackupType.Slip39_Advanced:
|
||||||
assert "SHARE" in layout.text_content()
|
assert "SHARE" in layout.title()
|
||||||
elif backup_type == messages.BackupType.Slip39_Basic:
|
elif backup_type == messages.BackupType.Slip39_Basic:
|
||||||
assert layout.text_content().startswith("SHARE #")
|
assert layout.title().startswith("SHARE #")
|
||||||
else:
|
else:
|
||||||
assert layout.text_content().startswith("RECOVERY SEED")
|
assert layout.title() == "STANDARD BACKUP"
|
||||||
|
|
||||||
|
assert "Write down" in layout.text_content()
|
||||||
|
layout = debug.press_right(wait=True)
|
||||||
|
|
||||||
# Swiping through all the pages and loading the words
|
# Swiping through all the pages and loading the words
|
||||||
for i in range(layout.page_count() - 1):
|
for _ in range(layout.page_count() - 1):
|
||||||
# In model R, first two pages are just informational
|
words.extend(layout.seed_words())
|
||||||
if not (debug.model == "R" and i < 2):
|
layout = debug.swipe_up(wait=True)
|
||||||
words.extend(layout.seed_words())
|
|
||||||
layout = debug.input(swipe=messages.DebugSwipeDirection.UP, wait=True)
|
|
||||||
assert layout is not None
|
assert layout is not None
|
||||||
if debug.model == "T":
|
if debug.model == "T":
|
||||||
words.extend(layout.seed_words())
|
words.extend(layout.seed_words())
|
||||||
|
@ -52,8 +52,8 @@ def test_reset_bip39(device_handler: "BackgroundDeviceHandler"):
|
|||||||
# confirm back up
|
# confirm back up
|
||||||
reset.confirm_read(debug, "Success")
|
reset.confirm_read(debug, "Success")
|
||||||
|
|
||||||
# confirm backup warning (hold-to-confirm on TR)
|
# confirm backup warning
|
||||||
reset.confirm_read(debug, "Caution", hold=True)
|
reset.confirm_read(debug, "Caution", middle_r=True)
|
||||||
|
|
||||||
# read words
|
# read words
|
||||||
words = reset.read_words(debug, messages.BackupType.Bip39)
|
words = reset.read_words(debug, messages.BackupType.Bip39)
|
||||||
|
@ -105,8 +105,8 @@ def test_reset_slip39_advanced(
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError("not a supported combination")
|
raise RuntimeError("not a supported combination")
|
||||||
|
|
||||||
# confirm backup warning (hold-to-confirm on TR)
|
# confirm backup warning
|
||||||
reset.confirm_read(debug, "Caution", hold=True)
|
reset.confirm_read(debug, "Caution", middle_r=True)
|
||||||
|
|
||||||
all_words: list[str] = []
|
all_words: list[str] = []
|
||||||
for _ in range(group_count):
|
for _ in range(group_count):
|
||||||
|
@ -85,8 +85,8 @@ def test_reset_slip39_basic(
|
|||||||
# confirm checklist
|
# confirm checklist
|
||||||
reset.confirm_read(debug, "Checklist")
|
reset.confirm_read(debug, "Checklist")
|
||||||
|
|
||||||
# confirm backup warning (hold-to-confirm on TR)
|
# confirm backup warning
|
||||||
reset.confirm_read(debug, "Caution", hold=True)
|
reset.confirm_read(debug, "Caution", middle_r=True)
|
||||||
|
|
||||||
all_words: list[str] = []
|
all_words: list[str] = []
|
||||||
for _ in range(num_of_shares):
|
for _ in range(num_of_shares):
|
||||||
|
@ -339,14 +339,14 @@ def read_and_confirm_mnemonic_tr(
|
|||||||
debug: "DebugLink", choose_wrong: bool = False
|
debug: "DebugLink", choose_wrong: bool = False
|
||||||
) -> Generator[None, "ButtonRequest", Optional[str]]:
|
) -> Generator[None, "ButtonRequest", Optional[str]]:
|
||||||
mnemonic: list[str] = []
|
mnemonic: list[str] = []
|
||||||
|
yield # write down all 12 words in order
|
||||||
|
debug.press_yes()
|
||||||
br = yield
|
br = yield
|
||||||
assert br.pages is not None
|
assert br.pages is not None
|
||||||
for i in range(br.pages - 1):
|
for _ in range(br.pages - 1):
|
||||||
layout = debug.wait_layout()
|
layout = debug.wait_layout()
|
||||||
# First two pages have just instructions
|
words = layout.seed_words()
|
||||||
if i > 1:
|
mnemonic.extend(words)
|
||||||
words = layout.seed_words()
|
|
||||||
mnemonic.extend(words)
|
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
debug.press_yes()
|
debug.press_yes()
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ def test_skip_backup_msg(client: Client, backup_type, backup_flow):
|
|||||||
@pytest.mark.skip_t1
|
@pytest.mark.skip_t1
|
||||||
@pytest.mark.parametrize("backup_type, backup_flow", VECTORS)
|
@pytest.mark.parametrize("backup_type, backup_flow", VECTORS)
|
||||||
@pytest.mark.setup_client(uninitialized=True)
|
@pytest.mark.setup_client(uninitialized=True)
|
||||||
def test_skip_backup_manual(client: Client, backup_type, backup_flow):
|
def test_skip_backup_manual(client: Client, backup_type: BackupType, backup_flow):
|
||||||
with WITH_MOCK_URANDOM, client:
|
with WITH_MOCK_URANDOM, client:
|
||||||
IF = InputFlowResetSkipBackup(client)
|
IF = InputFlowResetSkipBackup(client)
|
||||||
client.set_input_flow(IF.get())
|
client.set_input_flow(IF.get())
|
||||||
|
@ -2027,5 +2027,4 @@ class InputFlowResetSkipBackup(InputFlowBase):
|
|||||||
yield # Skip Backup
|
yield # Skip Backup
|
||||||
self.debug.press_no()
|
self.debug.press_no()
|
||||||
yield # Confirm skip backup
|
yield # Confirm skip backup
|
||||||
self.debug.press_right()
|
|
||||||
self.debug.press_no()
|
self.debug.press_no()
|
||||||
|
Loading…
Reference in New Issue
Block a user