From 06632ee32d6dd7aab5ba8f8b3e78826d7cdfafd1 Mon Sep 17 00:00:00 2001 From: Janez T Date: Sat, 14 Mar 2026 10:01:17 +0100 Subject: [PATCH] Update log display handling --- ios/fastlane/report.xml | 10 +- lib/providers/messages_provider.dart | 105 ++++++++++++++---- lib/services/message_storage_service.dart | 19 ++++ pubspec.lock | 4 +- pubspec.yaml | 2 +- .../message_storage_service_test.dart | 2 + 6 files changed, 110 insertions(+), 32 deletions(-) diff --git a/ios/fastlane/report.xml b/ios/fastlane/report.xml index 13832ca..d952e5e 100644 --- a/ios/fastlane/report.xml +++ b/ios/fastlane/report.xml @@ -5,24 +5,22 @@ - + - + - + - - - + diff --git a/lib/providers/messages_provider.dart b/lib/providers/messages_provider.dart index d04c739..ded9d76 100644 --- a/lib/providers/messages_provider.dart +++ b/lib/providers/messages_provider.dart @@ -549,6 +549,15 @@ class MessagesProvider with ChangeNotifier { } } + final incomingPathBytes = receptionDetailsSnapshot?.pathBytes; + if (incomingPathBytes != null && + incomingPathBytes.isNotEmpty && + finalMessage.pathBytes == null) { + finalMessage = finalMessage.copyWith( + pathBytes: Uint8List.fromList(incomingPathBytes), + ); + } + // Debug: Check if message is SAR if (message.text.startsWith('S:')) { debugPrint( @@ -582,6 +591,15 @@ class MessagesProvider with ChangeNotifier { } if (receptionDetailsSnapshot != null) { _messageReceptionDetails[existingId] = receptionDetailsSnapshot; + final existingMessage = _messages[duplicateIndex]; + final duplicatePathBytes = receptionDetailsSnapshot.pathBytes; + if (existingMessage.pathBytes == null && + duplicatePathBytes != null && + duplicatePathBytes.isNotEmpty) { + _messages[duplicateIndex] = existingMessage.copyWith( + pathBytes: Uint8List.fromList(duplicatePathBytes), + ); + } } _persistMessages(); return; // Skip duplicate @@ -692,7 +710,7 @@ class MessagesProvider with ChangeNotifier { incomingSenderName != null && existingSenderName == incomingSenderName) { if (!existing.isSentMessage && !message.isSentMessage) { - return true; + return withinChannelRepeatWindow; } if (!withinChannelRepeatWindow) { @@ -715,7 +733,8 @@ class MessagesProvider with ChangeNotifier { return false; } - return true; + // System messages and other types: never deduplicate by scope alone. + return false; } /// Add multiple messages @@ -725,7 +744,33 @@ class MessagesProvider with ChangeNotifier { for (final message in messages) { // Always enhance message with SAR parser to detect SAR markers - final enhancedMessage = SarMessageParser.enhanceMessage(message); + var enhancedMessage = SarMessageParser.enhanceMessage(message); + + // Check if it's a drawing message (D:...) and not already marked + if (DrawingMessageParser.isDrawingMessage(enhancedMessage.text) && + !enhancedMessage.isDrawing) { + final drawing = DrawingMessageParser.parseDrawingMessage( + enhancedMessage.text, + senderName: enhancedMessage.senderName, + messageId: enhancedMessage.id, + ); + enhancedMessage = enhancedMessage.copyWith( + isDrawing: true, + drawingId: drawing?.id, + ); + } + + // Check if it's a voice message and not already marked + if (!enhancedMessage.isVoice) { + final envelope = VoiceEnvelope.tryParseText(enhancedMessage.text); + if (envelope != null) { + enhancedMessage = enhancedMessage.copyWith( + isVoice: true, + voiceId: envelope.sessionId, + ); + } + } + enhancedMessage = _resolveSenderNameIfNeeded(enhancedMessage); // Check for duplicates if (_findDuplicateMessageIndex(enhancedMessage) != -1) { @@ -1028,6 +1073,7 @@ class MessagesProvider with ChangeNotifier { for (final messageId in messageIdsToRemove) { _timeoutTimers[messageId]?.cancel(); _timeoutTimers.remove(messageId); + _clearChannelSendWarning(messageId); _messageContactMap.remove(messageId); _groupedMessageMapping.remove(messageId); _messageContactLocations.remove(messageId); @@ -1245,11 +1291,7 @@ class MessagesProvider with ChangeNotifier { /// Clear all messages void clearMessages() { - for (final timer in _channelEchoWarningTimers.values) { - timer.cancel(); - } - _channelEchoWarningTimers.clear(); - _channelEchoWarningMessageIds.clear(); + _cancelAllTimers(); _messages.clear(); _sarMarkers.clear(); _removedSarMarkerIds.clear(); @@ -1257,6 +1299,14 @@ class MessagesProvider with ChangeNotifier { _messageReceptionDetails.clear(); _messageTransferDetails.clear(); _messageRouteMetadata.clear(); + _pendingSentMessages.clear(); + _messageContactMap.clear(); + _groupedMessageMapping.clear(); + _ackTagToRecipients.clear(); + _messageAckHistory.clear(); + _ackHistoryLookup.clear(); + _completedAckHistory.clear(); + _retryManager.clearAll(); _persistMessages(); unawaited(_storageService.saveRemovedSarMarkerIds(_removedSarMarkerIds)); notifyListeners(); @@ -1272,11 +1322,7 @@ class MessagesProvider with ChangeNotifier { /// Clear all data void clearAll() { - for (final timer in _channelEchoWarningTimers.values) { - timer.cancel(); - } - _channelEchoWarningTimers.clear(); - _channelEchoWarningMessageIds.clear(); + _cancelAllTimers(); _messages.clear(); _sarMarkers.clear(); _removedSarMarkerIds.clear(); @@ -1284,6 +1330,14 @@ class MessagesProvider with ChangeNotifier { _messageReceptionDetails.clear(); _messageTransferDetails.clear(); _messageRouteMetadata.clear(); + _pendingSentMessages.clear(); + _messageContactMap.clear(); + _groupedMessageMapping.clear(); + _ackTagToRecipients.clear(); + _messageAckHistory.clear(); + _ackHistoryLookup.clear(); + _completedAckHistory.clear(); + _retryManager.clearAll(); _persistMessages(); unawaited(_storageService.saveRemovedSarMarkerIds(_removedSarMarkerIds)); notifyListeners(); @@ -1964,7 +2018,7 @@ class MessagesProvider with ChangeNotifier { debugPrint(' Message index in list: $index'); if (index != -1) { - final updatedMessage = message.copyWith( + final updatedMessage = _messages[index].copyWith( deliveryStatus: MessageDeliveryStatus.delivered, roundTripTimeMs: roundTripTimeMs, deliveredAt: DateTime.now(), @@ -2323,6 +2377,7 @@ class MessagesProvider with ChangeNotifier { senderPublicKeyPrefix: message.senderPublicKeyPrefix, channelIdx: message.channelIdx, pathLen: message.pathLen, + pathBytes: message.pathBytes, textType: message.textType, senderTimestamp: message.senderTimestamp, text: message.text, @@ -2410,7 +2465,19 @@ class MessagesProvider with ChangeNotifier { @override void dispose() { - // Cancel all pending timeout timers + _cancelAllTimers(); + _completedAckHistory.clear(); + _messageAckHistory.clear(); + _ackHistoryLookup.clear(); + + // Clear retry manager + _retryManager.clearAll(); + + super.dispose(); + } + + /// Cancel all pending timers (timeout + echo warning). + void _cancelAllTimers() { for (final timer in _timeoutTimers.values) { timer.cancel(); } @@ -2420,14 +2487,6 @@ class MessagesProvider with ChangeNotifier { } _channelEchoWarningTimers.clear(); _channelEchoWarningMessageIds.clear(); - _completedAckHistory.clear(); - _messageAckHistory.clear(); - _ackHistoryLookup.clear(); - - // Clear retry manager - _retryManager.clearAll(); - - super.dispose(); } void _rememberCompletedAck(int ackCode) { diff --git a/lib/services/message_storage_service.dart b/lib/services/message_storage_service.dart index 3406d26..73edeb9 100644 --- a/lib/services/message_storage_service.dart +++ b/lib/services/message_storage_service.dart @@ -329,6 +329,8 @@ class MessageStorageService { Message message, { MessageReceptionDetails? receptionDetails, }) { + final persistedPathBytes = + message.pathBytes ?? _pathBytesFromSnapshot(receptionDetails); return { 'id': message.id, 'messageType': message.messageType.name, @@ -337,6 +339,7 @@ class MessageStorageService { : null, 'channelIdx': message.channelIdx, 'pathLen': message.pathLen, + 'pathBytes': persistedPathBytes?.toList(), 'textType': message.textType.value, 'senderTimestamp': message.senderTimestamp, 'text': message.text, @@ -465,6 +468,14 @@ class MessageStorageService { : null, channelIdx: json['channelIdx'] as int?, pathLen: json['pathLen'] as int, + pathBytes: json['pathBytes'] is List + ? Uint8List.fromList( + (json['pathBytes'] as List) + .whereType() + .map((b) => b.toInt()) + .toList(), + ) + : null, textType: MessageTextType.fromValue(json['textType'] as int), senderTimestamp: json['senderTimestamp'] as int, text: json['text'] as String, @@ -562,4 +573,12 @@ class MessageStorageService { return null; } } + + Uint8List? _pathBytesFromSnapshot(MessageReceptionDetails? receptionDetails) { + final pathBytes = receptionDetails?.pathBytes; + if (pathBytes == null || pathBytes.isEmpty) { + return null; + } + return Uint8List.fromList(pathBytes); + } } diff --git a/pubspec.lock b/pubspec.lock index a2575c5..58d059c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -795,8 +795,8 @@ packages: dependency: "direct main" description: path: "." - ref: "461f010" - resolved-ref: "461f010a2f1fcd343357c152228891e295d391be" + ref: "27e65f4" + resolved-ref: "27e65f48b1b5dc3dbcafc0167c4c5755770116ba" url: "https://github.com/dz0ny/meshcore_client.git" source: git version: "0.1.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9f50894..b78c488 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: meshcore_client: git: url: https://github.com/dz0ny/meshcore_client.git - ref: "461f010" + ref: "27e65f4" # Codec2 ultra-low-bitrate speech codec (FFI plugin) codec2_flutter: diff --git a/test/services/message_storage_service_test.dart b/test/services/message_storage_service_test.dart index d3e4e9b..c16beb7 100644 --- a/test/services/message_storage_service_test.dart +++ b/test/services/message_storage_service_test.dart @@ -43,9 +43,11 @@ void main() { await prefs.remove('stored_message_reception_details'); final restoredDetails = await storage.loadMessageReceptionDetails(); + final restoredMessages = await storage.loadMessages(); expect(restoredDetails.keys, contains(message.id)); expect(restoredDetails[message.id]?.pathBytes, [0xAA, 0xBB, 0xCC]); + expect(restoredMessages.single.pathBytes, [0xAA, 0xBB, 0xCC]); }, );