mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
refactor(core): convert apps.management.recovery_device to layouts
This commit is contained in:
parent
312876ab67
commit
5a0ea3f146
@ -4,9 +4,8 @@ import storage.recovery
|
||||
from trezor import config, ui, wire, workflow
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.components.tt.text import Text
|
||||
from trezor.ui.layouts import confirm_action, confirm_reset_device
|
||||
|
||||
from apps.common.confirm import require_confirm
|
||||
from apps.common.request_pin import (
|
||||
error_pin_invalid,
|
||||
request_pin_and_sd_salt,
|
||||
@ -90,15 +89,15 @@ def _validate(msg: RecoveryDevice) -> None:
|
||||
|
||||
async def _continue_dialog(ctx: wire.Context, msg: RecoveryDevice) -> None:
|
||||
if not msg.dry_run:
|
||||
text = Text("Recovery mode", ui.ICON_RECOVERY, new_lines=False)
|
||||
text.bold("Do you really want to recover a wallet?")
|
||||
text.br()
|
||||
text.br_half()
|
||||
text.normal("By continuing you agree")
|
||||
text.br()
|
||||
text.normal("to ")
|
||||
text.bold("https://trezor.io/tos")
|
||||
await confirm_reset_device(
|
||||
ctx, "Do you really want to\nrecover a wallet?", recovery=True
|
||||
)
|
||||
else:
|
||||
text = Text("Seed check", ui.ICON_RECOVERY, new_lines=False)
|
||||
text.normal("Do you really want to check the recovery seed?")
|
||||
await require_confirm(ctx, text, code=ButtonRequestType.ProtectCall)
|
||||
await confirm_action(
|
||||
ctx,
|
||||
"confirm_seedcheck",
|
||||
title="Seed check",
|
||||
description="Do you really want to check the recovery seed?",
|
||||
icon=ui.ICON_RECOVERY,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
|
@ -150,8 +150,7 @@ async def _finish_recovery(
|
||||
|
||||
|
||||
async def _request_word_count(ctx: wire.GenericContext, dry_run: bool) -> int:
|
||||
homepage = layout.RecoveryHomescreen("Select number of words")
|
||||
await layout.homescreen_dialog(ctx, homepage, "Select")
|
||||
await layout.homescreen_dialog(ctx, "Select", "Select number of words")
|
||||
|
||||
# ask for the number of words
|
||||
return await layout.request_word_count(ctx, dry_run)
|
||||
@ -187,15 +186,13 @@ async def _request_share_first_screen(
|
||||
if remaining:
|
||||
await _request_share_next_screen(ctx)
|
||||
else:
|
||||
content = layout.RecoveryHomescreen(
|
||||
"Enter any share", "(%d words)" % word_count
|
||||
await layout.homescreen_dialog(
|
||||
ctx, "Enter share", "Enter any share", "(%d words)" % word_count
|
||||
)
|
||||
await layout.homescreen_dialog(ctx, content, "Enter share")
|
||||
else: # BIP-39
|
||||
content = layout.RecoveryHomescreen(
|
||||
"Enter recovery seed", "(%d words)" % word_count
|
||||
await layout.homescreen_dialog(
|
||||
ctx, "Enter seed", "Enter recovery seed", "(%d words)" % word_count
|
||||
)
|
||||
await layout.homescreen_dialog(ctx, content, "Enter seed")
|
||||
|
||||
|
||||
async def _request_share_next_screen(ctx: wire.GenericContext) -> None:
|
||||
@ -206,14 +203,15 @@ async def _request_share_next_screen(ctx: wire.GenericContext) -> None:
|
||||
raise RuntimeError
|
||||
|
||||
if group_count > 1:
|
||||
content = layout.RecoveryHomescreen("More shares needed")
|
||||
await layout.homescreen_dialog(
|
||||
ctx, content, "Enter", _show_remaining_groups_and_shares
|
||||
ctx,
|
||||
"Enter",
|
||||
"More shares needed",
|
||||
info_func=_show_remaining_groups_and_shares,
|
||||
)
|
||||
else:
|
||||
text = strings.format_plural("{count} more {plural}", remaining[0], "share")
|
||||
content = layout.RecoveryHomescreen(text, "needed to enter")
|
||||
await layout.homescreen_dialog(ctx, content, "Enter share")
|
||||
await layout.homescreen_dialog(ctx, "Enter share", text, "needed to enter")
|
||||
|
||||
|
||||
async def _show_remaining_groups_and_shares(ctx: wire.GenericContext) -> None:
|
||||
|
@ -1,23 +1,22 @@
|
||||
import storage.recovery
|
||||
from trezor import strings, ui, wire
|
||||
from trezor.crypto.slip39 import MAX_SHARE_COUNT
|
||||
from trezor import ui, wire
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.ui.components.tt.scroll import Paginated
|
||||
from trezor.ui.components.tt.text import Text
|
||||
from trezor.ui.components.tt.word_select import WordSelector
|
||||
from trezor.ui.layouts import confirm_action, show_success, show_warning
|
||||
from trezor.ui.layouts.common import button_request
|
||||
|
||||
from apps.common.confirm import confirm, info_confirm, require_confirm
|
||||
from trezor.ui.layouts.tt.recovery import ( # noqa: F401
|
||||
continue_recovery,
|
||||
request_word,
|
||||
request_word_count,
|
||||
show_group_share_success,
|
||||
show_remaining_shares,
|
||||
)
|
||||
|
||||
from .. import backup_types
|
||||
from . import word_validity
|
||||
from .keyboard_bip39 import Bip39Keyboard
|
||||
from .keyboard_slip39 import Slip39Keyboard
|
||||
from .recover import RecoveryAborted
|
||||
|
||||
if False:
|
||||
from typing import Callable, Iterable
|
||||
from typing import Callable
|
||||
from trezor.enums import BackupType
|
||||
|
||||
|
||||
@ -44,37 +43,16 @@ async def confirm_abort(ctx: wire.GenericContext, dry_run: bool = False) -> None
|
||||
)
|
||||
|
||||
|
||||
async def request_word_count(ctx: wire.GenericContext, dry_run: bool) -> int:
|
||||
await button_request(ctx, code=ButtonRequestType.MnemonicWordCount)
|
||||
|
||||
if dry_run:
|
||||
text = Text("Seed check", ui.ICON_RECOVERY)
|
||||
else:
|
||||
text = Text("Recovery mode", ui.ICON_RECOVERY)
|
||||
text.normal("Number of words?")
|
||||
|
||||
count = await ctx.wait(WordSelector(text))
|
||||
# WordSelector can return int, or string if the value came from debuglink
|
||||
# ctx.wait has a return type Any
|
||||
# Hence, it is easier to convert the returned value to int explicitly
|
||||
return int(count)
|
||||
|
||||
|
||||
async def request_mnemonic(
|
||||
ctx: wire.GenericContext, word_count: int, backup_type: BackupType | None
|
||||
) -> str | None:
|
||||
await button_request(ctx, code=ButtonRequestType.MnemonicInput)
|
||||
await button_request(ctx, "mnemonic", code=ButtonRequestType.MnemonicInput)
|
||||
|
||||
words: list[str] = []
|
||||
for i in range(word_count):
|
||||
if backup_types.is_slip39_word_count(word_count):
|
||||
keyboard: Slip39Keyboard | Bip39Keyboard = Slip39Keyboard(
|
||||
"Type word %s of %s:" % (i + 1, word_count)
|
||||
)
|
||||
else:
|
||||
keyboard = Bip39Keyboard("Type word %s of %s:" % (i + 1, word_count))
|
||||
|
||||
word = await ctx.wait(keyboard)
|
||||
word = await request_word(
|
||||
ctx, i, word_count, is_slip39=backup_types.is_slip39_word_count(word_count)
|
||||
)
|
||||
words.append(word)
|
||||
|
||||
try:
|
||||
@ -92,52 +70,6 @@ async def request_mnemonic(
|
||||
return " ".join(words)
|
||||
|
||||
|
||||
async def show_remaining_shares(
|
||||
ctx: wire.GenericContext,
|
||||
groups: Iterable[tuple[int, tuple[str, ...]]], # remaining + list 3 words
|
||||
shares_remaining: list[int],
|
||||
group_threshold: int,
|
||||
) -> None:
|
||||
pages: list[ui.Component] = []
|
||||
for remaining, group in groups:
|
||||
if 0 < remaining < MAX_SHARE_COUNT:
|
||||
text = Text("Remaining Shares")
|
||||
text.bold(
|
||||
strings.format_plural(
|
||||
"{count} more {plural} starting", remaining, "share"
|
||||
)
|
||||
)
|
||||
for word in group:
|
||||
text.normal(word)
|
||||
pages.append(text)
|
||||
elif (
|
||||
remaining == MAX_SHARE_COUNT and shares_remaining.count(0) < group_threshold
|
||||
):
|
||||
text = Text("Remaining Shares")
|
||||
groups_remaining = group_threshold - shares_remaining.count(0)
|
||||
text.bold(
|
||||
strings.format_plural(
|
||||
"{count} more {plural} starting", groups_remaining, "group"
|
||||
)
|
||||
)
|
||||
for word in group:
|
||||
text.normal(word)
|
||||
pages.append(text)
|
||||
await confirm(ctx, Paginated(pages), cancel=None)
|
||||
|
||||
|
||||
async def show_group_share_success(
|
||||
ctx: wire.GenericContext, share_index: int, group_index: int
|
||||
) -> None:
|
||||
text = Text("Success", ui.ICON_CONFIRM)
|
||||
text.bold("You have entered")
|
||||
text.bold("Share %s" % (share_index + 1))
|
||||
text.normal("from")
|
||||
text.bold("Group %s" % (group_index + 1))
|
||||
|
||||
await confirm(ctx, text, confirm="Continue", cancel=None)
|
||||
|
||||
|
||||
async def show_dry_run_result(
|
||||
ctx: wire.GenericContext, result: bool, is_slip39: bool
|
||||
) -> None:
|
||||
@ -156,12 +88,14 @@ async def show_dry_run_result(
|
||||
|
||||
|
||||
async def show_dry_run_different_type(ctx: wire.GenericContext) -> None:
|
||||
text = Text("Dry run failure", ui.ICON_CANCEL)
|
||||
text.normal("Seed in the device was")
|
||||
text.normal("created using another")
|
||||
text.normal("backup mechanism.")
|
||||
await require_confirm(
|
||||
ctx, text, ButtonRequestType.ProtectCall, cancel=None, confirm="Continue"
|
||||
await show_warning(
|
||||
ctx,
|
||||
"warning_dry_recovery",
|
||||
header="Dry run failure",
|
||||
content="Seed in the device was\ncreated using another\nbackup mechanism.",
|
||||
icon=ui.ICON_CANCEL,
|
||||
icon_color=ui.ORANGE_ICON,
|
||||
br_code=ButtonRequestType.ProtectCall,
|
||||
)
|
||||
|
||||
|
||||
@ -204,72 +138,15 @@ async def show_group_threshold_reached(ctx: wire.GenericContext) -> None:
|
||||
)
|
||||
|
||||
|
||||
class RecoveryHomescreen(ui.Component):
|
||||
def __init__(self, text: str, subtext: str | None = None):
|
||||
super().__init__()
|
||||
self.text = text
|
||||
self.subtext = subtext
|
||||
self.dry_run = storage.recovery.is_dry_run()
|
||||
|
||||
def on_render(self) -> None:
|
||||
if not self.repaint:
|
||||
return
|
||||
|
||||
if self.dry_run:
|
||||
heading = "SEED CHECK"
|
||||
else:
|
||||
heading = "RECOVERY MODE"
|
||||
ui.header_warning(heading, clear=False)
|
||||
|
||||
if not self.subtext:
|
||||
ui.display.text_center(ui.WIDTH // 2, 80, self.text, ui.BOLD, ui.FG, ui.BG)
|
||||
else:
|
||||
ui.display.text_center(ui.WIDTH // 2, 65, self.text, ui.BOLD, ui.FG, ui.BG)
|
||||
ui.display.text_center(
|
||||
ui.WIDTH // 2, 92, self.subtext, ui.NORMAL, ui.FG, ui.BG
|
||||
)
|
||||
|
||||
ui.display.text_center(
|
||||
ui.WIDTH // 2, 130, "It is safe to eject Trezor", ui.NORMAL, ui.GREY, ui.BG
|
||||
)
|
||||
ui.display.text_center(
|
||||
ui.WIDTH // 2, 155, "and continue later", ui.NORMAL, ui.GREY, ui.BG
|
||||
)
|
||||
|
||||
self.repaint = False
|
||||
|
||||
if __debug__:
|
||||
|
||||
def read_content(self) -> list[str]:
|
||||
return [self.__class__.__name__, self.text, self.subtext or ""]
|
||||
|
||||
|
||||
async def homescreen_dialog(
|
||||
ctx: wire.GenericContext,
|
||||
homepage: RecoveryHomescreen,
|
||||
button_label: str,
|
||||
text: str,
|
||||
subtext: str | None = None,
|
||||
info_func: Callable | None = None,
|
||||
) -> None:
|
||||
while True:
|
||||
if info_func:
|
||||
continue_recovery = await info_confirm(
|
||||
ctx,
|
||||
homepage,
|
||||
code=ButtonRequestType.RecoveryHomepage,
|
||||
confirm=button_label,
|
||||
info_func=info_func,
|
||||
info="Info",
|
||||
cancel="Abort",
|
||||
)
|
||||
else:
|
||||
continue_recovery = await confirm(
|
||||
ctx,
|
||||
homepage,
|
||||
code=ButtonRequestType.RecoveryHomepage,
|
||||
confirm=button_label,
|
||||
major_confirm=True,
|
||||
)
|
||||
if continue_recovery:
|
||||
if await continue_recovery(ctx, button_label, text, subtext, info_func):
|
||||
# go forward in the recovery process
|
||||
break
|
||||
# user has chosen to abort, confirm the choice
|
||||
|
@ -1,7 +1,7 @@
|
||||
from trezor import loop, ui, wire
|
||||
|
||||
if False:
|
||||
from typing import Any, Awaitable
|
||||
from typing import Callable, Any, Awaitable
|
||||
|
||||
CONFIRMED = object()
|
||||
CANCELLED = object()
|
||||
@ -18,6 +18,20 @@ async def raise_if_cancelled(a: Awaitable, exc: Any = wire.ActionCancelled) -> N
|
||||
raise exc
|
||||
|
||||
|
||||
async def is_confirmed_info(
|
||||
ctx: wire.GenericContext,
|
||||
dialog: ui.Layout,
|
||||
info_func: Callable,
|
||||
) -> bool:
|
||||
while True:
|
||||
result = await ctx.wait(dialog)
|
||||
|
||||
if result is INFO:
|
||||
await info_func(ctx)
|
||||
else:
|
||||
return is_confirmed(result)
|
||||
|
||||
|
||||
class ConfirmBase(ui.Layout):
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -1,15 +1,11 @@
|
||||
from trezor import io, loop, res, ui, workflow
|
||||
from trezor.crypto import bip39
|
||||
from trezor.ui import display
|
||||
from trezor.ui.components.tt.button import (
|
||||
Button,
|
||||
ButtonClear,
|
||||
ButtonMono,
|
||||
ButtonMonoConfirm,
|
||||
)
|
||||
|
||||
from .button import Button, ButtonClear, ButtonMono, ButtonMonoConfirm
|
||||
|
||||
if False:
|
||||
from trezor.ui.components.tt.button import ButtonContent, ButtonStyleStateType
|
||||
from .button import ButtonContent, ButtonStyleStateType
|
||||
|
||||
|
||||
def compute_mask(text: str) -> int:
|
@ -1,15 +1,11 @@
|
||||
from trezor import io, loop, res, ui, workflow
|
||||
from trezor.crypto import slip39
|
||||
from trezor.ui import display
|
||||
from trezor.ui.components.tt.button import (
|
||||
Button,
|
||||
ButtonClear,
|
||||
ButtonMono,
|
||||
ButtonMonoConfirm,
|
||||
)
|
||||
|
||||
from .button import Button, ButtonClear, ButtonMono, ButtonMonoConfirm
|
||||
|
||||
if False:
|
||||
from trezor.ui.components.tt.button import ButtonContent, ButtonStyleStateType
|
||||
from .button import ButtonContent, ButtonStyleStateType
|
||||
|
||||
|
||||
class KeyButton(Button):
|
42
core/src/trezor/ui/components/tt/recovery.py
Normal file
42
core/src/trezor/ui/components/tt/recovery.py
Normal file
@ -0,0 +1,42 @@
|
||||
import storage.recovery
|
||||
from trezor import ui
|
||||
|
||||
|
||||
class RecoveryHomescreen(ui.Component):
|
||||
def __init__(self, text: str, subtext: str | None = None):
|
||||
super().__init__()
|
||||
self.text = text
|
||||
self.subtext = subtext
|
||||
self.dry_run = storage.recovery.is_dry_run()
|
||||
|
||||
def on_render(self) -> None:
|
||||
if not self.repaint:
|
||||
return
|
||||
|
||||
if self.dry_run:
|
||||
heading = "SEED CHECK"
|
||||
else:
|
||||
heading = "RECOVERY MODE"
|
||||
ui.header_warning(heading, clear=False)
|
||||
|
||||
if not self.subtext:
|
||||
ui.display.text_center(ui.WIDTH // 2, 80, self.text, ui.BOLD, ui.FG, ui.BG)
|
||||
else:
|
||||
ui.display.text_center(ui.WIDTH // 2, 65, self.text, ui.BOLD, ui.FG, ui.BG)
|
||||
ui.display.text_center(
|
||||
ui.WIDTH // 2, 92, self.subtext, ui.NORMAL, ui.FG, ui.BG
|
||||
)
|
||||
|
||||
ui.display.text_center(
|
||||
ui.WIDTH // 2, 130, "It is safe to eject Trezor", ui.NORMAL, ui.GREY, ui.BG
|
||||
)
|
||||
ui.display.text_center(
|
||||
ui.WIDTH // 2, 155, "and continue later", ui.NORMAL, ui.GREY, ui.BG
|
||||
)
|
||||
|
||||
self.repaint = False
|
||||
|
||||
if __debug__:
|
||||
|
||||
def read_content(self) -> list[str]:
|
||||
return [self.__class__.__name__, self.text, self.subtext or ""]
|
@ -149,8 +149,13 @@ async def confirm_action(
|
||||
)
|
||||
|
||||
|
||||
async def confirm_reset_device(ctx: wire.GenericContext, prompt: str) -> None:
|
||||
text = Text("Create new wallet", ui.ICON_RESET, new_lines=False)
|
||||
async def confirm_reset_device(
|
||||
ctx: wire.GenericContext, prompt: str, recovery: bool = False
|
||||
) -> None:
|
||||
if recovery:
|
||||
text = Text("Recovery mode", ui.ICON_RECOVERY, new_lines=False)
|
||||
else:
|
||||
text = Text("Create new wallet", ui.ICON_RESET, new_lines=False)
|
||||
text.bold(prompt)
|
||||
text.br()
|
||||
text.br_half()
|
||||
@ -161,9 +166,11 @@ async def confirm_reset_device(ctx: wire.GenericContext, prompt: str) -> None:
|
||||
await raise_if_cancelled(
|
||||
interact(
|
||||
ctx,
|
||||
Confirm(text, major_confirm=True),
|
||||
"setup_device",
|
||||
ButtonRequestType.ResetDevice,
|
||||
Confirm(text, major_confirm=not recovery),
|
||||
"recover_device" if recovery else "setup_device",
|
||||
ButtonRequestType.ProtectCall
|
||||
if recovery
|
||||
else ButtonRequestType.ResetDevice,
|
||||
)
|
||||
)
|
||||
|
||||
@ -442,6 +449,8 @@ def show_warning(
|
||||
subheader: str | None = None,
|
||||
button: str = "Try again",
|
||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||
icon: str = ui.ICON_WRONG,
|
||||
icon_color: int = ui.RED,
|
||||
) -> Awaitable[None]:
|
||||
return _show_modal(
|
||||
ctx,
|
||||
@ -452,8 +461,8 @@ def show_warning(
|
||||
content=content,
|
||||
button_confirm=button,
|
||||
button_cancel=None,
|
||||
icon=ui.ICON_WRONG,
|
||||
icon_color=ui.RED,
|
||||
icon=icon,
|
||||
icon_color=icon_color,
|
||||
)
|
||||
|
||||
|
||||
|
135
core/src/trezor/ui/layouts/tt/recovery.py
Normal file
135
core/src/trezor/ui/layouts/tt/recovery.py
Normal file
@ -0,0 +1,135 @@
|
||||
from trezor import strings, ui, wire
|
||||
from trezor.crypto.slip39 import MAX_SHARE_COUNT
|
||||
from trezor.enums import ButtonRequestType
|
||||
|
||||
from ...components.common.confirm import (
|
||||
is_confirmed,
|
||||
is_confirmed_info,
|
||||
raise_if_cancelled,
|
||||
)
|
||||
from ...components.tt.confirm import Confirm, InfoConfirm
|
||||
from ...components.tt.keyboard_bip39 import Bip39Keyboard
|
||||
from ...components.tt.keyboard_slip39 import Slip39Keyboard
|
||||
from ...components.tt.recovery import RecoveryHomescreen
|
||||
from ...components.tt.scroll import Paginated
|
||||
from ...components.tt.text import Text
|
||||
from ...components.tt.word_select import WordSelector
|
||||
from ..common import button_request, interact
|
||||
|
||||
if False:
|
||||
from typing import Callable, Iterable
|
||||
|
||||
|
||||
async def request_word_count(ctx: wire.GenericContext, dry_run: bool) -> int:
|
||||
await button_request(ctx, "word_count", code=ButtonRequestType.MnemonicWordCount)
|
||||
|
||||
if dry_run:
|
||||
text = Text("Seed check", ui.ICON_RECOVERY)
|
||||
else:
|
||||
text = Text("Recovery mode", ui.ICON_RECOVERY)
|
||||
text.normal("Number of words?")
|
||||
|
||||
count = await ctx.wait(WordSelector(text))
|
||||
# WordSelector can return int, or string if the value came from debuglink
|
||||
# ctx.wait has a return type Any
|
||||
# Hence, it is easier to convert the returned value to int explicitly
|
||||
return int(count)
|
||||
|
||||
|
||||
async def request_word(
|
||||
ctx: wire.GenericContext, word_index: int, word_count: int, is_slip39: bool
|
||||
) -> str:
|
||||
if is_slip39:
|
||||
keyboard: Slip39Keyboard | Bip39Keyboard = Slip39Keyboard(
|
||||
"Type word %s of %s:" % (word_index + 1, word_count)
|
||||
)
|
||||
else:
|
||||
keyboard = Bip39Keyboard("Type word %s of %s:" % (word_index + 1, word_count))
|
||||
|
||||
word: str = await ctx.wait(keyboard)
|
||||
return word
|
||||
|
||||
|
||||
async def show_remaining_shares(
|
||||
ctx: wire.GenericContext,
|
||||
groups: Iterable[tuple[int, tuple[str, ...]]], # remaining + list 3 words
|
||||
shares_remaining: list[int],
|
||||
group_threshold: int,
|
||||
) -> None:
|
||||
pages: list[ui.Component] = []
|
||||
for remaining, group in groups:
|
||||
if 0 < remaining < MAX_SHARE_COUNT:
|
||||
text = Text("Remaining Shares")
|
||||
text.bold(
|
||||
strings.format_plural(
|
||||
"{count} more {plural} starting", remaining, "share"
|
||||
)
|
||||
)
|
||||
for word in group:
|
||||
text.normal(word)
|
||||
pages.append(text)
|
||||
elif (
|
||||
remaining == MAX_SHARE_COUNT and shares_remaining.count(0) < group_threshold
|
||||
):
|
||||
text = Text("Remaining Shares")
|
||||
groups_remaining = group_threshold - shares_remaining.count(0)
|
||||
text.bold(
|
||||
strings.format_plural(
|
||||
"{count} more {plural} starting", groups_remaining, "group"
|
||||
)
|
||||
)
|
||||
for word in group:
|
||||
text.normal(word)
|
||||
pages.append(text)
|
||||
|
||||
pages[-1] = Confirm(pages[-1], cancel=None)
|
||||
await raise_if_cancelled(
|
||||
interact(ctx, Paginated(pages), "show_shares", ButtonRequestType.Other)
|
||||
)
|
||||
|
||||
|
||||
async def show_group_share_success(
|
||||
ctx: wire.GenericContext, share_index: int, group_index: int
|
||||
) -> None:
|
||||
text = Text("Success", ui.ICON_CONFIRM)
|
||||
text.bold("You have entered")
|
||||
text.bold("Share %s" % (share_index + 1))
|
||||
text.normal("from")
|
||||
text.bold("Group %s" % (group_index + 1))
|
||||
|
||||
await raise_if_cancelled(
|
||||
interact(
|
||||
ctx,
|
||||
Confirm(text, confirm="Continue", cancel=None),
|
||||
"share_success",
|
||||
ButtonRequestType.Other,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def continue_recovery(
|
||||
ctx: wire.GenericContext,
|
||||
button_label: str,
|
||||
text: str,
|
||||
subtext: str | None,
|
||||
info_func: Callable | None,
|
||||
) -> bool:
|
||||
homepage = RecoveryHomescreen(text, subtext)
|
||||
if info_func is not None:
|
||||
content = InfoConfirm(
|
||||
homepage,
|
||||
confirm=button_label,
|
||||
info="Info",
|
||||
cancel="Abort",
|
||||
)
|
||||
await button_request(ctx, "recovery", ButtonRequestType.RecoveryHomepage)
|
||||
return await is_confirmed_info(ctx, content, info_func)
|
||||
else:
|
||||
return is_confirmed(
|
||||
await interact(
|
||||
ctx,
|
||||
Confirm(homepage, confirm=button_label, major_confirm=True),
|
||||
"recovery",
|
||||
ButtonRequestType.RecoveryHomepage,
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue
Block a user