mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-27 13:35:44 +00:00
feat(core/ui): highlight repeated words on T3T1
ShowShareWords flow now informs the user if the word is repeated. The most typical usecase in 1-of-1 shamir (SingleShare) where 3rd and 4th word is "academic".
This commit is contained in:
parent
3a039c505f
commit
aaa2ece3ba
@ -246,6 +246,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_haptic_feedback__enable;
|
MP_QSTR_haptic_feedback__enable;
|
||||||
MP_QSTR_haptic_feedback__subtitle;
|
MP_QSTR_haptic_feedback__subtitle;
|
||||||
MP_QSTR_haptic_feedback__title;
|
MP_QSTR_haptic_feedback__title;
|
||||||
|
MP_QSTR_highlight_repeated;
|
||||||
MP_QSTR_hold;
|
MP_QSTR_hold;
|
||||||
MP_QSTR_hold_danger;
|
MP_QSTR_hold_danger;
|
||||||
MP_QSTR_homescreen__click_to_connect;
|
MP_QSTR_homescreen__click_to_connect;
|
||||||
@ -528,6 +529,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_reset__slip39_checklist_write_down;
|
MP_QSTR_reset__slip39_checklist_write_down;
|
||||||
MP_QSTR_reset__slip39_checklist_write_down_recovery;
|
MP_QSTR_reset__slip39_checklist_write_down_recovery;
|
||||||
MP_QSTR_reset__the_threshold_sets_the_number_of_shares;
|
MP_QSTR_reset__the_threshold_sets_the_number_of_shares;
|
||||||
|
MP_QSTR_reset__the_word_is_repeated;
|
||||||
MP_QSTR_reset__threshold_info;
|
MP_QSTR_reset__threshold_info;
|
||||||
MP_QSTR_reset__title_backup_is_done;
|
MP_QSTR_reset__title_backup_is_done;
|
||||||
MP_QSTR_reset__title_create_wallet;
|
MP_QSTR_reset__title_create_wallet;
|
||||||
|
@ -1339,6 +1339,7 @@ pub enum TranslatedString {
|
|||||||
reset__repeat_for_all_shares = 938, // "Repeat for all shares."
|
reset__repeat_for_all_shares = 938, // "Repeat for all shares."
|
||||||
homescreen__settings_subtitle = 939, // "Settings"
|
homescreen__settings_subtitle = 939, // "Settings"
|
||||||
homescreen__settings_title = 940, // "Homescreen"
|
homescreen__settings_title = 940, // "Homescreen"
|
||||||
|
reset__the_word_is_repeated = 941, // "The word is repeated"
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TranslatedString {
|
impl TranslatedString {
|
||||||
@ -2672,6 +2673,7 @@ impl TranslatedString {
|
|||||||
Self::reset__repeat_for_all_shares => "Repeat for all shares.",
|
Self::reset__repeat_for_all_shares => "Repeat for all shares.",
|
||||||
Self::homescreen__settings_subtitle => "Settings",
|
Self::homescreen__settings_subtitle => "Settings",
|
||||||
Self::homescreen__settings_title => "Homescreen",
|
Self::homescreen__settings_title => "Homescreen",
|
||||||
|
Self::reset__the_word_is_repeated => "The word is repeated",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4006,6 +4008,7 @@ impl TranslatedString {
|
|||||||
Qstr::MP_QSTR_reset__repeat_for_all_shares => Some(Self::reset__repeat_for_all_shares),
|
Qstr::MP_QSTR_reset__repeat_for_all_shares => Some(Self::reset__repeat_for_all_shares),
|
||||||
Qstr::MP_QSTR_homescreen__settings_subtitle => Some(Self::homescreen__settings_subtitle),
|
Qstr::MP_QSTR_homescreen__settings_subtitle => Some(Self::homescreen__settings_subtitle),
|
||||||
Qstr::MP_QSTR_homescreen__settings_title => Some(Self::homescreen__settings_title),
|
Qstr::MP_QSTR_homescreen__settings_title => Some(Self::homescreen__settings_title),
|
||||||
|
Qstr::MP_QSTR_reset__the_word_is_repeated => Some(Self::reset__the_word_is_repeated),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,10 @@ impl<'a> Label<'a> {
|
|||||||
self.text = text;
|
self.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_style(&mut self, style: TextStyle) {
|
||||||
|
self.layout.style = style;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn font(&self) -> Font {
|
pub fn font(&self) -> Font {
|
||||||
self.layout.style.text_font
|
self.layout.style.text_font
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn title_styled(mut self, style: TextStyle) -> Self {
|
|
||||||
self.title = self.title.styled(style);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn with_subtitle(mut self, subtitle: TString<'static>) -> Self {
|
pub fn with_subtitle(mut self, subtitle: TString<'static>) -> Self {
|
||||||
let style = theme::TEXT_SUB_GREY;
|
let style = theme::TEXT_SUB_GREY;
|
||||||
@ -125,6 +120,18 @@ where
|
|||||||
.button_styled(theme::button_danger())
|
.button_styled(theme::button_danger())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn title_styled(mut self, style: TextStyle) -> Self {
|
||||||
|
self.title = self.title.styled(style);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subtitle_styled(mut self, style: TextStyle) -> Self {
|
||||||
|
if let Some(subtitle) = self.subtitle.take() {
|
||||||
|
self.subtitle = Some(subtitle.styled(style))
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn button_styled(mut self, style: ButtonStyleSheet) -> Self {
|
pub fn button_styled(mut self, style: ButtonStyleSheet) -> Self {
|
||||||
if self.button.is_some() {
|
if self.button.is_some() {
|
||||||
self.button = Some(self.button.unwrap().styled(style));
|
self.button = Some(self.button.unwrap().styled(style));
|
||||||
@ -160,6 +167,25 @@ where
|
|||||||
ctx.request_paint();
|
ctx.request_paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_subtitle(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
new_subtitle: TString<'static>,
|
||||||
|
new_style: Option<TextStyle>,
|
||||||
|
) {
|
||||||
|
let style = new_style.unwrap_or(theme::TEXT_SUB_GREY);
|
||||||
|
match &mut self.subtitle {
|
||||||
|
Some(subtitle) => {
|
||||||
|
subtitle.set_style(style);
|
||||||
|
subtitle.set_text(new_subtitle);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.subtitle = Some(Label::new(new_subtitle, self.title.alignment(), style));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.request_paint();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_content<F, R>(&mut self, ctx: &mut EventCtx, update_fn: F) -> R
|
pub fn update_content<F, R>(&mut self, ctx: &mut EventCtx, update_fn: F) -> R
|
||||||
where
|
where
|
||||||
F: Fn(&mut EventCtx, &mut T) -> R,
|
F: Fn(&mut EventCtx, &mut T) -> R,
|
||||||
|
@ -5,10 +5,13 @@ use crate::{
|
|||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
animation::Animation,
|
animation::Animation,
|
||||||
component::{Component, Event, EventCtx, Never, SwipeDirection},
|
component::{
|
||||||
|
swipe_detect::{SwipeConfig, SwipeSettings},
|
||||||
|
Component, Event, EventCtx, Never, SwipeDirection,
|
||||||
|
},
|
||||||
event::SwipeEvent,
|
event::SwipeEvent,
|
||||||
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
||||||
model_mercury::component::Footer,
|
model_mercury::component::{Footer, Frame, FrameMsg},
|
||||||
shape::{self, Renderer},
|
shape::{self, Renderer},
|
||||||
util,
|
util,
|
||||||
},
|
},
|
||||||
@ -17,12 +20,112 @@ use heapless::Vec;
|
|||||||
|
|
||||||
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
|
||||||
const ANIMATION_DURATION_MS: Duration = Duration::from_millis(166);
|
const ANIMATION_DURATION_MS: Duration = Duration::from_millis(166);
|
||||||
|
type IndexVec = Vec<u8, MAX_WORDS>;
|
||||||
|
|
||||||
/// Component showing mnemonic/share words during backup procedure. Model T3T1
|
/// Component showing mnemonic/share words during backup procedure. Model T3T1
|
||||||
/// contains one word per screen. A user is instructed to swipe up/down to see
|
/// contains one word per screen. A user is instructed to swipe up/down to see
|
||||||
/// next/previous word.
|
/// next/previous word.
|
||||||
|
/// This is a wrapper around a Frame so that the subtitle and Footer of the
|
||||||
|
/// Frame can be updated based on the index of the word shown. Actual share
|
||||||
|
/// words are rendered within `ShareWordsInner` component,
|
||||||
pub struct ShareWords<'a> {
|
pub struct ShareWords<'a> {
|
||||||
area: Rect,
|
subtitle: TString<'static>,
|
||||||
|
frame: Frame<ShareWordsInner<'a>>,
|
||||||
|
repeated_indices: Option<IndexVec>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ShareWords<'a> {
|
||||||
|
pub fn new(
|
||||||
|
title: TString<'static>,
|
||||||
|
subtitle: TString<'static>,
|
||||||
|
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||||
|
highlight_repeated: bool,
|
||||||
|
) -> Self {
|
||||||
|
let repeated_indices = if highlight_repeated {
|
||||||
|
Some(Self::find_repeated(share_words.as_slice()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
subtitle,
|
||||||
|
frame: Frame::left_aligned(title, ShareWordsInner::new(share_words))
|
||||||
|
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
||||||
|
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||||
|
.with_vertical_pages()
|
||||||
|
.with_subtitle(subtitle),
|
||||||
|
repeated_indices,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_repeated(share_words: &[TString]) -> IndexVec {
|
||||||
|
let mut repeated_indices = IndexVec::new();
|
||||||
|
for i in (0..share_words.len()).rev() {
|
||||||
|
let word = share_words[i];
|
||||||
|
if share_words[..i].contains(&word) {
|
||||||
|
unwrap!(repeated_indices.push(i as u8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repeated_indices.reverse();
|
||||||
|
repeated_indices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Component for ShareWords<'a> {
|
||||||
|
type Msg = FrameMsg<Never>;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.frame.place(bounds);
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
let page_index = self.frame.inner().page_index as u8;
|
||||||
|
if let Some(repeated_indices) = &self.repeated_indices {
|
||||||
|
if repeated_indices.contains(&(page_index as usize)) {
|
||||||
|
let updated_subtitle = TString::from_translation(TR::reset__the_word_is_repeated);
|
||||||
|
self.frame
|
||||||
|
.update_subtitle(ctx, updated_subtitle, Some(theme::TEXT_SUB_GREEN_LIME));
|
||||||
|
} else {
|
||||||
|
self.frame
|
||||||
|
.update_subtitle(ctx, self.subtitle, Some(theme::TEXT_SUB_GREY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.frame.update_footer_counter(ctx, page_index + 1);
|
||||||
|
self.frame.event(ctx, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
// TODO: remove when ui-t3t1 done
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.frame.render(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_bounds")]
|
||||||
|
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "micropython")]
|
||||||
|
impl<'a> crate::ui::flow::Swipable for ShareWords<'a> {
|
||||||
|
fn get_swipe_config(&self) -> SwipeConfig {
|
||||||
|
self.frame.get_swipe_config()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_internal_page_count(&self) -> usize {
|
||||||
|
self.frame.get_internal_page_count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl<'a> crate::trace::Trace for ShareWords<'a> {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("ShareWords");
|
||||||
|
t.child("inner", &self.frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShareWordsInner<'a> {
|
||||||
share_words: Vec<TString<'a>, MAX_WORDS>,
|
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||||
page_index: i16,
|
page_index: i16,
|
||||||
next_index: i16,
|
next_index: i16,
|
||||||
@ -35,12 +138,11 @@ pub struct ShareWords<'a> {
|
|||||||
progress: i16,
|
progress: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ShareWords<'a> {
|
impl<'a> ShareWordsInner<'a> {
|
||||||
const AREA_WORD_HEIGHT: i16 = 91;
|
const AREA_WORD_HEIGHT: i16 = 91;
|
||||||
|
|
||||||
pub fn new(share_words: Vec<TString<'a>, MAX_WORDS>) -> Self {
|
fn new(share_words: Vec<TString<'a>, MAX_WORDS>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
area: Rect::zero(),
|
|
||||||
share_words,
|
share_words,
|
||||||
page_index: 0,
|
page_index: 0,
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
@ -76,25 +178,21 @@ impl<'a> ShareWords<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Component for ShareWords<'a> {
|
impl<'a> Component for ShareWordsInner<'a> {
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
self.area = bounds;
|
|
||||||
let used_area = bounds
|
let used_area = bounds
|
||||||
.inset(Insets::sides(theme::SPACING))
|
.inset(Insets::sides(theme::SPACING))
|
||||||
.inset(Insets::bottom(theme::SPACING));
|
.inset(Insets::bottom(theme::SPACING));
|
||||||
|
|
||||||
self.area_word = Rect::snap(
|
self.area_word = Rect::snap(
|
||||||
used_area.center(),
|
used_area.center(),
|
||||||
Offset::new(used_area.width(), ShareWords::AREA_WORD_HEIGHT),
|
Offset::new(used_area.width(), ShareWordsInner::AREA_WORD_HEIGHT),
|
||||||
Alignment2D::CENTER,
|
Alignment2D::CENTER,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.footer
|
bounds
|
||||||
.place(used_area.split_bottom(Footer::HEIGHT_SIMPLE).1);
|
|
||||||
|
|
||||||
self.area
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
@ -199,12 +297,61 @@ impl<'a> Component for ShareWords<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<'a> crate::trace::Trace for ShareWords<'a> {
|
impl<'a> crate::trace::Trace for ShareWordsInner<'a> {
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("ShareWords");
|
t.component("ShareWordsInner");
|
||||||
let word = &self.share_words[self.page_index as usize];
|
let word = &self.share_words[self.page_index as usize];
|
||||||
let content = word.map(|w| uformat!("{}. {}\n", self.page_index + 1, w));
|
let content = word.map(|w| uformat!("{}. {}\n", self.page_index + 1, w));
|
||||||
t.string("screen_content", content.as_str().into());
|
t.string("screen_content", content.as_str().into());
|
||||||
t.int("page_count", self.share_words.len() as i64)
|
t.int("page_count", self.share_words.len() as i64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_repeated_indices() {
|
||||||
|
let words0 = [];
|
||||||
|
let words1 = [
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("bbb"),
|
||||||
|
TString::from_str("ccc"),
|
||||||
|
];
|
||||||
|
let words2 = [
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("bbb"),
|
||||||
|
];
|
||||||
|
let words3 = [
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("bbb"),
|
||||||
|
TString::from_str("bbb"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
];
|
||||||
|
let words4 = [
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
TString::from_str("aaa"),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(ShareWords::find_repeated(&words0), IndexVec::new());
|
||||||
|
assert_eq!(ShareWords::find_repeated(&words1), IndexVec::new());
|
||||||
|
assert_eq!(
|
||||||
|
ShareWords::find_repeated(&words2),
|
||||||
|
IndexVec::from_slice(&[1]).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ShareWords::find_repeated(&words3),
|
||||||
|
IndexVec::from_slice(&[1, 3, 4]).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ShareWords::find_repeated(&words4),
|
||||||
|
IndexVec::from_slice(&[1, 2, 3, 4]).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -85,6 +85,7 @@ impl ShowShareWords {
|
|||||||
.and_then(|desc: TString| if desc.is_empty() { None } else { Some(desc) });
|
.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_info: Obj = kwargs.get(Qstr::MP_QSTR_text_info)?;
|
||||||
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
||||||
|
let highlight_repeated: bool = kwargs.get(Qstr::MP_QSTR_highlight_repeated)?.try_into()?;
|
||||||
let nwords = share_words_vec.len();
|
let nwords = share_words_vec.len();
|
||||||
|
|
||||||
let mut instructions_paragraphs = ParagraphVecShort::new();
|
let mut instructions_paragraphs = ParagraphVecShort::new();
|
||||||
@ -108,12 +109,8 @@ impl ShowShareWords {
|
|||||||
.one_button_request(ButtonRequestCode::ResetDevice.with_type("share_words"))
|
.one_button_request(ButtonRequestCode::ResetDevice.with_type("share_words"))
|
||||||
.with_pages(move |_| nwords + 2);
|
.with_pages(move |_| nwords + 2);
|
||||||
|
|
||||||
let content_words = Frame::left_aligned(title, ShareWords::new(share_words_vec))
|
let content_words =
|
||||||
.with_subtitle(subtitle)
|
ShareWords::new(title, subtitle, share_words_vec, highlight_repeated).map(|_| None);
|
||||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
|
||||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
|
||||||
.with_vertical_pages()
|
|
||||||
.map(|_| None);
|
|
||||||
|
|
||||||
let content_confirm =
|
let content_confirm =
|
||||||
Frame::left_aligned(text_confirm, PromptScreen::new_hold_to_confirm())
|
Frame::left_aligned(text_confirm, PromptScreen::new_hold_to_confirm())
|
||||||
|
@ -1710,6 +1710,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// description: str,
|
/// description: str,
|
||||||
/// text_info: Iterable[str],
|
/// text_info: Iterable[str],
|
||||||
/// text_confirm: str,
|
/// text_confirm: str,
|
||||||
|
/// highlight_repeated: bool,
|
||||||
/// ) -> LayoutObj[UiResult]:
|
/// ) -> LayoutObj[UiResult]:
|
||||||
/// """Show wallet backup words preceded by an instruction screen and followed by
|
/// """Show wallet backup words preceded by an instruction screen and followed by
|
||||||
/// confirmation."""
|
/// confirmation."""
|
||||||
|
@ -717,6 +717,8 @@ pub const TEXT_MAIN_GREY_LIGHT: TextStyle =
|
|||||||
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY, GREY);
|
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY, GREY);
|
||||||
pub const TEXT_SUB_GREY_LIGHT: TextStyle = TextStyle::new(Font::SUB, GREY_LIGHT, BG, GREY, GREY);
|
pub const TEXT_SUB_GREY_LIGHT: TextStyle = TextStyle::new(Font::SUB, GREY_LIGHT, BG, GREY, GREY);
|
||||||
pub const TEXT_SUB_GREY: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY);
|
pub const TEXT_SUB_GREY: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY);
|
||||||
|
pub const TEXT_SUB_GREEN_LIME: TextStyle =
|
||||||
|
TextStyle::new(Font::SUB, GREEN_LIME, BG, GREEN_LIME, GREEN_LIME);
|
||||||
pub const TEXT_WARNING: TextStyle = TextStyle::new(Font::NORMAL, ORANGE_LIGHT, BG, GREY, GREY);
|
pub const TEXT_WARNING: TextStyle = TextStyle::new(Font::NORMAL, ORANGE_LIGHT, BG, GREY, GREY);
|
||||||
pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, GREY_EXTRA_LIGHT, BG, GREY, GREY)
|
pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, GREY_EXTRA_LIGHT, BG, GREY, GREY)
|
||||||
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
|
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
|
||||||
|
@ -405,6 +405,7 @@ def flow_show_share_words(
|
|||||||
description: str,
|
description: str,
|
||||||
text_info: Iterable[str],
|
text_info: Iterable[str],
|
||||||
text_confirm: str,
|
text_confirm: str,
|
||||||
|
highlight_repeated: bool,
|
||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
"""Show wallet backup words preceded by an instruction screen and followed by
|
"""Show wallet backup words preceded by an instruction screen and followed by
|
||||||
confirmation."""
|
confirmation."""
|
||||||
|
@ -664,6 +664,7 @@ class TR:
|
|||||||
reset__slip39_checklist_write_down: str = "Write down and check all shares"
|
reset__slip39_checklist_write_down: str = "Write down and check all shares"
|
||||||
reset__slip39_checklist_write_down_recovery: str = "Write down & check all wallet backup shares"
|
reset__slip39_checklist_write_down_recovery: str = "Write down & check all wallet backup shares"
|
||||||
reset__the_threshold_sets_the_number_of_shares: str = "The threshold sets the number of shares "
|
reset__the_threshold_sets_the_number_of_shares: str = "The threshold sets the number of shares "
|
||||||
|
reset__the_word_is_repeated: str = "The word is repeated"
|
||||||
reset__threshold_info: str = "= minimum number of unique word lists used for recovery."
|
reset__threshold_info: str = "= minimum number of unique word lists used for recovery."
|
||||||
reset__title_backup_is_done: str = "Backup is done"
|
reset__title_backup_is_done: str = "Backup is done"
|
||||||
reset__title_create_wallet: str = "Create wallet"
|
reset__title_create_wallet: str = "Create wallet"
|
||||||
|
@ -23,6 +23,7 @@ async def show_share_words(
|
|||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
title = TR.reset__recovery_wallet_backup_title
|
title = TR.reset__recovery_wallet_backup_title
|
||||||
|
highlight_repeated = True
|
||||||
if share_index is None:
|
if share_index is None:
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
elif group_index is None:
|
elif group_index is None:
|
||||||
@ -51,6 +52,7 @@ async def show_share_words(
|
|||||||
description=description,
|
description=description,
|
||||||
text_info=text_info,
|
text_info=text_info,
|
||||||
text_confirm=text_confirm,
|
text_confirm=text_confirm,
|
||||||
|
highlight_repeated=highlight_repeated,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -666,6 +666,7 @@
|
|||||||
"reset__slip39_checklist_write_down": "Write down and check all shares",
|
"reset__slip39_checklist_write_down": "Write down and check all shares",
|
||||||
"reset__slip39_checklist_write_down_recovery": "Write down & check all wallet backup shares",
|
"reset__slip39_checklist_write_down_recovery": "Write down & check all wallet backup shares",
|
||||||
"reset__the_threshold_sets_the_number_of_shares": "The threshold sets the number of shares ",
|
"reset__the_threshold_sets_the_number_of_shares": "The threshold sets the number of shares ",
|
||||||
|
"reset__the_word_is_repeated": "The word is repeated",
|
||||||
"reset__threshold_info": "= minimum number of unique word lists used for recovery.",
|
"reset__threshold_info": "= minimum number of unique word lists used for recovery.",
|
||||||
"reset__title_backup_is_done": "Backup is done",
|
"reset__title_backup_is_done": "Backup is done",
|
||||||
"reset__title_create_wallet": "Create wallet",
|
"reset__title_create_wallet": "Create wallet",
|
||||||
|
@ -939,5 +939,6 @@
|
|||||||
"937": "reset__words_may_repeat",
|
"937": "reset__words_may_repeat",
|
||||||
"938": "reset__repeat_for_all_shares",
|
"938": "reset__repeat_for_all_shares",
|
||||||
"939": "homescreen__settings_subtitle",
|
"939": "homescreen__settings_subtitle",
|
||||||
"940": "homescreen__settings_title"
|
"940": "homescreen__settings_title",
|
||||||
|
"941": "reset__the_word_is_repeated"
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"merkle_root": "5b5781c3374ff27125228c121ab97436a2ba1f0581657ee56b9fa601fe3bde97",
|
"merkle_root": "e283dd87dd502e2b2c3e2cbe1f52efcbe99c2bf6cd8ed883ed26800a6885e4e7",
|
||||||
"datetime": "2024-06-24T13:47:55.544267",
|
"datetime": "2024-06-24T12:47:16.181365",
|
||||||
"commit": "d79768cec726c26ac1f82e62fc71b6d4568786a2"
|
"commit": "c66a73b895b37f2a9de3ac6427f372c649ecea8d"
|
||||||
},
|
},
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user