1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-18 11:21:11 +00:00

feat(core/ui): T3T1 backup checklist

Also add subtitle to ShareWords.

[no changelog]
This commit is contained in:
obrusvit 2024-05-27 01:05:04 +02:00 committed by Martin Milata
parent 43eeccac59
commit 27d733f89d
15 changed files with 196 additions and 138 deletions

View File

@ -498,6 +498,7 @@ static void _librust_qstrs(void) {
MP_QSTR_reset__share_completed_template;
MP_QSTR_reset__share_words_title;
MP_QSTR_reset__slip39_checklist_num_groups;
MP_QSTR_reset__slip39_checklist_num_groups_x_template;
MP_QSTR_reset__slip39_checklist_num_shares;
MP_QSTR_reset__slip39_checklist_num_shares_x_template;
MP_QSTR_reset__slip39_checklist_set_num_groups;

View File

@ -1307,11 +1307,11 @@ pub enum TranslatedString {
reset__incorrect_word_selected = 907, // "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 = 910, // "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet."
reset__num_of_shares_long_info = 910, // "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet."
reset__select_threshold = 911, // "Select the minimum shares required to recover your wallet."
reset__share_completed_template = 912, // "Share #{0} completed"
reset__slip39_checklist_num_shares_x_template = 913, // "Number of shares"
reset__slip39_checklist_threshold_x_template = 914, // "Recovery threshold:"
reset__slip39_checklist_num_shares_x_template = 913, // "Number of shares: {0}"
reset__slip39_checklist_threshold_x_template = 914, // "Recovery threshold: {0}"
send__transaction_signed = 915, // "Transaction signed"
tutorial__continue = 916, // "Continue tutorial"
tutorial__exit = 917, // "Exit tutorial"
@ -1328,6 +1328,7 @@ pub enum TranslatedString {
words__operation_cancelled = 928, // "Operation cancelled"
words__settings = 929, // "Settings"
words__try_again = 930, // "Try again."
reset__slip39_checklist_num_groups_x_template = 931, // "Number of groups: {0}"
}
impl TranslatedString {
@ -2630,11 +2631,11 @@ impl TranslatedString {
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 => "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.",
Self::reset__num_of_shares_long_info => "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.",
Self::reset__select_threshold => "Select the minimum shares required to recover your wallet.",
Self::reset__share_completed_template => "Share #{0} completed",
Self::reset__slip39_checklist_num_shares_x_template => "Number of shares",
Self::reset__slip39_checklist_threshold_x_template => "Recovery threshold:",
Self::reset__slip39_checklist_num_shares_x_template => "Number of shares: {0}",
Self::reset__slip39_checklist_threshold_x_template => "Recovery threshold: {0}",
Self::send__transaction_signed => "Transaction signed",
Self::tutorial__continue => "Continue tutorial",
Self::tutorial__exit => "Exit tutorial",
@ -2651,6 +2652,7 @@ impl TranslatedString {
Self::words__operation_cancelled => "Operation cancelled",
Self::words__settings => "Settings",
Self::words__try_again => "Try again.",
Self::reset__slip39_checklist_num_groups_x_template => "Number of groups: {0}",
}
}
@ -3975,6 +3977,7 @@ impl TranslatedString {
Qstr::MP_QSTR_words__operation_cancelled => Some(Self::words__operation_cancelled),
Qstr::MP_QSTR_words__settings => Some(Self::words__settings),
Qstr::MP_QSTR_words__try_again => Some(Self::words__try_again),
Qstr::MP_QSTR_reset__slip39_checklist_num_groups_x_template => Some(Self::reset__slip39_checklist_num_groups_x_template),
_ => None,
}
}

View File

@ -4,7 +4,7 @@ use crate::{
strutil::TString,
ui::{
component::{Component, Event, EventCtx, Never, Paginate},
display::toif::Icon,
display::{toif::Icon, Color, Font},
geometry::{
Alignment, Alignment2D, Dimensions, Insets, LinearPlacement, Offset, Point, Rect,
},
@ -14,6 +14,7 @@ use crate::{
};
use super::layout::{LayoutFit, TextLayout, TextStyle};
use heapless::String;
/// Used as an upper bound of number of different styles we may render on single
/// page.
@ -557,6 +558,8 @@ pub struct Checklist<T> {
current: usize,
icon_current: Icon,
icon_done: Icon,
icon_done_color: Option<Color>,
show_numerals: bool,
/// How wide will the left icon column be
check_width: i16,
/// Offset of the icon representing DONE
@ -565,7 +568,10 @@ pub struct Checklist<T> {
current_offset: Offset,
}
impl<T> Checklist<T> {
impl<'a, T> Checklist<T>
where
T: ParagraphSource<'a>,
{
pub fn from_paragraphs(
icon_current: Icon,
icon_done: Icon,
@ -578,6 +584,8 @@ impl<T> Checklist<T> {
current,
icon_current,
icon_done,
icon_done_color: None,
show_numerals: false,
check_width: 0,
done_offset: Offset::zero(),
current_offset: Offset::zero(),
@ -599,6 +607,38 @@ impl<T> Checklist<T> {
self
}
pub fn with_icon_done_color(mut self, col: Color) -> Self {
self.icon_done_color = Some(col);
self
}
pub fn with_numerals(mut self, show_numerals: bool) -> Self {
self.show_numerals = show_numerals;
self
}
fn render_left_column<'s>(&self, target: &mut impl Renderer<'s>) {
let current_visible = self.current.saturating_sub(self.paragraphs.offset.par);
for (i, layout) in self.paragraphs.visible.iter().enumerate() {
let l = &layout.layout(&self.paragraphs.source);
let base = Point::new(self.area.x0, l.bounds.y0);
if i < current_visible {
// finished tasks - labeled with icon "done"
let color = self.icon_done_color.unwrap_or(l.style.text_color);
self.render_icon(base + self.done_offset, self.icon_done, color, target)
} else {
// current and future tasks - ordinal numbers or icon on current task
if self.show_numerals {
let num_offset = Offset::y(Font::NORMAL.visible_text_height("1"));
self.render_numeral(base + num_offset, i, l.style.text_color, target);
} else if i == current_visible {
let color = l.style.text_color;
self.render_icon(base + self.current_offset, self.icon_current, color, target);
}
}
}
}
fn paint_icon(&self, layout: &TextLayout, icon: Icon, offset: Offset) {
let top_left = Point::new(self.area.x0, layout.bounds.y0);
icon.draw(
@ -609,16 +649,29 @@ impl<T> Checklist<T> {
);
}
fn render_icon<'s>(
fn render_numeral<'s>(
&self,
layout: &TextLayout,
icon: Icon,
offset: Offset,
base_point: Point,
n: usize,
color: Color,
target: &mut impl Renderer<'s>,
) {
let top_left = Point::new(self.area.x0, layout.bounds.y0);
shape::ToifImage::new(top_left + offset, icon.toif)
.with_fg(layout.style.text_color)
let numeral = build_string!(10, inttostr!(n as u8 + 1), ".");
shape::Text::new(base_point, numeral.as_str())
.with_font(Font::NORMAL)
.with_fg(color)
.render(target);
}
fn render_icon<'s>(
&self,
base_point: Point,
icon: Icon,
color: Color,
target: &mut impl Renderer<'s>,
) {
shape::ToifImage::new(base_point, icon.toif)
.with_fg(color)
.render(target);
}
}
@ -662,24 +715,7 @@ where
fn render<'s>(&self, target: &mut impl Renderer<'s>) {
self.paragraphs.render(target);
let current_visible = self.current.saturating_sub(self.paragraphs.offset.par);
for layout in self.paragraphs.visible.iter().take(current_visible) {
self.render_icon(
&layout.layout(&self.paragraphs.source),
self.icon_done,
self.done_offset,
target,
);
}
if let Some(layout) = self.paragraphs.visible.iter().nth(current_visible) {
self.render_icon(
&layout.layout(&self.paragraphs.source),
self.icon_current,
self.current_offset,
target,
);
}
self.render_left_column(target);
}
#[cfg(feature = "ui_bounds")]

View File

@ -74,6 +74,7 @@ pub extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs:
impl ShowShareWords {
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let subtitle: TString = kwargs.get(Qstr::MP_QSTR_subtitle)?.try_into()?;
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let share_words_vec: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
let text_info: TString = kwargs.get(Qstr::MP_QSTR_text_info)?.try_into()?;
@ -90,8 +91,9 @@ impl ShowShareWords {
.with_footer(TR::instructions__swipe_up.into(), None)
.map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed));
let content_words =
Frame::left_aligned(title, ShareWords::new(share_words_vec)).map(|_| None);
let content_words = Frame::left_aligned(title, ShareWords::new(share_words_vec))
.with_subtitle(subtitle)
.map(|_| None);
let content_confirm =
Frame::left_aligned(text_confirm, PromptScreen::new_hold_to_confirm())

View File

@ -1165,7 +1165,7 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let _button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let active: usize = kwargs.get(Qstr::MP_QSTR_active)?.try_into()?;
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
@ -1180,25 +1180,22 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
paragraphs.add(Paragraph::new(style, text));
}
let obj = LayoutObj::new(Frame::left_aligned(
title,
Dialog::new(
let checklist_content = SwipeUpScreen::new(
Checklist::from_paragraphs(
theme::ICON_LIST_CURRENT,
theme::ICON_LIST_CHECK,
theme::ICON_CHEVRON_RIGHT,
theme::ICON_BULLET_CHECKMARK,
active,
paragraphs
.into_paragraphs()
.with_spacing(theme::CHECKLIST_SPACING),
)
.with_check_width(theme::CHECKLIST_CHECK_WIDTH)
.with_current_offset(theme::CHECKLIST_CURRENT_OFFSET)
.with_done_offset(theme::CHECKLIST_DONE_OFFSET),
theme::button_bar(Button::with_text(button).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})),
),
))?;
.with_icon_done_color(theme::GREEN),
);
let obj = LayoutObj::new(
Frame::left_aligned(title, checklist_content)
.with_footer(TR::instructions__swipe_up.into(), None),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1812,6 +1809,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def flow_show_share_words(
/// *,
/// title: str,
/// subtitle: str,
/// words: Iterable[str],
/// text_info: str,
/// text_confirm: str,

View File

@ -161,10 +161,6 @@ include_icon!(ICON_CLICK, "model_tt/res/finger24.toif");
include_icon!(ICON_CORNER_CANCEL, "model_tt/res/x32.toif");
include_icon!(ICON_CORNER_INFO, "model_tt/res/info32.toif");
// Checklist symbols.
include_icon!(ICON_LIST_CURRENT, "model_tt/res/arrow-right16.toif");
include_icon!(ICON_LIST_CHECK, "model_tt/res/check16.toif");
// Homescreen notifications.
include_icon!(ICON_WARN, "model_tt/res/warning16.toif");
include_icon!(ICON_WARNING40, "model_tt/res/warning40.toif");
@ -689,23 +685,23 @@ pub const fn button_counter() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::DEMIBOLD,
text_color: FG,
button_color: GREY_DARK,
icon_color: GREY_LIGHT,
text_color: GREY,
button_color: GREY_EXTRA_DARK,
icon_color: GREY,
background_color: BG,
},
active: &ButtonStyle {
font: Font::DEMIBOLD,
text_color: FG,
button_color: GREY_MEDIUM,
icon_color: GREY_LIGHT,
text_color: BG,
button_color: GREY_LIGHT,
icon_color: BG,
background_color: BG,
},
disabled: &ButtonStyle {
font: Font::DEMIBOLD,
text_color: GREY_LIGHT,
button_color: GREY_DARK,
icon_color: GREY_LIGHT,
text_color: GREY_DARK,
button_color: BG,
icon_color: GREY_DARK,
background_color: BG,
},
}
@ -797,12 +793,10 @@ pub fn textstyle_number(num: i32) -> &'static TextStyle {
pub const TEXT_NORMAL_OFF_WHITE: TextStyle =
TextStyle::new(Font::NORMAL, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_CHECKLIST_DEFAULT: TextStyle =
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_CHECKLIST_DEFAULT: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY);
pub const TEXT_CHECKLIST_SELECTED: TextStyle =
TextStyle::new(Font::NORMAL, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_CHECKLIST_DONE: TextStyle =
TextStyle::new(Font::NORMAL, GREEN_DARK, BG, GREY_LIGHT, GREY_LIGHT);
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_CHECKLIST_DONE: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY);
/// Spacing between components (e.g. header and main content) and offsets from
/// the side of the screen. Generally applied everywhere except the top side of
@ -814,10 +808,11 @@ pub const BUTTON_HEIGHT: i16 = 62;
pub const BUTTON_WIDTH: i16 = 78;
pub const BUTTON_SPACING: i16 = SPACING;
pub const KEYBOARD_SPACING: i16 = BUTTON_SPACING;
pub const CHECKLIST_SPACING: i16 = 10;
pub const CHECKLIST_SPACING: i16 = 12;
pub const RECOVERY_SPACING: i16 = 18;
pub const CORNER_BUTTON_SIDE: i16 = 44;
pub const CORNER_BUTTON_SPACING: i16 = BUTTON_SPACING;
pub const COUNTER_BUTTON_HEIGHT: i16 = 60;
pub const INFO_BUTTON_HEIGHT: i16 = 44;
pub const PIN_BUTTON_HEIGHT: i16 = 52;
pub const MNEMONIC_BUTTON_HEIGHT: i16 = 62;
@ -828,9 +823,7 @@ pub const RESULT_FOOTER_HEIGHT: i16 = 62;
pub const DETAILS_SPACING: i16 = 8;
// checklist settings
pub const CHECKLIST_CHECK_WIDTH: i16 = 16;
pub const CHECKLIST_DONE_OFFSET: Offset = Offset::new(-2, 6);
pub const CHECKLIST_CURRENT_OFFSET: Offset = Offset::new(2, 3);
pub const CHECKLIST_CHECK_WIDTH: i16 = 32; // icon width (20px) + padding (12px)
pub const fn button_bar<T>(inner: T) -> FixedHeightBar<T> {
FixedHeightBar::bottom(inner, BUTTON_HEIGHT)

View File

@ -396,6 +396,7 @@ def flow_prompt_backup() -> LayoutObj[UiResult]
def flow_show_share_words(
*,
title: str,
subtitle: str,
words: Iterable[str],
text_info: str,
text_confirm: str,
@ -404,18 +405,6 @@ def flow_show_share_words(
confirmation."""
# rust/src/ui/model_mercury/layout.rs
def request_number(
*,
title: str,
count: int,
min_count: int,
max_count: int,
description: Callable[[int], str] | None = None,
) -> LayoutObj[tuple[UiResult, int]]:
"""Number input with + and - buttons, description, and info button."""
# rust/src/ui/model_mercury/layout.rs
def flow_request_number(
*,

View File

@ -627,7 +627,7 @@ class TR:
reset__num_of_shares_advanced_info_template: str = "Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {0}."
reset__num_of_shares_basic_info: str = "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet."
reset__num_of_shares_how_many: str = "How many wallet backup shares do you want to create?"
reset__num_of_shares_long_info: str = "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet."
reset__num_of_shares_long_info: str = "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet."
reset__num_shares_for_group_template: str = "The required number of shares to form Group {0}."
reset__number_of_shares_info: str = "= total number of unique word lists used for wallet backup."
reset__one_share: str = "1 share"
@ -644,14 +644,15 @@ class TR:
reset__share_completed_template: str = "Share #{0} completed"
reset__share_words_title: str = "Standard backup"
reset__slip39_checklist_num_groups: str = "Number of groups"
reset__slip39_checklist_num_groups_x_template: str = "Number of groups: {0}"
reset__slip39_checklist_num_shares: str = "Number of shares"
reset__slip39_checklist_num_shares_x_template: str = "Number of shares"
reset__slip39_checklist_num_shares_x_template: str = "Number of shares: {0}"
reset__slip39_checklist_set_num_groups: str = "Set number of groups"
reset__slip39_checklist_set_num_shares: str = "Set number of shares"
reset__slip39_checklist_set_sizes: str = "Set sizes and thresholds"
reset__slip39_checklist_set_sizes_longer: str = "Set size and threshold for each group"
reset__slip39_checklist_set_threshold: str = "Set threshold"
reset__slip39_checklist_threshold_x_template: str = "Recovery threshold:"
reset__slip39_checklist_threshold_x_template: str = "Recovery threshold: {0}"
reset__slip39_checklist_title: str = "Backup checklist"
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"

View File

@ -138,7 +138,7 @@ async def _backup_slip39_basic(
share_count = await layout.slip39_prompt_number_of_shares()
# get threshold
await layout.slip39_show_checklist(1, advanced=False)
await layout.slip39_show_checklist(1, advanced=False, count=share_count)
share_threshold = await layout.slip39_prompt_threshold(share_count)
mnemonics = _get_slip39_mnemonics(
@ -149,7 +149,9 @@ async def _backup_slip39_basic(
)
# show and confirm individual shares
await layout.slip39_show_checklist(2, advanced=False)
await layout.slip39_show_checklist(
2, advanced=False, count=share_count, threshold=share_threshold
)
await layout.slip39_basic_show_and_confirm_shares(mnemonics[0])

View File

@ -23,12 +23,13 @@ async def show_share_words(
group_index: int | None = None,
) -> None:
if share_index is None:
title = TR.reset__recovery_wallet_backup_title
if share_index is None:
subtitle = ""
elif group_index is None:
title = TR.reset__recovery_share_title_template.format(share_index + 1)
subtitle = TR.reset__recovery_share_title_template.format(share_index + 1)
else:
title = TR.reset__group_share_title_template.format(
subtitle = TR.reset__group_share_title_template.format(
group_index + 1, share_index + 1
)
words_count = len(share_words)
@ -39,6 +40,7 @@ async def show_share_words(
RustLayout(
trezorui2.flow_show_share_words(
title=title,
subtitle=subtitle,
words=share_words,
text_info=text_info,
text_confirm=text_confirm,
@ -90,29 +92,17 @@ async def select_word(
return words[result]
async def slip39_show_checklist(step: int, backup_type: BackupType) -> None:
from trezor.enums import BackupType
assert backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced)
items = (
(
TR.reset__slip39_checklist_set_num_shares,
TR.reset__slip39_checklist_set_threshold,
TR.reset__slip39_checklist_write_down_recovery,
)
if backup_type == BackupType.Slip39_Basic
else (
TR.reset__slip39_checklist_set_num_groups,
TR.reset__slip39_checklist_set_num_shares,
TR.reset__slip39_checklist_set_sizes_longer,
)
)
async def slip39_show_checklist(
step: int,
backup_type: BackupType,
count: int | None = None,
threshold: int | None = None,
) -> None:
items = _slip_39_checklist_items(step, backup_type, count, threshold)
result = await interact(
RustLayout(
trezorui2.show_checklist(
title=TR.reset__slip39_checklist_title,
title=TR.reset__title_shamir_backup,
button=TR.buttons__continue,
active=step,
items=items,
@ -125,6 +115,44 @@ async def slip39_show_checklist(step: int, backup_type: BackupType) -> None:
raise ActionCancelled
def _slip_39_checklist_items(
step: int,
backup_type: BackupType,
count: int | None = None,
threshold: int | None = None,
):
from trezor.enums import BackupType
assert backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced)
if backup_type == BackupType.Slip39_Basic:
entry_1 = (
TR.reset__slip39_checklist_num_shares_x_template.format(count)
if count
else TR.reset__slip39_checklist_set_num_shares
)
entry_2 = (
TR.reset__slip39_checklist_threshold_x_template.format(threshold)
if threshold
else TR.reset__slip39_checklist_set_threshold
)
entry_3 = TR.reset__slip39_checklist_write_down_recovery
return (entry_1, entry_2, entry_3)
else:
entry_1 = (
TR.reset__slip39_checklist_num_groups_x_template.format(count)
if count
else TR.reset__slip39_checklist_set_num_groups
)
entry_2 = (
TR.reset__slip39_checklist_threshold_x_template.format(threshold)
if threshold
else TR.reset__slip39_checklist_set_threshold
)
entry_3 = TR.reset__slip39_checklist_set_sizes_longer
return (entry_1, entry_2, entry_3)
async def _prompt_number(
title: str,
description: Callable[[int], str],
@ -149,7 +177,6 @@ async def _prompt_number(
)
if __debug__:
# TODO: is this still relevant?
if not isinstance(result, tuple):
# DebugLink currently can't send number of shares and it doesn't
# change the counter either so just use the initial value.
@ -173,12 +200,7 @@ async def slip39_prompt_threshold(
def description(count: int) -> str:
if group_id is None:
if count == 1:
return TR.reset__you_need_one_share
elif count == max_count:
return TR.reset__need_all_share_template.format(count)
else:
return TR.reset__need_any_share_template.format(count)
return TR.reset__select_threshold
else:
return TR.reset__num_shares_for_group_template.format(group_id + 1)
@ -186,6 +208,8 @@ async def slip39_prompt_threshold(
# TODO: this is madness...
text = TR.reset__the_threshold_sets_the_number_of_shares
if group_id is None:
# FIXME: need to propagate the argument in rust, temporary hack to show plausible value
count = num_of_shares - 1
text += TR.reset__needed_to_recover_your_wallet
text += TR.reset__set_it_to_count_template.format(count)
if num_of_shares == 1:
@ -225,9 +249,6 @@ async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int:
def description(i: int):
if group_id is None:
if i == 1:
return TR.reset__only_one_share_will_be_created
else:
return TR.reset__num_of_shares_how_many
else:
return TR.reset__total_number_of_shares_in_group_template.format(
@ -235,7 +256,7 @@ async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int:
)
if group_id is None:
info = TR.reset__num_of_shares_basic_info
info = TR.reset__num_of_shares_long_info
else:
info = TR.reset__num_of_shares_advanced_info_template.format(group_id + 1)

View File

@ -99,7 +99,12 @@ async def select_word(
return words[result]
async def slip39_show_checklist(step: int, advanced: bool) -> None:
async def slip39_show_checklist(
step: int,
advanced: bool,
count: int | None = None,
threshold: int | None = None,
) -> None:
items = (
(
TR.reset__slip39_checklist_num_shares,

View File

@ -103,7 +103,12 @@ async def select_word(
return words[result]
async def slip39_show_checklist(step: int, advanced: bool) -> None:
async def slip39_show_checklist(
step: int,
advanced: bool,
count: int | None = None,
threshold: int | None = None,
) -> None:
items = (
(
TR.reset__slip39_checklist_set_num_shares,

View File

@ -629,7 +629,7 @@
"reset__num_of_shares_how_many": "How many wallet backup shares do you want to create?",
"reset__num_of_shares_advanced_info_template": "Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {0}.",
"reset__num_of_shares_basic_info": "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet.",
"reset__num_of_shares_long_info": "Each recovery share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.",
"reset__num_of_shares_long_info": "Each backup share is a sequence of 20 words. Store each wordlist in a separate, safe location or share with trusted individuals. Collect as needed to recover your wallet.",
"reset__num_shares_for_group_template": "The required number of shares to form Group {0}.",
"reset__number_of_shares_info": "= total number of unique word lists used for wallet backup.",
"reset__one_share": "1 share",
@ -647,13 +647,14 @@
"reset__share_words_title": "Standard backup",
"reset__slip39_checklist_num_groups": "Number of groups",
"reset__slip39_checklist_num_shares": "Number of shares",
"reset__slip39_checklist_num_shares_x_template": "Number of shares",
"reset__slip39_checklist_num_shares_x_template": "Number of shares: {0}",
"reset__slip39_checklist_num_groups_x_template": "Number of groups: {0}",
"reset__slip39_checklist_set_num_groups": "Set number of groups",
"reset__slip39_checklist_set_num_shares": "Set number of shares",
"reset__slip39_checklist_set_sizes": "Set sizes and thresholds",
"reset__slip39_checklist_set_sizes_longer": "Set size and threshold for each group",
"reset__slip39_checklist_set_threshold": "Set threshold",
"reset__slip39_checklist_threshold_x_template": "Recovery threshold:",
"reset__slip39_checklist_threshold_x_template": "Recovery threshold: {0}",
"reset__slip39_checklist_title": "Backup checklist",
"reset__slip39_checklist_write_down": "Write down and check all shares",
"reset__slip39_checklist_write_down_recovery": "Write down & check all wallet backup shares",

View File

@ -929,5 +929,6 @@
"927": "words__good_to_know",
"928": "words__operation_cancelled",
"929": "words__settings",
"930": "words__try_again"
"930": "words__try_again",
"931": "reset__slip39_checklist_num_groups_x_template"
}

View File

@ -1,8 +1,8 @@
{
"current": {
"merkle_root": "070867d6d288bb46d1eb69dd95bc6d7b6fc09a3f299c75e997d756e7915860d6",
"datetime": "2024-05-22T11:43:31.930638",
"commit": "6bd6027a57b814483947f98aeb118a15f846e3bb"
"merkle_root": "e79fdd3c9052dfd140aef2b4800b6b0b8000ae99822cf3e8be083da0dc2b376c",
"datetime": "2024-05-26T15:18:17.124044",
"commit": "3e9598245d57044418f4eeb5ce5bc792f20587c3"
},
"history": [
{