From bdc7fc8a7145c778d17e41cf31da19d11b173507 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 3 May 2026 12:58:20 -0500 Subject: [PATCH] feat(identity): add identity screen to getting started. --- meshchatx/meshchat.py | 39 +- meshchatx/src/backend/identity_manager.py | 19 +- .../src/frontend/components/TutorialModal.vue | 505 ++++++++++++++++-- tests/backend/test_identity_restore.py | 46 ++ .../backend/test_identity_restore_http_api.py | 99 ++++ tests/frontend/TutorialModalMigration.test.js | 237 ++++++++ 6 files changed, 878 insertions(+), 67 deletions(-) create mode 100644 tests/backend/test_identity_restore_http_api.py diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py index 3dfe137..4b875a2 100644 --- a/meshchatx/meshchat.py +++ b/meshchatx/meshchat.py @@ -1772,11 +1772,25 @@ class ReticulumMeshChat: ) return self.identity_manager.delete_identity(identity_hash, current_hash) - def restore_identity_from_bytes(self, identity_bytes: bytes): - return self.identity_manager.restore_identity_from_bytes(identity_bytes) + def restore_identity_from_bytes( + self, + identity_bytes: bytes, + display_name: str | None = None, + ): + return self.identity_manager.restore_identity_from_bytes( + identity_bytes, + display_name=display_name, + ) - def restore_identity_from_base32(self, base32_value: str): - return self.identity_manager.restore_identity_from_base32(base32_value) + def restore_identity_from_base32( + self, + base32_value: str, + display_name: str | None = None, + ): + return self.identity_manager.restore_identity_from_base32( + base32_value, + display_name=display_name, + ) def update_identity_metadata_cache(self): if not hasattr(self, "identity") or not self.identity: @@ -6086,7 +6100,17 @@ class ReticulumMeshChat: with open(temp_path, "rb") as f: identity_bytes = f.read() os.remove(temp_path) - result = self.restore_identity_from_bytes(identity_bytes) + display_name = None + next_field = await reader.next() + while next_field is not None: + if next_field.name == "display_name": + display_name = (await next_field.text()).strip() + break + next_field = await reader.next() + result = self.restore_identity_from_bytes( + identity_bytes, + display_name=display_name, + ) else: data = await request.json() base32_value = data.get("base32") @@ -6095,7 +6119,10 @@ class ReticulumMeshChat: {"message": "base32 value is required"}, status=400, ) - result = self.restore_identity_from_base32(base32_value) + result = self.restore_identity_from_base32( + base32_value, + display_name=data.get("display_name"), + ) return web.json_response( { diff --git a/meshchatx/src/backend/identity_manager.py b/meshchatx/src/backend/identity_manager.py index f3826a2..3998d05 100644 --- a/meshchatx/src/backend/identity_manager.py +++ b/meshchatx/src/backend/identity_manager.py @@ -225,21 +225,32 @@ class IdentityManager: return True return False - def restore_identity_from_bytes(self, identity_bytes: bytes) -> dict: + def restore_identity_from_bytes( + self, + identity_bytes: bytes, + display_name: str | None = None, + ) -> dict: try: # We use RNS.Identity.from_bytes to validate and get the hash identity = RNS.Identity.from_bytes(identity_bytes) if not identity: raise ValueError("Could not load identity from bytes") - return self._save_new_identity(identity, "Restored Identity") + name = (display_name or "").strip() or "Restored Identity" + return self._save_new_identity(identity, name) except Exception as exc: raise ValueError(f"Failed to restore identity: {exc}") from exc - def restore_identity_from_base32(self, base32_value: str) -> dict: + def restore_identity_from_base32( + self, + base32_value: str, + display_name: str | None = None, + ) -> dict: try: identity_bytes = base64.b32decode(base32_value, casefold=True) - return self.restore_identity_from_bytes(identity_bytes) + return self.restore_identity_from_bytes( + identity_bytes, display_name=display_name + ) except Exception as exc: msg = f"Invalid base32 identity: {exc}" raise ValueError(msg) from exc diff --git a/meshchatx/src/frontend/components/TutorialModal.vue b/meshchatx/src/frontend/components/TutorialModal.vue index 3fd08f6..916a566 100644 --- a/meshchatx/src/frontend/components/TutorialModal.vue +++ b/meshchatx/src/frontend/components/TutorialModal.vue @@ -77,7 +77,7 @@
- -
+ +
+
+

+ {{ $t("tutorial.identity_title") }} +

+

+ {{ $t("tutorial.identity_desc") }} +

+
+ +
+ + +
+
+ + +
+ +