mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-19 21:08:07 +00:00
feat(core/ui): T3T1 receive flow
[no changelog]
This commit is contained in:
parent
1028c3500f
commit
5020868c2c
@ -30,14 +30,21 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_addr_mismatch__wrong_derivation_path;
|
||||
MP_QSTR_addr_mismatch__xpub_mismatch;
|
||||
MP_QSTR_address;
|
||||
MP_QSTR_address__cancel_contact_support;
|
||||
MP_QSTR_address__cancel_receive;
|
||||
MP_QSTR_address__confirmed;
|
||||
MP_QSTR_address__public_key;
|
||||
MP_QSTR_address__qr_code;
|
||||
MP_QSTR_address__title_cosigner;
|
||||
MP_QSTR_address__title_receive_address;
|
||||
MP_QSTR_address__title_yours;
|
||||
MP_QSTR_address_details__account_info;
|
||||
MP_QSTR_address_details__derivation_path;
|
||||
MP_QSTR_address_details__derivation_path_colon;
|
||||
MP_QSTR_address_details__title_receive_address;
|
||||
MP_QSTR_address_details__title_receiving_to;
|
||||
MP_QSTR_address_label;
|
||||
MP_QSTR_address_qr;
|
||||
MP_QSTR_address_title;
|
||||
MP_QSTR_allow_cancel;
|
||||
MP_QSTR_altcoin_tx_summary;
|
||||
@ -210,6 +217,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_flow_get_address;
|
||||
MP_QSTR_flow_prompt_backup;
|
||||
MP_QSTR_flow_show_share_words;
|
||||
MP_QSTR_flow_warning_hi_prio;
|
||||
MP_QSTR_get_language;
|
||||
MP_QSTR_hold;
|
||||
MP_QSTR_hold_danger;
|
||||
@ -237,6 +245,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_inputs__return;
|
||||
MP_QSTR_inputs__show;
|
||||
MP_QSTR_inputs__space;
|
||||
MP_QSTR_instructions__continue_in_app;
|
||||
MP_QSTR_instructions__hold_to_confirm;
|
||||
MP_QSTR_instructions__swipe_up;
|
||||
MP_QSTR_instructions__tap_to_confirm;
|
||||
@ -623,6 +632,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_words__array_of;
|
||||
MP_QSTR_words__blockhash;
|
||||
MP_QSTR_words__buying;
|
||||
MP_QSTR_words__cancel_and_exit;
|
||||
MP_QSTR_words__confirm;
|
||||
MP_QSTR_words__confirm_fee;
|
||||
MP_QSTR_words__contains;
|
||||
|
@ -20,7 +20,7 @@ pub enum TranslatedString {
|
||||
address__title_cosigner = 7, // "Cosigner"
|
||||
address__title_receive_address = 8, // "Receive address"
|
||||
address__title_yours = 9, // "Yours"
|
||||
address_details__derivation_path = 10, // "Derivation path:"
|
||||
address_details__derivation_path_colon = 10, // "Derivation path:"
|
||||
address_details__title_receive_address = 11, // "Receive address"
|
||||
address_details__title_receiving_to = 12, // "Receiving to"
|
||||
authenticate__confirm_template = 13, // "Allow connected computer to confirm your {0} is genuine?"
|
||||
@ -1260,6 +1260,14 @@ pub enum TranslatedString {
|
||||
reset__check_backup_instructions = 859, // "Let's do a quick check of your backup."
|
||||
words__instructions = 860, // "Instructions"
|
||||
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__qr_code = 865, // "QR code"
|
||||
address_details__derivation_path = 866, // "Derivation path"
|
||||
instructions__continue_in_app = 867, // "Continue in the app"
|
||||
words__cancel_and_exit = 868, // "Cancel and exit"
|
||||
address__confirmed = 869, // "Receive address confirmed"
|
||||
}
|
||||
|
||||
impl TranslatedString {
|
||||
@ -1275,7 +1283,7 @@ impl TranslatedString {
|
||||
Self::address__title_cosigner => "Cosigner",
|
||||
Self::address__title_receive_address => "Receive address",
|
||||
Self::address__title_yours => "Yours",
|
||||
Self::address_details__derivation_path => "Derivation path:",
|
||||
Self::address_details__derivation_path_colon => "Derivation path:",
|
||||
Self::address_details__title_receive_address => "Receive address",
|
||||
Self::address_details__title_receiving_to => "Receiving to",
|
||||
Self::authenticate__confirm_template => "Allow connected computer to confirm your {0} is genuine?",
|
||||
@ -2515,6 +2523,14 @@ impl TranslatedString {
|
||||
Self::reset__check_backup_instructions => "Let's do a quick check of your backup.",
|
||||
Self::words__instructions => "Instructions",
|
||||
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.",
|
||||
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",
|
||||
Self::words__cancel_and_exit => "Cancel and exit",
|
||||
Self::address__confirmed => "Receive address confirmed",
|
||||
}
|
||||
}
|
||||
|
||||
@ -2531,7 +2547,7 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_address__title_cosigner => Some(Self::address__title_cosigner),
|
||||
Qstr::MP_QSTR_address__title_receive_address => Some(Self::address__title_receive_address),
|
||||
Qstr::MP_QSTR_address__title_yours => Some(Self::address__title_yours),
|
||||
Qstr::MP_QSTR_address_details__derivation_path => Some(Self::address_details__derivation_path),
|
||||
Qstr::MP_QSTR_address_details__derivation_path_colon => Some(Self::address_details__derivation_path_colon),
|
||||
Qstr::MP_QSTR_address_details__title_receive_address => Some(Self::address_details__title_receive_address),
|
||||
Qstr::MP_QSTR_address_details__title_receiving_to => Some(Self::address_details__title_receiving_to),
|
||||
Qstr::MP_QSTR_authenticate__confirm_template => Some(Self::authenticate__confirm_template),
|
||||
@ -3771,6 +3787,14 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_reset__check_backup_instructions => Some(Self::reset__check_backup_instructions),
|
||||
Qstr::MP_QSTR_words__instructions => Some(Self::words__instructions),
|
||||
Qstr::MP_QSTR_words__not_recommended => Some(Self::words__not_recommended),
|
||||
Qstr::MP_QSTR_address_details__account_info => Some(Self::address_details__account_info),
|
||||
Qstr::MP_QSTR_address__cancel_contact_support => Some(Self::address__cancel_contact_support),
|
||||
Qstr::MP_QSTR_address__cancel_receive => Some(Self::address__cancel_receive),
|
||||
Qstr::MP_QSTR_address__qr_code => Some(Self::address__qr_code),
|
||||
Qstr::MP_QSTR_address_details__derivation_path => Some(Self::address_details__derivation_path),
|
||||
Qstr::MP_QSTR_instructions__continue_in_app => Some(Self::instructions__continue_in_app),
|
||||
Qstr::MP_QSTR_words__cancel_and_exit => Some(Self::words__cancel_and_exit),
|
||||
Qstr::MP_QSTR_address__confirmed => Some(Self::address__confirmed),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ pub struct Child<T> {
|
||||
}
|
||||
|
||||
impl<T> Child<T> {
|
||||
pub fn new(component: T) -> Self {
|
||||
pub const fn new(component: T) -> Self {
|
||||
Self {
|
||||
component,
|
||||
marked_for_paint: true,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Component, Event, EventCtx, Never};
|
||||
use crate::ui::{geometry::Rect, shape::Renderer};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Empty;
|
||||
|
||||
impl Component for Empty {
|
||||
|
@ -18,7 +18,7 @@ pub struct Label<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Label<'a> {
|
||||
pub fn new(text: TString<'a>, align: Alignment, style: TextStyle) -> Self {
|
||||
pub const fn new(text: TString<'a>, align: Alignment, style: TextStyle) -> Self {
|
||||
Self {
|
||||
text,
|
||||
layout: TextLayout::new(style).with_align(align),
|
||||
@ -26,34 +26,34 @@ impl<'a> Label<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_aligned(text: TString<'a>, style: TextStyle) -> Self {
|
||||
pub const fn left_aligned(text: TString<'a>, style: TextStyle) -> Self {
|
||||
Self::new(text, Alignment::Start, style)
|
||||
}
|
||||
|
||||
pub fn right_aligned(text: TString<'a>, style: TextStyle) -> Self {
|
||||
pub const fn right_aligned(text: TString<'a>, style: TextStyle) -> Self {
|
||||
Self::new(text, Alignment::End, style)
|
||||
}
|
||||
|
||||
pub fn centered(text: TString<'a>, style: TextStyle) -> Self {
|
||||
pub const fn centered(text: TString<'a>, style: TextStyle) -> Self {
|
||||
Self::new(text, Alignment::Center, style)
|
||||
}
|
||||
|
||||
pub fn top_aligned(mut self) -> Self {
|
||||
pub const fn top_aligned(mut self) -> Self {
|
||||
self.vertical = Alignment::Start;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vertically_centered(mut self) -> Self {
|
||||
pub const fn vertically_centered(mut self) -> Self {
|
||||
self.vertical = Alignment::Center;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bottom_aligned(mut self) -> Self {
|
||||
pub const fn bottom_aligned(mut self) -> Self {
|
||||
self.vertical = Alignment::End;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn styled(mut self, style: TextStyle) -> Self {
|
||||
pub const fn styled(mut self, style: TextStyle) -> Self {
|
||||
self.layout.style = style;
|
||||
self
|
||||
}
|
||||
@ -74,7 +74,7 @@ impl<'a> Label<'a> {
|
||||
self.layout.bounds
|
||||
}
|
||||
|
||||
pub fn alignment(&self) -> Alignment {
|
||||
pub const fn alignment(&self) -> Alignment {
|
||||
self.layout.align
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ impl TextStyle {
|
||||
impl TextLayout {
|
||||
/// Create a new text layout, with empty size and default text parameters
|
||||
/// filled from `T`.
|
||||
pub fn new(style: TextStyle) -> Self {
|
||||
pub const fn new(style: TextStyle) -> Self {
|
||||
Self {
|
||||
bounds: Rect::zero(),
|
||||
padding_top: 0,
|
||||
@ -210,12 +210,12 @@ impl TextLayout {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_bounds(mut self, bounds: Rect) -> Self {
|
||||
pub const fn with_bounds(mut self, bounds: Rect) -> Self {
|
||||
self.bounds = bounds;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_align(mut self, align: Alignment) -> Self {
|
||||
pub const fn with_align(mut self, align: Alignment) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use crate::{
|
||||
/// consumption and conversion time.
|
||||
pub const MAX_HEX_CHARS_ON_SCREEN: usize = 256;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum StrOrBytes {
|
||||
Str(TString<'static>),
|
||||
Bytes(Obj),
|
||||
@ -55,6 +56,7 @@ impl TryFrom<Obj> for StrOrBytes {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfirmBlob {
|
||||
pub description: TString<'static>,
|
||||
pub extra: TString<'static>,
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
|
||||
Component, Event, EventCtx, Paginate, Qr,
|
||||
Component, Event, EventCtx, Paginate,
|
||||
},
|
||||
geometry::Rect,
|
||||
shape::Renderer,
|
||||
@ -18,8 +18,8 @@ use super::{theme, Frame, FrameMsg};
|
||||
|
||||
const MAX_XPUBS: usize = 16;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AddressDetails {
|
||||
qr_code: Frame<Qr>,
|
||||
details: Frame<Paragraphs<ParagraphVecShort<'static>>>,
|
||||
xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
|
||||
xpubs: Vec<(TString<'static>, TString<'static>), MAX_XPUBS>,
|
||||
@ -29,43 +29,38 @@ pub struct AddressDetails {
|
||||
|
||||
impl AddressDetails {
|
||||
pub fn new(
|
||||
qr_title: TString<'static>,
|
||||
qr_address: TString<'static>,
|
||||
case_sensitive: bool,
|
||||
details_title: TString<'static>,
|
||||
account: Option<TString<'static>>,
|
||||
path: Option<TString<'static>>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut para = ParagraphVecShort::new();
|
||||
if let Some(a) = account {
|
||||
para.add(Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
TR::words__account_colon,
|
||||
para.add(Paragraph::new::<TString>(
|
||||
&theme::TEXT_SUB_GREY,
|
||||
TR::words__account.into(),
|
||||
));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, a));
|
||||
}
|
||||
if account.is_some() & path.is_some() {
|
||||
para.add(Paragraph::new(
|
||||
&theme::TEXT_SUB_GREY,
|
||||
TString::from_str(" "),
|
||||
));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO, a));
|
||||
}
|
||||
if let Some(p) = path {
|
||||
para.add(Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
TR::address_details__derivation_path,
|
||||
para.add(Paragraph::new::<TString>(
|
||||
&theme::TEXT_SUB_GREY,
|
||||
TR::address_details__derivation_path.into(),
|
||||
));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO, p));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, p));
|
||||
}
|
||||
let result = Self {
|
||||
qr_code: Frame::left_aligned(
|
||||
qr_title,
|
||||
qr_address
|
||||
.map(|s| Qr::new(s, case_sensitive))?
|
||||
.with_border(7),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_border(theme::borders_horizontal_scroll()),
|
||||
details: Frame::left_aligned(details_title, para.into_paragraphs())
|
||||
.with_cancel_button()
|
||||
.with_border(theme::borders_horizontal_scroll()),
|
||||
xpub_view: Frame::left_aligned(
|
||||
" \n ".into(),
|
||||
Paragraph::new(&theme::TEXT_MONO, "").into_paragraphs(),
|
||||
Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, "").into_paragraphs(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_border(theme::borders_horizontal_scroll()),
|
||||
@ -121,13 +116,13 @@ impl AddressDetails {
|
||||
impl Paginate for AddressDetails {
|
||||
fn page_count(&mut self) -> usize {
|
||||
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
|
||||
2usize.saturating_add(total_xpub_pages.into())
|
||||
1usize.saturating_add(total_xpub_pages.into())
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: usize) {
|
||||
self.current_page = to_page;
|
||||
if to_page > 1 {
|
||||
let i = to_page - 2;
|
||||
if to_page > 0 {
|
||||
let i = to_page - 1;
|
||||
let (xpub_index, xpub_page) = self.lookup(i);
|
||||
self.switch_xpub(xpub_index, xpub_page);
|
||||
}
|
||||
@ -138,7 +133,6 @@ impl Component for AddressDetails {
|
||||
type Msg = ();
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.qr_code.place(bounds);
|
||||
self.details.place(bounds);
|
||||
self.xpub_view.place(bounds);
|
||||
|
||||
@ -153,8 +147,7 @@ impl Component for AddressDetails {
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let msg = match self.current_page {
|
||||
0 => self.qr_code.event(ctx, event),
|
||||
1 => self.details.event(ctx, event),
|
||||
0 => self.details.event(ctx, event),
|
||||
_ => self.xpub_view.event(ctx, event),
|
||||
};
|
||||
match msg {
|
||||
@ -165,16 +158,14 @@ impl Component for AddressDetails {
|
||||
|
||||
fn paint(&mut self) {
|
||||
match self.current_page {
|
||||
0 => self.qr_code.paint(),
|
||||
1 => self.details.paint(),
|
||||
0 => self.details.paint(),
|
||||
_ => self.xpub_view.paint(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
match self.current_page {
|
||||
0 => self.qr_code.render(target),
|
||||
1 => self.details.render(target),
|
||||
0 => self.details.render(target),
|
||||
_ => self.xpub_view.render(target),
|
||||
}
|
||||
}
|
||||
@ -182,8 +173,7 @@ impl Component for AddressDetails {
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
match self.current_page {
|
||||
0 => self.qr_code.bounds(sink),
|
||||
1 => self.details.bounds(sink),
|
||||
0 => self.details.bounds(sink),
|
||||
_ => self.xpub_view.bounds(sink),
|
||||
}
|
||||
}
|
||||
@ -194,8 +184,7 @@ impl crate::trace::Trace for AddressDetails {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("AddressDetails");
|
||||
match self.current_page {
|
||||
0 => t.child("qr_code", &self.qr_code),
|
||||
1 => t.child("details", &self.details),
|
||||
0 => t.child("details", &self.details),
|
||||
_ => t.child("xpub_view", &self.xpub_view),
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl<T> Frame<T>
|
||||
where
|
||||
T: Component,
|
||||
{
|
||||
pub fn new(alignment: Alignment, title: TString<'static>, content: T) -> Self {
|
||||
pub const fn new(alignment: Alignment, title: TString<'static>, content: T) -> Self {
|
||||
Self {
|
||||
title: Child::new(
|
||||
Label::new(title, alignment, theme::label_title_main()).vertically_centered(),
|
||||
@ -49,19 +49,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_aligned(title: TString<'static>, content: T) -> Self {
|
||||
pub const fn left_aligned(title: TString<'static>, content: T) -> Self {
|
||||
Self::new(Alignment::Start, title, content)
|
||||
}
|
||||
|
||||
pub fn right_aligned(title: TString<'static>, content: T) -> Self {
|
||||
pub const fn right_aligned(title: TString<'static>, content: T) -> Self {
|
||||
Self::new(Alignment::End, title, content)
|
||||
}
|
||||
|
||||
pub fn centered(title: TString<'static>, content: T) -> Self {
|
||||
pub const fn centered(title: TString<'static>, content: T) -> Self {
|
||||
Self::new(Alignment::Center, title, content)
|
||||
}
|
||||
|
||||
pub fn with_border(mut self, border: Insets) -> Self {
|
||||
pub const fn with_border(mut self, border: Insets) -> Self {
|
||||
self.border = border;
|
||||
self
|
||||
}
|
||||
@ -130,6 +130,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_danger(self) -> Self {
|
||||
self.button_styled(theme::button_danger())
|
||||
.title_styled(theme::label_title_danger())
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &T {
|
||||
self.content.inner()
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ use super::{theme, Swipe, SwipeDirection};
|
||||
|
||||
/// Component showing status of an operation. Most typically embedded as a
|
||||
/// content of a Frame and showing success (checkmark with a circle around).
|
||||
#[derive(Clone)]
|
||||
pub struct StatusScreen {
|
||||
area: Rect,
|
||||
icon: Icon,
|
||||
@ -18,6 +19,7 @@ pub struct StatusScreen {
|
||||
dismiss_type: DismissType,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum DismissType {
|
||||
SwipeUp(Swipe),
|
||||
Timeout(Timeout),
|
||||
@ -62,6 +64,15 @@ impl StatusScreen {
|
||||
DismissType::SwipeUp(Swipe::new().up()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_neutral_timeout() -> Self {
|
||||
Self::new(
|
||||
theme::ICON_SIMPLE_CHECKMARK,
|
||||
theme::GREY_EXTRA_LIGHT,
|
||||
theme::GREY_DARK,
|
||||
DismissType::Timeout(Timeout::new(TIMEOUT_MS)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for StatusScreen {
|
||||
|
@ -10,6 +10,7 @@ use super::theme;
|
||||
|
||||
pub use crate::ui::component::SwipeDirection;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Swipe {
|
||||
pub area: Rect,
|
||||
pub allow_up: bool,
|
||||
|
@ -1,33 +1,41 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::{iter::IterBuf, qstr::Qstr},
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
image::BlendedImage,
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
ComponentExt, Qr, SwipeDirection, Timeout,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, Paragraphs},
|
||||
ComponentExt, Qr, SwipeDirection,
|
||||
},
|
||||
flow::{
|
||||
base::Decision, flow_store, FlowMsg, FlowState, FlowStore, IgnoreSwipe, SwipeFlow,
|
||||
SwipePage,
|
||||
},
|
||||
layout::util::ConfirmBlob,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, IconDialog, VerticalMenu, VerticalMenuChoiceMsg},
|
||||
component::{
|
||||
AddressDetails, CancelInfoConfirmMsg, Frame, FrameMsg, PromptScreen, StatusScreen,
|
||||
VerticalMenu, VerticalMenuChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
const LONGSTRING: &'static str = "https://youtu.be/iFkEs4GNgfc?si=Jl4UZSIAYGVcLQKohttps://youtu.be/iFkEs4GNgfc?si=Jl4UZSIAYGVcLQKohttps://youtu.be/iFkEs4GNgfc?si=Jl4UZSIAYGVcLQKohttps://youtu.be/iFkEs4GNgfc?si=Jl4UZSIAYGVcLQKohttps://youtu.be/iFkEs4GNgfc?si=Jl4UZSIAYGVcLQKo";
|
||||
const QR_BORDER: i16 = 4;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
pub enum GetAddress {
|
||||
Address,
|
||||
Tap,
|
||||
Confirmed,
|
||||
Menu,
|
||||
QrCode,
|
||||
AccountInfo,
|
||||
Cancel,
|
||||
Success,
|
||||
CancelTap,
|
||||
}
|
||||
|
||||
impl FlowState for GetAddress {
|
||||
@ -36,8 +44,9 @@ impl FlowState for GetAddress {
|
||||
(GetAddress::Address, SwipeDirection::Left) => {
|
||||
Decision::Goto(GetAddress::Menu, direction)
|
||||
}
|
||||
(GetAddress::Address, SwipeDirection::Up) => {
|
||||
Decision::Goto(GetAddress::Success, direction)
|
||||
(GetAddress::Address, SwipeDirection::Up) => Decision::Goto(GetAddress::Tap, direction),
|
||||
(GetAddress::Tap, SwipeDirection::Down) => {
|
||||
Decision::Goto(GetAddress::Address, direction)
|
||||
}
|
||||
(GetAddress::Menu, SwipeDirection::Right) => {
|
||||
Decision::Goto(GetAddress::Address, direction)
|
||||
@ -48,7 +57,15 @@ impl FlowState for GetAddress {
|
||||
(GetAddress::AccountInfo, SwipeDirection::Right) => {
|
||||
Decision::Goto(GetAddress::Menu, direction)
|
||||
}
|
||||
(GetAddress::Cancel, SwipeDirection::Up) => Decision::Return(FlowMsg::Cancelled),
|
||||
(GetAddress::Cancel, SwipeDirection::Up) => {
|
||||
Decision::Goto(GetAddress::CancelTap, direction)
|
||||
}
|
||||
(GetAddress::Cancel, SwipeDirection::Right) => {
|
||||
Decision::Goto(GetAddress::Menu, direction)
|
||||
}
|
||||
(GetAddress::CancelTap, SwipeDirection::Down) => {
|
||||
Decision::Goto(GetAddress::Cancel, direction)
|
||||
}
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
@ -59,6 +76,12 @@ impl FlowState for GetAddress {
|
||||
Decision::Goto(GetAddress::Menu, SwipeDirection::Left)
|
||||
}
|
||||
|
||||
(GetAddress::Tap, FlowMsg::Confirmed) => {
|
||||
Decision::Goto(GetAddress::Confirmed, SwipeDirection::Up)
|
||||
}
|
||||
|
||||
(GetAddress::Confirmed, _) => Decision::Return(FlowMsg::Confirmed),
|
||||
|
||||
(GetAddress::Menu, FlowMsg::Choice(0)) => {
|
||||
Decision::Goto(GetAddress::QrCode, SwipeDirection::Left)
|
||||
}
|
||||
@ -87,14 +110,19 @@ impl FlowState for GetAddress {
|
||||
Decision::Goto(GetAddress::Menu, SwipeDirection::Right)
|
||||
}
|
||||
|
||||
(GetAddress::Success, _) => Decision::Return(FlowMsg::Confirmed),
|
||||
(GetAddress::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled),
|
||||
|
||||
(GetAddress::CancelTap, FlowMsg::Cancelled) => {
|
||||
Decision::Goto(GetAddress::Menu, SwipeDirection::Right)
|
||||
}
|
||||
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{buffer::StrBuffer, map::Map, obj::Obj, util},
|
||||
micropython::{map::Map, obj::Obj, util},
|
||||
ui::layout::obj::LayoutObj,
|
||||
};
|
||||
|
||||
@ -103,83 +131,132 @@ pub extern "C" fn new_get_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
}
|
||||
|
||||
impl GetAddress {
|
||||
fn new(_args: &[Obj], _kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
fn new(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: TString = "Receive address".into(); // TODO: address__title_receive_address w/o uppercase
|
||||
let description: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let extra: Option<TString> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||
let address: Obj = kwargs.get(Qstr::MP_QSTR_address)?;
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
|
||||
let address_qr: TString = kwargs.get(Qstr::MP_QSTR_address_qr)?.try_into()?;
|
||||
let case_sensitive: bool = kwargs.get(Qstr::MP_QSTR_case_sensitive)?.try_into()?;
|
||||
|
||||
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
||||
let path: Option<TString> = kwargs.get(Qstr::MP_QSTR_path)?.try_into_option()?;
|
||||
let xpubs: Obj = kwargs.get(Qstr::MP_QSTR_xpubs)?;
|
||||
|
||||
// Address
|
||||
let data_style = if chunkify {
|
||||
let address: TString = address.try_into()?;
|
||||
theme::get_chunkified_text_style(address.len())
|
||||
} else {
|
||||
&theme::TEXT_MONO
|
||||
};
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: description.unwrap_or("".into()),
|
||||
extra: extra.unwrap_or("".into()),
|
||||
data: address.try_into()?,
|
||||
description_font: &theme::TEXT_NORMAL,
|
||||
extra_font: &theme::TEXT_DEMIBOLD,
|
||||
data_font: data_style,
|
||||
}
|
||||
.into_paragraphs();
|
||||
let content_address = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
|
||||
// .one_button_request(ButtonRequestCode::Address, "show_address");
|
||||
|
||||
// Tap
|
||||
let content_tap = Frame::left_aligned(title, PromptScreen::new_tap_to_confirm())
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let content_confirmed = IgnoreSwipe::new(
|
||||
Frame::left_aligned(
|
||||
TR::address__confirmed.into(),
|
||||
StatusScreen::new_success_timeout(),
|
||||
)
|
||||
.with_footer(TR::instructions__continue_in_app.into(), None),
|
||||
)
|
||||
.map(|_| Some(FlowMsg::Confirmed));
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_QR_CODE, TR::address__qr_code.into())
|
||||
.item(
|
||||
theme::ICON_CHEVRON_RIGHT,
|
||||
TR::address_details__account_info.into(),
|
||||
)
|
||||
.danger(theme::ICON_CANCEL, TR::address__cancel_receive.into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
// QrCode
|
||||
let content_qr = Frame::left_aligned(
|
||||
title,
|
||||
IgnoreSwipe::new(
|
||||
address_qr
|
||||
.map(|s| Qr::new(s, case_sensitive))?
|
||||
.with_border(QR_BORDER),
|
||||
),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Cancelled));
|
||||
|
||||
// AccountInfo
|
||||
let mut ad = AddressDetails::new(TR::address_details__account_info.into(), account, path)?;
|
||||
for i in IterBuf::new().try_iterate(xpubs)? {
|
||||
let [xtitle, text]: [TString; 2] = util::iter_into_array(i)?;
|
||||
ad.add_xpub(xtitle, text)?;
|
||||
}
|
||||
let content_account = SwipePage::vertical(ad).map(|_| Some(FlowMsg::Cancelled));
|
||||
|
||||
// Cancel
|
||||
let content_cancel_info = Frame::left_aligned(
|
||||
TR::address__cancel_receive.into(),
|
||||
SwipePage::vertical(Paragraphs::new(Paragraph::new(
|
||||
&theme::TEXT_MAIN_GREY_LIGHT,
|
||||
TR::address__cancel_contact_support,
|
||||
))),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Cancelled));
|
||||
|
||||
// CancelTap
|
||||
let content_cancel_tap = Frame::left_aligned(
|
||||
TR::address__cancel_receive.into(),
|
||||
PromptScreen::new_tap_to_cancel(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let store = flow_store()
|
||||
.add(
|
||||
Frame::left_aligned(
|
||||
"Receive".into(),
|
||||
SwipePage::vertical(Paragraphs::new(Paragraph::new(
|
||||
&theme::TEXT_MONO,
|
||||
StrBuffer::from(LONGSTRING),
|
||||
))),
|
||||
)
|
||||
.with_subtitle("address".into())
|
||||
.with_menu_button()
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info)),
|
||||
)?
|
||||
.add(
|
||||
Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_QR_CODE, "Address QR code".into())
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Account info".into())
|
||||
.danger(theme::ICON_CANCEL, "Cancel operation".into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => {
|
||||
Some(FlowMsg::Choice(i))
|
||||
}
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
}),
|
||||
)?
|
||||
.add(
|
||||
Frame::left_aligned(
|
||||
"Receive address".into(),
|
||||
IgnoreSwipe::new(Qr::new(
|
||||
"https://youtu.be/iFkEs4GNgfc?si=Jl4UZSIAYGVcLQKo",
|
||||
true,
|
||||
)?),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Cancelled)),
|
||||
)?
|
||||
.add(
|
||||
Frame::left_aligned(
|
||||
"Account info".into(),
|
||||
SwipePage::vertical(Paragraphs::new(Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
StrBuffer::from("taproot xp"),
|
||||
))),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Cancelled)),
|
||||
)?
|
||||
.add(
|
||||
Frame::left_aligned(
|
||||
"Cancel receive".into(),
|
||||
SwipePage::vertical(Paragraphs::new(Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
StrBuffer::from("O rly?"),
|
||||
))),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Cancelled)),
|
||||
)?
|
||||
.add(
|
||||
IconDialog::new(
|
||||
BlendedImage::new(
|
||||
theme::IMAGE_BG_CIRCLE,
|
||||
theme::IMAGE_FG_WARN,
|
||||
theme::SUCCESS_COLOR,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
),
|
||||
StrBuffer::from("Confirmed"),
|
||||
Timeout::new(100),
|
||||
)
|
||||
.map(|_| Some(FlowMsg::Confirmed)),
|
||||
)?;
|
||||
.add(content_address)?
|
||||
.add(content_tap)?
|
||||
.add(content_confirmed)?
|
||||
.add(content_menu)?
|
||||
.add(content_qr)?
|
||||
.add(content_account)?
|
||||
.add(content_cancel_info)?
|
||||
.add(content_cancel_tap)?;
|
||||
let res = SwipeFlow::new(GetAddress::Address, store)?;
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
|
@ -3,9 +3,11 @@ pub mod confirm_reset_recover;
|
||||
pub mod get_address;
|
||||
pub mod prompt_backup;
|
||||
pub mod show_share_words;
|
||||
pub mod warning_hi_prio;
|
||||
|
||||
pub use confirm_reset_create::ConfirmResetCreate;
|
||||
pub use confirm_reset_recover::ConfirmResetRecover;
|
||||
pub use get_address::GetAddress;
|
||||
pub use prompt_backup::PromptBackup;
|
||||
pub use show_share_words::ShowShareWords;
|
||||
pub use warning_hi_prio::WarningHiPrio;
|
||||
|
125
core/embed/rust/src/ui/model_mercury/flow/warning_hi_prio.rs
Normal file
125
core/embed/rust/src/ui/model_mercury/flow/warning_hi_prio.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::qstr::Qstr,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphSource},
|
||||
ComponentExt, SwipeDirection,
|
||||
},
|
||||
flow::{
|
||||
base::Decision, flow_store, FlowMsg, FlowState, FlowStore, IgnoreSwipe, SwipeFlow,
|
||||
SwipePage,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, StatusScreen, VerticalMenu, VerticalMenuChoiceMsg},
|
||||
theme,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
pub enum WarningHiPrio {
|
||||
Message,
|
||||
Menu,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl FlowState for WarningHiPrio {
|
||||
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||
match (self, direction) {
|
||||
(WarningHiPrio::Message, SwipeDirection::Left) => {
|
||||
Decision::Goto(WarningHiPrio::Menu, direction)
|
||||
}
|
||||
(WarningHiPrio::Message, SwipeDirection::Up) => {
|
||||
Decision::Goto(WarningHiPrio::Cancelled, direction)
|
||||
}
|
||||
(WarningHiPrio::Menu, SwipeDirection::Right) => {
|
||||
Decision::Goto(WarningHiPrio::Message, direction)
|
||||
}
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
match (self, msg) {
|
||||
(WarningHiPrio::Message, FlowMsg::Info) => {
|
||||
Decision::Goto(WarningHiPrio::Menu, SwipeDirection::Left)
|
||||
}
|
||||
(WarningHiPrio::Menu, FlowMsg::Choice(1)) => Decision::Return(FlowMsg::Confirmed),
|
||||
(WarningHiPrio::Menu, FlowMsg::Choice(_)) => {
|
||||
Decision::Goto(WarningHiPrio::Cancelled, SwipeDirection::Up)
|
||||
}
|
||||
(WarningHiPrio::Menu, FlowMsg::Cancelled) => {
|
||||
Decision::Goto(WarningHiPrio::Message, SwipeDirection::Right)
|
||||
}
|
||||
(WarningHiPrio::Cancelled, _) => Decision::Return(FlowMsg::Cancelled),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{map::Map, obj::Obj, util},
|
||||
ui::layout::obj::LayoutObj,
|
||||
};
|
||||
|
||||
pub extern "C" fn new_warning_hi_prio(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, WarningHiPrio::new) }
|
||||
}
|
||||
|
||||
impl WarningHiPrio {
|
||||
const EXTRA_PADDING: i16 = 6;
|
||||
|
||||
fn new(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: TString = kwargs.get_or(Qstr::MP_QSTR_title, TR::words__warning.try_into()?)?;
|
||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
|
||||
let cancel: TString = TR::words__cancel_and_exit.into();
|
||||
let confirm: TString = "Continue anyway".into(); // FIXME: en.json has punctuation
|
||||
let done_title: TString = "Operation cancelled".into();
|
||||
|
||||
// Message
|
||||
let paragraphs = [
|
||||
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description),
|
||||
Paragraph::new(&theme::TEXT_MAIN_GREY_EXTRA_LIGHT, value)
|
||||
.with_top_padding(Self::EXTRA_PADDING),
|
||||
]
|
||||
.into_paragraphs();
|
||||
let content_message = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), Some(cancel))
|
||||
.with_danger()
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
|
||||
// .one_button_request(ButtonRequestCode::Warning, br_type);
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_CANCEL, "Cancel".into()) // TODO: button__cancel after it's lowercase
|
||||
.danger(theme::ICON_CHEVRON_RIGHT, confirm),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
// Cancelled
|
||||
let content_cancelled = IgnoreSwipe::new(
|
||||
Frame::left_aligned(done_title, StatusScreen::new_neutral_timeout())
|
||||
.with_footer(TR::instructions__continue_in_app.into(), None),
|
||||
)
|
||||
.map(|_| Some(FlowMsg::Cancelled));
|
||||
|
||||
let store = flow_store()
|
||||
.add(content_message)?
|
||||
.add(content_menu)?
|
||||
.add(content_cancelled)?;
|
||||
let res = SwipeFlow::new(WarningHiPrio::Message, store)?;
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
}
|
@ -684,38 +684,6 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_show_address_details(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let qr_title: TString = kwargs.get(Qstr::MP_QSTR_qr_title)?.try_into()?;
|
||||
let details_title: TString = kwargs.get(Qstr::MP_QSTR_details_title)?.try_into()?;
|
||||
let address: TString = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
||||
let case_sensitive: bool = kwargs.get(Qstr::MP_QSTR_case_sensitive)?.try_into()?;
|
||||
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
||||
let path: Option<TString> = kwargs.get(Qstr::MP_QSTR_path)?.try_into_option()?;
|
||||
|
||||
let xpubs: Obj = kwargs.get(Qstr::MP_QSTR_xpubs)?;
|
||||
|
||||
let mut ad = AddressDetails::new(
|
||||
qr_title,
|
||||
address,
|
||||
case_sensitive,
|
||||
details_title,
|
||||
account,
|
||||
path,
|
||||
)?;
|
||||
|
||||
for i in IterBuf::new().try_iterate(xpubs)? {
|
||||
let [xtitle, text]: [TString; 2] = util::iter_into_array(i)?;
|
||||
ad.add_xpub(xtitle, text)?;
|
||||
}
|
||||
|
||||
let obj =
|
||||
LayoutObj::new(SimplePage::horizontal(ad, theme::BG).with_swipe_right_to_go_back())?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_show_info_with_cancel(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()?;
|
||||
@ -1012,12 +980,13 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
extern "C" fn new_show_warning(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 description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
|
||||
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
|
||||
|
||||
let content = SwipeUpScreen::new(Paragraphs::new([Paragraph::new(
|
||||
&theme::TEXT_MAIN_GREY_LIGHT,
|
||||
value,
|
||||
)]));
|
||||
let content = SwipeUpScreen::new(Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description),
|
||||
Paragraph::new(&theme::TEXT_MAIN_GREY_EXTRA_LIGHT, value),
|
||||
]));
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(title, content)
|
||||
.with_warning_button()
|
||||
@ -1753,19 +1722,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm TOS before creating a wallet and have a user hold to confirm creation."""
|
||||
Qstr::MP_QSTR_flow_confirm_reset_create => obj_fn_kw!(0, flow::confirm_reset_create::new_confirm_reset_create).as_obj(),
|
||||
|
||||
/// def show_address_details(
|
||||
/// *,
|
||||
/// qr_title: str,
|
||||
/// address: str,
|
||||
/// case_sensitive: bool,
|
||||
/// details_title: str,
|
||||
/// account: str | None,
|
||||
/// path: str | None,
|
||||
/// xpubs: list[tuple[str, str]],
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Show address details - QR code, account, path, cosigner xpubs."""
|
||||
Qstr::MP_QSTR_show_address_details => obj_fn_kw!(0, new_show_address_details).as_obj(),
|
||||
|
||||
/// def show_info_with_cancel(
|
||||
/// *,
|
||||
/// title: str,
|
||||
@ -2092,9 +2048,29 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Show single-line text in the middle of the screen."""
|
||||
Qstr::MP_QSTR_show_wait_text => obj_fn_1!(new_show_wait_text).as_obj(),
|
||||
|
||||
/// def flow_get_address() -> LayoutObj[UiResult]:
|
||||
/// def flow_get_address(
|
||||
/// *,
|
||||
/// address: str | bytes,
|
||||
/// description: str | None,
|
||||
/// extra: str | None,
|
||||
/// chunkify: bool,
|
||||
/// address_qr: str | None,
|
||||
/// case_sensitive: bool,
|
||||
/// account: str | None,
|
||||
/// path: str | None,
|
||||
/// xpubs: list[tuple[str, str]],
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Get address / receive funds."""
|
||||
Qstr::MP_QSTR_flow_get_address => obj_fn_kw!(0, flow::get_address::new_get_address).as_obj(),
|
||||
|
||||
/// def flow_warning_hi_prio(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// description: str,
|
||||
/// value: str = "",
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Warning modal with multiple steps to confirm."""
|
||||
Qstr::MP_QSTR_flow_warning_hi_prio => obj_fn_kw!(0, flow::warning_hi_prio::new_warning_hi_prio).as_obj(),
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -215,6 +215,16 @@ pub const fn label_title_main() -> TextStyle {
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn label_title_danger() -> TextStyle {
|
||||
TextStyle::new(
|
||||
Font::NORMAL,
|
||||
ORANGE_LIGHT,
|
||||
GREY_DARK,
|
||||
GREY_LIGHT,
|
||||
GREY_LIGHT,
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn label_title_sub() -> TextStyle {
|
||||
TextStyle::new(Font::SUB, GREY, GREY_DARK, GREY_LIGHT, GREY_LIGHT)
|
||||
}
|
||||
@ -355,9 +365,30 @@ pub const fn button_cancel() -> ButtonStyleSheet {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: delete
|
||||
pub const fn button_danger() -> ButtonStyleSheet {
|
||||
button_cancel()
|
||||
ButtonStyleSheet {
|
||||
normal: &ButtonStyle {
|
||||
font: Font::DEMIBOLD,
|
||||
text_color: ORANGE_LIGHT,
|
||||
button_color: BG,
|
||||
icon_color: ORANGE_LIGHT,
|
||||
background_color: BG,
|
||||
},
|
||||
active: &ButtonStyle {
|
||||
font: Font::DEMIBOLD,
|
||||
text_color: ORANGE_LIGHT,
|
||||
button_color: BG,
|
||||
icon_color: ORANGE_LIGHT,
|
||||
background_color: BG,
|
||||
},
|
||||
disabled: &ButtonStyle {
|
||||
font: Font::DEMIBOLD,
|
||||
text_color: ORANGE_LIGHT,
|
||||
button_color: BG,
|
||||
icon_color: ORANGE_LIGHT,
|
||||
background_color: BG,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn button_reset() -> ButtonStyleSheet {
|
||||
@ -591,6 +622,10 @@ pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, GREY_EXTRA_LIGHT, BG
|
||||
.with_page_breaking(PageBreaking::CutAndInsertEllipsisBoth)
|
||||
.with_ellipsis_icon(ICON_PAGE_NEXT, 0)
|
||||
.with_prev_page_icon(ICON_PAGE_PREV, 0);
|
||||
pub const TEXT_MONO_GREY_LIGHT: TextStyle = TextStyle {
|
||||
text_color: GREY_LIGHT,
|
||||
..TEXT_MONO
|
||||
};
|
||||
/// Makes sure that the displayed text (usually address) will get divided into
|
||||
/// smaller chunks.
|
||||
pub const TEXT_MONO_ADDRESS_CHUNKS: TextStyle = TEXT_MONO
|
||||
@ -656,6 +691,7 @@ pub const MNEMONIC_BUTTON_HEIGHT: i16 = 52;
|
||||
pub const RESULT_PADDING: i16 = 6;
|
||||
pub const RESULT_FOOTER_START: i16 = 171;
|
||||
pub const RESULT_FOOTER_HEIGHT: i16 = 62;
|
||||
pub const DETAILS_SPACING: i16 = 8;
|
||||
|
||||
// checklist settings
|
||||
pub const CHECKLIST_CHECK_WIDTH: i16 = 16;
|
||||
|
@ -52,7 +52,7 @@ impl AddressDetails {
|
||||
if let Some(path) = path {
|
||||
para.add(Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
TR::address_details__derivation_path,
|
||||
TR::address_details__derivation_path_colon,
|
||||
));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO, path));
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ impl AddressDetails {
|
||||
if let Some(p) = path {
|
||||
para.add(Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
TR::address_details__derivation_path,
|
||||
TR::address_details__derivation_path_colon,
|
||||
));
|
||||
para.add(Paragraph::new(&theme::TEXT_MONO, p));
|
||||
}
|
||||
|
@ -155,20 +155,6 @@ def flow_confirm_reset_create() -> LayoutObj[UiResult]:
|
||||
"""Confirm TOS before creating a wallet and have a user hold to confirm creation."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def show_address_details(
|
||||
*,
|
||||
qr_title: str,
|
||||
address: str,
|
||||
case_sensitive: bool,
|
||||
details_title: str,
|
||||
account: str | None,
|
||||
path: str | None,
|
||||
xpubs: list[tuple[str, str]],
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Show address details - QR code, account, path, cosigner xpubs."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def show_info_with_cancel(
|
||||
*,
|
||||
@ -529,8 +515,29 @@ def show_wait_text(message: str, /) -> LayoutObj[None]:
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def flow_get_address() -> LayoutObj[UiResult]:
|
||||
def flow_get_address(
|
||||
*,
|
||||
address: str | bytes,
|
||||
description: str | None,
|
||||
extra: str | None,
|
||||
chunkify: bool,
|
||||
address_qr: str | None,
|
||||
case_sensitive: bool,
|
||||
account: str | None,
|
||||
path: str | None,
|
||||
xpubs: list[tuple[str, str]],
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Get address / receive funds."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def flow_warning_hi_prio(
|
||||
*,
|
||||
title: str,
|
||||
description: str,
|
||||
value: str = "",
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Warning modal with multiple steps to confirm."""
|
||||
CONFIRMED: UiResult
|
||||
CANCELLED: UiResult
|
||||
INFO: UiResult
|
||||
|
@ -8,11 +8,17 @@ class TR:
|
||||
addr_mismatch__support_url: str = "trezor.io/support"
|
||||
addr_mismatch__wrong_derivation_path: str = "Wrong derivation path for selected account."
|
||||
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__confirmed: str = "Receive address confirmed"
|
||||
address__public_key: str = "Public key"
|
||||
address__qr_code: str = "QR code"
|
||||
address__title_cosigner: str = "Cosigner"
|
||||
address__title_receive_address: str = "Receive address"
|
||||
address__title_yours: str = "Yours"
|
||||
address_details__derivation_path: str = "Derivation path:"
|
||||
address_details__account_info: str = "Account info"
|
||||
address_details__derivation_path: str = "Derivation path"
|
||||
address_details__derivation_path_colon: str = "Derivation path:"
|
||||
address_details__title_receive_address: str = "Receive address"
|
||||
address_details__title_receiving_to: str = "Receiving to"
|
||||
authenticate__confirm_template: str = "Allow connected computer to confirm your {0} is genuine?"
|
||||
@ -356,6 +362,7 @@ class TR:
|
||||
inputs__return: str = "RETURN"
|
||||
inputs__show: str = "SHOW"
|
||||
inputs__space: str = "SPACE"
|
||||
instructions__continue_in_app: str = "Continue in the app"
|
||||
instructions__hold_to_confirm: str = "Hold to confirm"
|
||||
instructions__swipe_up: str = "Swipe up"
|
||||
instructions__tap_to_confirm: str = "Tap to confirm"
|
||||
@ -828,6 +835,7 @@ class TR:
|
||||
words__array_of: str = "Array of"
|
||||
words__blockhash: str = "Blockhash"
|
||||
words__buying: str = "Buying"
|
||||
words__cancel_and_exit: str = "Cancel and exit"
|
||||
words__confirm: str = "Confirm"
|
||||
words__confirm_fee: str = "Confirm fee"
|
||||
words__contains: str = "Contains"
|
||||
|
@ -31,10 +31,9 @@ def _get_xpubs(
|
||||
|
||||
@with_keychain
|
||||
async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Address:
|
||||
from trezor import TR
|
||||
from trezor.enums import InputScriptType
|
||||
from trezor.messages import Address
|
||||
from trezor.ui.layouts import show_address, show_warning
|
||||
from trezor.ui.layouts import confirm_multisig_warning, show_address
|
||||
|
||||
from apps.common.address_mac import get_address_mac
|
||||
from apps.common.paths import address_n_to_str, validate_path
|
||||
@ -104,11 +103,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad
|
||||
pubnodes = [hd.node for hd in multisig.pubkeys]
|
||||
multisig_index = multisig_pubkey_index(multisig, node.public_key())
|
||||
|
||||
await show_warning(
|
||||
"warning_multisig",
|
||||
TR.send__receiving_to_multisig,
|
||||
TR.words__continue_anyway,
|
||||
)
|
||||
await confirm_multisig_warning()
|
||||
|
||||
await show_address(
|
||||
address_short,
|
||||
|
@ -279,17 +279,6 @@ async def confirm_action(
|
||||
)
|
||||
|
||||
|
||||
async def flow_demo() -> None:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(trezorui2.flow_get_address()),
|
||||
"get_address",
|
||||
BR_TYPE_OTHER,
|
||||
),
|
||||
ActionCancelled,
|
||||
)
|
||||
|
||||
|
||||
async def confirm_single(
|
||||
br_type: str,
|
||||
title: str,
|
||||
@ -362,7 +351,7 @@ async def confirm_path_warning(
|
||||
path: str,
|
||||
path_type: str | None = None,
|
||||
) -> None:
|
||||
title = (
|
||||
description = (
|
||||
TR.addr_mismatch__wrong_derivation_path
|
||||
if not path_type
|
||||
else f"{TR.words__unknown} {path_type.lower()}."
|
||||
@ -370,11 +359,8 @@ async def confirm_path_warning(
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.show_warning(
|
||||
title=title,
|
||||
value=path,
|
||||
description=TR.words__continue_anyway,
|
||||
button=TR.buttons__continue,
|
||||
trezorui2.flow_warning_hi_prio(
|
||||
title=f"{TR.words__warning}!", description=description, value=path
|
||||
)
|
||||
),
|
||||
"path_warning",
|
||||
@ -383,6 +369,21 @@ async def confirm_path_warning(
|
||||
)
|
||||
|
||||
|
||||
async def confirm_multisig_warning() -> None:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.flow_warning_hi_prio(
|
||||
title=f"{TR.words__important}!",
|
||||
description=TR.send__receiving_to_multisig,
|
||||
)
|
||||
),
|
||||
"warning_multisig",
|
||||
br_code=ButtonRequestType.Warning,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def confirm_homescreen(
|
||||
image: bytes,
|
||||
) -> None:
|
||||
@ -417,78 +418,35 @@ async def show_address(
|
||||
br_code: ButtonRequestType = ButtonRequestType.Address,
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
mismatch_title = mismatch_title or TR.addr_mismatch__mismatch # def_arg
|
||||
send_button_request = True
|
||||
def xpub_title(i: int) -> str:
|
||||
result = f"Multisig XPUB #{i + 1}\n"
|
||||
result += (
|
||||
f"({TR.address__title_yours.lower()})"
|
||||
if i == multisig_index
|
||||
else f"({TR.address__title_cosigner.lower()})"
|
||||
)
|
||||
return result
|
||||
|
||||
if title is None:
|
||||
title = TR.address__title_receive_address
|
||||
if multisig_index is not None:
|
||||
title = f"{title}\n(MULTISIG)"
|
||||
details_title = TR.send__title_receiving_to
|
||||
elif details_title is None:
|
||||
details_title = title
|
||||
|
||||
layout = RustLayout(
|
||||
trezorui2.confirm_address(
|
||||
title=title,
|
||||
data=address,
|
||||
description=network or "",
|
||||
extra=None,
|
||||
chunkify=chunkify,
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.flow_get_address(
|
||||
address=address,
|
||||
description=network or "",
|
||||
extra=None,
|
||||
chunkify=chunkify,
|
||||
address_qr=address if address_qr is None else address_qr,
|
||||
case_sensitive=case_sensitive,
|
||||
account=account,
|
||||
path=path,
|
||||
xpubs=[(xpub_title(i), xpub) for i, xpub in enumerate(xpubs)],
|
||||
)
|
||||
),
|
||||
br_type,
|
||||
br_code,
|
||||
)
|
||||
)
|
||||
|
||||
while True:
|
||||
if send_button_request:
|
||||
send_button_request = False
|
||||
await button_request(
|
||||
br_type,
|
||||
br_code,
|
||||
pages=layout.page_count(),
|
||||
)
|
||||
layout.request_complete_repaint()
|
||||
result = await ctx_wait(layout)
|
||||
|
||||
# User pressed right button.
|
||||
if result is CONFIRMED:
|
||||
break
|
||||
|
||||
# User pressed corner button or swiped left, go to address details.
|
||||
elif result is INFO:
|
||||
|
||||
def xpub_title(i: int) -> str:
|
||||
result = f"MULTISIG XPUB #{i + 1}\n"
|
||||
result += (
|
||||
f"({TR.address__title_yours})"
|
||||
if i == multisig_index
|
||||
else f"({TR.address__title_cosigner})"
|
||||
)
|
||||
return result
|
||||
|
||||
result = await ctx_wait(
|
||||
RustLayout(
|
||||
trezorui2.show_address_details(
|
||||
qr_title=title,
|
||||
address=address if address_qr is None else address_qr,
|
||||
case_sensitive=case_sensitive,
|
||||
details_title=details_title,
|
||||
account=account,
|
||||
path=path,
|
||||
xpubs=[(xpub_title(i), xpub) for i, xpub in enumerate(xpubs)],
|
||||
)
|
||||
)
|
||||
)
|
||||
assert result is CANCELLED
|
||||
|
||||
else:
|
||||
result = await ctx_wait(
|
||||
RustLayout(trezorui2.show_mismatch(title=mismatch_title))
|
||||
)
|
||||
assert result in (CONFIRMED, CANCELLED)
|
||||
# Right button aborts action, left goes back to showing address.
|
||||
if result is CONFIRMED:
|
||||
raise ActionCancelled
|
||||
|
||||
|
||||
def show_pubkey(
|
||||
pubkey: str,
|
||||
@ -1305,7 +1263,7 @@ async def confirm_signverify(
|
||||
|
||||
items: list[tuple[str, str]] = []
|
||||
if account is not None:
|
||||
items.append((f"{TR.words__account}:", account))
|
||||
items.append((TR.words__account, account))
|
||||
if path is not None:
|
||||
items.append((TR.address_details__derivation_path, path))
|
||||
items.append(
|
||||
|
@ -492,6 +492,14 @@ def confirm_path_warning(
|
||||
)
|
||||
|
||||
|
||||
def confirm_multisig_warning() -> Awaitable[None]:
|
||||
return show_warning(
|
||||
"warning_multisig",
|
||||
TR.send__receiving_to_multisig,
|
||||
TR.words__continue_anyway,
|
||||
)
|
||||
|
||||
|
||||
def confirm_homescreen(image: bytes) -> Awaitable[None]:
|
||||
return raise_if_not_confirmed(
|
||||
interact(
|
||||
|
@ -431,6 +431,14 @@ def confirm_path_warning(path: str, path_type: str | None = None) -> Awaitable[N
|
||||
)
|
||||
|
||||
|
||||
def confirm_multisig_warning() -> Awaitable[None]:
|
||||
return show_warning(
|
||||
"warning_multisig",
|
||||
TR.send__receiving_to_multisig,
|
||||
TR.words__continue_anyway,
|
||||
)
|
||||
|
||||
|
||||
def confirm_homescreen(image: bytes) -> Awaitable[None]:
|
||||
return raise_if_not_confirmed(
|
||||
interact(
|
||||
@ -1349,7 +1357,7 @@ async def confirm_signverify(
|
||||
if account is not None:
|
||||
items.append((f"{TR.words__account}:", account))
|
||||
if path is not None:
|
||||
items.append((TR.address_details__derivation_path, path))
|
||||
items.append((TR.address_details__derivation_path_colon, path))
|
||||
items.append(
|
||||
(
|
||||
TR.sign_message__message_size,
|
||||
|
@ -46,7 +46,8 @@
|
||||
"address__title_cosigner": "Další podepisující",
|
||||
"address__title_receive_address": "Přijímací adresa",
|
||||
"address__title_yours": "Vaše",
|
||||
"address_details__derivation_path": "Derivační cesta:",
|
||||
"address_details__derivation_path": "Derivační cesta",
|
||||
"address_details__derivation_path_colon": "Derivační cesta:",
|
||||
"address_details__title_receive_address": "Přijímací adresa",
|
||||
"address_details__title_receiving_to": "Příjem",
|
||||
"authenticate__confirm_template": "Povolit připojenému počítači potvrdit pravost vašeho {0}?",
|
||||
|
@ -46,7 +46,8 @@
|
||||
"address__title_cosigner": "Mitunterzeich.",
|
||||
"address__title_receive_address": "Empfäng-adresse",
|
||||
"address__title_yours": "Deiner",
|
||||
"address_details__derivation_path": "Ableitungspfad:",
|
||||
"address_details__derivation_path": "Ableitungspfad",
|
||||
"address_details__derivation_path_colon": "Ableitungspfad:",
|
||||
"address_details__title_receive_address": "Empfäng-adresse",
|
||||
"address_details__title_receiving_to": "Empfänger",
|
||||
"authenticate__confirm_template": "Darf verbundener Computer bestätigen, dass {0} echt ist?",
|
||||
|
@ -10,11 +10,17 @@
|
||||
"addr_mismatch__support_url": "trezor.io/support",
|
||||
"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__confirmed": "Receive address confirmed",
|
||||
"address__public_key": "Public key",
|
||||
"address__qr_code": "QR code",
|
||||
"address__title_cosigner": "Cosigner",
|
||||
"address__title_receive_address": "Receive address",
|
||||
"address__title_yours": "Yours",
|
||||
"address_details__derivation_path": "Derivation path:",
|
||||
"address_details__derivation_path": "Derivation path",
|
||||
"address_details__derivation_path_colon": "Derivation path:",
|
||||
"address_details__account_info": "Account info",
|
||||
"address_details__title_receive_address": "Receive address",
|
||||
"address_details__title_receiving_to": "Receiving to",
|
||||
"authenticate__confirm_template": "Allow connected computer to confirm your {0} is genuine?",
|
||||
@ -358,6 +364,7 @@
|
||||
"inputs__previous": "PREVIOUS",
|
||||
"inputs__show": "SHOW",
|
||||
"inputs__space": "SPACE",
|
||||
"instructions__continue_in_app": "Continue in the app",
|
||||
"instructions__swipe_up": "Swipe up",
|
||||
"instructions__tap_to_confirm": "Tap to confirm",
|
||||
"instructions__hold_to_confirm": "Hold to confirm",
|
||||
@ -830,6 +837,7 @@
|
||||
"words__array_of": "Array of",
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Buying",
|
||||
"words__cancel_and_exit": "Cancel and exit",
|
||||
"words__confirm": "Confirm",
|
||||
"words__confirm_fee": "Confirm fee",
|
||||
"words__contains": "Contains",
|
||||
|
@ -46,7 +46,8 @@
|
||||
"address__title_cosigner": "Cofirmante",
|
||||
"address__title_receive_address": "Dirección destino",
|
||||
"address__title_yours": "Tuyo",
|
||||
"address_details__derivation_path": "Ruta de derivación:",
|
||||
"address_details__derivation_path": "Ruta de derivación",
|
||||
"address_details__derivation_path_colon": "Ruta de derivación:",
|
||||
"address_details__title_receive_address": "Dirección destino",
|
||||
"address_details__title_receiving_to": "Recibir en",
|
||||
"authenticate__confirm_template": "¿Confirmar con el ordenador conectado que tu {0} es original?",
|
||||
|
@ -46,7 +46,8 @@
|
||||
"address__title_cosigner": "Cosignataire",
|
||||
"address__title_receive_address": "Adr. de récep.",
|
||||
"address__title_yours": "La vôtre",
|
||||
"address_details__derivation_path": "Chemin de dérivation :",
|
||||
"address_details__derivation_path": "Chemin de dérivation",
|
||||
"address_details__derivation_path_colon": "Chemin de dérivation :",
|
||||
"address_details__title_receive_address": "Adr. de récep.",
|
||||
"address_details__title_receiving_to": "Récep. sur",
|
||||
"authenticate__confirm_template": "Autoriser l'ord. connecté à conf. que votre {0} est auth. ?",
|
||||
|
@ -9,7 +9,7 @@
|
||||
"7": "address__title_cosigner",
|
||||
"8": "address__title_receive_address",
|
||||
"9": "address__title_yours",
|
||||
"10": "address_details__derivation_path",
|
||||
"10": "address_details__derivation_path_colon",
|
||||
"11": "address_details__title_receive_address",
|
||||
"12": "address_details__title_receiving_to",
|
||||
"13": "authenticate__confirm_template",
|
||||
@ -860,5 +860,13 @@
|
||||
"858": "backup__create_backup_to_prevent_loss",
|
||||
"859": "reset__check_backup_instructions",
|
||||
"860": "words__instructions",
|
||||
"861": "words__not_recommended"
|
||||
"861": "words__not_recommended",
|
||||
"862": "address_details__account_info",
|
||||
"863": "address__cancel_contact_support",
|
||||
"864": "address__cancel_receive",
|
||||
"865": "address__qr_code",
|
||||
"866": "address_details__derivation_path",
|
||||
"867": "instructions__continue_in_app",
|
||||
"868": "words__cancel_and_exit",
|
||||
"869": "address__confirmed"
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "5261bdad43d4e3cda9263ec6ce080218a0d897e02307ca8cafa2525d6a8d3d6b",
|
||||
"datetime": "2024-05-17T10:18:14.246905",
|
||||
"commit": "58db509b926fd99b3a36eb3bd550945bf1a139c4"
|
||||
"merkle_root": "d2a00bb90ebc87448eb0786432129db7c4e67316de7c491bf854d8429d2db9b8",
|
||||
"datetime": "2024-05-17T10:29:37.039056",
|
||||
"commit": "b3379e14e0658ab2327bffdfff5227f6079c8f74"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
|
@ -434,7 +434,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
|
||||
layout = self.debug.swipe_left(wait=True)
|
||||
# address details
|
||||
assert "Multisig 2 of 3" in layout.screen_content()
|
||||
TR.assert_in(layout.screen_content(), "address_details__derivation_path")
|
||||
TR.assert_in(layout.screen_content(), "address_details__derivation_path_colon")
|
||||
|
||||
# Three xpub pages with the same testing logic
|
||||
for xpub_num in range(3):
|
||||
@ -508,7 +508,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
|
||||
layout = self.debug.swipe_left(wait=True)
|
||||
# address details
|
||||
assert "Multisig 2 of 3" in layout.screen_content()
|
||||
TR.assert_in(layout.screen_content(), "address_details__derivation_path")
|
||||
TR.assert_in(layout.screen_content(), "address_details__derivation_path_colon")
|
||||
|
||||
# Three xpub pages with the same testing logic
|
||||
for xpub_num in range(3):
|
||||
|
Loading…
Reference in New Issue
Block a user