mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: Implement compact JSON handling for map drawings and add chat notifications for sent/received drawings
This commit is contained in:
@@ -54,11 +54,29 @@ abstract class MapDrawing {
|
|||||||
/// Convert to JSON for persistence
|
/// Convert to JSON for persistence
|
||||||
Map<String, dynamic> toJson();
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
/// Convert to JSON for network transmission (includes sender name)
|
/// Convert to JSON for network transmission (compact format)
|
||||||
Map<String, dynamic> toNetworkJson(String senderName) {
|
/// Uses short field names and excludes createdAt to minimize message size
|
||||||
final json = toJson();
|
Map<String, dynamic> toNetworkJson(String senderName);
|
||||||
json['sender'] = senderName;
|
|
||||||
return json;
|
/// Parse network JSON (compact format)
|
||||||
|
static MapDrawing? fromNetworkJson(Map<String, dynamic> 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
|
/// Create from JSON
|
||||||
@@ -107,6 +125,18 @@ class LineDrawing extends MapDrawing {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> 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<String, dynamic> json) {
|
static LineDrawing fromJson(Map<String, dynamic> json) {
|
||||||
final pointsJson = json['points'] as List<dynamic>;
|
final pointsJson = json['points'] as List<dynamic>;
|
||||||
final points = pointsJson.map((p) => LatLng(p['lat'] as double, p['lon'] as double)).toList();
|
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<String, dynamic> json) {
|
||||||
|
// Parse compact format
|
||||||
|
final pointsFlat = (json['p'] as List<dynamic>).cast<double>();
|
||||||
|
final points = <LatLng>[];
|
||||||
|
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
|
/// Create a copy with updated points
|
||||||
LineDrawing copyWith({List<LatLng>? points}) {
|
LineDrawing copyWith({List<LatLng>? points}) {
|
||||||
return LineDrawing(
|
return LineDrawing(
|
||||||
@@ -169,6 +218,17 @@ class RectangleDrawing extends MapDrawing {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> 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<String, dynamic> json) {
|
static RectangleDrawing fromJson(Map<String, dynamic> json) {
|
||||||
final topLeftJson = json['topLeft'] as Map<String, dynamic>;
|
final topLeftJson = json['topLeft'] as Map<String, dynamic>;
|
||||||
final bottomRightJson = json['bottomRight'] as Map<String, dynamic>;
|
final bottomRightJson = json['bottomRight'] as Map<String, dynamic>;
|
||||||
@@ -185,6 +245,22 @@ class RectangleDrawing extends MapDrawing {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RectangleDrawing fromNetworkJson(Map<String, dynamic> json) {
|
||||||
|
// Parse compact format
|
||||||
|
final bounds = (json['b'] as List<dynamic>).cast<double>();
|
||||||
|
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
|
/// Create a copy with updated corners
|
||||||
RectangleDrawing copyWith({
|
RectangleDrawing copyWith({
|
||||||
LatLng? topLeft,
|
LatLng? topLeft,
|
||||||
|
|||||||
@@ -72,10 +72,20 @@ class AppProvider with ChangeNotifier {
|
|||||||
if (drawing != null) {
|
if (drawing != null) {
|
||||||
debugPrint('🎨 [AppProvider] Drawing parsed successfully: ${drawing.type.name} from ${drawing.senderName ?? "unknown"}');
|
debugPrint('🎨 [AppProvider] Drawing parsed successfully: ${drawing.type.name} from ${drawing.senderName ?? "unknown"}');
|
||||||
drawingProvider.addReceivedDrawing(drawing);
|
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 {
|
} else {
|
||||||
debugPrint('⚠️ [AppProvider] Failed to parse drawing message');
|
debugPrint('⚠️ [AppProvider] Failed to parse drawing message');
|
||||||
}
|
}
|
||||||
// Don't add drawing messages to the message list
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ class DrawingMessageParser {
|
|||||||
// Parse JSON
|
// Parse JSON
|
||||||
final json = jsonDecode(jsonStr) as Map<String, dynamic>;
|
final json = jsonDecode(jsonStr) as Map<String, dynamic>;
|
||||||
|
|
||||||
// Use existing fromJson method
|
// Use compact network format parser
|
||||||
return MapDrawing.fromJson(json);
|
return MapDrawing.fromNetworkJson(json);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../../providers/drawing_provider.dart';
|
import '../../providers/drawing_provider.dart';
|
||||||
import '../../providers/connection_provider.dart';
|
import '../../providers/connection_provider.dart';
|
||||||
import '../../providers/contacts_provider.dart';
|
import '../../providers/contacts_provider.dart';
|
||||||
|
import '../../providers/messages_provider.dart';
|
||||||
import '../../models/map_drawing.dart';
|
import '../../models/map_drawing.dart';
|
||||||
import '../../models/contact.dart';
|
import '../../models/contact.dart';
|
||||||
|
|
||||||
@@ -480,6 +481,13 @@ class DrawingToolbar extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add informational message to chat
|
||||||
|
final messagesProvider = Provider.of<MessagesProvider>(context, listen: false);
|
||||||
|
messagesProvider.logSystemMessage(
|
||||||
|
text: '📤 Sent ${drawings.length} map drawing${drawings.length > 1 ? 's' : ''} to Public Channel',
|
||||||
|
level: 'info',
|
||||||
|
);
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
@@ -547,6 +555,13 @@ class DrawingToolbar extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add informational message to chat
|
||||||
|
final messagesProvider = Provider.of<MessagesProvider>(context, listen: false);
|
||||||
|
messagesProvider.logSystemMessage(
|
||||||
|
text: '📤 Sent ${drawings.length} map drawing${drawings.length > 1 ? 's' : ''} to ${room.advName}',
|
||||||
|
level: 'info',
|
||||||
|
);
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
|
|||||||
Reference in New Issue
Block a user