The archive confirm names the contact with its type icon, as the Contacts list does
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5cba667542
commit
4ba0a29691
3 files changed
+48
-9
No files matched your search
@@ -1538,10 +1538,30 @@ async def _archive_contact(ctx: AppContext, contact: Contact, self_key: str, lab
|
||||
assert isinstance(ctx.ui, TuiUi) # guaranteed by open_node_detail
|
||||
session = ctx.ui.session
|
||||
|
||||
# The contact is named as the Contacts list names it: the type mark in its own colour,
|
||||
# then the name in its key-derived hue (a bare id standing in for a nameless one is
|
||||
# ``node.unknown``). The prose carries the amber span by span rather than as the
|
||||
# prompt's base style, whose bold Rich would merge into the mark.
|
||||
glyph, glyph_style = NODE_GLYPHS.get(contact.node_type, DEFAULT_GLYPH)
|
||||
prompt = Text()
|
||||
prompt.append("Archive ", style="warn")
|
||||
prompt.append(f"{glyph} ", style=glyph_style)
|
||||
prompt.append(
|
||||
label,
|
||||
style=(
|
||||
name_style(label, contact.public_key or contact.key_prefix)
|
||||
if contact.name
|
||||
else "node.unknown"
|
||||
),
|
||||
)
|
||||
prompt.append(
|
||||
"? It comes off this device's contact list, freeing a slot for a new one. MeshTerm "
|
||||
"keeps it — with its key, reception history and messages — and you can restore it "
|
||||
"at any time.",
|
||||
style="warn",
|
||||
)
|
||||
if not await ctx.ui.dialog(
|
||||
f"Archive {label}? It comes off this device's contact list, freeing a slot for a "
|
||||
"new one. MeshTerm keeps it — with its key, reception history and messages — and "
|
||||
"you can restore it at any time.",
|
||||
prompt,
|
||||
[("Cancel", False), ("Archive", True)],
|
||||
title="Archive contact",
|
||||
default=1,
|
||||
|
||||
@@ -330,7 +330,7 @@ class Ui:
|
||||
|
||||
async def dialog(
|
||||
self,
|
||||
prompt: str,
|
||||
prompt: str | Text,
|
||||
buttons: list[tuple[str, Any]],
|
||||
*,
|
||||
title: str = "",
|
||||
@@ -348,6 +348,10 @@ class Ui:
|
||||
sibling. Buttons follow the platform-dialog convention: the safe way out sits on
|
||||
the left and the committing action on the right, which is also the sensible
|
||||
``default`` so Enter commits it while Esc always backs out.
|
||||
|
||||
A pre-styled :class:`~rich.text.Text` prompt is drawn as it is, so it can name a
|
||||
node in its own colours; the tier then themes only the border, and the prompt's
|
||||
prose carries the tier's colour itself.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -634,7 +638,7 @@ class PlainUi(Ui):
|
||||
|
||||
async def dialog(
|
||||
self,
|
||||
prompt: str,
|
||||
prompt: str | Text,
|
||||
buttons: list[tuple[str, Any]],
|
||||
*,
|
||||
title: str = "",
|
||||
@@ -905,7 +909,7 @@ class TuiUi(Ui):
|
||||
|
||||
async def dialog(
|
||||
self,
|
||||
prompt: str,
|
||||
prompt: str | Text,
|
||||
buttons: list[tuple[str, Any]],
|
||||
*,
|
||||
title: str = "",
|
||||
|
||||
@@ -1035,12 +1035,15 @@ async def test_archiving_one_contact_takes_it_off_the_device_and_keeps_it() -> N
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
|
||||
from meshterm.core.models import Contact
|
||||
from meshterm.core.models import NODE_TYPE_REPEATER, Contact
|
||||
from meshterm.ui.node_detail_screen import _archive_contact
|
||||
from meshterm.ui.surface import TuiUi
|
||||
from meshterm.ui.theme import name_style
|
||||
from meshterm.ui.tui.session import TuiSession
|
||||
|
||||
hub = Contact(name="Hub", public_key="3d" * 32, key_prefix="3d" * 6)
|
||||
hub = Contact(
|
||||
name="Hub", public_key="3d" * 32, key_prefix="3d" * 6, node_type=NODE_TYPE_REPEATER
|
||||
)
|
||||
removed: list[str] = []
|
||||
archived: list[tuple[str, str]] = []
|
||||
invalidated: list[bool] = []
|
||||
@@ -1073,7 +1076,19 @@ async def test_archiving_one_contact_takes_it_off_the_device_and_keeps_it() -> N
|
||||
assert asked[0]["danger"] is True and "destructive" not in asked[0]
|
||||
assert [label for label, _v in asked[0]["buttons"]] == ["Cancel", "Archive"]
|
||||
assert asked[0]["default"] == 1
|
||||
assert "restore it at any time" in asked[0]["prompt"]
|
||||
prompt = asked[0]["prompt"]
|
||||
assert "restore it at any time" in prompt
|
||||
# The contact is named as the Contacts list names it — its type mark to the left of the
|
||||
# name, each in its own colour — while the sentence around them keeps the amber.
|
||||
assert prompt.plain.startswith("Archive ▲ Hub? ")
|
||||
|
||||
def styles_at(index: int) -> list[str]:
|
||||
return [str(span.style) for span in prompt.spans if span.start <= index < span.end]
|
||||
|
||||
assert styles_at(prompt.plain.index("▲")) == ["type.repeater"]
|
||||
assert styles_at(prompt.plain.index("Hub")) == [name_style("Hub", "3d" * 32)]
|
||||
assert styles_at(0) == styles_at(prompt.plain.index("restore")) == ["warn"]
|
||||
assert not prompt.style, "a base style would merge the amber's bold into the mark"
|
||||
# Off the radio, kept here, and the cached contact list dropped so the list re-reads.
|
||||
assert removed == ["Hub"]
|
||||
assert archived == [("cc" * 32, "3d" * 32)]
|
||||
|
||||
Reference in new issue
Block a user