mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-17 06:10:47 +10:00
Dev discovery (#291)
* Refactor contact handling: replace DiscoveryContact with Contact, update related methods and settings * Enhance contact handling: include latitude, longitude, and last modified timestamp in contact updates; refactor path handling to accommodate discovered contacts across multiple screens * Enhance SNRIndicator: include discovered contacts in name resolution for repeaters * Refactor path handling: replace addReturnPath with buildPath to improve path construction logic and handle target contact types * Update lib/screens/map_screen.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add localization for "Show Discovery Contacts" in multiple languages and refactor location plausibility check in map screen * Enhance contact management: update discovered contacts' active status and improve contact handling with flags and raw packet data * Refactor ChannelsScreen: pass ChannelMessageStore to buildExpandedContent and ensure messages are cleared after channel creation * Update MapScreen: adjust label zoom threshold and refactor guessed marker building to include labels * Refactor ChannelsScreen: change channelMessageStore to a private getter and update its usage in buildExpandedContent calls * Enhance location plausibility check: add latitude and longitude bounds to ensure valid coordinates * Update lib/connector/meshcore_connector.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Refactor MeshCoreConnector and related stores: update discovered contacts handling, migrate legacy keys, and set public key in community store * Refactor MeshCoreConnector and ChannelsScreen: update discovered contacts handling and set public key in community store; enhance location plausibility check in MapScreen * Update CMD_ADD_UPDATE_CONTACT frame format to include optional latitude and longitude fields --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'package:meshcore_open/models/discovery_contact.dart';
|
||||
import 'package:pointycastle/export.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
@@ -120,7 +119,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
|
||||
final List<ScanResult> _scanResults = [];
|
||||
final List<Contact> _contacts = [];
|
||||
final List<DiscoveryContact> _discoveredContacts = [];
|
||||
final List<Contact> _discoveredContacts = [];
|
||||
final List<Channel> _channels = [];
|
||||
final Map<String, List<Message>> _conversations = {};
|
||||
final Map<int, List<ChannelMessage>> _channelMessages = {};
|
||||
@@ -281,7 +280,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
List<DiscoveryContact> get discoveredContacts {
|
||||
List<Contact> get discoveredContacts {
|
||||
return List.unmodifiable(_discoveredContacts);
|
||||
}
|
||||
|
||||
@@ -664,7 +663,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
// Initialize notification service
|
||||
_notificationService.initialize();
|
||||
_loadChannelOrder();
|
||||
_loadDiscoveredContactCache();
|
||||
|
||||
// Initialize retry service callbacks
|
||||
_retryService?.initialize(
|
||||
@@ -1904,7 +1902,11 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
Future<void> removeContact(Contact contact) async {
|
||||
if (!isConnected) return;
|
||||
|
||||
_handleDiscovery(contact, Uint8List(0), noNotify: true);
|
||||
_handleDiscovery(
|
||||
contact,
|
||||
contact.rawPacket ?? Uint8List(0),
|
||||
noNotify: true,
|
||||
);
|
||||
|
||||
await sendFrame(buildRemoveContactFrame(contact.publicKey));
|
||||
_contacts.removeWhere((c) => c.publicKeyHex == contact.publicKeyHex);
|
||||
@@ -1920,7 +1922,20 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> removeDiscoveredContact(DiscoveryContact contact) async {
|
||||
Future<void> updateKnownDiscovered() async {
|
||||
if (!isConnected) return;
|
||||
for (int i = 0; i < _discoveredContacts.length; i++) {
|
||||
_discoveredContacts[i] = _discoveredContacts[i].copyWith(
|
||||
isActive: _knownContactKeys.contains(
|
||||
_discoveredContacts[i].publicKeyHex,
|
||||
),
|
||||
);
|
||||
}
|
||||
unawaited(_persistDiscoveredContacts());
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> removeDiscoveredContact(Contact contact) async {
|
||||
if (!isConnected) return;
|
||||
_discoveredContacts.removeWhere(
|
||||
(c) => c.publicKeyHex == contact.publicKeyHex,
|
||||
@@ -1929,7 +1944,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> importDiscoveredContact(DiscoveryContact contact) async {
|
||||
Future<void> importDiscoveredContact(Contact contact) async {
|
||||
if (!isConnected) return;
|
||||
|
||||
await sendFrame(
|
||||
@@ -1938,11 +1953,23 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
contact.path,
|
||||
contact.pathLength,
|
||||
type: contact.type,
|
||||
flags: 0,
|
||||
flags: contact.flags,
|
||||
name: contact.name,
|
||||
lat: contact.latitude,
|
||||
lon: contact.longitude,
|
||||
lastModified: contact.lastSeen,
|
||||
),
|
||||
);
|
||||
|
||||
// Update the discovered contact to mark it as active (imported)
|
||||
final discoveredIndex = _discoveredContacts.indexWhere(
|
||||
(c) => c.publicKeyHex == contact.publicKeyHex,
|
||||
);
|
||||
if (discoveredIndex >= 0) {
|
||||
_discoveredContacts[discoveredIndex] =
|
||||
_discoveredContacts[discoveredIndex].copyWith(isActive: true);
|
||||
}
|
||||
|
||||
_handleContactAdvert(
|
||||
Contact(
|
||||
publicKey: contact.publicKey,
|
||||
@@ -1953,6 +1980,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
latitude: contact.latitude,
|
||||
longitude: contact.longitude,
|
||||
lastSeen: DateTime.now(),
|
||||
flags: contact.flags,
|
||||
),
|
||||
);
|
||||
notifyListeners();
|
||||
@@ -1969,6 +1997,8 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
final existing = _contacts[existingIndex];
|
||||
// Use copyWith to preserve pathOverride and pathOverrideBytes
|
||||
_contacts[existingIndex] = existing.copyWith(
|
||||
pathOverride: null,
|
||||
pathOverrideBytes: null,
|
||||
pathLength: -1,
|
||||
path: Uint8List(0),
|
||||
);
|
||||
@@ -2324,6 +2354,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
debugPrint('Got END_OF_CONTACTS');
|
||||
_isLoadingContacts = false;
|
||||
_preserveContactsOnRefresh = false;
|
||||
unawaited(updateKnownDiscovered());
|
||||
notifyListeners();
|
||||
unawaited(_persistContacts());
|
||||
if (PlatformInfo.isWeb &&
|
||||
@@ -2510,6 +2541,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
// Load persisted channel messages
|
||||
loadAllChannelMessages();
|
||||
loadUnreadState();
|
||||
_loadDiscoveredContactCache();
|
||||
|
||||
_awaitingSelfInfo = false;
|
||||
_selfInfoRetryTimer?.cancel();
|
||||
@@ -4406,7 +4438,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
}
|
||||
|
||||
importDiscoveredContact(
|
||||
DiscoveryContact(
|
||||
Contact(
|
||||
rawPacket: frame,
|
||||
publicKey: publicKey,
|
||||
name: name,
|
||||
@@ -4477,6 +4509,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
|
||||
if (isNewContact) {
|
||||
final newContact = Contact(
|
||||
rawPacket: rawPacket,
|
||||
publicKey: publicKey,
|
||||
name: name,
|
||||
type: type,
|
||||
@@ -4622,13 +4655,15 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
latitude: contact.latitude,
|
||||
longitude: contact.longitude,
|
||||
lastSeen: contact.lastSeen,
|
||||
flags: 0,
|
||||
isActive: false,
|
||||
);
|
||||
notifyListeners();
|
||||
unawaited(_persistDiscoveredContacts());
|
||||
return;
|
||||
}
|
||||
|
||||
final disContact = DiscoveryContact(
|
||||
final disContact = Contact(
|
||||
rawPacket: rawPacket,
|
||||
publicKey: contact.publicKey,
|
||||
name: contact.name,
|
||||
@@ -4638,6 +4673,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
latitude: contact.latitude,
|
||||
longitude: contact.longitude,
|
||||
lastSeen: contact.lastSeen,
|
||||
lastMessageAt: contact.lastMessageAt,
|
||||
isActive: false,
|
||||
flags: 0,
|
||||
);
|
||||
_discoveredContacts.add(disContact);
|
||||
|
||||
|
||||
@@ -148,6 +148,19 @@ class BufferWriter {
|
||||
void writeHex(String hex) {
|
||||
writeBytes(hex2Uint8List(hex));
|
||||
}
|
||||
|
||||
void writeBytesPadded(Uint8List bytes, int totalLength) {
|
||||
// Path data (64 bytes, zero-padded)
|
||||
final bytesPadded = Uint8List(totalLength);
|
||||
final len = bytes.length < totalLength ? bytes.length : totalLength;
|
||||
if (bytes.isNotEmpty && len > 0) {
|
||||
final copyLen = bytes.length < totalLength ? bytes.length : totalLength;
|
||||
for (int i = 0; i < copyLen; i++) {
|
||||
bytesPadded[i] = bytes[i];
|
||||
}
|
||||
}
|
||||
writeBytes(bytesPadded);
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List hex2Uint8List(String hex) {
|
||||
@@ -676,14 +689,17 @@ Uint8List buildResetPathFrame(Uint8List pubKey) {
|
||||
}
|
||||
|
||||
// Build CMD_ADD_UPDATE_CONTACT frame to set custom path
|
||||
// Format: [cmd][pub_key x32][type][flags][path_len][path x64][name x32][timestamp x4]
|
||||
// Format: [cmd][pub_key x32][type][flags][path_len][path x64][name x32][Lat? x4, Lon? x4][timestamp? x4]
|
||||
Uint8List buildUpdateContactPathFrame(
|
||||
Uint8List pubKey,
|
||||
Uint8List customPath,
|
||||
Uint8List path,
|
||||
int pathLen, {
|
||||
int type = 1, // ADV_TYPE_CHAT
|
||||
int flags = 0,
|
||||
String name = '',
|
||||
double? lat,
|
||||
double? lon,
|
||||
DateTime? lastModified,
|
||||
}) {
|
||||
final writer = BufferWriter();
|
||||
writer.writeByte(cmdAddUpdateContact);
|
||||
@@ -692,17 +708,7 @@ Uint8List buildUpdateContactPathFrame(
|
||||
writer.writeByte(flags);
|
||||
writer.writeByte(pathLen);
|
||||
|
||||
// Path data (64 bytes, zero-padded)
|
||||
final pathPadded = Uint8List(maxPathSize);
|
||||
if (customPath.isNotEmpty && pathLen > 0) {
|
||||
final copyLen = customPath.length < maxPathSize
|
||||
? customPath.length
|
||||
: maxPathSize;
|
||||
for (int i = 0; i < copyLen; i++) {
|
||||
pathPadded[i] = customPath[i];
|
||||
}
|
||||
}
|
||||
writer.writeBytes(pathPadded);
|
||||
writer.writeBytesPadded(path, maxPathSize);
|
||||
|
||||
// Name (32 bytes, null-padded)
|
||||
writer.writeCString(name, maxNameSize);
|
||||
@@ -711,6 +717,27 @@ Uint8List buildUpdateContactPathFrame(
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
writer.writeUInt32LE(timestamp);
|
||||
|
||||
if ((lat == null || lon == null) && lastModified != null) {
|
||||
// If lat/lon not provided, write zeros
|
||||
writer.writeInt32LE(0);
|
||||
writer.writeInt32LE(0);
|
||||
} else {
|
||||
// Latitude and Longitude are expected in degrees, convert to int by multiplying by 1e6
|
||||
// Latitude
|
||||
final latitude = lat ?? 0.0;
|
||||
writer.writeInt32LE((latitude * 1e6).round());
|
||||
|
||||
// Longitude
|
||||
final longitude = lon ?? 0.0;
|
||||
writer.writeInt32LE((longitude * 1e6).round());
|
||||
}
|
||||
|
||||
if (lastModified != null) {
|
||||
// Last modified
|
||||
final lastModifiedTimestamp = lastModified.millisecondsSinceEpoch ~/ 1000;
|
||||
writer.writeUInt32LE(lastModifiedTimestamp);
|
||||
}
|
||||
|
||||
return writer.toBytes();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user