mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-24 01:38:45 +00:00
feat(eckhart): confirm output flow
This commit is contained in:
parent
12f46d3e67
commit
0d40bb76b3
@ -648,6 +648,8 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_send__maximum_fee;
|
MP_QSTR_send__maximum_fee;
|
||||||
MP_QSTR_send__receiving_to_multisig;
|
MP_QSTR_send__receiving_to_multisig;
|
||||||
MP_QSTR_send__send_from;
|
MP_QSTR_send__send_from;
|
||||||
|
MP_QSTR_send__send_in_the_app;
|
||||||
|
MP_QSTR_send__sign_cancelled;
|
||||||
MP_QSTR_send__sign_transaction;
|
MP_QSTR_send__sign_transaction;
|
||||||
MP_QSTR_send__title_confirm_sending;
|
MP_QSTR_send__title_confirm_sending;
|
||||||
MP_QSTR_send__title_joint_transaction;
|
MP_QSTR_send__title_joint_transaction;
|
||||||
@ -818,6 +820,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_words__receive;
|
MP_QSTR_words__receive;
|
||||||
MP_QSTR_words__recipient;
|
MP_QSTR_words__recipient;
|
||||||
MP_QSTR_words__recovery_share;
|
MP_QSTR_words__recovery_share;
|
||||||
|
MP_QSTR_words__send;
|
||||||
MP_QSTR_words__settings;
|
MP_QSTR_words__settings;
|
||||||
MP_QSTR_words__sign;
|
MP_QSTR_words__sign;
|
||||||
MP_QSTR_words__signer;
|
MP_QSTR_words__signer;
|
||||||
@ -834,6 +837,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_words__try_again;
|
MP_QSTR_words__try_again;
|
||||||
MP_QSTR_words__unknown;
|
MP_QSTR_words__unknown;
|
||||||
MP_QSTR_words__unlocked;
|
MP_QSTR_words__unlocked;
|
||||||
|
MP_QSTR_words__wallet;
|
||||||
MP_QSTR_words__warning;
|
MP_QSTR_words__warning;
|
||||||
MP_QSTR_words__writable;
|
MP_QSTR_words__writable;
|
||||||
MP_QSTR_words__yes;
|
MP_QSTR_words__yes;
|
||||||
|
@ -1242,7 +1242,7 @@ pub enum TranslatedString {
|
|||||||
pin__cancel_description = 870, // "Continue without PIN"
|
pin__cancel_description = 870, // "Continue without PIN"
|
||||||
pin__cancel_info = 871, // "Without a PIN, anyone can access this device."
|
pin__cancel_info = 871, // "Without a PIN, anyone can access this device."
|
||||||
pin__cancel_setup = 872, // "Cancel PIN setup"
|
pin__cancel_setup = 872, // "Cancel PIN setup"
|
||||||
send__cancel_sign = 873, // "Cancel sign"
|
send__cancel_sign = 873, // {"Bolt": "Cancel sign", "Caesar": "Cancel sign", "Delizia": "Cancel sign", "Eckhart": "Cancel sign?"}
|
||||||
send__send_from = 874, // "Send from"
|
send__send_from = 874, // "Send from"
|
||||||
instructions__hold_to_sign = 875, // "Hold to sign"
|
instructions__hold_to_sign = 875, // "Hold to sign"
|
||||||
confirm_total__fee_rate = 876, // "Fee rate"
|
confirm_total__fee_rate = 876, // "Fee rate"
|
||||||
|
520
core/embed/rust/src/ui/layout_eckhart/flow/confirm_output.rs
Normal file
520
core/embed/rust/src/ui/layout_eckhart/flow/confirm_output.rs
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error,
|
||||||
|
micropython::obj::Obj,
|
||||||
|
strutil::TString,
|
||||||
|
translations::TR,
|
||||||
|
ui::{
|
||||||
|
button_request::ButtonRequest,
|
||||||
|
component::{
|
||||||
|
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs},
|
||||||
|
ButtonRequestExt, ComponentExt, MsgMap,
|
||||||
|
},
|
||||||
|
flow::{
|
||||||
|
base::{Decision, DecisionBuilder as _},
|
||||||
|
FlowController, FlowMsg, SwipeFlow,
|
||||||
|
},
|
||||||
|
geometry::{Alignment, Direction, LinearPlacement, Offset},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
component::Button,
|
||||||
|
firmware::{
|
||||||
|
ActionBar, Header, TextScreen, TextScreenMsg, VerticalMenu, VerticalMenuScreen,
|
||||||
|
VerticalMenuScreenMsg,
|
||||||
|
},
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MENU_ITEM_CANCEL: usize = 0;
|
||||||
|
const MENU_ITEM_FEE_INFO: usize = 1;
|
||||||
|
const MENU_ITEM_ADDRESS_INFO: usize = 2;
|
||||||
|
const MENU_ITEM_ACCOUNT_INFO: usize = 3;
|
||||||
|
const MENU_ITEM_EXTRA_INFO: usize = 4;
|
||||||
|
|
||||||
|
const TIMEOUT_MS: u32 = 2000;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ConfirmOutput {
|
||||||
|
Address,
|
||||||
|
Menu,
|
||||||
|
AccountInfo,
|
||||||
|
Cancel,
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowController for ConfirmOutput {
|
||||||
|
#[inline]
|
||||||
|
fn index(&'static self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_swipe(&'static self, _direction: Direction) -> Decision {
|
||||||
|
self.do_nothing()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||||
|
match (self, msg) {
|
||||||
|
(Self::Address, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||||
|
(Self::Address, FlowMsg::Info) => Self::Menu.goto(),
|
||||||
|
(Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::Cancel.goto(),
|
||||||
|
(Self::Menu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => Self::AccountInfo.goto(),
|
||||||
|
(Self::Menu, FlowMsg::Cancelled) => Self::Address.goto(),
|
||||||
|
(Self::AccountInfo, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||||
|
(Self::Cancel, FlowMsg::Confirmed) => Self::Cancelled.goto(),
|
||||||
|
(Self::Cancel, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||||
|
(Self::Cancelled, _) => self.return_msg(FlowMsg::Cancelled),
|
||||||
|
_ => self.do_nothing(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ConfirmOutputWithAmount {
|
||||||
|
Address,
|
||||||
|
AddressMenu,
|
||||||
|
AddressAccountInfo,
|
||||||
|
AddressCancel,
|
||||||
|
Amount,
|
||||||
|
AmountMenu,
|
||||||
|
AmountAccountInfo,
|
||||||
|
AmountCancel,
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowController for ConfirmOutputWithAmount {
|
||||||
|
#[inline]
|
||||||
|
fn index(&'static self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_swipe(&'static self, _direction: Direction) -> Decision {
|
||||||
|
self.do_nothing()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||||
|
match (self, msg) {
|
||||||
|
(Self::Address, FlowMsg::Confirmed) => Self::Amount.goto(),
|
||||||
|
(Self::Address, FlowMsg::Info) => Self::AddressMenu.goto(),
|
||||||
|
(Self::AddressMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::AddressCancel.goto(),
|
||||||
|
(Self::AddressMenu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => {
|
||||||
|
Self::AddressAccountInfo.goto()
|
||||||
|
}
|
||||||
|
(Self::AddressAccountInfo, FlowMsg::Cancelled) => Self::AddressMenu.goto(),
|
||||||
|
(Self::AddressMenu, FlowMsg::Cancelled) => Self::Address.goto(),
|
||||||
|
(Self::AddressCancel, FlowMsg::Confirmed) => Self::Cancelled.goto(),
|
||||||
|
(Self::AddressCancel, FlowMsg::Cancelled) => Self::AddressMenu.goto(),
|
||||||
|
(Self::Amount, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||||
|
(Self::Amount, FlowMsg::Cancelled) => Self::Address.goto(),
|
||||||
|
(Self::Amount, FlowMsg::Info) => Self::AmountMenu.goto(),
|
||||||
|
(Self::AmountMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::AmountCancel.goto(),
|
||||||
|
(Self::AmountMenu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => {
|
||||||
|
Self::AmountAccountInfo.goto()
|
||||||
|
}
|
||||||
|
(Self::AmountAccountInfo, FlowMsg::Cancelled) => Self::AmountMenu.goto(),
|
||||||
|
(Self::AmountMenu, FlowMsg::Cancelled) => Self::Amount.goto(),
|
||||||
|
(Self::AmountCancel, FlowMsg::Confirmed) => Self::Cancelled.goto(),
|
||||||
|
(Self::AmountCancel, FlowMsg::Cancelled) => Self::AmountMenu.goto(),
|
||||||
|
(Self::Cancelled, _) => self.return_msg(FlowMsg::Cancelled),
|
||||||
|
_ => self.do_nothing(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ConfirmOutputWithSummary {
|
||||||
|
Main,
|
||||||
|
MainMenu,
|
||||||
|
MainMenuCancel,
|
||||||
|
MainMenuAddresInfo,
|
||||||
|
MainMenuAccountInfo,
|
||||||
|
Summary,
|
||||||
|
SummaryMenu,
|
||||||
|
SummaryMenuCancel,
|
||||||
|
SummaryMenuFeeInfo,
|
||||||
|
SummaryMenuAccountInfo,
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowController for ConfirmOutputWithSummary {
|
||||||
|
#[inline]
|
||||||
|
fn index(&'static self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_swipe(&'static self, _direction: Direction) -> Decision {
|
||||||
|
self.do_nothing()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||||
|
match (self, msg) {
|
||||||
|
(Self::Main, FlowMsg::Confirmed) => Self::Summary.goto(),
|
||||||
|
(Self::Main, FlowMsg::Info) => Self::MainMenu.goto(),
|
||||||
|
(Self::MainMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::MainMenuCancel.goto(),
|
||||||
|
(Self::MainMenu, FlowMsg::Choice(MENU_ITEM_ADDRESS_INFO)) => {
|
||||||
|
Self::MainMenuAddresInfo.goto()
|
||||||
|
}
|
||||||
|
(Self::MainMenu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => {
|
||||||
|
Self::MainMenuAccountInfo.goto()
|
||||||
|
}
|
||||||
|
(Self::MainMenu, FlowMsg::Cancelled) => Self::Main.goto(),
|
||||||
|
(Self::MainMenuAccountInfo | Self::MainMenuAddresInfo, FlowMsg::Cancelled) => {
|
||||||
|
Self::MainMenu.goto()
|
||||||
|
}
|
||||||
|
(Self::MainMenuCancel, FlowMsg::Cancelled) => Self::MainMenu.goto(),
|
||||||
|
(Self::MainMenuCancel, FlowMsg::Confirmed) => Self::Cancelled.goto(),
|
||||||
|
(Self::Summary, FlowMsg::Info) => Self::SummaryMenu.goto(),
|
||||||
|
(Self::Summary, FlowMsg::Cancelled) => Self::Main.goto(),
|
||||||
|
(Self::Summary, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||||
|
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => {
|
||||||
|
Self::SummaryMenuCancel.goto()
|
||||||
|
}
|
||||||
|
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => {
|
||||||
|
Self::SummaryMenuFeeInfo.goto()
|
||||||
|
}
|
||||||
|
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_EXTRA_INFO)) => {
|
||||||
|
Self::SummaryMenuAccountInfo.goto()
|
||||||
|
}
|
||||||
|
(Self::SummaryMenu, FlowMsg::Cancelled) => Self::Summary.goto(),
|
||||||
|
(Self::SummaryMenuCancel, FlowMsg::Cancelled) => Self::SummaryMenu.goto(),
|
||||||
|
(Self::SummaryMenuCancel, FlowMsg::Confirmed) => Self::Cancelled.goto(),
|
||||||
|
(Self::SummaryMenuAccountInfo | Self::SummaryMenuFeeInfo, FlowMsg::Cancelled) => {
|
||||||
|
Self::SummaryMenu.goto()
|
||||||
|
}
|
||||||
|
(Self::Cancelled, _) => self.return_msg(FlowMsg::Cancelled),
|
||||||
|
_ => self.do_nothing(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content_cancel(
|
||||||
|
) -> MsgMap<TextScreen<Paragraphs<Paragraph<'static>>>, impl Fn(TextScreenMsg) -> Option<FlowMsg>> {
|
||||||
|
TextScreen::new(
|
||||||
|
Paragraph::new(&theme::TEXT_REGULAR, TR::send__cancel_sign)
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical()),
|
||||||
|
)
|
||||||
|
.with_header(Header::new(TR::words__send.into()))
|
||||||
|
.with_action_bar(ActionBar::new_double(
|
||||||
|
Button::with_icon(theme::ICON_CHEVRON_LEFT),
|
||||||
|
Button::with_text(TR::buttons__cancel.into()).styled(theme::button_cancel()),
|
||||||
|
))
|
||||||
|
.map(|msg| match msg {
|
||||||
|
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||||
|
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content_main_menu(
|
||||||
|
address_title: TString<'static>,
|
||||||
|
address_params: bool,
|
||||||
|
account_params: bool,
|
||||||
|
cancel_menu_label: TString<'static>,
|
||||||
|
) -> MsgMap<VerticalMenuScreen, impl Fn(VerticalMenuScreenMsg) -> Option<FlowMsg>> {
|
||||||
|
let mut main_menu = VerticalMenu::empty();
|
||||||
|
let mut main_menu_items = Vec::<usize, 3>::new();
|
||||||
|
if address_params {
|
||||||
|
main_menu = main_menu.item(
|
||||||
|
Button::with_text(address_title)
|
||||||
|
.styled(theme::menu_item_title())
|
||||||
|
.with_text_align(Alignment::Start),
|
||||||
|
);
|
||||||
|
unwrap!(main_menu_items.push(MENU_ITEM_ADDRESS_INFO));
|
||||||
|
}
|
||||||
|
if account_params {
|
||||||
|
main_menu = main_menu.item(
|
||||||
|
Button::with_text(TR::address_details__account_info.into())
|
||||||
|
.styled(theme::menu_item_title())
|
||||||
|
.with_text_align(Alignment::Start)
|
||||||
|
.with_content_offset(Offset::x(12)),
|
||||||
|
);
|
||||||
|
unwrap!(main_menu_items.push(MENU_ITEM_ACCOUNT_INFO));
|
||||||
|
}
|
||||||
|
main_menu = main_menu.item(
|
||||||
|
Button::with_text(cancel_menu_label)
|
||||||
|
.styled(theme::menu_item_title_orange())
|
||||||
|
.with_text_align(Alignment::Start)
|
||||||
|
.with_content_offset(Offset::x(12)),
|
||||||
|
);
|
||||||
|
unwrap!(main_menu_items.push(MENU_ITEM_CANCEL));
|
||||||
|
|
||||||
|
VerticalMenuScreen::new(main_menu)
|
||||||
|
.with_header(Header::new(TString::empty()).with_close_button())
|
||||||
|
.map(move |msg| match msg {
|
||||||
|
VerticalMenuScreenMsg::Selected(i) => {
|
||||||
|
let selected_item = main_menu_items[i];
|
||||||
|
Some(FlowMsg::Choice(selected_item))
|
||||||
|
}
|
||||||
|
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content_menu_info(
|
||||||
|
title: TString<'static>,
|
||||||
|
subtitle: Option<TString<'static>>,
|
||||||
|
paragraphs: Option<ParagraphVecShort<'static>>,
|
||||||
|
) -> MsgMap<
|
||||||
|
TextScreen<Paragraphs<ParagraphVecShort<'static>>>,
|
||||||
|
impl Fn(TextScreenMsg) -> Option<FlowMsg>,
|
||||||
|
> {
|
||||||
|
TextScreen::new(
|
||||||
|
paragraphs
|
||||||
|
.map_or_else(ParagraphVecShort::new, |p| p)
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical())
|
||||||
|
.with_spacing(12),
|
||||||
|
)
|
||||||
|
.with_header(Header::new(title).with_close_button())
|
||||||
|
.with_subtitle(subtitle.unwrap_or(TString::empty()))
|
||||||
|
.map(|_| Some(FlowMsg::Cancelled))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn new_confirm_output(
|
||||||
|
title: Option<TString<'static>>,
|
||||||
|
subtitle: Option<TString<'static>>,
|
||||||
|
chunkify: bool,
|
||||||
|
message: Obj,
|
||||||
|
amount: Option<Obj>,
|
||||||
|
br_name: TString<'static>,
|
||||||
|
br_code: u16,
|
||||||
|
account_title: TString<'static>,
|
||||||
|
account_paragraphs: Option<ParagraphVecShort<'static>>,
|
||||||
|
address_title: Option<TString<'static>>,
|
||||||
|
address_paragraphs: Option<ParagraphVecShort<'static>>,
|
||||||
|
summary_title: Option<TString<'static>>,
|
||||||
|
summary_paragraphs: Option<ParagraphVecShort<'static>>,
|
||||||
|
summary_br_code: Option<u16>,
|
||||||
|
summary_br_name: Option<TString<'static>>,
|
||||||
|
fee_params: Option<ParagraphVecShort<'static>>,
|
||||||
|
cancel_menu_label: Option<TString<'static>>,
|
||||||
|
) -> Result<SwipeFlow, error::Error> {
|
||||||
|
let cancel_menu_label = cancel_menu_label.unwrap_or(TR::buttons__cancel.into());
|
||||||
|
let address_menu_item = address_paragraphs.is_some();
|
||||||
|
let account_menu_item = account_paragraphs.is_some();
|
||||||
|
let fee_menu_item = fee_params.is_some();
|
||||||
|
let address_title = address_title.unwrap_or(TR::words__address.into());
|
||||||
|
let account_subtitle = Some(TR::send__send_from.into());
|
||||||
|
|
||||||
|
// Main
|
||||||
|
let main_paragraphs = Paragraph::new(
|
||||||
|
if chunkify {
|
||||||
|
&theme::TEXT_MONO_ADDRESS_CHUNKS
|
||||||
|
} else {
|
||||||
|
&theme::TEXT_MONO_LIGHT
|
||||||
|
},
|
||||||
|
message.try_into().unwrap_or(TString::empty()),
|
||||||
|
);
|
||||||
|
let content_main = TextScreen::new(
|
||||||
|
main_paragraphs
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical()),
|
||||||
|
)
|
||||||
|
.with_header(Header::new(title.unwrap_or(TString::empty())).with_menu_button())
|
||||||
|
.with_action_bar(ActionBar::new_single(Button::with_text(
|
||||||
|
TR::buttons__continue.into(),
|
||||||
|
)))
|
||||||
|
.with_subtitle(subtitle.unwrap_or(TString::empty()))
|
||||||
|
.map(|msg| match msg {
|
||||||
|
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||||
|
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||||
|
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||||
|
})
|
||||||
|
.one_button_request(ButtonRequest::from_num(br_code, br_name))
|
||||||
|
.with_pages(|_| 1);
|
||||||
|
|
||||||
|
// Cancelled
|
||||||
|
let content_cancelled = TextScreen::new(
|
||||||
|
Paragraph::new(&theme::TEXT_REGULAR, TR::send__sign_cancelled)
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical()),
|
||||||
|
)
|
||||||
|
.with_header(Header::new(TR::words__title_done.into()).with_icon(theme::ICON_DONE, theme::GREY))
|
||||||
|
.with_action_bar(ActionBar::new_timeout(
|
||||||
|
Button::with_text(TR::instructions__continue_in_app.into()),
|
||||||
|
TIMEOUT_MS,
|
||||||
|
))
|
||||||
|
.map(|_| Some(FlowMsg::Confirmed));
|
||||||
|
|
||||||
|
let res = if let Some(amount) = amount {
|
||||||
|
let amount_paragraphs = ParagraphVecShort::from_iter([
|
||||||
|
Paragraph::new(&theme::TEXT_SMALL_LIGHT, TR::words__amount).no_break(),
|
||||||
|
Paragraph::new(
|
||||||
|
&theme::TEXT_MONO_MEDIUM_LIGHT,
|
||||||
|
amount.try_into().unwrap_or(TString::empty()),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let content_amount = TextScreen::new(
|
||||||
|
amount_paragraphs
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical()),
|
||||||
|
)
|
||||||
|
.with_header(Header::new(TR::words__amount.into()).with_menu_button())
|
||||||
|
.with_action_bar(ActionBar::new_double(
|
||||||
|
Button::with_icon(theme::ICON_CHEVRON_UP),
|
||||||
|
Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()),
|
||||||
|
))
|
||||||
|
.with_subtitle(subtitle.unwrap_or(TString::empty()))
|
||||||
|
.map(|msg| match msg {
|
||||||
|
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||||
|
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||||
|
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||||
|
})
|
||||||
|
.one_button_request(ButtonRequest::from_num(br_code, br_name))
|
||||||
|
.with_pages(|_| 1);
|
||||||
|
|
||||||
|
let mut flow = SwipeFlow::new(&ConfirmOutputWithAmount::Address)?;
|
||||||
|
flow.add_page(&ConfirmOutputWithAmount::Address, content_main)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithAmount::AddressMenu,
|
||||||
|
content_main_menu(
|
||||||
|
address_title,
|
||||||
|
address_menu_item,
|
||||||
|
account_menu_item,
|
||||||
|
cancel_menu_label,
|
||||||
|
),
|
||||||
|
)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithAmount::AddressAccountInfo,
|
||||||
|
content_menu_info(
|
||||||
|
TR::address_details__account_info.into(),
|
||||||
|
account_subtitle,
|
||||||
|
account_paragraphs.clone(),
|
||||||
|
),
|
||||||
|
)?
|
||||||
|
.add_page(&ConfirmOutputWithAmount::AddressCancel, content_cancel())?
|
||||||
|
.add_page(&ConfirmOutputWithAmount::Amount, content_amount)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithAmount::AmountMenu,
|
||||||
|
content_main_menu(
|
||||||
|
address_title,
|
||||||
|
address_menu_item,
|
||||||
|
account_menu_item,
|
||||||
|
cancel_menu_label,
|
||||||
|
),
|
||||||
|
)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithAmount::AmountAccountInfo,
|
||||||
|
content_menu_info(account_title, account_subtitle, account_paragraphs.clone()),
|
||||||
|
)?
|
||||||
|
.add_page(&ConfirmOutputWithAmount::AmountCancel, content_cancel())?
|
||||||
|
.add_page(&ConfirmOutputWithAmount::Cancelled, content_cancelled)?;
|
||||||
|
flow
|
||||||
|
} else if let Some(summary_paragraphs) = summary_paragraphs {
|
||||||
|
// Summary
|
||||||
|
let content_summary = TextScreen::new(
|
||||||
|
summary_paragraphs
|
||||||
|
.into_paragraphs()
|
||||||
|
.with_placement(LinearPlacement::vertical())
|
||||||
|
.with_spacing(12),
|
||||||
|
)
|
||||||
|
.with_header(
|
||||||
|
Header::new(summary_title.unwrap_or(TR::words__title_summary.into()))
|
||||||
|
.with_menu_button(),
|
||||||
|
)
|
||||||
|
.with_action_bar(ActionBar::new_double(
|
||||||
|
Button::with_icon(theme::ICON_CHEVRON_UP),
|
||||||
|
Button::with_text(TR::instructions__hold_to_sign.into())
|
||||||
|
.styled(theme::button_confirm()),
|
||||||
|
))
|
||||||
|
.map(|msg| match msg {
|
||||||
|
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||||
|
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||||
|
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||||
|
})
|
||||||
|
.one_button_request(ButtonRequest::from_num(
|
||||||
|
summary_br_code.unwrap(),
|
||||||
|
summary_br_name.unwrap(),
|
||||||
|
))
|
||||||
|
.with_pages(|_| 1);
|
||||||
|
|
||||||
|
// SummaryMenu
|
||||||
|
let mut summary_menu = VerticalMenu::empty();
|
||||||
|
let mut summary_menu_items = Vec::<usize, 3>::new();
|
||||||
|
if account_menu_item {
|
||||||
|
summary_menu = summary_menu.item(Button::with_text(account_title));
|
||||||
|
unwrap!(summary_menu_items.push(MENU_ITEM_EXTRA_INFO));
|
||||||
|
}
|
||||||
|
if fee_menu_item {
|
||||||
|
summary_menu =
|
||||||
|
summary_menu.item(Button::with_text(TR::confirm_total__title_fee.into()));
|
||||||
|
unwrap!(summary_menu_items.push(MENU_ITEM_FEE_INFO));
|
||||||
|
}
|
||||||
|
summary_menu = summary_menu
|
||||||
|
.item(Button::with_text(cancel_menu_label).styled(theme::menu_item_title_orange()));
|
||||||
|
unwrap!(summary_menu_items.push(MENU_ITEM_CANCEL));
|
||||||
|
let content_summary_menu = VerticalMenuScreen::new(summary_menu)
|
||||||
|
.with_header(Header::new(TString::empty()).with_close_button())
|
||||||
|
.map(move |msg| match msg {
|
||||||
|
VerticalMenuScreenMsg::Selected(i) => {
|
||||||
|
let selected_item = summary_menu_items[i];
|
||||||
|
Some(FlowMsg::Choice(selected_item))
|
||||||
|
}
|
||||||
|
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut flow = SwipeFlow::new(&ConfirmOutputWithSummary::Main)?;
|
||||||
|
flow.add_page(&ConfirmOutputWithSummary::Main, content_main)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithSummary::MainMenu,
|
||||||
|
content_main_menu(
|
||||||
|
address_title,
|
||||||
|
address_menu_item,
|
||||||
|
account_menu_item,
|
||||||
|
cancel_menu_label,
|
||||||
|
),
|
||||||
|
)?
|
||||||
|
.add_page(&ConfirmOutputWithSummary::MainMenuCancel, content_cancel())?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithSummary::MainMenuAddresInfo,
|
||||||
|
content_menu_info(address_title, None, address_paragraphs),
|
||||||
|
)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithSummary::MainMenuAccountInfo,
|
||||||
|
content_menu_info(account_title, account_subtitle, account_paragraphs.clone()),
|
||||||
|
)?
|
||||||
|
.add_page(&ConfirmOutputWithSummary::Summary, content_summary)?
|
||||||
|
.add_page(&ConfirmOutputWithSummary::SummaryMenu, content_summary_menu)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithSummary::SummaryMenuCancel,
|
||||||
|
content_cancel(),
|
||||||
|
)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithSummary::SummaryMenuFeeInfo,
|
||||||
|
content_menu_info(TR::confirm_total__title_fee.into(), None, fee_params),
|
||||||
|
)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutputWithSummary::SummaryMenuAccountInfo,
|
||||||
|
content_menu_info(account_title, account_subtitle, account_paragraphs),
|
||||||
|
)?
|
||||||
|
.add_page(&ConfirmOutputWithSummary::Cancelled, content_cancelled)?;
|
||||||
|
flow
|
||||||
|
} else {
|
||||||
|
let mut flow = SwipeFlow::new(&ConfirmOutput::Address)?;
|
||||||
|
flow.add_page(&ConfirmOutput::Address, content_main)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutput::Menu,
|
||||||
|
content_main_menu(
|
||||||
|
address_title,
|
||||||
|
address_menu_item,
|
||||||
|
account_menu_item,
|
||||||
|
cancel_menu_label,
|
||||||
|
),
|
||||||
|
)?
|
||||||
|
.add_page(
|
||||||
|
&ConfirmOutput::AccountInfo,
|
||||||
|
content_menu_info(account_title, account_subtitle, account_paragraphs),
|
||||||
|
)?
|
||||||
|
.add_page(&ConfirmOutput::Cancel, content_cancel())?
|
||||||
|
.add_page(&ConfirmOutput::Cancelled, content_cancelled)?;
|
||||||
|
flow
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
pub mod confirm_output;
|
||||||
pub mod confirm_reset;
|
pub mod confirm_reset;
|
||||||
pub mod confirm_set_new_pin;
|
pub mod confirm_set_new_pin;
|
||||||
pub mod confirm_summary;
|
pub mod confirm_summary;
|
||||||
@ -8,6 +9,7 @@ pub mod request_passphrase;
|
|||||||
pub mod show_danger;
|
pub mod show_danger;
|
||||||
pub mod show_share_words;
|
pub mod show_share_words;
|
||||||
|
|
||||||
|
pub use confirm_output::new_confirm_output;
|
||||||
pub use confirm_reset::new_confirm_reset;
|
pub use confirm_reset::new_confirm_reset;
|
||||||
pub use confirm_set_new_pin::new_set_new_pin;
|
pub use confirm_set_new_pin::new_set_new_pin;
|
||||||
pub use confirm_summary::new_confirm_summary;
|
pub use confirm_summary::new_confirm_summary;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use heapless::Vec;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
@ -393,29 +392,114 @@ impl FirmwareUI for UIEckhart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn flow_confirm_output(
|
fn flow_confirm_output(
|
||||||
_title: Option<TString<'static>>,
|
title: Option<TString<'static>>,
|
||||||
_subtitle: Option<TString<'static>>,
|
subtitle: Option<TString<'static>>,
|
||||||
_description: Option<TString<'static>>,
|
_description: Option<TString<'static>>,
|
||||||
_extra: Option<TString<'static>>,
|
_extra: Option<TString<'static>>,
|
||||||
_message: Obj,
|
message: Obj,
|
||||||
_amount: Option<Obj>,
|
amount: Option<Obj>,
|
||||||
_chunkify: bool,
|
chunkify: bool,
|
||||||
_text_mono: bool,
|
_text_mono: bool,
|
||||||
_account_title: TString<'static>,
|
account_title: TString<'static>,
|
||||||
_account: Option<TString<'static>>,
|
account: Option<TString<'static>>,
|
||||||
_account_path: Option<TString<'static>>,
|
account_path: Option<TString<'static>>,
|
||||||
_br_code: u16,
|
br_code: u16,
|
||||||
_br_name: TString<'static>,
|
br_name: TString<'static>,
|
||||||
_address_item: Option<(TString<'static>, Obj)>,
|
address_item: Option<(TString<'static>, Obj)>,
|
||||||
_extra_item: Option<(TString<'static>, Obj)>,
|
_extra_item: Option<(TString<'static>, Obj)>,
|
||||||
_summary_items: Option<Obj>,
|
summary_items: Option<Obj>,
|
||||||
_fee_items: Option<Obj>,
|
fee_items: Option<Obj>,
|
||||||
_summary_title: Option<TString<'static>>,
|
summary_title: Option<TString<'static>>,
|
||||||
_summary_br_code: Option<u16>,
|
summary_br_code: Option<u16>,
|
||||||
_summary_br_name: Option<TString<'static>>,
|
summary_br_name: Option<TString<'static>>,
|
||||||
_cancel_text: Option<TString<'static>>,
|
cancel_text: Option<TString<'static>>,
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
let (address_title, address_paragraphs) = if let Some(address_item) = address_item {
|
||||||
|
let mut paragraphs = ParagraphVecShort::new();
|
||||||
|
for pair in IterBuf::new().try_iterate(address_item.1)? {
|
||||||
|
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_SMALL_LIGHT, label).no_break()));
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_MONO_MEDIUM_LIGHT, value)));
|
||||||
|
}
|
||||||
|
(Some(address_item.0), Some(paragraphs))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
// collect available info
|
||||||
|
let account_paragraphs = {
|
||||||
|
let mut paragraphs = ParagraphVecShort::new();
|
||||||
|
if let Some(account) = account {
|
||||||
|
unwrap!(paragraphs.push(
|
||||||
|
Paragraph::new(
|
||||||
|
&theme::TEXT_SMALL_LIGHT,
|
||||||
|
TString::from_translation(TR::words__wallet)
|
||||||
|
)
|
||||||
|
.no_break()
|
||||||
|
));
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_MONO_LIGHT, account)));
|
||||||
|
}
|
||||||
|
if let Some(path) = account_path {
|
||||||
|
unwrap!(paragraphs.push(
|
||||||
|
Paragraph::new(
|
||||||
|
&theme::TEXT_SMALL_LIGHT,
|
||||||
|
TString::from_translation(TR::address_details__derivation_path)
|
||||||
|
)
|
||||||
|
.no_break()
|
||||||
|
));
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_MONO_LIGHT, path)));
|
||||||
|
}
|
||||||
|
if paragraphs.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(paragraphs)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let summary_paragraphs = if let Some(items) = summary_items {
|
||||||
|
let mut paragraphs = ParagraphVecShort::new();
|
||||||
|
for pair in IterBuf::new().try_iterate(items)? {
|
||||||
|
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_SMALL_LIGHT, label).no_break()));
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_MONO_MEDIUM_LIGHT, value)));
|
||||||
|
}
|
||||||
|
Some(paragraphs)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let fee_paragraphs = if let Some(items) = fee_items {
|
||||||
|
let mut paragraphs = ParagraphVecShort::new();
|
||||||
|
for pair in IterBuf::new().try_iterate(items)? {
|
||||||
|
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_SMALL_LIGHT, label).no_break()));
|
||||||
|
unwrap!(paragraphs.push(Paragraph::new(&theme::TEXT_MONO_LIGHT, value)));
|
||||||
|
}
|
||||||
|
Some(paragraphs)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let flow = flow::confirm_output::new_confirm_output(
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
chunkify,
|
||||||
|
message,
|
||||||
|
amount,
|
||||||
|
br_name,
|
||||||
|
br_code,
|
||||||
|
account_title,
|
||||||
|
account_paragraphs,
|
||||||
|
address_title,
|
||||||
|
address_paragraphs,
|
||||||
|
summary_title,
|
||||||
|
summary_paragraphs,
|
||||||
|
summary_br_code,
|
||||||
|
summary_br_name,
|
||||||
|
fee_paragraphs,
|
||||||
|
cancel_text,
|
||||||
|
)?;
|
||||||
|
Ok(flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flow_confirm_set_new_pin(
|
fn flow_confirm_set_new_pin(
|
||||||
|
@ -744,6 +744,8 @@ class TR:
|
|||||||
send__maximum_fee: str = "Maximum fee"
|
send__maximum_fee: str = "Maximum fee"
|
||||||
send__receiving_to_multisig: str = "Receiving to a multisig address."
|
send__receiving_to_multisig: str = "Receiving to a multisig address."
|
||||||
send__send_from: str = "Send from"
|
send__send_from: str = "Send from"
|
||||||
|
send__send_in_the_app: str = "After signing, send the transaction in the app."
|
||||||
|
send__sign_cancelled: str = "Sign cancelled."
|
||||||
send__sign_transaction: str = "Sign transaction"
|
send__sign_transaction: str = "Sign transaction"
|
||||||
send__title_confirm_sending: str = "Confirm sending"
|
send__title_confirm_sending: str = "Confirm sending"
|
||||||
send__title_joint_transaction: str = "Joint transaction"
|
send__title_joint_transaction: str = "Joint transaction"
|
||||||
@ -968,6 +970,7 @@ class TR:
|
|||||||
words__receive: str = "Receive"
|
words__receive: str = "Receive"
|
||||||
words__recipient: str = "Recipient"
|
words__recipient: str = "Recipient"
|
||||||
words__recovery_share: str = "Recovery share"
|
words__recovery_share: str = "Recovery share"
|
||||||
|
words__send: str = "Send"
|
||||||
words__settings: str = "Settings"
|
words__settings: str = "Settings"
|
||||||
words__sign: str = "Sign"
|
words__sign: str = "Sign"
|
||||||
words__signer: str = "Signer"
|
words__signer: str = "Signer"
|
||||||
@ -984,6 +987,7 @@ class TR:
|
|||||||
words__try_again: str = "Try again."
|
words__try_again: str = "Try again."
|
||||||
words__unknown: str = "Unknown"
|
words__unknown: str = "Unknown"
|
||||||
words__unlocked: str = "Unlocked"
|
words__unlocked: str = "Unlocked"
|
||||||
|
words__wallet: str = "Wallet"
|
||||||
words__warning: str = "Warning"
|
words__warning: str = "Warning"
|
||||||
words__writable: str = "Writable"
|
words__writable: str = "Writable"
|
||||||
words__yes: str = "Yes"
|
words__yes: str = "Yes"
|
||||||
|
@ -392,6 +392,7 @@ async def confirm_output(
|
|||||||
source_account: str | None = None,
|
source_account: str | None = None,
|
||||||
source_account_path: str | None = None,
|
source_account_path: str | None = None,
|
||||||
cancel_text: str | None = None,
|
cancel_text: str | None = None,
|
||||||
|
description: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if address_label is not None:
|
if address_label is not None:
|
||||||
title = address_label
|
title = address_label
|
||||||
@ -402,9 +403,32 @@ async def confirm_output(
|
|||||||
else:
|
else:
|
||||||
title = TR.send__title_sending_to
|
title = TR.send__title_sending_to
|
||||||
|
|
||||||
# FIXME: not implemented
|
await raise_if_not_confirmed(
|
||||||
# use `flow_confirm_output` or `TT/TR` style?
|
trezorui_api.flow_confirm_output(
|
||||||
raise NotImplementedError
|
title=TR.words__address,
|
||||||
|
subtitle=title,
|
||||||
|
message=address,
|
||||||
|
extra=None,
|
||||||
|
amount=amount,
|
||||||
|
chunkify=chunkify,
|
||||||
|
text_mono=True,
|
||||||
|
account_title=TR.send__send_from,
|
||||||
|
account=source_account,
|
||||||
|
account_path=source_account_path,
|
||||||
|
address_item=None,
|
||||||
|
extra_item=None,
|
||||||
|
br_code=br_code,
|
||||||
|
br_name="confirm_output",
|
||||||
|
summary_items=None,
|
||||||
|
fee_items=None,
|
||||||
|
summary_title=None,
|
||||||
|
summary_br_name=None,
|
||||||
|
summary_br_code=None,
|
||||||
|
cancel_text=cancel_text,
|
||||||
|
description=description,
|
||||||
|
),
|
||||||
|
br_name=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def should_show_payment_request_details(
|
async def should_show_payment_request_details(
|
||||||
|
@ -803,7 +803,13 @@
|
|||||||
"sd_card__wanna_format": "Do you really want to format the SD card?",
|
"sd_card__wanna_format": "Do you really want to format the SD card?",
|
||||||
"sd_card__wrong_sd_card": "Wrong SD card.",
|
"sd_card__wrong_sd_card": "Wrong SD card.",
|
||||||
"send__address_path": "address path",
|
"send__address_path": "address path",
|
||||||
"send__cancel_sign": "Cancel sign",
|
"send__cancel_sign": {
|
||||||
|
"Bolt": "Cancel sign",
|
||||||
|
"Caesar": "Cancel sign",
|
||||||
|
"Delizia": "Cancel sign",
|
||||||
|
"Eckhart": "Cancel sign?"
|
||||||
|
},
|
||||||
|
"send__sign_cancelled": "Sign cancelled.",
|
||||||
"send__confirm_sending": "Sending amount",
|
"send__confirm_sending": "Sending amount",
|
||||||
"send__from_multiple_accounts": "Sending from multiple accounts.",
|
"send__from_multiple_accounts": "Sending from multiple accounts.",
|
||||||
"send__incl_transaction_fee": "incl. Transaction fee",
|
"send__incl_transaction_fee": "incl. Transaction fee",
|
||||||
@ -812,6 +818,7 @@
|
|||||||
"send__receiving_to_multisig": "Receiving to a multisig address.",
|
"send__receiving_to_multisig": "Receiving to a multisig address.",
|
||||||
"send__send_from": "Send from",
|
"send__send_from": "Send from",
|
||||||
"send__sign_transaction": "Sign transaction",
|
"send__sign_transaction": "Sign transaction",
|
||||||
|
"send__send_in_the_app": "After signing, send the transaction in the app.",
|
||||||
"send__title_confirm_sending": "Confirm sending",
|
"send__title_confirm_sending": "Confirm sending",
|
||||||
"send__title_joint_transaction": "Joint transaction",
|
"send__title_joint_transaction": "Joint transaction",
|
||||||
"send__title_receiving_to": "Receiving to",
|
"send__title_receiving_to": "Receiving to",
|
||||||
@ -1035,6 +1042,7 @@
|
|||||||
"words__receive": "Receive",
|
"words__receive": "Receive",
|
||||||
"words__recipient": "Recipient",
|
"words__recipient": "Recipient",
|
||||||
"words__recovery_share": "Recovery share",
|
"words__recovery_share": "Recovery share",
|
||||||
|
"words__send": "Send",
|
||||||
"words__settings": "Settings",
|
"words__settings": "Settings",
|
||||||
"words__sign": "Sign",
|
"words__sign": "Sign",
|
||||||
"words__signer": "Signer",
|
"words__signer": "Signer",
|
||||||
@ -1056,6 +1064,7 @@
|
|||||||
},
|
},
|
||||||
"words__unknown": "Unknown",
|
"words__unknown": "Unknown",
|
||||||
"words__unlocked": "Unlocked",
|
"words__unlocked": "Unlocked",
|
||||||
|
"words__wallet": "Wallet",
|
||||||
"words__warning": "Warning",
|
"words__warning": "Warning",
|
||||||
"words__writable": "Writable",
|
"words__writable": "Writable",
|
||||||
"words__yes": "Yes"
|
"words__yes": "Yes"
|
||||||
|
@ -1006,5 +1006,9 @@
|
|||||||
"1004": "words__receive",
|
"1004": "words__receive",
|
||||||
"1005": "reset__recovery_share_description",
|
"1005": "reset__recovery_share_description",
|
||||||
"1006": "reset__recovery_share_number",
|
"1006": "reset__recovery_share_number",
|
||||||
"1007": "words__recovery_share"
|
"1007": "words__recovery_share",
|
||||||
|
"1008": "send__send_in_the_app",
|
||||||
|
"1009": "send__sign_cancelled",
|
||||||
|
"1010": "words__send",
|
||||||
|
"1011": "words__wallet"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user