From 5a096c048e738b62926a31e8eeba418ce2462c9d Mon Sep 17 00:00:00 2001 From: Janez T Date: Wed, 15 Oct 2025 22:45:30 +0200 Subject: [PATCH] feat: Implement compact JSON handling for map drawings and add chat notifications for sent/received drawings --- lib/models/map_drawing.dart | 86 +++++++++++++++++++++++++-- lib/providers/app_provider.dart | 12 +++- lib/utils/drawing_message_parser.dart | 4 +- lib/widgets/map/drawing_toolbar.dart | 15 +++++ 4 files changed, 109 insertions(+), 8 deletions(-) diff --git a/lib/models/map_drawing.dart b/lib/models/map_drawing.dart index fa89b2a..1cf1eb8 100644 --- a/lib/models/map_drawing.dart +++ b/lib/models/map_drawing.dart @@ -54,11 +54,29 @@ abstract class MapDrawing { /// Convert to JSON for persistence Map toJson(); - /// Convert to JSON for network transmission (includes sender name) - Map toNetworkJson(String senderName) { - final json = toJson(); - json['sender'] = senderName; - return json; + /// Convert to JSON for network transmission (compact format) + /// Uses short field names and excludes createdAt to minimize message size + Map toNetworkJson(String senderName); + + /// Parse network JSON (compact format) + static MapDrawing? fromNetworkJson(Map json) { + final typeStr = json['t'] as String?; + if (typeStr == null) return null; + + try { + final type = DrawingShapeType.values.firstWhere( + (e) => e.name == typeStr, + ); + + switch (type) { + case DrawingShapeType.line: + return LineDrawing.fromNetworkJson(json); + case DrawingShapeType.rectangle: + return RectangleDrawing.fromNetworkJson(json); + } + } catch (e) { + return null; + } } /// Create from JSON @@ -107,6 +125,18 @@ class LineDrawing extends MapDrawing { }; } + @override + Map toNetworkJson(String senderName) { + // Compact format: t=type, c=color, s=sender, p=points + // Points are encoded as flat array [lat1,lon1,lat2,lon2,...] + return { + 't': type.name, + 'c': color.value, + 's': senderName, + 'p': points.expand((p) => [p.latitude, p.longitude]).toList(), + }; + } + static LineDrawing fromJson(Map json) { final pointsJson = json['points'] as List; final points = pointsJson.map((p) => LatLng(p['lat'] as double, p['lon'] as double)).toList(); @@ -122,6 +152,25 @@ class LineDrawing extends MapDrawing { ); } + static LineDrawing fromNetworkJson(Map json) { + // Parse compact format + final pointsFlat = (json['p'] as List).cast(); + final points = []; + for (int i = 0; i < pointsFlat.length; i += 2) { + points.add(LatLng(pointsFlat[i], pointsFlat[i + 1])); + } + final senderName = json['s'] as String?; + + return LineDrawing( + id: DateTime.now().millisecondsSinceEpoch.toString(), // Generate new ID + color: Color(json['c'] as int), + createdAt: DateTime.now(), + points: points, + senderName: senderName, + isReceived: true, + ); + } + /// Create a copy with updated points LineDrawing copyWith({List? points}) { return LineDrawing( @@ -169,6 +218,17 @@ class RectangleDrawing extends MapDrawing { }; } + @override + Map toNetworkJson(String senderName) { + // Compact format: t=type, c=color, s=sender, b=bounds [lat1,lon1,lat2,lon2] + return { + 't': type.name, + 'c': color.value, + 's': senderName, + 'b': [topLeft.latitude, topLeft.longitude, bottomRight.latitude, bottomRight.longitude], + }; + } + static RectangleDrawing fromJson(Map json) { final topLeftJson = json['topLeft'] as Map; final bottomRightJson = json['bottomRight'] as Map; @@ -185,6 +245,22 @@ class RectangleDrawing extends MapDrawing { ); } + static RectangleDrawing fromNetworkJson(Map json) { + // Parse compact format + final bounds = (json['b'] as List).cast(); + final senderName = json['s'] as String?; + + return RectangleDrawing( + id: DateTime.now().millisecondsSinceEpoch.toString(), // Generate new ID + color: Color(json['c'] as int), + createdAt: DateTime.now(), + topLeft: LatLng(bounds[0], bounds[1]), + bottomRight: LatLng(bounds[2], bounds[3]), + senderName: senderName, + isReceived: true, + ); + } + /// Create a copy with updated corners RectangleDrawing copyWith({ LatLng? topLeft, diff --git a/lib/providers/app_provider.dart b/lib/providers/app_provider.dart index 1b0ef17..e92a039 100644 --- a/lib/providers/app_provider.dart +++ b/lib/providers/app_provider.dart @@ -72,10 +72,20 @@ class AppProvider with ChangeNotifier { if (drawing != null) { debugPrint('🎨 [AppProvider] Drawing parsed successfully: ${drawing.type.name} from ${drawing.senderName ?? "unknown"}'); drawingProvider.addReceivedDrawing(drawing); + + // Add informational message to chat + final drawingTypeStr = drawing.type.name.substring(0, 1).toUpperCase() + + drawing.type.name.substring(1); + final infoMessage = message.copyWith( + text: '📍 Received map drawing ($drawingTypeStr) from ${drawing.senderName ?? "unknown"}', + ); + messagesProvider.addMessage( + infoMessage, + contactLookup: (name) => '', + ); } else { debugPrint('⚠️ [AppProvider] Failed to parse drawing message'); } - // Don't add drawing messages to the message list return; } diff --git a/lib/utils/drawing_message_parser.dart b/lib/utils/drawing_message_parser.dart index c2586ce..ae3a442 100644 --- a/lib/utils/drawing_message_parser.dart +++ b/lib/utils/drawing_message_parser.dart @@ -25,8 +25,8 @@ class DrawingMessageParser { // Parse JSON final json = jsonDecode(jsonStr) as Map; - // Use existing fromJson method - return MapDrawing.fromJson(json); + // Use compact network format parser + return MapDrawing.fromNetworkJson(json); } catch (e) { return null; } diff --git a/lib/widgets/map/drawing_toolbar.dart b/lib/widgets/map/drawing_toolbar.dart index 88b35f5..3d118d7 100644 --- a/lib/widgets/map/drawing_toolbar.dart +++ b/lib/widgets/map/drawing_toolbar.dart @@ -4,6 +4,7 @@ import 'package:provider/provider.dart'; import '../../providers/drawing_provider.dart'; import '../../providers/connection_provider.dart'; import '../../providers/contacts_provider.dart'; +import '../../providers/messages_provider.dart'; import '../../models/map_drawing.dart'; import '../../models/contact.dart'; @@ -480,6 +481,13 @@ class DrawingToolbar extends StatelessWidget { return; } + // Add informational message to chat + final messagesProvider = Provider.of(context, listen: false); + messagesProvider.logSystemMessage( + text: '📤 Sent ${drawings.length} map drawing${drawings.length > 1 ? 's' : ''} to Public Channel', + level: 'info', + ); + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( @@ -547,6 +555,13 @@ class DrawingToolbar extends StatelessWidget { return; } + // Add informational message to chat + final messagesProvider = Provider.of(context, listen: false); + messagesProvider.logSystemMessage( + text: '📤 Sent ${drawings.length} map drawing${drawings.length > 1 ? 's' : ''} to ${room.advName}', + level: 'info', + ); + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(