diff --git a/assets/changelog.json b/assets/changelog.json index 5e2148c..8e8ebb7 100644 --- a/assets/changelog.json +++ b/assets/changelog.json @@ -1,4 +1,9 @@ { + "2.4.4": { + "content": [ + "• Search results now prioritize locations near your current map view" + ] + }, "2.4.3": { "content": [ "• Fixed 360° FOV rendering - devices with full circle coverage now render as complete rings instead of having a wedge cut out or being a line", diff --git a/lib/app_state.dart b/lib/app_state.dart index 0f517d4..0101d23 100644 --- a/lib/app_state.dart +++ b/lib/app_state.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:http/http.dart' as http; import 'package:latlong2/latlong.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -569,8 +570,8 @@ class AppState extends ChangeNotifier { } // ---------- Navigation Methods - Simplified ---------- - void enterSearchMode(LatLng mapCenter) { - _navigationState.enterSearchMode(mapCenter); + void enterSearchMode(LatLng mapCenter, {LatLngBounds? viewbox}) { + _navigationState.enterSearchMode(mapCenter, viewbox: viewbox); } void cancelNavigation() { diff --git a/lib/screens/coordinators/navigation_coordinator.dart b/lib/screens/coordinators/navigation_coordinator.dart index e89a9d3..62a1e7e 100644 --- a/lib/screens/coordinators/navigation_coordinator.dart +++ b/lib/screens/coordinators/navigation_coordinator.dart @@ -138,7 +138,8 @@ class NavigationCoordinator { // Enter search mode try { final center = mapController.mapController.camera.center; - appState.enterSearchMode(center); + final viewbox = mapController.mapController.camera.visibleBounds; + appState.enterSearchMode(center, viewbox: viewbox); } catch (e) { debugPrint('[NavigationCoordinator] Could not get map center for search: $e'); // Fallback to default location diff --git a/lib/services/search_service.dart b/lib/services/search_service.dart index eb47dca..95e46ca 100644 --- a/lib/services/search_service.dart +++ b/lib/services/search_service.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:http/http.dart' as http; import 'package:latlong2/latlong.dart'; @@ -12,19 +13,19 @@ class SearchService { static const Duration _timeout = Duration(seconds: 10); /// Search for places using Nominatim geocoding service - Future> search(String query) async { + Future> search(String query, {LatLngBounds? viewbox}) async { if (query.trim().isEmpty) { return []; } - + // Check if query looks like coordinates first final coordResult = _tryParseCoordinates(query.trim()); if (coordResult != null) { return [coordResult]; } - + // Otherwise, use Nominatim API - return await _searchNominatim(query.trim()); + return await _searchNominatim(query.trim(), viewbox: viewbox); } /// Try to parse various coordinate formats @@ -52,14 +53,37 @@ class SearchService { } /// Search using Nominatim API - Future> _searchNominatim(String query) async { - final uri = Uri.parse('$_baseUrl/search').replace(queryParameters: { + Future> _searchNominatim(String query, {LatLngBounds? viewbox}) async { + final params = { 'q': query, 'format': 'json', 'limit': _maxResults.toString(), 'addressdetails': '1', 'extratags': '1', - }); + }; + + if (viewbox != null) { + double round1(double v) => (v * 10).round() / 10; + var west = round1(viewbox.west); + var east = round1(viewbox.east); + var south = round1(viewbox.south); + var north = round1(viewbox.north); + + if (east - west < 0.5) { + final mid = (east + west) / 2; + west = mid - 0.25; + east = mid + 0.25; + } + if (north - south < 0.5) { + final mid = (north + south) / 2; + south = mid - 0.25; + north = mid + 0.25; + } + + params['viewbox'] = '$west,$north,$east,$south'; + } + + final uri = Uri.parse('$_baseUrl/search').replace(queryParameters: params); debugPrint('[SearchService] Searching Nominatim: $uri'); diff --git a/lib/state/navigation_state.dart b/lib/state/navigation_state.dart index 48b90eb..f87ebea 100644 --- a/lib/state/navigation_state.dart +++ b/lib/state/navigation_state.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:latlong2/latlong.dart'; import '../models/search_result.dart'; @@ -31,7 +32,8 @@ class NavigationState extends ChangeNotifier { bool _isSearchLoading = false; List _searchResults = []; String _lastQuery = ''; - + LatLngBounds? _searchViewbox; + // Location state LatLng? _provisionalPinLocation; String? _provisionalPinAddress; @@ -106,19 +108,20 @@ class NavigationState extends ChangeNotifier { } /// BRUTALIST: Single entry point to search mode - void enterSearchMode(LatLng mapCenter) { + void enterSearchMode(LatLng mapCenter, {LatLngBounds? viewbox}) { debugPrint('[NavigationState] enterSearchMode - current mode: $_mode'); - + if (_mode != AppNavigationMode.normal) { debugPrint('[NavigationState] Cannot enter search mode - not in normal mode'); return; } - + _mode = AppNavigationMode.search; _provisionalPinLocation = mapCenter; _provisionalPinAddress = null; + _searchViewbox = viewbox; _clearSearchResults(); - + debugPrint('[NavigationState] Entered search mode'); notifyListeners(); } @@ -149,7 +152,8 @@ class NavigationState extends ChangeNotifier { _showingOverview = false; _nextPointIsStart = false; _routingError = null; - + _searchViewbox = null; + // Clear search _clearSearchResults(); @@ -336,21 +340,21 @@ class NavigationState extends ChangeNotifier { _clearSearchResults(); return; } - + if (query.trim() == _lastQuery.trim()) return; - + _setSearchLoading(true); _lastQuery = query.trim(); - + try { - final results = await _searchService.search(query.trim()); + final results = await _searchService.search(query.trim(), viewbox: _searchViewbox); _searchResults = results; debugPrint('[NavigationState] Found ${results.length} results'); } catch (e) { debugPrint('[NavigationState] Search failed: $e'); _searchResults = []; } - + _setSearchLoading(false); } diff --git a/pubspec.yaml b/pubspec.yaml index 5f7a69c..0927da3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: deflockapp description: Map public surveillance infrastructure with OpenStreetMap publish_to: "none" -version: 2.4.3+41 # The thing after the + is the version code, incremented with each release +version: 2.4.4+42 # The thing after the + is the version code, incremented with each release environment: sdk: ">=3.5.0 <4.0.0" # oauth2_client 4.x needs Dart 3.5+