mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 12:28:09 +00:00
fix(core/ui): T3T1 ButtonRequests from rust
[no changelog]
This commit is contained in:
parent
a212b325fe
commit
b16411631b
@ -101,6 +101,8 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_bitcoin__voting_rights;
|
||||
MP_QSTR_bootscreen;
|
||||
MP_QSTR_bounds;
|
||||
MP_QSTR_br_code;
|
||||
MP_QSTR_br_type;
|
||||
MP_QSTR_button;
|
||||
MP_QSTR_button_event;
|
||||
MP_QSTR_button_request;
|
||||
|
@ -63,3 +63,21 @@ pub trait ButtonRequestExt {
|
||||
}
|
||||
|
||||
impl<T: Component> ButtonRequestExt for T {}
|
||||
|
||||
#[cfg(all(feature = "micropython", feature = "touch", feature = "new_rendering"))]
|
||||
impl<T> crate::ui::flow::Swipable for OneButtonRequest<T>
|
||||
where
|
||||
T: Component + crate::ui::flow::Swipable,
|
||||
{
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: crate::ui::component::SwipeDirection,
|
||||
) -> bool {
|
||||
self.inner.swipe_start(ctx, direction)
|
||||
}
|
||||
|
||||
fn swipe_finished(&self) -> bool {
|
||||
self.inner.swipe_finished()
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{ComponentExt, SwipeDirection},
|
||||
button_request::ButtonRequest,
|
||||
component::{ButtonRequestExt, ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
},
|
||||
};
|
||||
@ -91,6 +92,8 @@ impl ConfirmOutput {
|
||||
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
||||
let account_path: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_account_path)?.try_into_option()?;
|
||||
let br_type: TString = kwargs.get(Qstr::MP_QSTR_br_type)?.try_into()?;
|
||||
let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
|
||||
|
||||
let address: Obj = kwargs.get(Qstr::MP_QSTR_address)?;
|
||||
let amount: Obj = kwargs.get(Qstr::MP_QSTR_amount)?;
|
||||
@ -105,8 +108,8 @@ impl ConfirmOutput {
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_chunkify(chunkify)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()?;
|
||||
// .one_button_request(ButtonRequestCode::ConfirmOutput, br_type);
|
||||
.into_layout()?
|
||||
.one_button_request(ButtonRequest::from_tstring(br_code, br_type));
|
||||
|
||||
// Amount
|
||||
let content_amount = ConfirmBlobParams::new(TR::words__amount.into(), amount, None)
|
||||
@ -114,8 +117,8 @@ impl ConfirmOutput {
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()?;
|
||||
// .one_button_request(ButtonRequestCode::ConfirmOutput, br_type);
|
||||
.into_layout()?
|
||||
.one_button_request(ButtonRequest::from_tstring(br_code, br_type));
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
|
@ -4,7 +4,8 @@ use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{ComponentExt, SwipeDirection},
|
||||
button_request::ButtonRequest,
|
||||
component::{ButtonRequestExt, ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow},
|
||||
},
|
||||
};
|
||||
@ -91,6 +92,8 @@ impl ConfirmSummary {
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let account_items: Obj = kwargs.get(Qstr::MP_QSTR_account_items)?;
|
||||
let fee_items: Obj = kwargs.get(Qstr::MP_QSTR_fee_items)?;
|
||||
let br_type: TString = kwargs.get(Qstr::MP_QSTR_br_type)?.try_into()?;
|
||||
let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
|
||||
|
||||
// Summary
|
||||
let mut summary = ShowInfoParams::new(title)
|
||||
@ -100,7 +103,9 @@ impl ConfirmSummary {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
summary = unwrap!(summary.add(label, value));
|
||||
}
|
||||
let content_summary = summary.into_layout()?;
|
||||
let content_summary = summary
|
||||
.into_layout()?
|
||||
.one_button_request(ButtonRequest::from_tstring(br_code, br_type));
|
||||
|
||||
// Hold to confirm
|
||||
let content_hold = Frame::left_aligned(
|
||||
|
@ -4,9 +4,10 @@ use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::ButtonRequest,
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphSource, Paragraphs},
|
||||
ComponentExt, Qr, SwipeDirection,
|
||||
ButtonRequestExt, ComponentExt, Qr, SwipeDirection,
|
||||
},
|
||||
flow::{
|
||||
base::Decision, flow_store, FlowMsg, FlowState, FlowStore, IgnoreSwipe, SwipeFlow,
|
||||
@ -147,6 +148,9 @@ impl GetAddress {
|
||||
let path: Option<TString> = kwargs.get(Qstr::MP_QSTR_path)?.try_into_option()?;
|
||||
let xpubs: Obj = kwargs.get(Qstr::MP_QSTR_xpubs)?;
|
||||
|
||||
let br_type: TString = kwargs.get(Qstr::MP_QSTR_br_type)?.try_into()?;
|
||||
let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
|
||||
|
||||
// Address
|
||||
let data_style = if chunkify {
|
||||
let address: TString = address.try_into()?;
|
||||
@ -166,8 +170,8 @@ impl GetAddress {
|
||||
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");
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info))
|
||||
.one_button_request(ButtonRequest::from_tstring(br_code, br_type));
|
||||
|
||||
// Tap
|
||||
let content_tap = Frame::left_aligned(title, PromptScreen::new_tap_to_confirm())
|
||||
|
@ -1980,6 +1980,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// account: str | None,
|
||||
/// path: str | None,
|
||||
/// xpubs: list[tuple[str, str]],
|
||||
/// br_code: ButtonRequestType,
|
||||
/// br_type: 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(),
|
||||
@ -2001,6 +2003,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// chunkify: bool,
|
||||
/// account: str | None,
|
||||
/// account_path: str | None,
|
||||
/// br_code: ButtonRequestType,
|
||||
/// br_type: str,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Confirm recipient."""
|
||||
Qstr::MP_QSTR_flow_confirm_output => obj_fn_kw!(0, flow::new_confirm_output).as_obj(),
|
||||
@ -2011,6 +2015,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// items: Iterable[tuple[str, str]],
|
||||
/// account_items: Iterable[tuple[str, str]],
|
||||
/// fee_items: Iterable[tuple[str, str]],
|
||||
/// br_code: ButtonRequestType,
|
||||
/// br_type: str,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Total summary and hold to confirm."""
|
||||
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, flow::new_confirm_summary).as_obj(),
|
||||
|
@ -537,6 +537,8 @@ def flow_get_address(
|
||||
account: str | None,
|
||||
path: str | None,
|
||||
xpubs: list[tuple[str, str]],
|
||||
br_code: ButtonRequestType,
|
||||
br_type: str,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Get address / receive funds."""
|
||||
|
||||
@ -560,6 +562,8 @@ def flow_confirm_output(
|
||||
chunkify: bool,
|
||||
account: str | None,
|
||||
account_path: str | None,
|
||||
br_code: ButtonRequestType,
|
||||
br_type: str,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Confirm recipient."""
|
||||
|
||||
@ -571,6 +575,8 @@ def flow_confirm_summary(
|
||||
items: Iterable[tuple[str, str]],
|
||||
account_items: Iterable[tuple[str, str]],
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
br_code: ButtonRequestType,
|
||||
br_type: str,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Total summary and hold to confirm."""
|
||||
CONFIRMED: UiResult
|
||||
|
@ -1,10 +1,10 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import trezorui2
|
||||
from trezor import TR, io, loop, ui, utils
|
||||
from trezor import TR, io, log, loop, ui, utils
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.wire import ActionCancelled
|
||||
from trezor.wire.context import wait as ctx_wait
|
||||
from trezor.messages import ButtonAck, ButtonRequest
|
||||
from trezor.wire import ActionCancelled, context
|
||||
|
||||
from ..common import button_request, interact
|
||||
|
||||
@ -34,9 +34,11 @@ class RustLayout(ui.Layout):
|
||||
|
||||
# pylint: disable=super-init-not-called
|
||||
def __init__(self, layout: Any):
|
||||
self.br_chan = loop.chan()
|
||||
self.layout = layout
|
||||
self.timer = loop.Timer()
|
||||
self.layout.attach_timer_fn(self.set_timer)
|
||||
self._send_button_request()
|
||||
|
||||
def set_timer(self, token: int, deadline: int) -> None:
|
||||
self.timer.schedule(deadline, token)
|
||||
@ -57,13 +59,23 @@ class RustLayout(ui.Layout):
|
||||
if __debug__:
|
||||
|
||||
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
|
||||
return (
|
||||
self.handle_timers(),
|
||||
self.handle_input_and_rendering(),
|
||||
self.handle_swipe(),
|
||||
self.handle_click_signal(),
|
||||
self.handle_result_signal(),
|
||||
)
|
||||
if context.CURRENT_CONTEXT:
|
||||
return (
|
||||
self.handle_timers(),
|
||||
self.handle_input_and_rendering(),
|
||||
self.handle_swipe(),
|
||||
self.handle_click_signal(),
|
||||
self.handle_result_signal(),
|
||||
self.handle_usb(context.get_context()),
|
||||
)
|
||||
else:
|
||||
return (
|
||||
self.handle_timers(),
|
||||
self.handle_input_and_rendering(),
|
||||
self.handle_swipe(),
|
||||
self.handle_click_signal(),
|
||||
self.handle_result_signal(),
|
||||
)
|
||||
|
||||
async def handle_result_signal(self) -> None:
|
||||
"""Enables sending arbitrary input - ui.Result.
|
||||
@ -110,6 +122,7 @@ class RustLayout(ui.Layout):
|
||||
(io.TOUCH_END, orig_x + 2 * off_x, orig_y + 2 * off_y),
|
||||
):
|
||||
msg = self.layout.touch_event(event, x, y)
|
||||
self._send_button_request()
|
||||
self._paint()
|
||||
if msg is not None:
|
||||
raise ui.Result(msg)
|
||||
@ -129,10 +142,12 @@ class RustLayout(ui.Layout):
|
||||
from apps.debug import notify_layout_change
|
||||
|
||||
self.layout.touch_event(io.TOUCH_START, x, y)
|
||||
self._send_button_request()
|
||||
self._paint()
|
||||
if hold_ms is not None:
|
||||
await loop.sleep(hold_ms)
|
||||
msg = self.layout.touch_event(io.TOUCH_END, x, y)
|
||||
self._send_button_request()
|
||||
|
||||
if msg is not None:
|
||||
debug_storage.new_layout_event_id = event_id
|
||||
@ -159,7 +174,17 @@ class RustLayout(ui.Layout):
|
||||
else:
|
||||
|
||||
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
|
||||
return self.handle_timers(), self.handle_input_and_rendering()
|
||||
if context.CURRENT_CONTEXT:
|
||||
return (
|
||||
self.handle_timers(),
|
||||
self.handle_input_and_rendering(),
|
||||
self.handle_usb(context.get_context()),
|
||||
)
|
||||
else:
|
||||
return (
|
||||
self.handle_timers(),
|
||||
self.handle_input_and_rendering(),
|
||||
)
|
||||
|
||||
def _first_paint(self) -> None:
|
||||
ui.backlight_fade(ui.style.BACKLIGHT_NONE)
|
||||
@ -199,6 +224,7 @@ class RustLayout(ui.Layout):
|
||||
msg = None
|
||||
if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END):
|
||||
msg = self.layout.touch_event(event, x, y)
|
||||
self._send_button_request()
|
||||
if msg is not None:
|
||||
raise ui.Result(msg)
|
||||
self._paint()
|
||||
@ -208,6 +234,7 @@ class RustLayout(ui.Layout):
|
||||
# Using `yield` instead of `await` to avoid allocations.
|
||||
token = yield self.timer
|
||||
msg = self.layout.timer(token)
|
||||
self._send_button_request()
|
||||
if msg is not None:
|
||||
raise ui.Result(msg)
|
||||
self._paint()
|
||||
@ -215,6 +242,20 @@ class RustLayout(ui.Layout):
|
||||
def page_count(self) -> int:
|
||||
return self.layout.page_count()
|
||||
|
||||
async def handle_usb(self, ctx: context.Context):
|
||||
while True:
|
||||
br_code, br_type, page_count = await loop.race(
|
||||
ctx.read(()), self.br_chan.take()
|
||||
)
|
||||
log.debug(__name__, "ButtonRequest.type=%s", br_type)
|
||||
await ctx.call(ButtonRequest(code=br_code, pages=page_count), ButtonAck)
|
||||
|
||||
def _send_button_request(self):
|
||||
res = self.layout.button_request()
|
||||
if res is not None:
|
||||
br_code, br_type = res
|
||||
self.br_chan.publish((br_code, br_type, self.layout.page_count()))
|
||||
|
||||
|
||||
def draw_simple(layout: Any) -> None:
|
||||
# Simple drawing not supported for layouts that set timers.
|
||||
@ -421,22 +462,20 @@ async def show_address(
|
||||
return result
|
||||
|
||||
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,
|
||||
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_type,
|
||||
br_code=br_code,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -551,21 +590,18 @@ async def confirm_output(
|
||||
else:
|
||||
title = TR.send__title_sending_to
|
||||
|
||||
# TODO: this should send 2x ButtonRequest
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_output(
|
||||
address=address,
|
||||
amount=amount,
|
||||
title=title,
|
||||
chunkify=chunkify,
|
||||
account=source_account,
|
||||
account_path=source_account_path,
|
||||
)
|
||||
),
|
||||
"confirm_output",
|
||||
br_code,
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_output(
|
||||
address=address,
|
||||
amount=amount,
|
||||
title=title,
|
||||
chunkify=chunkify,
|
||||
account=source_account,
|
||||
account_path=source_account_path,
|
||||
br_code=br_code,
|
||||
br_type="confirm_output",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -877,17 +913,15 @@ async def confirm_total(
|
||||
fee_items.append((TR.confirm_total__fee_rate, fee_rate_amount))
|
||||
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_summary(
|
||||
title=title,
|
||||
items=items,
|
||||
fee_items=fee_items,
|
||||
account_items=account_items,
|
||||
)
|
||||
),
|
||||
br_type,
|
||||
br_code,
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_summary(
|
||||
title=title,
|
||||
items=items,
|
||||
fee_items=fee_items,
|
||||
account_items=account_items,
|
||||
br_type=br_type,
|
||||
br_code=br_code,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -910,10 +944,10 @@ async def confirm_summary(
|
||||
items=items or (),
|
||||
fee_items=(),
|
||||
account_items=info_items or (),
|
||||
br_type=br_type,
|
||||
br_code=br_code,
|
||||
)
|
||||
)
|
||||
# TODO br_type,
|
||||
# TODO br_code,
|
||||
)
|
||||
|
||||
|
||||
@ -1109,7 +1143,7 @@ async def confirm_modify_output(
|
||||
address_layout.page_count(),
|
||||
)
|
||||
address_layout.request_complete_repaint()
|
||||
await raise_if_not_confirmed(ctx_wait(address_layout))
|
||||
await raise_if_not_confirmed(address_layout)
|
||||
|
||||
if send_button_request:
|
||||
send_button_request = False
|
||||
@ -1119,7 +1153,7 @@ async def confirm_modify_output(
|
||||
modify_layout.page_count(),
|
||||
)
|
||||
modify_layout.request_complete_repaint()
|
||||
result = await ctx_wait(modify_layout)
|
||||
result = await modify_layout
|
||||
|
||||
if result is CONFIRMED:
|
||||
break
|
||||
@ -1134,11 +1168,11 @@ async def with_info(
|
||||
await button_request(br_type, br_code, pages=main_layout.page_count())
|
||||
|
||||
while True:
|
||||
result = await ctx_wait(main_layout)
|
||||
result = await main_layout
|
||||
|
||||
if result is INFO:
|
||||
info_layout.request_complete_repaint()
|
||||
result = await ctx_wait(info_layout)
|
||||
result = await info_layout
|
||||
assert result is CANCELLED
|
||||
main_layout.request_complete_repaint()
|
||||
continue
|
||||
@ -1266,8 +1300,8 @@ async def confirm_signverify(
|
||||
address_layout, info_layout, br_type, br_code=BR_TYPE_OTHER
|
||||
)
|
||||
if result is not CONFIRMED:
|
||||
result = await ctx_wait(
|
||||
RustLayout(trezorui2.show_mismatch(title=TR.addr_mismatch__mismatch))
|
||||
result = await RustLayout(
|
||||
trezorui2.show_mismatch(title=TR.addr_mismatch__mismatch)
|
||||
)
|
||||
assert result in (CONFIRMED, CANCELLED)
|
||||
# Right button aborts action, left goes back to showing address.
|
||||
|
@ -3,7 +3,6 @@ from typing import Callable, Iterable
|
||||
import trezorui2
|
||||
from trezor import TR
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.wire.context import wait as ctx_wait
|
||||
|
||||
from ..common import interact
|
||||
from . import RustLayout, raise_if_not_confirmed
|
||||
@ -17,7 +16,7 @@ async def _is_confirmed_info(
|
||||
info_func: Callable,
|
||||
) -> bool:
|
||||
while True:
|
||||
result = await ctx_wait(dialog)
|
||||
result = await dialog
|
||||
|
||||
if result is trezorui2.INFO:
|
||||
await info_func()
|
||||
@ -50,7 +49,7 @@ async def request_word(
|
||||
)
|
||||
)
|
||||
|
||||
word: str = await ctx_wait(keyboard)
|
||||
word: str = await keyboard
|
||||
return word
|
||||
|
||||
|
||||
@ -143,7 +142,7 @@ async def continue_recovery(
|
||||
if info_func is not None:
|
||||
return await _is_confirmed_info(homepage, info_func)
|
||||
else:
|
||||
result = await ctx_wait(homepage)
|
||||
result = await homepage
|
||||
return result is CONFIRMED
|
||||
|
||||
|
||||
|
@ -4,7 +4,6 @@ import trezorui2
|
||||
from trezor import TR
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.wire import ActionCancelled
|
||||
from trezor.wire.context import wait as ctx_wait
|
||||
|
||||
from ..common import interact
|
||||
from . import RustLayout, raise_if_not_confirmed
|
||||
@ -76,15 +75,13 @@ async def select_word(
|
||||
while len(words) < 3:
|
||||
words.append(words[-1])
|
||||
|
||||
result = await ctx_wait(
|
||||
RustLayout(
|
||||
trezorui2.select_word(
|
||||
title=TR.reset__select_word_x_of_y_template.format(
|
||||
checked_index + 1, count
|
||||
),
|
||||
description=description,
|
||||
words=(words[0], words[1], words[2]),
|
||||
)
|
||||
result = await RustLayout(
|
||||
trezorui2.select_word(
|
||||
title=TR.reset__select_word_x_of_y_template.format(
|
||||
checked_index + 1, count
|
||||
),
|
||||
description=description,
|
||||
words=(words[0], words[1], words[2]),
|
||||
)
|
||||
)
|
||||
if __debug__ and isinstance(result, str):
|
||||
@ -164,13 +161,11 @@ async def _prompt_number(
|
||||
assert isinstance(value, int)
|
||||
return value
|
||||
|
||||
await ctx_wait(
|
||||
RustLayout(
|
||||
trezorui2.show_simple(
|
||||
title=None,
|
||||
description=info(value),
|
||||
button=TR.buttons__ok_i_understand,
|
||||
)
|
||||
await RustLayout(
|
||||
trezorui2.show_simple(
|
||||
title=None,
|
||||
description=info(value),
|
||||
button=TR.buttons__ok_i_understand,
|
||||
)
|
||||
)
|
||||
num_input.request_complete_repaint()
|
||||
|
Loading…
Reference in New Issue
Block a user