mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-12 00:40:28 +00:00
feat: Add zoom level clamping for WMS layer switching and improve message filtering in simple mode
This commit is contained in:
@@ -291,6 +291,9 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
orElse: () => MapLayer.openStreetMap,
|
||||
);
|
||||
_currentLayer = mbtilesLayer;
|
||||
} else if (layerType == MapLayerType.wmsBase) {
|
||||
// Use Slovenian aerial layer if that's what was saved
|
||||
_currentLayer = _slovenianAerialLayer;
|
||||
} else {
|
||||
// Use default layer
|
||||
_currentLayer = MapLayer.allLayers.firstWhere(
|
||||
@@ -299,6 +302,12 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp saved zoom if it exceeds the current layer's maximum
|
||||
// For WMS layers, use a middle zoom (11) instead of max zoom to avoid extreme close-up
|
||||
if (_savedMapZoom != null && _savedMapZoom! > _currentLayer.maxZoom) {
|
||||
_savedMapZoom = _currentLayer.isWms ? 11.0 : _currentLayer.maxZoom;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -436,6 +445,18 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
}
|
||||
}
|
||||
|
||||
// Reset map rotation to north (0 degrees)
|
||||
void _resetMapRotation() {
|
||||
if (!_isMapReady) return;
|
||||
try {
|
||||
final camera = _mapController.camera;
|
||||
_mapController.moveAndRotate(camera.center, camera.zoom, 0);
|
||||
} catch (e) {
|
||||
// Silently fail if map controller not ready
|
||||
debugPrint('Failed to reset map rotation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
LatLng _calculateCenter(List<Contact> contacts, List<SarMarker> sarMarkers) {
|
||||
return _markerService.calculateCenter(
|
||||
contacts: contacts,
|
||||
@@ -540,6 +561,13 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_currentLayer = layer;
|
||||
// Clamp zoom level if current zoom exceeds new layer's max
|
||||
if (_isMapReady && _mapController.camera.zoom > layer.maxZoom) {
|
||||
_mapController.move(
|
||||
_mapController.camera.center,
|
||||
layer.maxZoom,
|
||||
);
|
||||
}
|
||||
});
|
||||
_saveSettings();
|
||||
Navigator.pop(context);
|
||||
@@ -557,6 +585,14 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_currentLayer = _slovenianAerialLayer;
|
||||
// Clamp zoom level if current zoom exceeds new layer's max
|
||||
// For WMS layers, use a middle zoom (11) instead of max zoom to avoid extreme close-up
|
||||
if (_isMapReady && _mapController.camera.zoom > _slovenianAerialLayer.maxZoom) {
|
||||
_mapController.move(
|
||||
_mapController.camera.center,
|
||||
11.0, // Middle zoom for WMS
|
||||
);
|
||||
}
|
||||
});
|
||||
_saveSettings();
|
||||
Navigator.pop(context);
|
||||
@@ -592,6 +628,13 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
|
||||
setState(() {
|
||||
_currentLayer = layer;
|
||||
// Clamp zoom level if current zoom exceeds new layer's max
|
||||
if (_isMapReady && _mapController.camera.zoom > layer.maxZoom) {
|
||||
_mapController.move(
|
||||
_mapController.camera.center,
|
||||
layer.maxZoom,
|
||||
);
|
||||
}
|
||||
});
|
||||
_saveSettings();
|
||||
|
||||
@@ -601,9 +644,10 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
},
|
||||
)),
|
||||
],
|
||||
// WMS Overlays section (only for Slovenian/Croatian regions)
|
||||
if (AppLocalizations.of(context)!.localeName == 'sl' ||
|
||||
AppLocalizations.of(context)!.localeName == 'hr') ...[
|
||||
// WMS Overlays section (only for Slovenian/Croatian regions and when WMS base layer is selected)
|
||||
if ((AppLocalizations.of(context)!.localeName == 'sl' ||
|
||||
AppLocalizations.of(context)!.localeName == 'hr') &&
|
||||
_currentLayer.isWms) ...[
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
@@ -1156,6 +1200,10 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
if (event is MapEventMoveEnd || event is MapEventScrollWheelZoom) {
|
||||
_saveMapPosition();
|
||||
}
|
||||
// Trigger rebuild on rotation change to show/hide reset button
|
||||
if (event is MapEventRotateEnd || event is MapEventRotateStart) {
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
onLongPress: (tapPosition, point) {
|
||||
// Handle measurement mode - set measurement points
|
||||
@@ -1828,6 +1876,13 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
FloatingActionButton.small(
|
||||
heroTag: 'center_map',
|
||||
onPressed: !_isMapReady ? null : () async {
|
||||
// First tap: reset rotation if not 0
|
||||
// Second tap (or if rotation is 0): jump to current location
|
||||
if (_getMapRotation() != 0) {
|
||||
_resetMapRotation();
|
||||
return;
|
||||
}
|
||||
|
||||
// Force update GPS location and jump to it
|
||||
final position = await _locationService.getCurrentPosition();
|
||||
if (position != null && mounted) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import '../providers/contacts_provider.dart';
|
||||
import '../providers/map_provider.dart';
|
||||
import '../providers/connection_provider.dart';
|
||||
import '../providers/drawing_provider.dart';
|
||||
import '../providers/app_provider.dart';
|
||||
import '../models/message.dart';
|
||||
import '../models/contact.dart';
|
||||
import '../models/sar_marker.dart';
|
||||
@@ -564,20 +565,25 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
// Get all recent messages
|
||||
final allMessages = messagesProvider.getRecentMessages(count: 100);
|
||||
|
||||
// Get simple mode setting from AppProvider
|
||||
final appProvider = context.read<AppProvider>();
|
||||
final isSimpleMode = appProvider.isSimpleMode;
|
||||
|
||||
List<Message> filteredMessages;
|
||||
|
||||
// If public channel is selected, show ALL messages
|
||||
if (_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeChannel &&
|
||||
_selectedRecipient == null) {
|
||||
return allMessages;
|
||||
filteredMessages = allMessages;
|
||||
}
|
||||
|
||||
// If a contact or room is selected, filter by recipient
|
||||
if ((_destinationType ==
|
||||
else if ((_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeContact ||
|
||||
_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeRoom) &&
|
||||
_selectedRecipient != null) {
|
||||
return allMessages.where((message) {
|
||||
filteredMessages = allMessages.where((message) {
|
||||
// Include messages sent TO this recipient
|
||||
if (message.recipientPublicKey != null &&
|
||||
message.recipientPublicKey!.length >= 6 &&
|
||||
@@ -603,10 +609,19 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
|
||||
return false;
|
||||
}).toList();
|
||||
} else {
|
||||
// Default: show all messages (fallback case)
|
||||
filteredMessages = allMessages;
|
||||
}
|
||||
|
||||
// Default: show all messages (fallback case)
|
||||
return allMessages;
|
||||
// In simple mode, filter out system messages (toast logs)
|
||||
if (isSimpleMode) {
|
||||
filteredMessages = filteredMessages
|
||||
.where((message) => !message.isSystemMessage)
|
||||
.toList();
|
||||
}
|
||||
|
||||
return filteredMessages;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user