diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index a5c52902..de39d7e4 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -1680,12 +1680,6 @@ class MeshCoreConnector extends ChangeNotifier { _isLoadingContacts = true; notifyListeners(); break; - case pushCodeNewAdvert: - debugPrint('Got NEW_ADVERT'); - _handleContact(frame); - notifyListeners(); - unawaited(_persistContacts()); - break; case respCodeContact: debugPrint('Got CONTACT'); _handleContact(frame); @@ -1743,7 +1737,6 @@ class MeshCoreConnector extends ChangeNotifier { break; case respCodeCustomVars: _handleCustomVars(frame); - break; default: debugPrint('Unknown frame code: $code'); } diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index 0609adbd..df077583 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -18,6 +18,10 @@ class BufferReader { return data; } + void skipBytes(int count) { + _pointer += count; + } + Uint8List readRemainingBytes() => readBytes(remaining); String readString() => @@ -135,6 +139,7 @@ const int cmdSendStatusReq = 27; const int cmdGetContactByKey = 30; const int cmdGetChannel = 31; const int cmdSetChannel = 32; +const int cmdSendTracePath = 36; const int cmdGetRadioSettings = 57; const int cmdGetTelemetryReq = 39; const int cmdGetCustomVar = 40; @@ -185,6 +190,7 @@ const int pushCodeLoginSuccess = 0x85; const int pushCodeLoginFail = 0x86; const int pushCodeStatusResponse = 0x87; const int pushCodeLogRxData = 0x88; +const int pushCodeTraceData = 0x89; const int pushCodeNewAdvert = 0x8A; const int pushCodeTelemetryResponse = 0x8B; const int pushCodeBinaryResponse = 0x8C; @@ -718,6 +724,21 @@ Uint8List buildSendBinaryReq(Uint8List repeaterPubKey, {Uint8List? payload}) { return writer.toBytes(); } +//Build a trace request frame +//[cmd][tag x4][auth x4][flag][payload] +Uint8List buildTraceReq(int tag, int auth, int flag, {Uint8List? payload}) +{ + final writer = BufferWriter(); + writer.writeByte(cmdSendTracePath); + writer.writeUInt32LE(tag); + writer.writeUInt32LE(auth); + writer.writeByte(flag); + if (payload != null && payload.isNotEmpty) { + writer.writeBytes(payload); + } + return writer.toBytes(); +} + // Build a export contact frame // [cmd][pub_key x32 / if empty exports your contact info] Uint8List buildExportContactFrame(Uint8List pubKey) { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 3a9383ae..706d3613 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1327,5 +1327,9 @@ "contacts_clipboardEmpty": "Clipboard Is Empty.", "contacts_invalidAdvertFormat": "Invalid Contact Data", "contacts_contactImported": "Contact has been Imported.", - "contacts_contactImportFailed": "Contact Failed to Imported." + "contacts_contactImportFailed": "Contact Failed to Imported.", + "contacts_zeroHopAdvert":"Zero Hop Advert", + "contacts_floodAdvert":"Flood Advert", + "contacts_copyAdvertToClipboard":"Copy Advert to Clipboard", + "contacts_addContactFromClipboard":"Add Contact from Clipboard" } diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 364defff..9599e010 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -102,6 +102,10 @@ class Contact { return parts.join(','); } + String get shortPubKeyHex { + return "<${publicKeyHex.substring(0, 8)}...${publicKeyHex.substring(publicKeyHex.length - 8)}>"; + } + Uint8List get _pathBytesForDisplay { if (pathOverride != null) { if (pathOverride! < 0) return Uint8List(0); diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 64c29242..e9aef263 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -177,7 +177,7 @@ class _ContactsScreenState extends State children: [ const Icon(Icons.connect_without_contact), const SizedBox(width: 8), - Text("Zero Hop Advert"), + Text(context.l10n.contacts_zeroHopAdvert), ], ), onTap: () => { @@ -192,7 +192,7 @@ class _ContactsScreenState extends State children: [ const Icon(Icons.cell_tower), const SizedBox(width: 8), - Text("Flood Advert"), + Text(context.l10n.contacts_floodAdvert), ], ), onTap: () => { @@ -207,7 +207,7 @@ class _ContactsScreenState extends State children: [ const Icon(Icons.copy), const SizedBox(width: 8), - Text("Copy Advert to Clipboard"), + Text(context.l10n.contacts_copyAdvertToClipboard), ], ), onTap: () => _contactExport(Uint8List.fromList([])), @@ -217,7 +217,7 @@ class _ContactsScreenState extends State children: [ const Icon(Icons.paste), const SizedBox(width: 8), - Text("Add Contact from Clipboard"), + Text(context.l10n.contacts_addContactFromClipboard), ], ), onTap: () => _contactImport(), @@ -227,12 +227,22 @@ class _ContactsScreenState extends State ), PopupMenuButton( itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.logout, color: Colors.red), + const SizedBox(width: 8), + Text(context.l10n.common_disconnect), + ], + ), + onTap: () => _disconnect(context, connector), + ), PopupMenuItem( child: Row( children: [ const Icon(Icons.settings), const SizedBox(width: 8), - const Text('Settings'), + Text(context.l10n.settings_title), ], ), onTap: () => Navigator.push( @@ -240,16 +250,6 @@ class _ContactsScreenState extends State MaterialPageRoute(builder: (context) => const SettingsScreen()), ), ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.logout, color: Colors.red), - const SizedBox(width: 8), - const Text('Disconnect'), - ], - ), - onTap: () => _disconnect(context, connector), - ) ], icon: const Icon(Icons.more_vert), ), @@ -930,7 +930,7 @@ class _ContactsScreenState extends State _showRoomLogin(context, contact, RoomLoginDestination.management); }, ), - ] else + ] else ...[ ListTile( leading: const Icon(Icons.chat), title: Text(context.l10n.contacts_openChat), @@ -1005,17 +1005,16 @@ class _ContactTile extends StatelessWidget { @override Widget build(BuildContext context) { - final shotPublicKey = - "<${contact.publicKeyHex.substring(0, 8)}...${contact.publicKeyHex.substring(contact.publicKeyHex.length - 8)}>"; return ListTile( leading: CircleAvatar( backgroundColor: _getTypeColor(contact.type), child: _buildContactAvatar(contact), ), title: Text(contact.name), - subtitle: Text( - '${contact.typeLabel}\n${contact.shortPubKeyHex}', - ), + subtitle: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(contact.pathLabel), + Text(contact.shortPubKeyHex, style: TextStyle(fontSize: 12)) + ],), // Clamp text scaling in trailing section to prevent overflow while // maintaining accessibility. Primary content (title/subtitle) scales normally. trailing: MediaQuery( @@ -1039,21 +1038,15 @@ class _ContactTile extends StatelessWidget { Row( mainAxisSize: MainAxisSize.min, children: [ - Text(contact.pathLabel, - style: TextStyle(fontSize: 12, color: Colors.grey[600])), if (contact.hasLocation) Icon(Icons.location_on, size: 14, color: Colors.grey[400]), ], ), - Text( - _formatLastSeen(context, lastSeen), - style: TextStyle(fontSize: 12, color: Colors.grey[600]), - ), - if (contact.hasLocation) - Icon(Icons.location_on, size: 14, color: Colors.grey[400]), ], ), ), + onTap: onTap, + onLongPress: onLongPress, ); }