1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-03-03 16:56:07 +00:00

chore(eckhart): update existing components accordint to new design

This commit is contained in:
Lukas Bielesch 2025-02-21 14:15:37 +01:00 committed by Lukáš Bielesch
parent 19bc40dd48
commit 270164198f
8 changed files with 167 additions and 82 deletions

View File

@ -851,7 +851,7 @@ pub enum TranslatedString {
reset__recovery_share_title_template = 567, // "Recovery share #{0}"
reset__required_number_of_groups = 568, // "The required number of groups for recovery."
reset__select_correct_word = 569, // "Select the correct word for each position."
reset__select_word_template = 570, // {"Bolt": "Select {0} word", "Caesar": "Select {0} word", "Delizia": "Select {0} word", "Eckhart": "Select word #{0} from your wallet backup"}
reset__select_word_template = 570, // {"Bolt": "Select {0} word", "Caesar": "Select {0} word", "Delizia": "Select {0} word", "Eckhart": "Select word no. {0} from your wallet backup"}
reset__select_word_x_of_y_template = 571, // "Select word {0} of {1}:"
reset__set_it_to_count_template = 572, // "Set it to {0} and you will need "
reset__share_checked_successfully_template = 573, // "Share #{0} checked successfully."
@ -1273,7 +1273,7 @@ pub enum TranslatedString {
auto_lock__turned_on = 879, // "Auto-lock turned on"
backup__info_multi_share_backup = 880, // "Your wallet backup contains multiple lists of words in a specific order (shares)."
backup__info_single_share_backup = 881, // "Your wallet backup contains {0} words in a specific order."
backup__title_backup_completed = 882, // "Wallet backup completed"
backup__title_backup_completed = 882, // {"Bolt": "Wallet backup completed", "Caesar": "Wallet backup completed", "Delizia": "Wallet backup completed", "Eckhart": "Wallet backup completed."}
backup__title_create_wallet_backup = 883, // "Create wallet backup"
haptic_feedback__disable = 884, // "Disable haptic feedback?"
haptic_feedback__enable = 885, // "Enable haptic feedback?"
@ -1297,7 +1297,7 @@ pub enum TranslatedString {
recovery__info_about_disconnect = 904, // "It's safe to disconnect your Trezor while recovering your wallet and continue later."
recovery__share_does_not_match = 905, // "Share doesn't match"
reset__cancel_create_wallet = 906, // "Cancel create wallet"
reset__incorrect_word_selected = 907, // "Incorrect word selected"
reset__incorrect_word_selected = 907, // {"Bolt": "Incorrect word selected", "Caesar": "Incorrect word selected", "Delizia": "Incorrect word selected", "Eckhart": "Incorrect word selected."}
reset__more_at = 908, // "More at"
reset__num_of_shares_how_many = 909, // "How many wallet backup shares do you want to create?"
reset__num_of_shares_long_info_template = 910, // "Each backup share is a sequence of {0} words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet."
@ -1316,7 +1316,7 @@ pub enum TranslatedString {
words__good_to_know = 927, // "Good to know"
words__operation_cancelled = 928, // "Operation cancelled"
words__settings = 929, // "Settings"
words__try_again = 930, // "Try again."
words__try_again = 930, // {"Bolt": "Try again.", "Caesar": "Try again.", "Delizia": "Try again.", "Eckhart": "Try again"}
reset__slip39_checklist_num_groups_x_template = 931, // "Number of groups: {0}"
brightness__title = 932, // "Display brightness"
recovery__title_unlock_repeated_backup = 933, // "Multi-share backup"
@ -2273,7 +2273,7 @@ impl TranslatedString {
#[cfg(feature = "layout_delizia")]
Self::reset__select_word_template => "Select {0} word",
#[cfg(feature = "layout_eckhart")]
Self::reset__select_word_template => "Select word #{0} from your wallet backup",
Self::reset__select_word_template => "Select word no. {0} from your wallet backup",
Self::reset__select_word_x_of_y_template => "Select word {0} of {1}:",
Self::reset__set_it_to_count_template => "Set it to {0} and you will need ",
Self::reset__share_checked_successfully_template => "Share #{0} checked successfully.",
@ -2709,7 +2709,14 @@ impl TranslatedString {
Self::auto_lock__turned_on => "Auto-lock turned on",
Self::backup__info_multi_share_backup => "Your wallet backup contains multiple lists of words in a specific order (shares).",
Self::backup__info_single_share_backup => "Your wallet backup contains {0} words in a specific order.",
#[cfg(feature = "layout_bolt")]
Self::backup__title_backup_completed => "Wallet backup completed",
#[cfg(feature = "layout_caesar")]
Self::backup__title_backup_completed => "Wallet backup completed",
#[cfg(feature = "layout_delizia")]
Self::backup__title_backup_completed => "Wallet backup completed",
#[cfg(feature = "layout_eckhart")]
Self::backup__title_backup_completed => "Wallet backup completed.",
Self::backup__title_create_wallet_backup => "Create wallet backup",
Self::haptic_feedback__disable => "Disable haptic feedback?",
Self::haptic_feedback__enable => "Enable haptic feedback?",
@ -2740,7 +2747,14 @@ impl TranslatedString {
Self::recovery__info_about_disconnect => "It's safe to disconnect your Trezor while recovering your wallet and continue later.",
Self::recovery__share_does_not_match => "Share doesn't match",
Self::reset__cancel_create_wallet => "Cancel create wallet",
#[cfg(feature = "layout_bolt")]
Self::reset__incorrect_word_selected => "Incorrect word selected",
#[cfg(feature = "layout_caesar")]
Self::reset__incorrect_word_selected => "Incorrect word selected",
#[cfg(feature = "layout_delizia")]
Self::reset__incorrect_word_selected => "Incorrect word selected",
#[cfg(feature = "layout_eckhart")]
Self::reset__incorrect_word_selected => "Incorrect word selected.",
Self::reset__more_at => "More at",
Self::reset__num_of_shares_how_many => "How many wallet backup shares do you want to create?",
Self::reset__num_of_shares_long_info_template => "Each backup share is a sequence of {0} words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.",
@ -2759,7 +2773,14 @@ impl TranslatedString {
Self::words__good_to_know => "Good to know",
Self::words__operation_cancelled => "Operation cancelled",
Self::words__settings => "Settings",
#[cfg(feature = "layout_bolt")]
Self::words__try_again => "Try again.",
#[cfg(feature = "layout_caesar")]
Self::words__try_again => "Try again.",
#[cfg(feature = "layout_delizia")]
Self::words__try_again => "Try again.",
#[cfg(feature = "layout_eckhart")]
Self::words__try_again => "Try again",
Self::reset__slip39_checklist_num_groups_x_template => "Number of groups: {0}",
Self::brightness__title => "Display brightness",
Self::recovery__title_unlock_repeated_backup => "Multi-share backup",

View File

@ -27,10 +27,10 @@ pub enum SelectWordMsg {
}
impl SelectWordScreen {
const INSET: i16 = 24;
const DESCRIPTION_HEIGHT: i16 = 52;
const BUTTON_RADIUS: u8 = 12;
const DESCRIPTION_HEIGHT: i16 = 58;
const MENU_HEIGHT: i16 = 360;
const DESCRIPTION_PADDING: i16 = 24;
const MENU_PADDING: i16 = 12;
pub fn new(
share_words_vec: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
description: TString<'static>,
@ -41,14 +41,14 @@ impl SelectWordScreen {
menu = menu.item(
Button::with_text(word)
.styled(theme::button_select_word())
.with_radius(Self::BUTTON_RADIUS),
.with_radius(12),
);
}
Self {
header: Header::new(TString::empty()),
description: Label::new(description, Alignment::Start, theme::TEXT_MEDIUM)
.vertically_centered(),
.top_aligned(),
menu,
}
}
@ -69,10 +69,10 @@ impl Component for SelectWordScreen {
let (header_area, rest) = bounds.split_top(Header::HEADER_HEIGHT);
let (description_area, rest) = rest.split_top(Self::DESCRIPTION_HEIGHT);
let (_, rest) = rest.split_top(Self::INSET);
let (menu_area, _) = rest.split_bottom(Self::INSET);
let (menu_area, _) = rest.split_top(Self::MENU_HEIGHT);
let description_area = description_area.inset(Insets::sides(Self::INSET));
let description_area = description_area.inset(Insets::sides(Self::DESCRIPTION_PADDING));
let menu_area = menu_area.inset(Insets::sides(Self::MENU_PADDING));
self.menu.place(menu_area);
self.description.place(description_area);
@ -106,3 +106,19 @@ impl crate::trace::Trace for SelectWordScreen {
t.component("SelectWordScreen");
}
}
#[cfg(test)]
mod tests {
use super::{super::super::constant::SCREEN, *};
#[test]
fn test_component_heights_fit_screen() {
assert!(
SelectWordScreen::DESCRIPTION_HEIGHT
+ SelectWordScreen::MENU_HEIGHT
+ Header::HEADER_HEIGHT
<= SCREEN.height(),
"Components overflow the screen height",
);
}
}

View File

@ -88,11 +88,8 @@ impl<'a> ShareWordsScreen<'a> {
// Update hint content based on the current page
// First word gets a special hint
if self.content.pager().is_first() {
self.hint = Hint::new_instruction(TR::reset__share_words_first, Some(theme::ICON_INFO));
// Repeated words get a special hint
} else if self.content.is_repeated() {
if self.content.is_repeated() {
self.hint = Hint::new_instruction_green(
TR::reset__the_word_is_repeated,
Some(theme::ICON_INFO),

View File

@ -4,11 +4,8 @@ use crate::{
translations::TR,
ui::{
component::{
text::{
op::OpTextLayout,
paragraphs::{Paragraph, ParagraphSource},
},
ComponentExt, FormattedText,
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort},
ComponentExt,
},
flow::{
base::{Decision, DecisionBuilder as _},
@ -24,7 +21,7 @@ use super::super::{
component::{
ActionBar, Button, Header, ShareWordsScreen, ShareWordsScreenMsg, TextScreen, TextScreenMsg,
},
fonts, theme,
theme,
};
#[derive(Copy, Clone, PartialEq, Eq)]
@ -32,6 +29,7 @@ pub enum ShowShareWords {
Instruction,
ShareWords,
Confirm,
CheckBackupIntro,
}
impl FlowController for ShowShareWords {
@ -53,7 +51,8 @@ impl FlowController for ShowShareWords {
(Self::ShareWords, FlowMsg::Cancelled) => Self::Instruction.goto(),
(Self::ShareWords, FlowMsg::Confirmed) => Self::Confirm.goto(),
(Self::Confirm, FlowMsg::Cancelled) => Self::ShareWords.goto(),
(Self::Confirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
(Self::Confirm, FlowMsg::Confirmed) => Self::CheckBackupIntro.goto(),
(Self::CheckBackupIntro, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
_ => self.do_nothing(),
}
}
@ -62,19 +61,18 @@ impl FlowController for ShowShareWords {
pub fn new_show_share_words_flow(
words: Vec<TString<'static>, 33>,
_subtitle: TString<'static>,
instruction: Paragraph<'static>,
instructions_paragraphs: ParagraphVecShort<'static>,
text_confirm: TString<'static>,
) -> Result<SwipeFlow, error::Error> {
let instruction = TextScreen::new(
instruction
instructions_paragraphs
.into_paragraphs()
.with_placement(LinearPlacement::vertical()),
.with_placement(LinearPlacement::vertical().with_spacing(24)),
)
.with_header(Header::new(TR::reset__recovery_wallet_backup_title.into()))
.with_action_bar(ActionBar::new_double(
Button::with_icon(theme::ICON_CHEVRON_UP),
Button::with_text(TR::buttons__continue.into()),
))
.with_action_bar(ActionBar::new_single(Button::with_text(
TR::buttons__continue.into(),
)))
.map(|msg| match msg {
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
@ -86,13 +84,14 @@ pub fn new_show_share_words_flow(
ShareWordsScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
});
let op_confirm =
OpTextLayout::new(theme::TEXT_NORMAL).text(text_confirm, fonts::FONT_SATOSHI_REGULAR_38);
let confirm_paragraphs = Paragraph::new(&theme::TEXT_REGULAR, text_confirm)
.into_paragraphs()
.with_placement(LinearPlacement::vertical());
let confirm = TextScreen::new(FormattedText::new(op_confirm))
let confirm = TextScreen::new(confirm_paragraphs)
.with_header(Header::new(TR::reset__recovery_wallet_backup_title.into()))
.with_action_bar(ActionBar::new_double(
Button::with_icon(theme::ICON_CHEVRON_LEFT),
Button::with_icon(theme::ICON_CHEVRON_UP),
Button::with_text(TR::buttons__hold_to_confirm.into())
.styled(theme::button_confirm())
.with_long_press(theme::CONFIRM_HOLD_DURATION),
@ -103,9 +102,25 @@ pub fn new_show_share_words_flow(
TextScreenMsg::Menu => Some(FlowMsg::Cancelled),
});
let check_backup_paragraphs =
Paragraph::new(&theme::TEXT_REGULAR, TR::reset__check_backup_instructions)
.into_paragraphs()
.with_placement(LinearPlacement::vertical());
let check_backup_intro = TextScreen::new(check_backup_paragraphs)
.with_header(Header::new(TR::reset__check_wallet_backup_title.into()))
.with_action_bar(ActionBar::new_single(Button::with_text(
TR::buttons__continue.into(),
)))
.map(|msg| match msg {
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
_ => None,
});
let res = SwipeFlow::new(&ShowShareWords::Instruction)?
.with_page(&ShowShareWords::Instruction, instruction)?
.with_page(&ShowShareWords::ShareWords, share_words)?
.with_page(&ShowShareWords::Confirm, confirm)?;
.with_page(&ShowShareWords::Confirm, confirm)?
.with_page(&ShowShareWords::CheckBackupIntro, check_backup_intro)?;
Ok(res)
}

View File

@ -12,6 +12,7 @@ use crate::{
},
Empty, FormattedText,
},
geometry::LinearPlacement,
layout::{
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
util::{ConfirmValueParams, RecoveryType, StrOrBytes},
@ -420,10 +421,7 @@ impl FirmwareUI for UIEckhart {
description: TString<'static>,
words: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
) -> Result<impl LayoutMaybeTrace, Error> {
let component = SelectWordScreen::new(words, description).with_header(
Header::new(title)
.with_right_button(Button::with_icon(theme::ICON_MENU), HeaderMsg::Cancelled),
);
let component = SelectWordScreen::new(words, description).with_header(Header::new(title));
let layout = RootComponent::new(component);
@ -628,17 +626,16 @@ impl FirmwareUI for UIEckhart {
_text_footer: Option<TString<'static>>,
text_confirm: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
// TODO: add support for multiple instructions
let instruction: TString = IterBuf::new()
.try_iterate(instructions)?
.next()
.unwrap()
.try_into()?;
let mut instructions_paragraphs = ParagraphVecShort::new();
for item in IterBuf::new().try_iterate(instructions)? {
let text: TString = item.try_into()?;
instructions_paragraphs.add(Paragraph::new(&theme::TEXT_REGULAR, text));
}
let flow = flow::show_share_words::new_show_share_words_flow(
words,
subtitle.unwrap_or(TString::empty()),
Paragraph::new(&theme::TEXT_REGULAR, instruction),
instructions_paragraphs,
text_confirm,
)?;
Ok(flow)
@ -674,8 +671,12 @@ impl FirmwareUI for UIEckhart {
allow_cancel: bool,
_time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
let paragraphs = Paragraph::new(&theme::TEXT_REGULAR, description).into_paragraphs();
let header = Header::new(title).with_icon(theme::ICON_DONE, theme::GREEN_LIGHT);
let paragraphs = Paragraph::new(&theme::TEXT_REGULAR, description)
.into_paragraphs()
.with_placement(LinearPlacement::vertical());
let header = Header::new(title)
.with_icon(theme::ICON_DONE, theme::GREEN_LIGHT)
.with_text_style(theme::label_title_confirm());
let action_bar = if allow_cancel {
ActionBar::new_double(
Button::with_icon(theme::ICON_CROSS),
@ -704,18 +705,27 @@ impl FirmwareUI for UIEckhart {
value: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
_danger: bool, // TODO: review if `danger` needed in all layouts since we have show_danger
danger: bool,
) -> Result<Gc<LayoutObj>, Error> {
let paragraphs = ParagraphVecShort::from_iter([
Paragraph::new(&theme::TEXT_SMALL, description),
Paragraph::new(&theme::TEXT_REGULAR, value),
])
.into_paragraphs();
.into_paragraphs()
.with_placement(LinearPlacement::vertical());
let header = Header::new(title).with_icon(theme::ICON_INFO, theme::GREEN_LIGHT);
let (color, style) = if danger {
(theme::ORANGE, theme::label_title_danger())
} else {
(theme::YELLOW, theme::label_title_warning())
};
let header = Header::new(title)
.with_icon(theme::ICON_INFO, color)
.with_text_style(style);
let action_bar = if allow_cancel {
ActionBar::new_double(
Button::with_icon(theme::ICON_CROSS),
Button::with_icon(theme::ICON_CROSS).styled(theme::button_cancel()),
Button::with_text(button),
)
} else {

View File

@ -95,7 +95,12 @@ def confirm_reset_device(recovery: bool = False) -> Awaitable[None]:
async def show_wallet_created_success() -> None:
await interact(
trezorui_api.show_success(title=TR.backup__new_wallet_created, button=""),
trezorui_api.show_success(
title=TR.words__title_done,
description=TR.backup__new_wallet_created,
button=TR.buttons__continue,
allow_cancel=False,
),
"backup_device",
ButtonRequestType.ResetDevice,
)

View File

@ -27,8 +27,10 @@ def show_share_words(
words_count = len(share_words)
description = None
# Eckhart currently has only one instruction, other are shown in the hint area
instructions = [TR.reset__write_down_words_template.format(words_count)]
assert len(instructions) == 1
instructions = [
TR.reset__write_down_words_template.format(words_count),
TR.reset__words_may_repeat,
]
text_confirm = TR.reset__words_written_down_template.format(words_count)
return raise_if_not_confirmed(
@ -283,6 +285,7 @@ async def show_intro_backup(single_share: bool, num_of_words: int | None) -> Non
trezorui_api.show_info(
title=TR.backup__title_create_wallet_backup,
description=description,
button=TR.buttons__continue
),
"backup_intro",
ButtonRequestType.ResetDevice,
@ -294,7 +297,7 @@ def show_warning_backup() -> Awaitable[ui.UiResult]:
trezorui_api.show_warning(
title=TR.words__important,
value=TR.reset__never_make_digital_copy,
button="",
button=TR.buttons__continue,
allow_cancel=False,
danger=False, # Use a less severe icon color
),
@ -307,6 +310,8 @@ def show_success_backup() -> Awaitable[None]:
return show_success(
"success_backup",
TR.backup__title_backup_completed,
TR.words__title_done,
TR.buttons__continue,
)
@ -320,9 +325,9 @@ def show_reset_warning(
return raise_if_not_confirmed(
trezorui_api.show_warning(
title=subheader or "",
description=content,
value="",
button="",
description="",
value=content,
button=button or "",
allow_cancel=False,
danger=True,
),
@ -338,40 +343,41 @@ async def show_share_confirmation_success(
) -> None:
if share_index is None or num_of_shares is None:
# it is a BIP39 or a 1-of-1 SLIP39 backup
# mercury UI shows only final wallet backup confirmation screen later
# delizia and eckhart UIs show only final wallet backup confirmation screen later
return
# TODO: super-shamir copy not done
if share_index == num_of_shares - 1:
title = TR.reset__share_completed_template.format(share_index + 1)
content = TR.reset__share_completed_template.format(share_index + 1)
if group_index is None:
footer_description = ""
title = TR.words__title_done
else:
footer_description = TR.reset__finished_verifying_group_template.format(
group_index + 1
)
title = TR.reset__finished_verifying_group_template.format(group_index + 1)
else:
if group_index is None:
title = TR.reset__share_completed_template.format(share_index + 1)
footer_description = (
TR.instructions__shares_continue_with_x_template.format(share_index + 2)
content = TR.reset__share_completed_template.format(share_index + 1)
title = TR.instructions__shares_continue_with_x_template.format(
share_index + 2
)
else:
title = TR.reset__continue_with_next_share
footer_description = (
TR.reset__group_share_checked_successfully_template.format(
group_index + 1, share_index + 1
)
content = TR.reset__continue_with_next_share
title = TR.reset__group_share_checked_successfully_template.format(
group_index + 1, share_index + 1
)
await show_success("success_recovery", title, subheader=footer_description)
await show_success(
"success_recovery",
content,
subheader=title,
button=TR.buttons__continue,
)
def show_share_confirmation_failure() -> Awaitable[None]:
return show_reset_warning(
"warning_backup_check",
TR.words__try_again,
TR.reset__incorrect_word_selected,
"",
TR.words__pay_attention,
TR.words__try_again,
ButtonRequestType.ResetDevice,
)

View File

@ -49,7 +49,12 @@
"backup__new_wallet_created": "Wallet created.\n",
"backup__new_wallet_successfully_created": "Wallet created successfully.",
"backup__recover_anytime": "You can use your backup to recover your wallet at any time.",
"backup__title_backup_completed": "Wallet backup completed",
"backup__title_backup_completed": {
"Bolt": "Wallet backup completed",
"Caesar": "Wallet backup completed",
"Delizia": "Wallet backup completed",
"Eckhart": "Wallet backup completed."
},
"backup__title_backup_wallet": "Back up wallet",
"backup__title_create_wallet_backup": "Create wallet backup",
"backup__title_skip": "Skip backup",
@ -654,7 +659,12 @@
"reset__group_info": "Each group has a set number of shares and its own threshold. In the next steps you will set the numbers of shares and the thresholds.",
"reset__group_share_checked_successfully_template": "Group {0} - Share {1} checked successfully.",
"reset__group_share_title_template": "Group {0} - share {1}",
"reset__incorrect_word_selected": "Incorrect word selected",
"reset__incorrect_word_selected": {
"Bolt": "Incorrect word selected",
"Caesar": "Incorrect word selected",
"Delizia": "Incorrect word selected",
"Eckhart": "Incorrect word selected."
},
"reset__more_at": "More at",
"reset__more_info_at": "More info at",
"reset__need_all_share_template": "For recovery you need all {0} of the shares.",
@ -681,7 +691,7 @@
"Bolt": "Select {0} word",
"Caesar": "Select {0} word",
"Delizia": "Select {0} word",
"Eckhart": "Select word #{0} from your wallet backup"
"Eckhart": "Select word no. {0} from your wallet backup"
},
"reset__select_word_x_of_y_template": "Select word {0} of {1}:",
"reset__set_it_to_count_template": "Set it to {0} and you will need ",
@ -1013,7 +1023,12 @@
"words__title_success": "Success",
"words__title_summary": "Summary",
"words__title_threshold": "Threshold",
"words__try_again": "Try again.",
"words__try_again": {
"Bolt": "Try again.",
"Caesar": "Try again.",
"Delizia": "Try again.",
"Eckhart": "Try again"
},
"words__unknown": "Unknown",
"words__warning": "Warning",
"words__writable": "Writable",