Fix contact add failure

This commit is contained in:
Janez T
2026-03-15 09:19:59 +01:00
parent 4557fb4b9e
commit c2e0fa952e
14 changed files with 608 additions and 1662 deletions

View File

@@ -948,7 +948,7 @@ class AppProvider with ChangeNotifier {
contactName: contact.advName.trim().isEmpty
? null
: contact.advName,
),
),
);
}
if (contact.type == ContactType.repeater &&
@@ -966,9 +966,6 @@ class AppProvider with ChangeNotifier {
devicePublicKey: devicePublicKey,
);
unawaited(_pathHistoryService.recordLearnedPath(contact));
// Broadcast to SSE clients if server is running
connectionProvider.broadcastContactToSseClients(contact);
};
// When all contacts are received
@@ -982,11 +979,6 @@ class AppProvider with ChangeNotifier {
unawaited(_pathHistoryService.recordLearnedPath(contact));
}
debugPrint('Received ${contacts.length} contacts');
// Broadcast all contacts to SSE clients if server is running
for (final contact in contacts) {
connectionProvider.broadcastContactToSseClients(contact);
}
};
// Setup callback for ConnectionProvider to query channel info
@@ -1212,9 +1204,6 @@ class AppProvider with ChangeNotifier {
contactLocationSnapshot: contactLocationSnapshot,
receptionDetailsSnapshot: receptionDetailsSnapshot,
);
// Broadcast drawing message to SSE clients if server is running
connectionProvider.broadcastMessageToSseClients(updatedMessage);
} else {
debugPrint('⚠️ [AppProvider] Failed to parse drawing message');
}
@@ -1255,7 +1244,6 @@ class AppProvider with ChangeNotifier {
contactLocationSnapshot: contactLocationSnapshot,
receptionDetailsSnapshot: receptionDetailsSnapshot,
);
connectionProvider.broadcastMessageToSseClients(enrichedMessage);
return;
}
@@ -1289,7 +1277,6 @@ class AppProvider with ChangeNotifier {
contactLocationSnapshot: contactLocationSnapshot,
receptionDetailsSnapshot: receptionDetailsSnapshot,
);
connectionProvider.broadcastMessageToSseClients(enrichedMessage);
return;
}
@@ -1314,9 +1301,6 @@ class AppProvider with ChangeNotifier {
contactLocationSnapshot: contactLocationSnapshot,
receptionDetailsSnapshot: receptionDetailsSnapshot,
);
// Broadcast message to SSE clients if server is running
connectionProvider.broadcastMessageToSseClients(enrichedMessage);
};
// Keep a compact receive-time snapshot because packet logs roll over.
@@ -1353,20 +1337,21 @@ class AppProvider with ChangeNotifier {
connectionProvider.onRawDataReceived = (payload, snrRaw, rssiDbm) {
final parsedAdvert = _tryParseRawAdvert(payload);
if (parsedAdvert != null) {
final isNewPendingAdvert = contactsProvider.addOrUpdatePendingAdvertMetadata(
publicKey: parsedAdvert.publicKey,
typeValue: parsedAdvert.typeValue,
devicePublicKey: connectionProvider.deviceInfo.publicKey,
flags: parsedAdvert.flags,
advName: parsedAdvert.advName,
lastAdvert: parsedAdvert.lastAdvert,
advLat: parsedAdvert.advLat,
advLon: parsedAdvert.advLon,
signedEncodedPathLen: parsedAdvert.signedEncodedPathLen,
paddedPathBytes: parsedAdvert.paddedPathBytes,
rxRssiDbm: rssiDbm,
rxSnrRaw: snrRaw,
);
final isNewPendingAdvert = contactsProvider
.addOrUpdatePendingAdvertMetadata(
publicKey: parsedAdvert.publicKey,
typeValue: parsedAdvert.typeValue,
devicePublicKey: connectionProvider.deviceInfo.publicKey,
flags: parsedAdvert.flags,
advName: parsedAdvert.advName,
lastAdvert: parsedAdvert.lastAdvert,
advLat: parsedAdvert.advLat,
advLon: parsedAdvert.advLon,
signedEncodedPathLen: parsedAdvert.signedEncodedPathLen,
paddedPathBytes: parsedAdvert.paddedPathBytes,
rxRssiDbm: rssiDbm,
rxSnrRaw: snrRaw,
);
if (isNewPendingAdvert) {
final contactKey = parsedAdvert.publicKey
.map((b) => b.toRadixString(16).padLeft(2, '0'))
@@ -1382,7 +1367,9 @@ class AppProvider with ChangeNotifier {
unawaited(_requestRepeaterStatus(parsedAdvert.publicKey));
unawaited(_maybeRequestRepeaterOwnerInfo(parsedAdvert.publicKey));
}
if (contactsProvider.shouldEnrichPendingAdvert(parsedAdvert.publicKey)) {
if (contactsProvider.shouldEnrichPendingAdvert(
parsedAdvert.publicKey,
)) {
unawaited(connectionProvider.previewContact(parsedAdvert.publicKey));
}
return;
@@ -1669,7 +1656,9 @@ class AppProvider with ChangeNotifier {
unawaited(
_notificationService.showContactDiscoveredNotification(
contactKey: keyHex,
contactName: contactsProvider.pendingAdvertByKey(publicKey)?.advName,
contactName: contactsProvider
.pendingAdvertByKey(publicKey)
?.advName,
),
);
}
@@ -2248,10 +2237,9 @@ class AppProvider with ChangeNotifier {
}
try {
final payload = utf8.decode(
responseData.sublist(4),
allowMalformed: true,
).trim();
final payload = utf8
.decode(responseData.sublist(4), allowMalformed: true)
.trim();
if (payload.isEmpty) {
return null;
}
@@ -2351,10 +2339,9 @@ class AppProvider with ChangeNotifier {
String? advName;
if (hasName && reader.remainingBytesCount > 0) {
final decodedName = utf8.decode(
reader.readRemainingBytes(),
allowMalformed: true,
).trim();
final decodedName = utf8
.decode(reader.readRemainingBytes(), allowMalformed: true)
.trim();
if (decodedName.isNotEmpty) {
advName = decodedName;
}

View File

@@ -9,9 +9,7 @@ import 'package:crypto/crypto.dart';
import '../models/contact.dart';
import '../models/device_info.dart';
import '../models/room_login_state.dart';
import '../models/sse_server_config.dart';
import 'package:meshcore_client/meshcore_client.dart' hide Contact;
import '../services/sse_server_service.dart';
import '../utils/sar_message_parser.dart';
import 'helpers/room_login_manager.dart';
import 'helpers/message_delivery_tracker.dart';
@@ -73,7 +71,6 @@ class _PendingContactRequest {
class ConnectionProvider with ChangeNotifier {
static const int _controlTypeNodeDiscoverReq = 0x80;
final MeshCoreBleService _bleService = MeshCoreBleService();
final SseServerService _sseServer = SseServerService();
MeshCoreTcpService? _tcpService;
/// Expose BLE service for background location tracking
@@ -89,10 +86,6 @@ class ConnectionProvider with ChangeNotifier {
ConnectionMode _connectionMode = ConnectionMode.ble;
ConnectionMode get connectionMode => _connectionMode;
/// SSE server configuration
SseServerConfig _sseServerConfig = const SseServerConfig();
SseServerConfig get sseServerConfig => _sseServerConfig;
/// TCP host last connected to (for display / reconnection info)
String? _tcpHost;
String? get tcpHost => _tcpHost;
@@ -471,11 +464,6 @@ class ConnectionProvider with ChangeNotifier {
spectrumScanMaxKhz: deviceInfo['spectrumScanMaxKhz'] as int?,
);
notifyListeners();
if (_sseServer.isRunning) {
_sseServer.setDeviceName(
_deviceInfo.deviceName ?? _deviceInfo.selfName,
);
}
};
service.onSelfInfoReceived = (selfInfo) {
@@ -495,11 +483,6 @@ class ConnectionProvider with ChangeNotifier {
selfName: selfInfo['selfName'] as String?,
);
notifyListeners();
if (_sseServer.isRunning) {
_sseServer.setDeviceName(
_deviceInfo.deviceName ?? _deviceInfo.selfName,
);
}
};
service.onBatteryAndStorage = (millivolts, usedKb, totalKb) {
@@ -857,12 +840,19 @@ class ConnectionProvider with ChangeNotifier {
}
try {
_error = null;
_markSingleContactRequested(
publicKey,
source: ContactReceiveSource.requestedSingle,
);
await _activeService.getContactByKey(publicKey);
} catch (e) {
final errorText = e.toString();
if (_error == 'Not found' || errorText.contains('Not found')) {
_error = 'Not found';
notifyListeners();
return;
}
_error = 'Failed to get contact: $e';
debugPrint(
'⚠️ [Provider] Failed to get contact by key, falling back to full contact sync',
@@ -1360,6 +1350,7 @@ class ConnectionProvider with ChangeNotifier {
}
try {
_error = null;
await _activeService.addOrUpdateContact(contact);
} catch (e) {
_error = 'Failed to add/update contact: $e';
@@ -2578,110 +2569,6 @@ class ConnectionProvider with ChangeNotifier {
return _roomLoginManager.isLoggedIntoRoom(publicKeyPrefix);
}
// ============================================================================
// SSE Server Methods
// ============================================================================
/// Start SSE server to share BLE device with multiple clients
Future<void> startSseServer(SseServerConfig config) async {
if (_sseServer.isRunning) {
debugPrint('⚠️ [ConnectionProvider] SSE server already running');
return;
}
try {
debugPrint('🚀 [ConnectionProvider] Starting SSE server...');
_sseServerConfig = config;
// Wire up callbacks
_sseServer.onSendMessage = (recipientPublicKey, text) async {
// Convert hex string to Uint8List
final bytes = <int>[];
for (int i = 0; i < recipientPublicKey.length; i += 2) {
bytes.add(
int.parse(recipientPublicKey.substring(i, i + 2), radix: 16),
);
}
return await sendTextMessage(
contactPublicKey: Uint8List.fromList(bytes),
text: text,
);
};
_sseServer.onSendChannelMessage = (channelIdx, text) async {
await sendChannelMessage(channelIdx: channelIdx, text: text);
};
_sseServer.onSyncContacts = () async {
await getContacts();
};
await _sseServer.startServer(config);
// Set initial device name
_sseServer.setDeviceName(_deviceInfo.deviceName ?? _deviceInfo.selfName);
_connectionMode = ConnectionMode.sseServer;
notifyListeners();
debugPrint('✅ [ConnectionProvider] SSE server started');
} catch (e) {
_error = 'Failed to start SSE server: $e';
debugPrint('❌ [ConnectionProvider] Failed to start SSE server: $e');
notifyListeners();
rethrow;
}
}
/// Stop SSE server
Future<void> stopSseServer() async {
if (!_sseServer.isRunning) {
return;
}
debugPrint('🛑 [ConnectionProvider] Stopping SSE server...');
await _sseServer.stopServer();
if (_connectionMode == ConnectionMode.sseServer) {
_connectionMode = ConnectionMode.ble;
}
notifyListeners();
debugPrint('✅ [ConnectionProvider] SSE server stopped');
}
/// Broadcast message to SSE clients (call this when receiving messages from BLE)
void broadcastMessageToSseClients(Message message) {
if (_sseServer.isRunning) {
_sseServer.broadcastMessage(message);
}
}
/// Broadcast contact to SSE clients (call this when receiving contacts from BLE)
void broadcastContactToSseClients(Contact contact) {
if (_sseServer.isRunning) {
_sseServer.broadcastContact(contact);
}
}
/// Get SSE server status
bool get isSseServerRunning => _sseServer.isRunning;
/// Get number of connected SSE clients
int get sseClientCount => _sseServer.connectedClients;
/// Set connection mode
void setConnectionMode(ConnectionMode mode) {
_connectionMode = mode;
notifyListeners();
}
/// Update SSE server configuration
void updateSseServerConfig(SseServerConfig config) {
_sseServerConfig = config;
notifyListeners();
}
@override
void dispose() {
_rxActivityTimer?.cancel();
@@ -2689,7 +2576,6 @@ class ConnectionProvider with ChangeNotifier {
_stopAckCleanupTimer();
_bleService.dispose();
_tcpService?.dispose();
_sseServer.stopServer();
super.dispose();
}
}