feat: Implement optimized contact fetching by public key to enhance performance

This commit is contained in:
Janez T
2025-10-28 10:44:36 +01:00
parent d4bc847530
commit a1207ad844
6 changed files with 56 additions and 5 deletions

View File

@@ -236,11 +236,12 @@ class AppProvider with ChangeNotifier {
// When a contact's routing path is updated in the mesh network // When a contact's routing path is updated in the mesh network
connectionProvider.onPathUpdated = (publicKey) { connectionProvider.onPathUpdated = (publicKey) {
debugPrint('🔄 [AppProvider] Path updated for contact: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...'); debugPrint('🔄 [AppProvider] Path updated for contact: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...');
// Trigger a contact sync to get the updated path information // Trigger a single contact fetch to get the updated path information
// This is much more efficient than fetching all contacts
// This happens asynchronously to avoid blocking the event handler // This happens asynchronously to avoid blocking the event handler
Future.delayed(const Duration(milliseconds: 100), () { Future.delayed(const Duration(milliseconds: 100), () {
if (connectionProvider.deviceInfo.isConnected) { if (connectionProvider.deviceInfo.isConnected) {
connectionProvider.getContacts(); connectionProvider.getContact(publicKey);
} }
}); });
}; };
@@ -252,11 +253,12 @@ class AppProvider with ChangeNotifier {
// Check if this is an existing contact that might have updated location // Check if this is an existing contact that might have updated location
final contact = contactsProvider.findContactByKey(publicKey); final contact = contactsProvider.findContactByKey(publicKey);
if (contact != null) { if (contact != null) {
debugPrint(' Existing contact "${contact.advName}" - triggering contact sync for updates'); debugPrint(' Existing contact "${contact.advName}" - fetching updated contact info (optimized)');
// Trigger a contact sync to get the updated contact information // Trigger a single contact fetch to get the updated contact information
// This is much more efficient than fetching all contacts
Future.delayed(const Duration(milliseconds: 100), () { Future.delayed(const Duration(milliseconds: 100), () {
if (connectionProvider.deviceInfo.isConnected) { if (connectionProvider.deviceInfo.isConnected) {
connectionProvider.getContacts(); connectionProvider.getContact(publicKey);
} }
}); });
} else { } else {

View File

@@ -686,6 +686,30 @@ class ConnectionProvider with ChangeNotifier {
} }
} }
/// Get a single contact by public key from device
///
/// This is more efficient than getContacts() when you only need to refresh
/// one specific contact (e.g., after receiving an advertisement or path update).
///
/// The contact will be delivered via the onContactReceived callback.
Future<void> getContact(Uint8List publicKey) async {
if (!_bleService.isConnected) {
_error = 'Not connected to device';
notifyListeners();
return;
}
try {
await _bleService.getContactByKey(publicKey);
} catch (e) {
_error = 'Failed to get contact: $e';
debugPrint('⚠️ [Provider] Failed to get contact by key, falling back to full contact sync');
// Fallback to full contact sync if command not supported
await _bleService.getContacts();
notifyListeners();
}
}
/// Sync all channels from device /// Sync all channels from device
Future<void> syncChannels({int? maxChannels}) async { Future<void> syncChannels({int? maxChannels}) async {
if (!_bleService.isConnected) { if (!_bleService.isConnected) {

View File

@@ -311,6 +311,20 @@ class MeshCoreBleService {
await _commandSender.writeData(FrameBuilder.buildGetContacts()); await _commandSender.writeData(FrameBuilder.buildGetContacts());
} }
/// Get a single contact by public key from device
///
/// This is more efficient than getContacts() when you only need to refresh
/// one specific contact (e.g., after receiving an advertisement).
///
/// The contact will be delivered via the onContactReceived callback.
Future<void> getContactByKey(Uint8List publicKey) async {
debugPrint('🔍 [BLE] Requesting single contact by key:');
debugPrint(
' Public key prefix: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...',
);
await _commandSender.writeData(FrameBuilder.buildGetContactByKey(publicKey));
}
/// Manually add or update a contact on the companion radio /// Manually add or update a contact on the companion radio
Future<void> addOrUpdateContact(Contact contact) async { Future<void> addOrUpdateContact(Contact contact) async {
debugPrint('📝 [BLE] Adding/updating contact on companion radio:'); debugPrint('📝 [BLE] Adding/updating contact on companion radio:');

View File

@@ -39,6 +39,7 @@ class MeshCoreConstants {
static const int cmdSendRawData = 25; static const int cmdSendRawData = 25;
static const int cmdSendLogin = 26; static const int cmdSendLogin = 26;
static const int cmdSendStatusReq = 27; static const int cmdSendStatusReq = 27;
static const int cmdGetContactByKey = 30;
static const int cmdGetChannel = 31; static const int cmdGetChannel = 31;
static const int cmdSetChannel = 32; static const int cmdSetChannel = 32;
static const int cmdSignStart = 33; static const int cmdSignStart = 33;

View File

@@ -59,6 +59,8 @@ class MeshCoreOpcodeNames {
return 'SEND_LOGIN'; return 'SEND_LOGIN';
case MeshCoreConstants.cmdSendStatusReq: case MeshCoreConstants.cmdSendStatusReq:
return 'SEND_STATUS_REQ'; return 'SEND_STATUS_REQ';
case MeshCoreConstants.cmdGetContactByKey:
return 'GET_CONTACT_BY_KEY';
case MeshCoreConstants.cmdGetChannel: case MeshCoreConstants.cmdGetChannel:
return 'GET_CHANNEL'; return 'GET_CHANNEL';
case MeshCoreConstants.cmdSetChannel: case MeshCoreConstants.cmdSetChannel:

View File

@@ -31,6 +31,14 @@ class FrameBuilder {
return writer.toBytes(); return writer.toBytes();
} }
/// Build GetContactByKey command - retrieves a single contact by public key
static Uint8List buildGetContactByKey(Uint8List publicKey) {
final writer = BufferWriter();
writer.writeByte(MeshCoreConstants.cmdGetContactByKey); // 0x1E (30)
writer.writeBytes(publicKey); // 32 bytes
return writer.toBytes();
}
/// Build AddUpdateContact command /// Build AddUpdateContact command
static Uint8List buildAddUpdateContact(Contact contact) { static Uint8List buildAddUpdateContact(Contact contact) {
final writer = BufferWriter(); final writer = BufferWriter();