maps/mobile/lib/features/search/data/search_repository.dart
2026-03-30 09:22:16 +02:00

136 lines
3.7 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/api/api_client.dart';
import '../../../core/database/app_database.dart';
/// A single search result parsed from GeoJSON.
class SearchResult {
final int osmId;
final String osmType;
final String name;
final String? street;
final String? housenumber;
final String? postcode;
final String? city;
final String? state;
final String? country;
final String? countryCode;
final String type;
final double latitude;
final double longitude;
final List<double>? extent;
SearchResult({
required this.osmId,
required this.osmType,
required this.name,
this.street,
this.housenumber,
this.postcode,
this.city,
this.state,
this.country,
this.countryCode,
required this.type,
required this.latitude,
required this.longitude,
this.extent,
});
String get displayAddress {
final parts = <String>[];
if (street != null) {
parts.add(housenumber != null ? '$street $housenumber' : street!);
}
if (city != null) parts.add(city!);
if (country != null) parts.add(country!);
return parts.join(', ');
}
factory SearchResult.fromGeoJsonFeature(Map<String, dynamic> feature) {
final geometry = feature['geometry'] as Map<String, dynamic>;
final coords = geometry['coordinates'] as List;
final props = feature['properties'] as Map<String, dynamic>;
return SearchResult(
osmId: props['osm_id'] as int,
osmType: props['osm_type'] as String,
name: props['name'] as String? ?? '',
street: props['street'] as String?,
housenumber: props['housenumber'] as String?,
postcode: props['postcode'] as String?,
city: props['city'] as String?,
state: props['state'] as String?,
country: props['country'] as String?,
countryCode: props['country_code'] as String?,
type: props['type'] as String? ?? 'unknown',
longitude: (coords[0] as num).toDouble(),
latitude: (coords[1] as num).toDouble(),
extent: (props['extent'] as List?)
?.map((e) => (e as num).toDouble())
.toList(),
);
}
}
class SearchRepository {
final ApiClient _apiClient;
final AppDatabase _db;
SearchRepository(this._apiClient, this._db);
/// Forward search via GET /api/search.
Future<List<SearchResult>> search(
String query, {
double? lat,
double? lon,
int limit = 10,
}) async {
final params = <String, dynamic>{
'q': query,
'limit': limit,
};
if (lat != null && lon != null) {
params['lat'] = lat;
params['lon'] = lon;
}
final data = await _apiClient.get('/api/search', queryParameters: params);
final features = (data['features'] as List?) ?? [];
return features
.map((f) =>
SearchResult.fromGeoJsonFeature(f as Map<String, dynamic>))
.toList();
}
/// Save a query to search history.
Future<void> saveToHistory(String query, {double? lat, double? lon}) {
return _db.addSearch(query, lat: lat, lon: lon);
}
/// Get recent search history.
Future<List<SearchHistoryData>> getRecentSearches() {
return _db.getRecentSearches();
}
/// Watch recent search history.
Stream<List<SearchHistoryData>> watchRecentSearches() {
return _db.watchRecentSearches();
}
/// Delete a single search history entry.
Future<void> deleteHistoryEntry(int id) {
return _db.deleteSearchEntry(id);
}
/// Clear all search history.
Future<void> clearHistory() {
return _db.clearSearchHistory();
}
}
final searchRepositoryProvider = Provider<SearchRepository>((ref) {
return SearchRepository(
ref.watch(apiClientProvider),
ref.watch(appDatabaseProvider),
);
});