mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-12 00:40:28 +00:00
feat: Enhance message handling for drawings
- Added properties to the Message model to track drawing status and associated drawing ID. - Updated AppProvider to mark messages as drawings and link them to their respective drawing IDs. - Enhanced DrawingProvider to manage received drawings and their visibility. - Implemented navigation to drawings in MapProvider, including dynamic zoom levels based on drawing size. - Updated MessagesProvider to parse drawing messages and manage linked drawings. - Enhanced MessagesTab to support drawing message interactions, including navigation and coordinate copying. - Created DrawingMinimapPreview widget for visual representation of drawings in message bubbles. - Improved DrawingToolbar to handle sharing of drawings, ensuring only unshared drawings are sent. - Added utility functions in DrawingMessageParser for extracting drawing metadata.
This commit is contained in:
@@ -150,10 +150,16 @@ class AppProvider with ChangeNotifier {
|
||||
debugPrint(' Drawing linked to message ID: ${message.id}');
|
||||
drawingProvider.addReceivedDrawing(drawing);
|
||||
|
||||
// Add the original drawing message to chat (not a modified info message)
|
||||
// Update message to mark as drawing and link to drawing ID
|
||||
final updatedMessage = message.copyWith(
|
||||
isDrawing: true,
|
||||
drawingId: drawing.id,
|
||||
);
|
||||
|
||||
// Add the drawing message to chat with drawing metadata
|
||||
// This allows users to click on the drawing message to navigate to it
|
||||
messagesProvider.addMessage(
|
||||
message,
|
||||
updatedMessage,
|
||||
contactLookup: (name) => '',
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -31,9 +31,17 @@ class DrawingProvider with ChangeNotifier {
|
||||
Color get selectedColor => _selectedColor;
|
||||
bool get showReceivedDrawings => _showReceivedDrawings;
|
||||
bool get showSarMarkers => _showSarMarkers;
|
||||
List<MapDrawing> get drawings => _showReceivedDrawings
|
||||
? List.unmodifiable(_drawings)
|
||||
: List.unmodifiable(_drawings.where((d) => !d.isReceived).toList());
|
||||
List<MapDrawing> get drawings {
|
||||
// Filter out hidden drawings first
|
||||
var visibleDrawings = _drawings.where((d) => !d.isHidden);
|
||||
|
||||
// Then filter by received status if needed
|
||||
if (!_showReceivedDrawings) {
|
||||
visibleDrawings = visibleDrawings.where((d) => !d.isReceived);
|
||||
}
|
||||
|
||||
return List.unmodifiable(visibleDrawings.toList());
|
||||
}
|
||||
MapDrawing? get currentDrawing => _currentDrawing;
|
||||
List<LatLng> get currentLinePoints => List.unmodifiable(_currentLinePoints);
|
||||
LatLng? get rectangleStartPoint => _rectangleStartPoint;
|
||||
@@ -281,7 +289,155 @@ class DrawingProvider with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
_drawings.add(drawing);
|
||||
// Mark as received when adding
|
||||
final receivedDrawing = _createReceivedCopy(drawing);
|
||||
_drawings.add(receivedDrawing);
|
||||
_saveDrawings();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Create a copy of a drawing marked as received
|
||||
MapDrawing _createReceivedCopy(MapDrawing drawing) {
|
||||
if (drawing is LineDrawing) {
|
||||
return LineDrawing(
|
||||
id: drawing.id,
|
||||
color: drawing.color,
|
||||
createdAt: drawing.createdAt,
|
||||
points: drawing.points,
|
||||
senderName: drawing.senderName,
|
||||
isReceived: true,
|
||||
messageId: drawing.messageId,
|
||||
isShared: drawing.isShared,
|
||||
isSent: drawing.isSent,
|
||||
isHidden: drawing.isHidden,
|
||||
);
|
||||
} else if (drawing is RectangleDrawing) {
|
||||
return RectangleDrawing(
|
||||
id: drawing.id,
|
||||
color: drawing.color,
|
||||
createdAt: drawing.createdAt,
|
||||
topLeft: drawing.topLeft,
|
||||
bottomRight: drawing.bottomRight,
|
||||
senderName: drawing.senderName,
|
||||
isReceived: true,
|
||||
messageId: drawing.messageId,
|
||||
isShared: drawing.isShared,
|
||||
isSent: drawing.isSent,
|
||||
isHidden: drawing.isHidden,
|
||||
);
|
||||
}
|
||||
return drawing;
|
||||
}
|
||||
|
||||
/// Get a drawing by its ID
|
||||
MapDrawing? getDrawingById(String id) {
|
||||
try {
|
||||
return _drawings.firstWhere((d) => d.id == id);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all unshared drawings (local drawings not yet sent)
|
||||
List<MapDrawing> getUnsharedDrawings() {
|
||||
return _drawings.where((d) => !d.isShared && !d.isReceived).toList();
|
||||
}
|
||||
|
||||
/// Mark a drawing as shared
|
||||
void markDrawingAsShared(String id) {
|
||||
final index = _drawings.indexWhere((d) => d.id == id);
|
||||
if (index != -1) {
|
||||
final drawing = _drawings[index];
|
||||
|
||||
// Create a copy with isShared = true
|
||||
if (drawing is LineDrawing) {
|
||||
_drawings[index] = LineDrawing(
|
||||
id: drawing.id,
|
||||
color: drawing.color,
|
||||
createdAt: drawing.createdAt,
|
||||
points: drawing.points,
|
||||
senderName: drawing.senderName,
|
||||
isReceived: drawing.isReceived,
|
||||
messageId: drawing.messageId,
|
||||
isShared: true,
|
||||
isSent: drawing.isSent,
|
||||
isHidden: drawing.isHidden,
|
||||
);
|
||||
} else if (drawing is RectangleDrawing) {
|
||||
_drawings[index] = RectangleDrawing(
|
||||
id: drawing.id,
|
||||
color: drawing.color,
|
||||
createdAt: drawing.createdAt,
|
||||
topLeft: drawing.topLeft,
|
||||
bottomRight: drawing.bottomRight,
|
||||
senderName: drawing.senderName,
|
||||
isReceived: drawing.isReceived,
|
||||
messageId: drawing.messageId,
|
||||
isShared: true,
|
||||
isSent: drawing.isSent,
|
||||
isHidden: drawing.isHidden,
|
||||
);
|
||||
}
|
||||
|
||||
_saveDrawings();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle visibility of a drawing (doesn't save to storage)
|
||||
void toggleDrawingVisibility(String id) {
|
||||
final index = _drawings.indexWhere((d) => d.id == id);
|
||||
if (index != -1) {
|
||||
final drawing = _drawings[index];
|
||||
|
||||
// Create a copy with toggled isHidden flag
|
||||
if (drawing is LineDrawing) {
|
||||
_drawings[index] = LineDrawing(
|
||||
id: drawing.id,
|
||||
color: drawing.color,
|
||||
createdAt: drawing.createdAt,
|
||||
points: drawing.points,
|
||||
senderName: drawing.senderName,
|
||||
isReceived: drawing.isReceived,
|
||||
messageId: drawing.messageId,
|
||||
isShared: drawing.isShared,
|
||||
isSent: drawing.isSent,
|
||||
isHidden: !drawing.isHidden,
|
||||
);
|
||||
} else if (drawing is RectangleDrawing) {
|
||||
_drawings[index] = RectangleDrawing(
|
||||
id: drawing.id,
|
||||
color: drawing.color,
|
||||
createdAt: drawing.createdAt,
|
||||
topLeft: drawing.topLeft,
|
||||
bottomRight: drawing.bottomRight,
|
||||
senderName: drawing.senderName,
|
||||
isReceived: drawing.isReceived,
|
||||
messageId: drawing.messageId,
|
||||
isShared: drawing.isShared,
|
||||
isSent: drawing.isSent,
|
||||
isHidden: !drawing.isHidden,
|
||||
);
|
||||
}
|
||||
|
||||
// Don't save to storage - visibility toggle is temporary
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a drawing and its linked message
|
||||
void removeDrawingAndMessage(String drawingId, dynamic messagesProvider) {
|
||||
final drawing = getDrawingById(drawingId);
|
||||
if (drawing == null) return;
|
||||
|
||||
// Remove the drawing
|
||||
_drawings.removeWhere((d) => d.id == drawingId);
|
||||
|
||||
// If the drawing has a linked message, remove it too
|
||||
if (drawing.messageId != null && messagesProvider != null) {
|
||||
messagesProvider.deleteMessage(drawing.messageId!);
|
||||
}
|
||||
|
||||
_saveDrawings();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,56 @@ class MapProvider with ChangeNotifier {
|
||||
// Don't notify listeners to avoid rebuilds
|
||||
}
|
||||
|
||||
/// Navigate to a drawing by its ID
|
||||
void navigateToDrawing(String drawingId, dynamic drawingProvider) {
|
||||
// Find the drawing in the provider
|
||||
final drawings = drawingProvider.drawings as List;
|
||||
final drawing = drawings.cast<dynamic>().firstWhere(
|
||||
(d) => d.id == drawingId,
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
if (drawing == null) {
|
||||
debugPrint('⚠️ [MapProvider] Drawing $drawingId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use MapDrawing's built-in getCenter and getBounds methods
|
||||
final center = drawing.getCenter();
|
||||
final bounds = drawing.getBounds();
|
||||
|
||||
// Calculate appropriate zoom level based on bounds
|
||||
// For larger drawings, use lower zoom to fit the whole drawing
|
||||
// For smaller drawings, use higher zoom for better detail
|
||||
final latDiff = (bounds.north - bounds.south).abs();
|
||||
final lonDiff = (bounds.east - bounds.west).abs();
|
||||
final maxDiff = latDiff > lonDiff ? latDiff : lonDiff;
|
||||
|
||||
// Zoom scale: smaller drawings get higher zoom
|
||||
// 0.001 degrees (~100m) -> zoom 17
|
||||
// 0.005 degrees (~500m) -> zoom 16
|
||||
// 0.01 degrees (~1km) -> zoom 15
|
||||
// 0.05 degrees (~5km) -> zoom 13
|
||||
// 0.1 degrees (~10km) -> zoom 12
|
||||
double zoom = 15.0;
|
||||
if (maxDiff < 0.001) {
|
||||
zoom = 17.0;
|
||||
} else if (maxDiff < 0.005) {
|
||||
zoom = 16.0;
|
||||
} else if (maxDiff < 0.01) {
|
||||
zoom = 15.0;
|
||||
} else if (maxDiff < 0.05) {
|
||||
zoom = 13.0;
|
||||
} else if (maxDiff < 0.1) {
|
||||
zoom = 12.0;
|
||||
} else {
|
||||
zoom = 10.0;
|
||||
}
|
||||
|
||||
debugPrint('🗺️ [MapProvider] Navigating to drawing: ${drawing.type.name}, zoom: $zoom');
|
||||
navigateToLocation(location: center, zoom: zoom, animate: true);
|
||||
}
|
||||
|
||||
void updateZoom(double zoom) {
|
||||
_targetZoom = zoom;
|
||||
notifyListeners();
|
||||
|
||||
@@ -6,6 +6,7 @@ import '../models/sar_marker.dart';
|
||||
import '../services/message_storage_service.dart';
|
||||
import '../services/notification_service.dart';
|
||||
import '../utils/sar_message_parser.dart';
|
||||
import '../utils/drawing_message_parser.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'helpers/message_retry_manager.dart';
|
||||
|
||||
@@ -139,7 +140,25 @@ class MessagesProvider with ChangeNotifier {
|
||||
String Function(String name)? contactLookup,
|
||||
}) {
|
||||
// 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
|
||||
// Don't overwrite if already set by the sender (preserves correct drawing ID)
|
||||
if (DrawingMessageParser.isDrawingMessage(enhancedMessage.text) &&
|
||||
!enhancedMessage.isDrawing) {
|
||||
// Parse the drawing to get its ID
|
||||
final drawing = DrawingMessageParser.parseDrawingMessage(
|
||||
enhancedMessage.text,
|
||||
senderName: enhancedMessage.senderName,
|
||||
messageId: enhancedMessage.id,
|
||||
);
|
||||
|
||||
// Mark message as drawing and link to drawing ID
|
||||
enhancedMessage = enhancedMessage.copyWith(
|
||||
isDrawing: true,
|
||||
drawingId: drawing?.id,
|
||||
);
|
||||
}
|
||||
|
||||
// For channel messages with sender name, try to link with contact
|
||||
Message finalMessage = enhancedMessage;
|
||||
@@ -497,6 +516,23 @@ class MessagesProvider with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a drawing message and its linked drawing
|
||||
void deleteDrawingMessage(String messageId, dynamic drawingProvider) {
|
||||
final message = _messages.firstWhere(
|
||||
(m) => m.id == messageId,
|
||||
orElse: () => throw Exception('Message not found'),
|
||||
);
|
||||
|
||||
// If the message has a linked drawing, remove it
|
||||
if (message.drawingId != null && drawingProvider != null) {
|
||||
// Remove the drawing (DrawingProvider will handle removing this message)
|
||||
drawingProvider.removeDrawing(message.drawingId!);
|
||||
} else {
|
||||
// No linked drawing, just delete the message
|
||||
deleteMessage(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all messages
|
||||
void clearMessages() {
|
||||
_messages.clear();
|
||||
@@ -585,7 +621,25 @@ class MessagesProvider with ChangeNotifier {
|
||||
);
|
||||
|
||||
// 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
|
||||
// Don't overwrite if already set by the sender (preserves correct drawing ID)
|
||||
if (DrawingMessageParser.isDrawingMessage(enhancedMessage.text) &&
|
||||
!enhancedMessage.isDrawing) {
|
||||
// Parse the drawing to get its ID
|
||||
final drawing = DrawingMessageParser.parseDrawingMessage(
|
||||
enhancedMessage.text,
|
||||
senderName: enhancedMessage.senderName,
|
||||
messageId: enhancedMessage.id,
|
||||
);
|
||||
|
||||
// Mark message as drawing and link to drawing ID
|
||||
enhancedMessage = enhancedMessage.copyWith(
|
||||
isDrawing: true,
|
||||
drawingId: drawing?.id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check for duplicates (shouldn't happen for sent messages, but be safe)
|
||||
if (_isDuplicate(enhancedMessage)) {
|
||||
|
||||
Reference in New Issue
Block a user