Refactor contact handling and enhance UI with new advert options and localized strings

This commit is contained in:
Winston Lowe
2026-01-28 11:04:34 -08:00
parent d0c8fab6fb
commit 42115bf200
5 changed files with 52 additions and 37 deletions
-7
View File
@@ -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');
}
+21
View File
@@ -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) {
+5 -1
View File
@@ -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"
}
+4
View File
@@ -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);
+22 -29
View File
@@ -177,7 +177,7 @@ class _ContactsScreenState extends State<ContactsScreen>
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<ContactsScreen>
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<ContactsScreen>
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<ContactsScreen>
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<ContactsScreen>
),
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<ContactsScreen>
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<ContactsScreen>
_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,
);
}