mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 14:16:06 +00:00
feat(eckhart): receive address flow
This commit is contained in:
parent
ffc821aa2d
commit
5e8a75619e
@ -52,6 +52,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_address;
|
||||
MP_QSTR_address__cancel_contact_support;
|
||||
MP_QSTR_address__cancel_receive;
|
||||
MP_QSTR_address__check_with_source;
|
||||
MP_QSTR_address__confirmed;
|
||||
MP_QSTR_address__public_key;
|
||||
MP_QSTR_address__public_key_confirmed;
|
||||
@ -780,6 +781,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_words__please_check_again;
|
||||
MP_QSTR_words__please_try_again;
|
||||
MP_QSTR_words__really_wanna;
|
||||
MP_QSTR_words__receive;
|
||||
MP_QSTR_words__recipient;
|
||||
MP_QSTR_words__settings;
|
||||
MP_QSTR_words__sign;
|
||||
|
@ -1255,7 +1255,7 @@ pub enum TranslatedString {
|
||||
words__not_recommended = 861, // "Not recommended!"
|
||||
address_details__account_info = 862, // "Account info"
|
||||
address__cancel_contact_support = 863, // "If receive address doesn't match, contact Trezor Support at trezor.io/support."
|
||||
address__cancel_receive = 864, // "Cancel receive"
|
||||
address__cancel_receive = 864, // {"Bolt": "Cancel receive", "Caesar": "Cancel receive", "Delizia": "Cancel receive", "Eckhart": "Cancel receive?"}
|
||||
address__qr_code = 865, // "QR code"
|
||||
address_details__derivation_path = 866, // "Derivation path"
|
||||
instructions__continue_in_app = 867, // "Continue in the app"
|
||||
@ -1407,6 +1407,8 @@ pub enum TranslatedString {
|
||||
reset__share_words_first = 993, // "Write down the first word from the backup."
|
||||
backup__not_recommend = 994, // "We don't recommend to skip wallet backup creation."
|
||||
words__pay_attention = 995, // "Pay attention"
|
||||
address__check_with_source = 996, // "Check the address with source."
|
||||
words__receive = 997, // "Receive"
|
||||
}
|
||||
|
||||
impl TranslatedString {
|
||||
@ -2693,7 +2695,14 @@ impl TranslatedString {
|
||||
Self::words__not_recommended => "Not recommended!",
|
||||
Self::address_details__account_info => "Account info",
|
||||
Self::address__cancel_contact_support => "If receive address doesn't match, contact Trezor Support at trezor.io/support.",
|
||||
#[cfg(feature = "layout_bolt")]
|
||||
Self::address__cancel_receive => "Cancel receive",
|
||||
#[cfg(feature = "layout_caesar")]
|
||||
Self::address__cancel_receive => "Cancel receive",
|
||||
#[cfg(feature = "layout_delizia")]
|
||||
Self::address__cancel_receive => "Cancel receive",
|
||||
#[cfg(feature = "layout_eckhart")]
|
||||
Self::address__cancel_receive => "Cancel receive?",
|
||||
Self::address__qr_code => "QR code",
|
||||
Self::address_details__derivation_path => "Derivation path",
|
||||
Self::instructions__continue_in_app => "Continue in the app",
|
||||
@ -2880,6 +2889,8 @@ impl TranslatedString {
|
||||
Self::reset__share_words_first => "Write down the first word from the backup.",
|
||||
Self::backup__not_recommend => "We don't recommend to skip wallet backup creation.",
|
||||
Self::words__pay_attention => "Pay attention",
|
||||
Self::address__check_with_source => "Check the address with source.",
|
||||
Self::words__receive => "Receive",
|
||||
}
|
||||
}
|
||||
|
||||
@ -4282,6 +4293,8 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_reset__share_words_first => Some(Self::reset__share_words_first),
|
||||
Qstr::MP_QSTR_backup__not_recommend => Some(Self::backup__not_recommend),
|
||||
Qstr::MP_QSTR_words__pay_attention => Some(Self::words__pay_attention),
|
||||
Qstr::MP_QSTR_address__check_with_source => Some(Self::address__check_with_source),
|
||||
Qstr::MP_QSTR_words__receive => Some(Self::words__receive),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
236
core/embed/rust/src/ui/layout_eckhart/flow/get_address.rs
Normal file
236
core/embed/rust/src/ui/layout_eckhart/flow/get_address.rs
Normal file
@ -0,0 +1,236 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::obj::Obj,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt},
|
||||
ComponentExt, Qr,
|
||||
},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
FlowController, FlowMsg, SwipeFlow,
|
||||
},
|
||||
geometry::{Alignment, Direction, LinearPlacement, Offset},
|
||||
layout::util::ConfirmValueParams,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::Button,
|
||||
firmware::{
|
||||
ActionBar, Header, HeaderMsg, Hint, QrScreen, TextScreen, TextScreenMsg, VerticalMenu,
|
||||
VerticalMenuScreen, VerticalMenuScreenMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
const TIMEOUT_MS: u32 = 2000;
|
||||
const ITEM_PADDING: i16 = 16;
|
||||
const GROUP_PADDING: i16 = 20;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum GetAddress {
|
||||
Address,
|
||||
Confirmed,
|
||||
Menu,
|
||||
QrCode,
|
||||
AccountInfo,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl FlowController for GetAddress {
|
||||
#[inline]
|
||||
fn index(&'static self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
fn handle_swipe(&'static self, direction: Direction) -> Decision {
|
||||
match (self, direction) {
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
|
||||
match (self, msg) {
|
||||
(Self::Address, FlowMsg::Info) => Self::Menu.goto(),
|
||||
(Self::Address, FlowMsg::Confirmed) => Self::Confirmed.goto(),
|
||||
(Self::Confirmed, _) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => Self::QrCode.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Choice(1)) => Self::AccountInfo.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Choice(2)) => Self::Cancel.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Address.swipe_right(),
|
||||
(Self::QrCode, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||
(Self::AccountInfo, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||
(Self::Cancel, FlowMsg::Cancelled) => Self::Address.goto(),
|
||||
(Self::Cancel, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_get_address(
|
||||
title: TString<'static>,
|
||||
_description: Option<TString<'static>>,
|
||||
extra: Option<TString<'static>>,
|
||||
address: Obj, // TODO: get rid of Obj
|
||||
chunkify: bool,
|
||||
address_qr: TString<'static>,
|
||||
case_sensitive: bool,
|
||||
account: Option<TString<'static>>,
|
||||
path: Option<TString<'static>>,
|
||||
_xpubs: Obj, // TODO: get rid of Obj
|
||||
title_success: TString<'static>,
|
||||
_br_code: u16,
|
||||
_br_name: TString<'static>,
|
||||
) -> Result<SwipeFlow, error::Error> {
|
||||
// Address
|
||||
let flow_title: TString = TR::words__receive.into();
|
||||
let paragraphs = ConfirmValueParams {
|
||||
description: title,
|
||||
extra: extra.unwrap_or_else(|| "".into()),
|
||||
value: address.try_into()?,
|
||||
font: if chunkify {
|
||||
let address: TString = address.try_into()?;
|
||||
theme::get_chunkified_text_style(address.len())
|
||||
} else {
|
||||
&theme::TEXT_MONO_ADDRESS
|
||||
},
|
||||
description_font: &theme::TEXT_MEDIUM_EXTRA_LIGHT,
|
||||
extra_font: &theme::TEXT_SMALL,
|
||||
}
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical().with_spacing(14));
|
||||
let content_address = TextScreen::new(paragraphs)
|
||||
.with_header(Header::new(flow_title).with_menu_button())
|
||||
.with_action_bar(ActionBar::new_single(
|
||||
Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()),
|
||||
))
|
||||
.with_hint(Hint::new_instruction(
|
||||
TR::address__check_with_source,
|
||||
Some(theme::ICON_INFO),
|
||||
))
|
||||
.map(|msg| match msg {
|
||||
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||
});
|
||||
|
||||
let content_confirmed =
|
||||
TextScreen::new(Paragraph::new(&theme::TEXT_REGULAR, title_success).into_paragraphs())
|
||||
.with_header(
|
||||
Header::new(TR::words__title_done.into())
|
||||
.with_icon(theme::ICON_DONE, theme::GREEN_LIGHT)
|
||||
.with_text_style(theme::label_title_confirm()),
|
||||
)
|
||||
.with_action_bar(ActionBar::new_timeout(
|
||||
Button::with_text(TR::instructions__continue_in_app.into()),
|
||||
TIMEOUT_MS,
|
||||
))
|
||||
.map(|_| Some(FlowMsg::Confirmed));
|
||||
|
||||
// Menu
|
||||
|
||||
let content_menu = VerticalMenuScreen::new(
|
||||
VerticalMenu::empty()
|
||||
.item(
|
||||
Button::with_text(TR::address__qr_code.into())
|
||||
.styled(theme::menu_item_title())
|
||||
.with_text_align(Alignment::Start)
|
||||
.with_content_offset(Offset::x(12)),
|
||||
)
|
||||
.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)),
|
||||
)
|
||||
.item(
|
||||
Button::with_text(TR::buttons__cancel.into())
|
||||
.styled(theme::menu_item_title_orange())
|
||||
.with_text_align(Alignment::Start)
|
||||
.with_content_offset(Offset::x(12)),
|
||||
),
|
||||
)
|
||||
.with_header(
|
||||
Header::new(flow_title)
|
||||
.with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled),
|
||||
)
|
||||
.map(|msg| match msg {
|
||||
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
|
||||
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
// QrCode
|
||||
let content_qr = QrScreen::new(address_qr.map(|s| Qr::new(s, case_sensitive))?)
|
||||
.with_header(
|
||||
Header::new(TR::address_details__title_receive_address.into())
|
||||
.with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled),
|
||||
)
|
||||
.map(|_| Some(FlowMsg::Cancelled));
|
||||
|
||||
// AccountInfo
|
||||
let mut para = ParagraphVecShort::new();
|
||||
if let Some(a) = account {
|
||||
para.add(Paragraph::new::<TString>(
|
||||
&theme::TEXT_SMALL_LIGHT,
|
||||
TR::words__account.into(),
|
||||
));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO_EXTRA_LIGHT, a).with_top_padding(ITEM_PADDING));
|
||||
}
|
||||
|
||||
if let Some(p) = path {
|
||||
para.add(
|
||||
Paragraph::new::<TString>(
|
||||
&theme::TEXT_SMALL_LIGHT,
|
||||
TR::address_details__derivation_path.into(),
|
||||
)
|
||||
.with_top_padding(GROUP_PADDING),
|
||||
);
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO_EXTRA_LIGHT, p).with_top_padding(ITEM_PADDING));
|
||||
}
|
||||
|
||||
let content_account = TextScreen::new(
|
||||
para.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
)
|
||||
.with_header(
|
||||
Header::new(TR::address_details__account_info.into())
|
||||
.with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled),
|
||||
)
|
||||
.map(|_| Some(FlowMsg::Cancelled));
|
||||
|
||||
// Cancel
|
||||
|
||||
let content_cancel_info = TextScreen::new(
|
||||
Paragraph::new(&theme::TEXT_REGULAR, TR::address__cancel_receive)
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
)
|
||||
.with_header(Header::new(flow_title))
|
||||
.with_action_bar(ActionBar::new_double(
|
||||
Button::with_icon(theme::ICON_CHEVRON_LEFT),
|
||||
Button::with_text(TR::buttons__cancel.into()).styled(theme::button_cancel()),
|
||||
))
|
||||
.with_hint(Hint::new_instruction(
|
||||
TR::address__cancel_contact_support,
|
||||
Some(theme::ICON_INFO),
|
||||
))
|
||||
.map(|msg| match msg {
|
||||
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mut res = SwipeFlow::new(&GetAddress::Address)?;
|
||||
res.add_page(&GetAddress::Address, content_address)?
|
||||
.add_page(&GetAddress::Confirmed, content_confirmed)?
|
||||
.add_page(&GetAddress::Menu, content_menu)?
|
||||
.add_page(&GetAddress::QrCode, content_qr)?
|
||||
.add_page(&GetAddress::AccountInfo, content_account)?
|
||||
.add_page(&GetAddress::Cancel, content_cancel_info)?;
|
||||
Ok(res)
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
pub mod confirm_reset;
|
||||
pub mod get_address;
|
||||
pub mod prompt_backup;
|
||||
pub mod request_passphrase;
|
||||
pub mod show_share_words;
|
||||
|
||||
pub use confirm_reset::new_confirm_reset;
|
||||
pub use get_address::GetAddress;
|
||||
pub use prompt_backup::PromptBackup;
|
||||
pub use request_passphrase::RequestPassphrase;
|
||||
pub use show_share_words::new_show_share_words_flow;
|
||||
|
@ -335,21 +335,36 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn flow_get_address(
|
||||
_address: Obj,
|
||||
_title: TString<'static>,
|
||||
_description: Option<TString<'static>>,
|
||||
_extra: Option<TString<'static>>,
|
||||
_chunkify: bool,
|
||||
_address_qr: TString<'static>,
|
||||
_case_sensitive: bool,
|
||||
_account: Option<TString<'static>>,
|
||||
_path: Option<TString<'static>>,
|
||||
_xpubs: Obj,
|
||||
_title_success: TString<'static>,
|
||||
_br_code: u16,
|
||||
_br_name: TString<'static>,
|
||||
address: Obj,
|
||||
title: TString<'static>,
|
||||
description: Option<TString<'static>>,
|
||||
extra: Option<TString<'static>>,
|
||||
chunkify: bool,
|
||||
address_qr: TString<'static>,
|
||||
case_sensitive: bool,
|
||||
account: Option<TString<'static>>,
|
||||
path: Option<TString<'static>>,
|
||||
xpubs: Obj,
|
||||
title_success: TString<'static>,
|
||||
br_code: u16,
|
||||
br_name: TString<'static>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
||||
let flow = flow::get_address::new_get_address(
|
||||
title,
|
||||
description,
|
||||
extra,
|
||||
address,
|
||||
chunkify,
|
||||
address_qr,
|
||||
case_sensitive,
|
||||
account,
|
||||
path,
|
||||
xpubs,
|
||||
title_success,
|
||||
br_code,
|
||||
br_name,
|
||||
)?;
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
fn multiple_pages_texts(
|
||||
|
@ -10,6 +10,7 @@ class TR:
|
||||
addr_mismatch__xpub_mismatch: str = "XPUB mismatch?"
|
||||
address__cancel_contact_support: str = "If receive address doesn't match, contact Trezor Support at trezor.io/support."
|
||||
address__cancel_receive: str = "Cancel receive"
|
||||
address__check_with_source: str = "Check the address with source."
|
||||
address__confirmed: str = "Receive address confirmed"
|
||||
address__public_key: str = "Public key"
|
||||
address__public_key_confirmed: str = "Public key confirmed"
|
||||
@ -968,6 +969,7 @@ class TR:
|
||||
words__please_check_again: str = "Please check again"
|
||||
words__please_try_again: str = "Please try again"
|
||||
words__really_wanna: str = "Do you really want to"
|
||||
words__receive: str = "Receive"
|
||||
words__recipient: str = "Recipient"
|
||||
words__settings: str = "Settings"
|
||||
words__sign: str = "Sign"
|
||||
|
@ -11,7 +11,12 @@
|
||||
"addr_mismatch__wrong_derivation_path": "Wrong derivation path for selected account.",
|
||||
"addr_mismatch__xpub_mismatch": "XPUB mismatch?",
|
||||
"address__cancel_contact_support": "If receive address doesn't match, contact Trezor Support at trezor.io/support.",
|
||||
"address__cancel_receive": "Cancel receive",
|
||||
"address__cancel_receive": {
|
||||
"Bolt": "Cancel receive",
|
||||
"Caesar": "Cancel receive",
|
||||
"Delizia": "Cancel receive",
|
||||
"Eckhart": "Cancel receive?"
|
||||
},
|
||||
"address__confirmed": "Receive address confirmed",
|
||||
"address__public_key": "Public key",
|
||||
"address__public_key_confirmed": "Public key confirmed",
|
||||
@ -19,6 +24,7 @@
|
||||
"address__title_cosigner": "Cosigner",
|
||||
"address__title_receive_address": "Receive address",
|
||||
"address__title_yours": "Yours",
|
||||
"address__check_with_source": "Check the address with source.",
|
||||
"address_details__account_info": "Account info",
|
||||
"address_details__derivation_path": "Derivation path",
|
||||
"address_details__derivation_path_colon": "Derivation path:",
|
||||
@ -404,7 +410,12 @@
|
||||
"inputs__return": "RETURN",
|
||||
"inputs__show": "SHOW",
|
||||
"inputs__space": "SPACE",
|
||||
"instructions__continue_holding": {"Bolt": "", "Caesar": "", "Delizia": "Continue\nholding", "Eckhart": "Keep holding"},
|
||||
"instructions__continue_holding": {
|
||||
"Bolt": "",
|
||||
"Caesar": "",
|
||||
"Delizia": "Continue\nholding",
|
||||
"Eckhart": "Keep holding"
|
||||
},
|
||||
"instructions__continue_in_app": "Continue in the app",
|
||||
"instructions__enter_next_share": "Enter next share",
|
||||
"instructions__hold_to_confirm": "Hold to confirm",
|
||||
@ -1010,6 +1021,7 @@
|
||||
"words__please_check_again": "Please check again",
|
||||
"words__please_try_again": "Please try again",
|
||||
"words__really_wanna": "Do you really want to",
|
||||
"words__receive": "Receive",
|
||||
"words__recipient": "Recipient",
|
||||
"words__settings": "Settings",
|
||||
"words__sign": "Sign",
|
||||
|
@ -994,5 +994,7 @@
|
||||
"992": "nostr__event_kind_template",
|
||||
"993": "reset__share_words_first",
|
||||
"994": "backup__not_recommend",
|
||||
"995": "words__pay_attention"
|
||||
"995": "words__pay_attention",
|
||||
"996": "address__check_with_source",
|
||||
"997": "words__receive"
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "f00c1c603fb911b9b65dbe30261310c4ed90ac1ff90f9c5e52e0ce34acce2745",
|
||||
"datetime": "2025-03-06T16:38:48.918299",
|
||||
"commit": "6509d2b3f29d5bd3ee24a3843a6f3e6d9877cf30"
|
||||
"merkle_root": "e89cf3cbdee6db85be61f81f9efbfa2a7f0e025f1472165cb0e6688f3acfdb7c",
|
||||
"datetime": "2025-03-08T13:14:44.068121",
|
||||
"commit": "b624879561ee577733b3d954b4d242a6e4f21d0d"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user