async def confirm_output( ctx: wire.GenericContext, address: str, amount: str, font_amount: int = ui.NORMAL, # TODO cleanup @ redesign title: str = "Confirm sending", subtitle: str | None = None, # TODO cleanup @ redesign color_to: int = ui.FG, # TODO cleanup @ redesign to_str: str = " to\n", # TODO cleanup @ redesign to_paginated: bool = False, # TODO cleanup @ redesign width: int = MONO_ADDR_PER_LINE, width_paginated: int = MONO_ADDR_PER_LINE - 1, br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, icon: str = ui.ICON_SEND, ) -> None: header_lines = to_str.count("\n") + int(subtitle is not None) if len(address) > (TEXT_MAX_LINES - header_lines) * width: para = [] if subtitle is not None: para.append((ui.NORMAL, subtitle)) para.append((font_amount, amount)) if to_paginated: para.append((ui.NORMAL, "to")) para.extend( (ui.MONO, line) for line in chunks(address, width_paginated)) content: ui.Layout = paginate_paragraphs(para, title, icon, ui.GREEN) else: text = Text(title, icon, ui.GREEN, new_lines=False) if subtitle is not None: text.normal(subtitle, "\n") text.content = [ font_amount, amount, ui.NORMAL, color_to, to_str, ui.FG ] text.mono(*chunks_intersperse(address, width)) content = Confirm(text) await raise_if_cancelled(interact(ctx, content, "confirm_output", br_code))
async def confirm_blob( ctx: wire.GenericContext, br_type: str, title: str, data: bytes | str, description: str | None = None, hold: bool = False, br_code: ButtonRequestType = ButtonRequestType.Other, icon: str = ui.ICON_SEND, # TODO cleanup @ redesign icon_color: int = ui.GREEN, # TODO cleanup @ redesign ask_pagination: bool = False, ) -> None: """Confirm data blob. Applicable for public keys, signatures, hashes. In general, any kind of data that is not human-readable, and can be wrapped at any character. For addresses, use `confirm_address`. Displays in monospace font. Paginates automatically. If data is provided as bytes or bytearray, it is converted to hex. """ if isinstance(data, (bytes, bytearray)): data_str = hexlify(data).decode() else: data_str = data span = Span() lines = 0 if description is not None: span.reset(description, 0, ui.NORMAL) lines += span.count_lines() data_lines = (len(data_str) + MONO_HEX_PER_LINE - 1) // MONO_HEX_PER_LINE lines += data_lines if lines <= TEXT_MAX_LINES: text = Text(title, icon, icon_color, new_lines=False) if description is not None: text.normal(description) text.br() # special case: if len(data_str) % 16 == 0: # sanity checks: # (a) we must not exceed MONO_HEX_PER_LINE assert MONO_HEX_PER_LINE > 16 # (b) we must not increase number of lines assert (len(data_str) // 16) <= data_lines # the above holds true for MONO_HEX_PER_LINE == 18 and TEXT_MAX_LINES == 5 per_line = 16 else: per_line = MONO_HEX_PER_LINE text.mono(ui.FG, *chunks_intersperse(data_str, per_line)) content: ui.Layout = HoldToConfirm(text) if hold else Confirm(text) return await raise_if_cancelled( interact(ctx, content, br_type, br_code)) elif ask_pagination: para = [(ui.MONO, line) for line in chunks(data_str, MONO_HEX_PER_LINE - 2)] para_truncated = [] if description is not None: para_truncated.append((ui.NORMAL, description)) para_truncated.extend(para[:TEXT_MAX_LINES]) return await _confirm_ask_pagination(ctx, br_type, title, para, para_truncated, br_code, icon, icon_color) else: para = [] if description is not None: para.append((ui.NORMAL, description)) para.extend((ui.MONO, line) for line in chunks(data_str, MONO_HEX_PER_LINE - 2)) paginated = paginate_paragraphs( para, title, icon, icon_color, confirm=HoldToConfirm if hold else Confirm) return await raise_if_cancelled( interact(ctx, paginated, br_type, br_code))