mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-21 01:29:02 +00:00
feat(eckhart): implement trait functions: confirm_value_intro,
confirm_modify_output, confirm_modify_fee, confirm_with_info, show_info_with_cancel and confirm_props
This commit is contained in:
parent
f5acf7b052
commit
9de999f13b
@ -0,0 +1,133 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::obj::Obj,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphSource},
|
||||
ComponentExt,
|
||||
},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
FlowController, FlowMsg, SwipeFlow,
|
||||
},
|
||||
geometry::{Alignment, Direction, LinearPlacement, Offset},
|
||||
layout::util::StrOrBytes,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::Button,
|
||||
firmware::{
|
||||
ActionBar, Header, TextScreen, TextScreenMsg, VerticalMenu, VerticalMenuScreen,
|
||||
VerticalMenuScreenMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
const TIMEOUT_MS: u32 = 2000;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ConfirmValueIntro {
|
||||
Intro,
|
||||
Menu,
|
||||
}
|
||||
|
||||
impl FlowController for ConfirmValueIntro {
|
||||
#[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::Intro, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::Intro, FlowMsg::Info) => Self::Menu.goto(),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Info),
|
||||
(Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Cancelled),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Intro.goto(),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_confirm_value_intro(
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
value: Obj,
|
||||
value_menu_label: TString<'static>,
|
||||
cancel_menu_label: Option<TString<'static>>,
|
||||
confirm_button_label: Option<TString<'static>>,
|
||||
hold: bool,
|
||||
chunkify: bool,
|
||||
) -> Result<SwipeFlow, error::Error> {
|
||||
let cancel_menu_label = cancel_menu_label.unwrap_or(TR::buttons__cancel.into());
|
||||
|
||||
// Intro
|
||||
let mut confirm_button = Button::with_text(
|
||||
confirm_button_label.unwrap_or(TR::sign_message__confirm_without_review.into()),
|
||||
)
|
||||
.styled(theme::button_confirm());
|
||||
if hold {
|
||||
confirm_button = confirm_button.with_long_press(theme::LOCK_HOLD_DURATION);
|
||||
}
|
||||
|
||||
let value = if value != Obj::const_none() {
|
||||
unwrap!(value.try_into())
|
||||
} else {
|
||||
StrOrBytes::Str("".into())
|
||||
};
|
||||
|
||||
let intro_style = if chunkify {
|
||||
&theme::TEXT_MONO_ADDRESS_CHUNKS
|
||||
} else {
|
||||
&theme::TEXT_MONO_ADDRESS
|
||||
};
|
||||
let content_intro = TextScreen::new(
|
||||
Paragraph::new(intro_style, value.as_str_offset(0))
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
)
|
||||
.with_page_limit(1)
|
||||
.with_header(Header::new(title).with_menu_button())
|
||||
.with_subtitle(subtitle.unwrap_or(TString::empty()))
|
||||
.with_action_bar(ActionBar::new_single(confirm_button))
|
||||
.map(|msg| match msg {
|
||||
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
|
||||
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
|
||||
TextScreenMsg::Menu => Some(FlowMsg::Info),
|
||||
});
|
||||
|
||||
let menu_items = VerticalMenu::empty()
|
||||
.item(
|
||||
Button::with_text(value_menu_label)
|
||||
.styled(theme::menu_item_title())
|
||||
.with_text_align(Alignment::Start)
|
||||
.with_content_offset(Offset::x(12)),
|
||||
)
|
||||
.item(
|
||||
Button::with_text(cancel_menu_label)
|
||||
.styled(theme::menu_item_title_orange())
|
||||
.with_text_align(Alignment::Start)
|
||||
.with_content_offset(Offset::x(12)),
|
||||
);
|
||||
|
||||
let content_menu = VerticalMenuScreen::new(menu_items)
|
||||
.with_header(Header::new(TString::empty()).with_close_button())
|
||||
.map(move |msg| match msg {
|
||||
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
|
||||
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mut res = SwipeFlow::new(&ConfirmValueIntro::Intro)?;
|
||||
res.add_page(&ConfirmValueIntro::Intro, content_intro)?
|
||||
.add_page(&ConfirmValueIntro::Menu, content_menu)?;
|
||||
Ok(res)
|
||||
}
|
113
core/embed/rust/src/ui/layout_eckhart/flow/confirm_with_menu.rs
Normal file
113
core/embed/rust/src/ui/layout_eckhart/flow/confirm_with_menu.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use crate::{
|
||||
error,
|
||||
maybe_trace::MaybeTrace,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::ComponentExt,
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
FlowController, FlowMsg, SwipeFlow,
|
||||
},
|
||||
geometry::{Alignment, Direction, Offset},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::Button,
|
||||
firmware::{
|
||||
ActionBar, AllowedTextContent, Header, TextScreen, TextScreenMsg, VerticalMenu,
|
||||
VerticalMenuScreen, VerticalMenuScreenMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
const TIMEOUT_MS: u32 = 2000;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ConfirmWithMenu {
|
||||
Value,
|
||||
Menu,
|
||||
}
|
||||
|
||||
impl FlowController for ConfirmWithMenu {
|
||||
#[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::Value, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
|
||||
(Self::Value, FlowMsg::Info) => Self::Menu.goto(),
|
||||
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Info),
|
||||
(Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Cancelled),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Value.goto(),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_confirm_with_menu<T: AllowedTextContent + MaybeTrace + 'static>(
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
content: T,
|
||||
confirm_label: Option<TString<'static>>,
|
||||
hold: bool,
|
||||
extra_menu_label: Option<TString<'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 confirm_label = confirm_label.unwrap_or(TR::buttons__confirm.into());
|
||||
|
||||
// Value
|
||||
let mut confirm_button = Button::with_text(confirm_label).styled(theme::button_confirm());
|
||||
if hold {
|
||||
confirm_button = confirm_button.with_long_press(theme::LOCK_HOLD_DURATION);
|
||||
}
|
||||
let content_value = TextScreen::new(content)
|
||||
.with_header(Header::new(title).with_menu_button())
|
||||
.with_action_bar(ActionBar::new_single(confirm_button))
|
||||
.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),
|
||||
});
|
||||
|
||||
let mut menu_items = VerticalMenu::empty();
|
||||
|
||||
if let Some(extra_menu_label) = extra_menu_label {
|
||||
menu_items = menu_items.item(
|
||||
Button::with_text(extra_menu_label)
|
||||
.styled(theme::menu_item_title())
|
||||
.with_text_align(Alignment::Start)
|
||||
.with_content_offset(Offset::x(12)),
|
||||
);
|
||||
}
|
||||
|
||||
menu_items = menu_items.item(
|
||||
Button::with_text(cancel_menu_label)
|
||||
.styled(theme::menu_item_title_orange())
|
||||
.with_text_align(Alignment::Start)
|
||||
.with_content_offset(Offset::x(12)),
|
||||
);
|
||||
|
||||
let content_menu = VerticalMenuScreen::new(menu_items)
|
||||
.with_header(Header::new(TString::empty()).with_close_button())
|
||||
.map(move |msg| match msg {
|
||||
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
|
||||
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mut res = SwipeFlow::new(&ConfirmWithMenu::Value)?;
|
||||
res.add_page(&ConfirmWithMenu::Value, content_value)?
|
||||
.add_page(&ConfirmWithMenu::Menu, content_menu)?;
|
||||
Ok(res)
|
||||
}
|
@ -2,6 +2,8 @@ pub mod confirm_output;
|
||||
pub mod confirm_reset;
|
||||
pub mod confirm_set_new_pin;
|
||||
pub mod confirm_summary;
|
||||
pub mod confirm_value_intro;
|
||||
pub mod confirm_with_menu;
|
||||
pub mod continue_recovery_homepage;
|
||||
pub mod get_address;
|
||||
pub mod prompt_backup;
|
||||
@ -14,6 +16,8 @@ pub use confirm_output::new_confirm_output;
|
||||
pub use confirm_reset::new_confirm_reset;
|
||||
pub use confirm_set_new_pin::new_set_new_pin;
|
||||
pub use confirm_summary::new_confirm_summary;
|
||||
pub use confirm_value_intro::new_confirm_value_intro;
|
||||
pub use confirm_with_menu::new_confirm_with_menu;
|
||||
pub use continue_recovery_homepage::new_continue_recovery_homepage;
|
||||
pub use get_address::GetAddress;
|
||||
pub use prompt_backup::PromptBackup;
|
||||
|
@ -14,13 +14,14 @@ use crate::{
|
||||
Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort,
|
||||
Paragraphs, VecExt,
|
||||
},
|
||||
TextStyle,
|
||||
},
|
||||
Empty, FormattedText,
|
||||
},
|
||||
geometry::{Alignment, LinearPlacement, Offset},
|
||||
layout::{
|
||||
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
||||
util::{ConfirmValueParams, RecoveryType, StrOrBytes},
|
||||
util::{ConfirmValueParams, PropsList, RecoveryType, StrOrBytes},
|
||||
},
|
||||
ui_firmware::{
|
||||
FirmwareUI, MAX_CHECKLIST_ITEMS, MAX_GROUP_SHARE_LINES, MAX_WORD_QUIZ_ITEMS,
|
||||
@ -179,21 +180,84 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn confirm_modify_fee(
|
||||
_title: TString<'static>,
|
||||
_sign: i32,
|
||||
_user_fee_change: TString<'static>,
|
||||
_total_fee_new: TString<'static>,
|
||||
title: TString<'static>,
|
||||
sign: i32,
|
||||
user_fee_change: TString<'static>,
|
||||
total_fee_new: TString<'static>,
|
||||
_fee_rate_amount: Option<TString<'static>>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
||||
let (description, change, total_label) = match sign {
|
||||
s if s < 0 => (
|
||||
TR::modify_fee__decrease_fee,
|
||||
user_fee_change,
|
||||
TR::modify_fee__new_transaction_fee,
|
||||
),
|
||||
s if s > 0 => (
|
||||
TR::modify_fee__increase_fee,
|
||||
user_fee_change,
|
||||
TR::modify_fee__new_transaction_fee,
|
||||
),
|
||||
_ => (
|
||||
TR::modify_fee__no_change,
|
||||
"".into(),
|
||||
TR::modify_fee__transaction_fee,
|
||||
),
|
||||
};
|
||||
|
||||
let paragraphs = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_SMALL_LIGHT, description),
|
||||
Paragraph::new(&theme::TEXT_MONO_MEDIUM_LIGHT, change),
|
||||
Paragraph::new(&theme::TEXT_SMALL_LIGHT, total_label),
|
||||
Paragraph::new(&theme::TEXT_MONO_MEDIUM_LIGHT, total_fee_new),
|
||||
]);
|
||||
|
||||
let flow = flow::new_confirm_with_menu(
|
||||
title,
|
||||
None,
|
||||
paragraphs
|
||||
.into_paragraphs()
|
||||
.with_spacing(12)
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
None,
|
||||
false,
|
||||
Some(TR::words__title_information.into()),
|
||||
None,
|
||||
)?;
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
fn confirm_modify_output(
|
||||
_sign: i32,
|
||||
_amount_change: TString<'static>,
|
||||
_amount_new: TString<'static>,
|
||||
sign: i32,
|
||||
amount_change: TString<'static>,
|
||||
amount_new: TString<'static>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
||||
let description = if sign < 0 {
|
||||
TR::modify_amount__decrease_amount
|
||||
} else {
|
||||
TR::modify_amount__increase_amount
|
||||
};
|
||||
|
||||
let paragraphs = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_SMALL_LIGHT, description),
|
||||
Paragraph::new(&theme::TEXT_MONO_MEDIUM_LIGHT, amount_change),
|
||||
Paragraph::new(&theme::TEXT_SMALL_LIGHT, TR::modify_amount__new_amount),
|
||||
Paragraph::new(&theme::TEXT_MONO_MEDIUM_LIGHT, amount_new),
|
||||
]);
|
||||
|
||||
let layout = RootComponent::new(
|
||||
TextScreen::new(
|
||||
paragraphs
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical())
|
||||
.with_spacing(12),
|
||||
)
|
||||
.with_header(Header::new(TR::modify_amount__title.into()))
|
||||
.with_action_bar(ActionBar::new_double(
|
||||
Button::with_icon(theme::ICON_CROSS),
|
||||
Button::with_text(TR::buttons__confirm.into()),
|
||||
)),
|
||||
);
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn confirm_more(
|
||||
@ -261,11 +325,30 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn confirm_properties(
|
||||
_title: TString<'static>,
|
||||
_items: Obj,
|
||||
_hold: bool,
|
||||
title: TString<'static>,
|
||||
items: Obj,
|
||||
hold: bool,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
||||
let paragraphs = PropsList::new(
|
||||
items,
|
||||
&theme::TEXT_SMALL_LIGHT,
|
||||
&theme::TEXT_MEDIUM,
|
||||
&theme::TEXT_MONO_LIGHT,
|
||||
)?;
|
||||
|
||||
let flow = flow::new_confirm_with_menu(
|
||||
title,
|
||||
None,
|
||||
paragraphs
|
||||
.into_paragraphs()
|
||||
.with_spacing(12)
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
None,
|
||||
hold,
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
fn confirm_value(
|
||||
@ -333,25 +416,65 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn confirm_value_intro(
|
||||
_title: TString<'static>,
|
||||
_value: Obj,
|
||||
_subtitle: Option<TString<'static>>,
|
||||
_verb: Option<TString<'static>>,
|
||||
_verb_cancel: Option<TString<'static>>,
|
||||
_hold: bool,
|
||||
_chunkify: bool,
|
||||
title: TString<'static>,
|
||||
value: Obj,
|
||||
subtitle: Option<TString<'static>>,
|
||||
verb: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
hold: bool,
|
||||
chunkify: bool,
|
||||
) -> Result<Gc<LayoutObj>, Error> {
|
||||
Err::<Gc<LayoutObj>, Error>(Error::ValueError(c"confirm_value_intro not implemented"))
|
||||
let flow = flow::new_confirm_value_intro(
|
||||
title,
|
||||
subtitle,
|
||||
value,
|
||||
TR::buttons__view_all_data.into(),
|
||||
verb_cancel,
|
||||
verb,
|
||||
hold,
|
||||
chunkify,
|
||||
)?;
|
||||
|
||||
LayoutObj::new_root(flow)
|
||||
}
|
||||
|
||||
fn confirm_with_info(
|
||||
_title: TString<'static>,
|
||||
_items: Obj,
|
||||
_verb: TString<'static>,
|
||||
_verb_info: TString<'static>,
|
||||
title: TString<'static>,
|
||||
items: Obj,
|
||||
verb: TString<'static>,
|
||||
verb_info: TString<'static>,
|
||||
_verb_cancel: Option<TString<'static>>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
|
||||
for para in IterBuf::new().try_iterate(items)? {
|
||||
let [text, is_data]: [Obj; 2] = util::iter_into_array(para)?;
|
||||
let is_data = is_data.try_into()?;
|
||||
let style: &TextStyle = if is_data {
|
||||
&theme::TEXT_SMALL_LIGHT
|
||||
} else {
|
||||
&theme::TEXT_MONO_MEDIUM_LIGHT
|
||||
};
|
||||
let text: TString = text.try_into()?;
|
||||
paragraphs.add(Paragraph::new(style, text));
|
||||
if paragraphs.is_full() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let flow = flow::new_confirm_with_menu(
|
||||
title,
|
||||
None,
|
||||
paragraphs
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical())
|
||||
.with_spacing(12),
|
||||
Some(verb),
|
||||
false,
|
||||
Some(verb_info),
|
||||
None,
|
||||
)?;
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
fn check_homescreen_format(image: BinaryData, _accept_toif: bool) -> bool {
|
||||
@ -902,19 +1025,24 @@ impl FirmwareUI for UIEckhart {
|
||||
let [key, value]: [Obj; 2] = util::iter_into_array(para)?;
|
||||
let key: TString = key.try_into()?;
|
||||
let value: TString = value.try_into()?;
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MEDIUM, key).no_break());
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_SMALL_LIGHT, key).no_break());
|
||||
if chunkify {
|
||||
paragraphs.add(Paragraph::new(
|
||||
theme::get_chunkified_text_style(value.len()),
|
||||
value,
|
||||
));
|
||||
} else {
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO_MEDIUM, value));
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO_LIGHT, value));
|
||||
}
|
||||
}
|
||||
|
||||
let screen = TextScreen::new(paragraphs.into_paragraphs())
|
||||
.with_header(Header::new(title).with_close_button());
|
||||
let screen = TextScreen::new(
|
||||
paragraphs
|
||||
.into_paragraphs()
|
||||
.with_spacing(12)
|
||||
.with_placement(LinearPlacement::vertical()),
|
||||
)
|
||||
.with_header(Header::new(title).with_close_button());
|
||||
let layout = RootComponent::new(screen);
|
||||
Ok(layout)
|
||||
}
|
||||
|
@ -899,8 +899,36 @@ async def confirm_modify_output(
|
||||
amount_change: str,
|
||||
amount_new: str,
|
||||
) -> None:
|
||||
# FIXME: not implemented
|
||||
raise NotImplementedError
|
||||
address_layout = trezorui_api.confirm_value(
|
||||
title=TR.modify_amount__title,
|
||||
value=address,
|
||||
verb=TR.buttons__continue,
|
||||
verb_cancel=None,
|
||||
description=f"{TR.words__address}:",
|
||||
)
|
||||
modify_layout = trezorui_api.confirm_modify_output(
|
||||
sign=sign,
|
||||
amount_change=amount_change,
|
||||
amount_new=amount_new,
|
||||
)
|
||||
|
||||
send_button_request = True
|
||||
while True:
|
||||
await raise_if_not_confirmed(
|
||||
address_layout,
|
||||
"modify_output" if send_button_request else None,
|
||||
ButtonRequestType.ConfirmOutput,
|
||||
)
|
||||
result = await interact(
|
||||
modify_layout,
|
||||
"modify_output" if send_button_request else None,
|
||||
ButtonRequestType.ConfirmOutput,
|
||||
raise_on_cancel=None,
|
||||
)
|
||||
send_button_request = False
|
||||
|
||||
if result is CONFIRMED:
|
||||
break
|
||||
|
||||
|
||||
def confirm_modify_fee(
|
||||
@ -910,8 +938,21 @@ def confirm_modify_fee(
|
||||
total_fee_new: str,
|
||||
fee_rate_amount: str | None = None,
|
||||
) -> Awaitable[None]:
|
||||
# FIXME: not implemented
|
||||
raise NotImplementedError
|
||||
fee_layout = trezorui_api.confirm_modify_fee(
|
||||
title=title,
|
||||
sign=sign,
|
||||
user_fee_change=user_fee_change,
|
||||
total_fee_new=total_fee_new,
|
||||
fee_rate_amount=fee_rate_amount,
|
||||
)
|
||||
items: list[tuple[str, str]] = []
|
||||
if fee_rate_amount:
|
||||
items.append((TR.bitcoin__new_fee_rate, fee_rate_amount))
|
||||
info_layout = trezorui_api.show_info_with_cancel(
|
||||
title=TR.confirm_total__title_fee,
|
||||
items=items,
|
||||
)
|
||||
return with_info(fee_layout, info_layout, "modify_fee", ButtonRequestType.SignTx)
|
||||
|
||||
|
||||
def confirm_coinjoin(max_rounds: int, max_fee_per_vbyte: str) -> Awaitable[None]:
|
||||
|
Loading…
Reference in New Issue
Block a user