feat(core): improve sign message designs

[no changelog]
pull/3188/head
grdddj 10 months ago committed by Jiří Musil
parent 09cd64123b
commit 492ff958a5

@ -351,7 +351,10 @@ pub struct ButtonDetails<T> {
offset: Offset,
}
impl<T> ButtonDetails<T> {
impl<T> ButtonDetails<T>
where
T: StringType,
{
/// Text button.
pub fn text(text: T) -> Self {
Self {
@ -376,6 +379,19 @@ impl<T> ButtonDetails<T> {
}
}
/// Resolves text and finds possible icon names.
pub fn from_text_possible_icon(text: T) -> Self {
if text.as_ref() == "" {
Self::cancel_icon()
} else if text.as_ref() == "left_arrow_icon" {
Self::left_arrow_icon()
} else if text.as_ref() == "up_arrow_icon" {
Self::up_arrow_icon()
} else {
Self::text(text)
}
}
/// Text with arms signalling double press.
pub fn armed_text(text: T) -> Self {
Self::text(text).with_arms()
@ -529,6 +545,15 @@ where
)
}
/// Left text, armed text and right info icon/text.
pub fn text_armed_info(left: T, middle: T) -> Self {
Self::new(
Some(ButtonDetails::from_text_possible_icon(left)),
Some(ButtonDetails::armed_text(middle)),
Some(ButtonDetails::text("i".into()).with_fixed_width(theme::BUTTON_ICON_WIDTH)),
)
}
/// Left cancel, armed text and right info icon/text.
pub fn cancel_armed_info(middle: T) -> Self {
Self::new(
@ -559,16 +584,16 @@ where
/// Left and right texts.
pub fn text_none_text(left: T, right: T) -> Self {
Self::new(
Some(ButtonDetails::text(left)),
Some(ButtonDetails::from_text_possible_icon(left)),
None,
Some(ButtonDetails::text(right)),
Some(ButtonDetails::from_text_possible_icon(right)),
)
}
/// Left text and right arrow.
pub fn text_none_arrow(text: T) -> Self {
Self::new(
Some(ButtonDetails::text(text)),
Some(ButtonDetails::from_text_possible_icon(text)),
None,
Some(ButtonDetails::right_arrow_icon()),
)
@ -577,7 +602,7 @@ where
/// Left text and WIDE right arrow.
pub fn text_none_arrow_wide(text: T) -> Self {
Self::new(
Some(ButtonDetails::text(text)),
Some(ButtonDetails::from_text_possible_icon(text)),
None,
Some(ButtonDetails::down_arrow_icon_wide()),
)
@ -585,7 +610,11 @@ where
/// Only right text.
pub fn none_none_text(text: T) -> Self {
Self::new(None, None, Some(ButtonDetails::text(text)))
Self::new(
None,
None,
Some(ButtonDetails::from_text_possible_icon(text)),
)
}
/// Left and right arrow icons for navigation.
@ -602,7 +631,7 @@ where
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text(text)),
Some(ButtonDetails::from_text_possible_icon(text)),
)
}
@ -611,7 +640,7 @@ where
Self::new(
Some(ButtonDetails::up_arrow_icon()),
None,
Some(ButtonDetails::text(text)),
Some(ButtonDetails::from_text_possible_icon(text)),
)
}
@ -656,7 +685,7 @@ where
Self::new(
Some(ButtonDetails::cancel_icon()),
None,
Some(ButtonDetails::text(text)),
Some(ButtonDetails::from_text_possible_icon(text)),
)
}

@ -27,8 +27,12 @@ where
T: Component,
U: StringType + Clone,
{
pub fn new(content: T) -> Self {
let btn_layout = ButtonLayout::cancel_armed_info("CONFIRM".into());
pub fn new(content: T, cancel_button: Option<U>, button: U) -> Self {
let btn_layout = if let Some(cancel_text) = cancel_button {
ButtonLayout::text_armed_info(cancel_text, button)
} else {
ButtonLayout::cancel_armed_info(button)
};
Self {
content: Child::new(content),
buttons: Child::new(ButtonController::new(btn_layout)),

@ -254,19 +254,7 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
hold: bool,
) -> Result<Obj, Error> {
// Left button - icon, text or nothing.
let cancel_btn = if let Some(verb_cancel) = verb_cancel {
if !verb_cancel.is_empty() {
if verb_cancel.as_ref() == "left_arrow_icon" {
Some(ButtonDetails::left_arrow_icon())
} else {
Some(ButtonDetails::text(verb_cancel))
}
} else {
Some(ButtonDetails::cancel_icon())
}
} else {
None
};
let cancel_btn = verb_cancel.map(ButtonDetails::from_text_possible_icon);
// Right button - text or nothing.
// Optional HoldToConfirm
@ -1060,6 +1048,11 @@ extern "C" fn new_show_mismatch() -> Obj {
extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let verb_cancel: Option<StrBuffer> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
let mut paragraphs = ParagraphVecShort::new();
@ -1078,6 +1071,8 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
title,
ShowMore::<Paragraphs<ParagraphVecShort<StrBuffer>>, StrBuffer>::new(
paragraphs.into_paragraphs(),
verb_cancel,
button,
),
))?;
Ok(obj.into())
@ -1670,9 +1665,10 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def confirm_with_info(
/// *,
/// title: str,
/// button: str, # unused on TR
/// button: str,
/// info_button: str, # unused on TR
/// items: Iterable[Tuple[int, str]],
/// verb_cancel: str | None = None,
/// ) -> object:
/// """Confirm given items but with third button. Always single page
/// without scrolling."""

@ -223,9 +223,10 @@ def show_mismatch() -> object:
def confirm_with_info(
*,
title: str,
button: str, # unused on TR
button: str,
info_button: str, # unused on TR
items: Iterable[Tuple[int, str]],
verb_cancel: str | None = None,
) -> object:
"""Confirm given items but with third button. Always single page
without scrolling."""

@ -711,6 +711,7 @@ async def should_show_more(
br_type: str = "should_show_more",
br_code: ButtonRequestType = BR_TYPE_OTHER,
confirm: str | bytes | None = None,
verb_cancel: str | None = None,
) -> bool:
"""Return True if the user wants to show more (they click a special button)
and False when the user wants to continue without showing details.
@ -726,7 +727,8 @@ async def should_show_more(
title=title.upper(),
items=para,
button=confirm.upper(),
info_button=button_text.upper(),
verb_cancel=verb_cancel, # type: ignore [No parameter named "verb_cancel"]
info_button=button_text.upper(), # unused on TR
)
),
br_type,
@ -747,6 +749,8 @@ async def confirm_blob(
title: str,
data: bytes | str,
description: str | None = None,
verb: str = "CONFIRM",
verb_cancel: str | None = "", # icon
hold: bool = False,
br_code: ButtonRequestType = BR_TYPE_OTHER,
ask_pagination: bool = False,
@ -759,15 +763,17 @@ async def confirm_blob(
description=description,
data=data,
extra=None,
verb_cancel="", # to show the cancel icon
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
verb_cancel="", # icon
)
)
if ask_pagination and layout.page_count() > 1:
assert not hold
await _confirm_ask_pagination(br_type, title, data, description, br_code)
await _confirm_ask_pagination(
br_type, title, data, description, verb_cancel, br_code
)
else:
await raise_if_not_confirmed(
@ -784,6 +790,7 @@ async def _confirm_ask_pagination(
title: str,
data: bytes | str,
description: str,
verb_cancel: str | None,
br_code: ButtonRequestType,
) -> None:
paginated: ui.Layout | None = None
@ -796,6 +803,7 @@ async def _confirm_ask_pagination(
if not await should_show_more(
title,
para=[(ui.NORMAL, description), (ui.MONO, data)],
verb_cancel=verb_cancel,
br_type=br_type,
br_code=br_code,
):
@ -806,7 +814,7 @@ async def _confirm_ask_pagination(
trezorui2.confirm_more(
title=title,
button="GO BACK",
items=[(ui.MONO, data)],
items=[(ui.BOLD, f"Size: {len(data)} bytes"), (ui.MONO, data)],
)
)
else:
@ -1089,29 +1097,31 @@ async def confirm_sign_identity(
async def confirm_signverify(
coin: str, message: str, address: str, verify: bool
) -> None:
if verify:
header = f"Verify {coin} message"
br_type = "verify_message"
else:
header = f"Sign {coin} message"
br_type = "sign_message"
br_type = "verify_message" if verify else "sign_message"
await confirm_blob(
br_type,
header.upper(),
address,
"Confirm address:",
br_code=BR_TYPE_OTHER,
)
# Allowing to go back from the second screen
while True:
await confirm_blob(
br_type,
"SIGNING ADDRESS",
address,
verb="CONTINUE",
br_code=BR_TYPE_OTHER,
)
await confirm_value(
header.upper(),
message,
"Confirm message:",
br_type,
BR_TYPE_OTHER,
verb="CONFIRM",
)
try:
await confirm_blob(
br_type,
"CONFIRM MESSAGE",
message,
verb_cancel="up_arrow_icon",
br_code=BR_TYPE_OTHER,
ask_pagination=True,
)
except ActionCancelled:
continue
else:
break
async def show_error_popup(

@ -725,6 +725,7 @@ async def confirm_blob(
data: bytes | str,
description: str | None = None,
verb: str = "CONFIRM",
verb_cancel: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = BR_TYPE_OTHER,
ask_pagination: bool = False,
@ -739,6 +740,7 @@ async def confirm_blob(
extra=None,
hold=hold,
verb=verb,
verb_cancel=verb_cancel,
)
)

@ -90,6 +90,15 @@ def test_cancel_on_paginated(client: Client):
resp = client._raw_read()
assert isinstance(resp, m.ButtonRequest)
# In TR, confirm message is no longer paginated by default,
# user needs to click info button
if client.debug.model == "R":
client._raw_write(m.ButtonAck())
client.debug.press_right()
resp = client._raw_read()
assert isinstance(resp, m.ButtonRequest)
assert resp.pages is not None
client._raw_write(m.ButtonAck())

@ -235,6 +235,11 @@ class InputFlowSignMessagePagination(InputFlowBase):
yield
self.debug.press_yes()
# press info
yield
self.debug.press_right()
# paginate through the whole message
br = yield
# TODO: try load the message_read the same way as in model T
if br.pages is not None:
@ -243,6 +248,10 @@ class InputFlowSignMessagePagination(InputFlowBase):
self.debug.swipe_up()
self.debug.press_yes()
# confirm message
yield
self.debug.press_yes()
class InputFlowShowAddressQRCode(InputFlowBase):
def __init__(self, client: Client):
@ -789,12 +798,18 @@ class InputFlowEthereumSignTxScrollDown(InputFlowBase):
self.debug.wait_layout()
self.debug.press_yes()
yield # confirm data
self.debug.press_info()
br = yield # paginated data
assert br.pages is not None
for _ in range(br.pages):
self.debug.wait_layout()
self.debug.swipe_up()
yield # confirm data
self.debug.press_yes()
yield # confirm amount
self.debug.wait_layout()
self.debug.press_yes()

Loading…
Cancel
Save