From 200e506df82feacf7ed73ea494b0e35947e51704 Mon Sep 17 00:00:00 2001 From: Janez T Date: Thu, 23 Oct 2025 19:33:35 +0200 Subject: [PATCH] feat: Add drawing tools localization and update SAR message handling --- lib/l10n/app_de.arb | 5 +++ lib/l10n/app_en.arb | 5 +++ lib/l10n/app_es.arb | 5 +++ lib/l10n/app_fr.arb | 5 +++ lib/l10n/app_hr.arb | 2 + lib/l10n/app_it.arb | 5 +++ lib/l10n/app_localizations.dart | 6 +++ lib/l10n/app_localizations_de.dart | 3 ++ lib/l10n/app_localizations_en.dart | 3 ++ lib/l10n/app_localizations_es.dart | 3 ++ lib/l10n/app_localizations_fr.dart | 3 ++ lib/l10n/app_localizations_hr.dart | 3 ++ lib/l10n/app_localizations_it.dart | 3 ++ lib/l10n/app_localizations_sl.dart | 3 ++ lib/l10n/app_sl.arb | 2 + lib/models/message.dart | 26 +++++++++-- lib/services/message_storage_service.dart | 8 ---- lib/utils/sample_data_generator.dart | 55 ++++++++++++++++++++--- lib/utils/sar_message_parser.dart | 5 +-- 19 files changed, 129 insertions(+), 21 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 6c74161..a85f20e 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -614,6 +614,11 @@ "description": "Aktion zum Löschen einer Kartenzeichnung" }, + "drawingTools": "Zeichenwerkzeuge", + "@drawingTools": { + "description": "Zeichenwerkzeuge Abschnitt oder Menütitel" + }, + "drawLine": "Linie zeichnen", "@drawLine": { "description": "Kartenzeichnungsmodus: Linie" diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9c3d41f..21f1884 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -614,6 +614,11 @@ "description": "Action to delete a map drawing" }, + "drawingTools": "Drawing Tools", + "@drawingTools": { + "description": "Drawing tools section or menu title" + }, + "drawLine": "Draw Line", "@drawLine": { "description": "Map drawing mode: line" diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index c6e1e2a..61e65a6 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -614,6 +614,11 @@ "description": "Acción para eliminar un dibujo del mapa" }, + "drawingTools": "Herramientas de dibujo", + "@drawingTools": { + "description": "Sección de herramientas de dibujo o título del menú" + }, + "drawLine": "Dibujar línea", "@drawLine": { "description": "Modo de dibujo del mapa: línea" diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index baaa18b..5af1b6d 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -614,6 +614,11 @@ "description": "Action pour supprimer un dessin de carte" }, + "drawingTools": "Outils de dessin", + "@drawingTools": { + "description": "Section des outils de dessin ou titre du menu" + }, + "drawLine": "Tracer une ligne", "@drawLine": { "description": "Mode de dessin de carte : ligne" diff --git a/lib/l10n/app_hr.arb b/lib/l10n/app_hr.arb index b7d77dc..6073da2 100644 --- a/lib/l10n/app_hr.arb +++ b/lib/l10n/app_hr.arb @@ -213,6 +213,8 @@ "deleteDrawing": "Izbriši crtež", + "drawingTools": "Alati za crtanje", + "drawLine": "Nacrtaj liniju", "drawLineDesc": "Nacrtaj slobodnu liniju na karti", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index f5e3878..2b1b8ae 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -614,6 +614,11 @@ "description": "Azione per eliminare un disegno sulla mappa" }, + "drawingTools": "Strumenti di disegno", + "@drawingTools": { + "description": "Sezione strumenti di disegno o titolo del menu" + }, + "drawLine": "Disegna Linea", "@drawLine": { "description": "Modalità disegno mappa: linea" diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index f41ef44..80a1cfe 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -749,6 +749,12 @@ abstract class AppLocalizations { /// **'Delete Drawing'** String get deleteDrawing; + /// Drawing tools section or menu title + /// + /// In en, this message translates to: + /// **'Drawing Tools'** + String get drawingTools; + /// Map drawing mode: line /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index dab190f..dd71104 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -374,6 +374,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get deleteDrawing => 'Zeichnung löschen'; + @override + String get drawingTools => 'Zeichenwerkzeuge'; + @override String get drawLine => 'Linie zeichnen'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 73edb58..8dc8dc6 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -371,6 +371,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get deleteDrawing => 'Delete Drawing'; + @override + String get drawingTools => 'Drawing Tools'; + @override String get drawLine => 'Draw Line'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index d6b25cc..55e0b7f 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -372,6 +372,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get deleteDrawing => 'Eliminar dibujo'; + @override + String get drawingTools => 'Herramientas de dibujo'; + @override String get drawLine => 'Dibujar línea'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 7155b97..c61348e 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -374,6 +374,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get deleteDrawing => 'Supprimer le dessin'; + @override + String get drawingTools => 'Outils de dessin'; + @override String get drawLine => 'Tracer une ligne'; diff --git a/lib/l10n/app_localizations_hr.dart b/lib/l10n/app_localizations_hr.dart index e335d80..1de66be 100644 --- a/lib/l10n/app_localizations_hr.dart +++ b/lib/l10n/app_localizations_hr.dart @@ -371,6 +371,9 @@ class AppLocalizationsHr extends AppLocalizations { @override String get deleteDrawing => 'Izbriši crtež'; + @override + String get drawingTools => 'Alati za crtanje'; + @override String get drawLine => 'Nacrtaj liniju'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 34d29e0..bd1aaeb 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -373,6 +373,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get deleteDrawing => 'Elimina Disegno'; + @override + String get drawingTools => 'Strumenti di disegno'; + @override String get drawLine => 'Disegna Linea'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 5f076d0..d032da1 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -371,6 +371,9 @@ class AppLocalizationsSl extends AppLocalizations { @override String get deleteDrawing => 'Izbriši risbo'; + @override + String get drawingTools => 'Orodja za risanje'; + @override String get drawLine => 'Nariši črto'; diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 03cb03c..a5673f7 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -213,6 +213,8 @@ "deleteDrawing": "Izbriši risbo", + "drawingTools": "Orodja za risanje", + "drawLine": "Nariši črto", "drawLineDesc": "Nariši prosto črto na zemljevidu", diff --git a/lib/models/message.dart b/lib/models/message.dart index 906a562..8cdc360 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -49,7 +49,6 @@ class Message { // SAR marker data (if this is a SAR message) final bool isSarMarker; - final SarMarkerType? sarMarkerType; final LatLng? sarGpsCoordinates; final String? sarNotes; // Optional message/notes for SAR marker final String? sarCustomEmoji; // Custom emoji for unknown SAR marker types @@ -91,7 +90,6 @@ class Message { required this.senderTimestamp, required this.text, this.isSarMarker = false, - this.sarMarkerType, this.sarGpsCoordinates, this.sarNotes, this.sarCustomEmoji, @@ -112,6 +110,28 @@ class Message { this.firstEchoAt, }); + /// Get SAR marker type by inferring from message content + /// Returns the type inferred from sarCustomEmoji or by parsing the message text + SarMarkerType? get sarMarkerType { + if (!isSarMarker) return null; + + // If we have a custom emoji stored, infer type from it + if (sarCustomEmoji != null && sarCustomEmoji!.isNotEmpty) { + return SarMarkerType.fromEmoji(sarCustomEmoji!); + } + + // Otherwise, parse the message text to extract the emoji + final trimmed = text.trim(); + if (!trimmed.startsWith('S:')) return null; + + // Extract emoji from format: S::... or S:::... + final parts = trimmed.split(':'); + if (parts.length < 3) return null; + + final emoji = parts[1]; + return SarMarkerType.fromEmoji(emoji); + } + /// Get sender public key as hex string String? get senderKeyShort { if (senderPublicKeyPrefix == null) return null; @@ -288,7 +308,6 @@ class Message { int? senderTimestamp, String? text, bool? isSarMarker, - SarMarkerType? sarMarkerType, LatLng? sarGpsCoordinates, String? sarNotes, String? sarCustomEmoji, @@ -319,7 +338,6 @@ class Message { senderTimestamp: senderTimestamp ?? this.senderTimestamp, text: text ?? this.text, isSarMarker: isSarMarker ?? this.isSarMarker, - sarMarkerType: sarMarkerType ?? this.sarMarkerType, sarGpsCoordinates: sarGpsCoordinates ?? this.sarGpsCoordinates, sarNotes: sarNotes ?? this.sarNotes, sarCustomEmoji: sarCustomEmoji ?? this.sarCustomEmoji, diff --git a/lib/services/message_storage_service.dart b/lib/services/message_storage_service.dart index b8fd081..088839c 100644 --- a/lib/services/message_storage_service.dart +++ b/lib/services/message_storage_service.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/message.dart'; -import '../models/sar_marker.dart'; import 'package:latlong2/latlong.dart'; /// Service for persisting messages to local storage @@ -111,7 +110,6 @@ class MessageStorageService { 'senderTimestamp': message.senderTimestamp, 'text': message.text, 'isSarMarker': message.isSarMarker, - 'sarMarkerType': message.sarMarkerType?.name, 'sarGpsLat': message.sarGpsCoordinates?.latitude, 'sarGpsLon': message.sarGpsCoordinates?.longitude, 'receivedAtMillis': message.receivedAt.millisecondsSinceEpoch, @@ -148,12 +146,6 @@ class MessageStorageService { senderTimestamp: json['senderTimestamp'] as int, text: json['text'] as String, isSarMarker: json['isSarMarker'] as bool? ?? false, - sarMarkerType: json['sarMarkerType'] != null - ? SarMarkerType.values.firstWhere( - (e) => e.name == json['sarMarkerType'], - orElse: () => SarMarkerType.unknown, - ) - : null, sarGpsCoordinates: json['sarGpsLat'] != null && json['sarGpsLon'] != null ? LatLng(json['sarGpsLat'] as double, json['sarGpsLon'] as double) diff --git a/lib/utils/sample_data_generator.dart b/lib/utils/sample_data_generator.dart index 04cd97e..da40794 100644 --- a/lib/utils/sample_data_generator.dart +++ b/lib/utils/sample_data_generator.dart @@ -1,10 +1,10 @@ import 'dart:typed_data'; import 'dart:math'; +import 'package:flutter/material.dart'; import 'package:latlong2/latlong.dart'; import '../models/contact.dart'; import '../models/contact_telemetry.dart'; import '../models/message.dart'; -import '../models/sar_marker.dart'; import '../l10n/app_localizations.dart'; /// Generates sample data for testing/demo purposes @@ -148,8 +148,8 @@ class SampleDataGenerator { text: 'S:🧑:${lat.toStringAsFixed(5)},${lon.toStringAsFixed(5)}', receivedAt: timestamp, isSarMarker: true, - sarMarkerType: SarMarkerType.foundPerson, sarGpsCoordinates: LatLng(lat, lon), + sarCustomEmoji: '🧑', senderName: l10n.sampleTeamMember, )); messageId++; @@ -177,8 +177,8 @@ class SampleDataGenerator { text: 'S:🔥:${lat.toStringAsFixed(5)},${lon.toStringAsFixed(5)}', receivedAt: timestamp, isSarMarker: true, - sarMarkerType: SarMarkerType.fire, sarGpsCoordinates: LatLng(lat, lon), + sarCustomEmoji: '🔥', senderName: l10n.sampleScout, )); messageId++; @@ -206,8 +206,8 @@ class SampleDataGenerator { text: 'S:🏕️:${lat.toStringAsFixed(5)},${lon.toStringAsFixed(5)}', receivedAt: timestamp, isSarMarker: true, - sarMarkerType: SarMarkerType.stagingArea, sarGpsCoordinates: LatLng(lat, lon), + sarCustomEmoji: '🏕️', senderName: l10n.sampleBase, )); messageId++; @@ -242,8 +242,8 @@ class SampleDataGenerator { text: 'S:📦:${lat.toStringAsFixed(5)},${lon.toStringAsFixed(5)}${notes[i % notes.length]}', receivedAt: timestamp, isSarMarker: true, - sarMarkerType: SarMarkerType.object, sarGpsCoordinates: LatLng(lat, lon), + sarCustomEmoji: '📦', senderName: l10n.sampleSearcher, )); messageId++; @@ -252,6 +252,51 @@ class SampleDataGenerator { return messages; } + /// Generate sample map drawings + static List generateDrawings({ + required LatLng centerLocation, + required AppLocalizations l10n, + }) { + final drawings = []; + final now = DateTime.now(); + + // Generate a line drawing (e.g., search path) + final linePoints = [ + LatLng(centerLocation.latitude + 0.002, centerLocation.longitude - 0.003), + LatLng(centerLocation.latitude + 0.004, centerLocation.longitude - 0.002), + LatLng(centerLocation.latitude + 0.005, centerLocation.longitude + 0.001), + LatLng(centerLocation.latitude + 0.003, centerLocation.longitude + 0.003), + ]; + + drawings.add({ + 'type': 'line', + 'id': 'sample_line_${now.millisecondsSinceEpoch}', + 'color': Colors.blue.toARGB32(), + 'createdAt': now.subtract(const Duration(minutes: 15)).toIso8601String(), + 'points': linePoints.map((p) => {'lat': p.latitude, 'lon': p.longitude}).toList(), + 'sender': l10n.sampleTeamMember, + }); + + // Generate a rectangle drawing (e.g., search area) + drawings.add({ + 'type': 'rectangle', + 'id': 'sample_rect_${now.millisecondsSinceEpoch + 1}', + 'color': Colors.red.toARGB32(), + 'createdAt': now.subtract(const Duration(minutes: 10)).toIso8601String(), + 'topLeft': { + 'lat': centerLocation.latitude - 0.003, + 'lon': centerLocation.longitude - 0.004, + }, + 'bottomRight': { + 'lat': centerLocation.latitude - 0.001, + 'lon': centerLocation.longitude - 0.001, + }, + 'sender': l10n.sampleScout, + }); + + return drawings; + } + /// Generate sample channel messages for public channels static List generateChannelMessages({ LatLng? centerLocation, diff --git a/lib/utils/sar_message_parser.dart b/lib/utils/sar_message_parser.dart index c7a8fe8..2337c36 100644 --- a/lib/utils/sar_message_parser.dart +++ b/lib/utils/sar_message_parser.dart @@ -117,12 +117,9 @@ class SarMessageParser { return message.copyWith( isSarMarker: true, - sarMarkerType: sarInfo.type, sarGpsCoordinates: sarInfo.location, sarNotes: sarInfo.notes, // Extract and store notes - sarCustomEmoji: sarInfo.type == SarMarkerType.unknown - ? sarInfo.emoji // Preserve custom emoji for unknown types - : null, + sarCustomEmoji: sarInfo.emoji, // Always store emoji for type inference sarColorIndex: sarInfo.colorIndex, // Store color index ); }