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

164 lines
4.9 KiB
Dart

import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/api/api_client.dart';
import '../../../core/database/app_database.dart';
/// A POI parsed from the API response.
class PlaceData {
final int osmId;
final String osmType;
final String name;
final String category;
final double latitude;
final double longitude;
final Map<String, dynamic>? address;
final String? openingHours;
final Map<String, dynamic>? openingHoursParsed;
final String? phone;
final String? website;
final String? wheelchair;
final Map<String, dynamic>? tags;
PlaceData({
required this.osmId,
required this.osmType,
required this.name,
required this.category,
required this.latitude,
required this.longitude,
this.address,
this.openingHours,
this.openingHoursParsed,
this.phone,
this.website,
this.wheelchair,
this.tags,
});
String get displayAddress {
if (address == null) return '';
final parts = <String>[];
final street = address!['street'] as String?;
final housenumber = address!['housenumber'] as String?;
if (street != null) {
parts.add(housenumber != null ? '$street $housenumber' : street);
}
final postcode = address!['postcode'] as String?;
final city = address!['city'] as String?;
if (postcode != null && city != null) {
parts.add('$postcode $city');
} else if (city != null) {
parts.add(city);
}
return parts.join(', ');
}
factory PlaceData.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 PlaceData(
osmId: props['osm_id'] as int,
osmType: props['osm_type'] as String,
name: props['name'] as String? ?? '',
category: props['category'] as String? ?? 'unknown',
longitude: (coords[0] as num).toDouble(),
latitude: (coords[1] as num).toDouble(),
address: props['address'] as Map<String, dynamic>?,
openingHours: props['opening_hours'] as String?,
openingHoursParsed:
props['opening_hours_parsed'] as Map<String, dynamic>?,
phone: props['phone'] as String?,
website: props['website'] as String?,
wheelchair: props['wheelchair'] as String?,
tags: props['tags'] as Map<String, dynamic>?,
);
}
/// Parse a single Feature response (GET /api/pois/{osm_type}/{osm_id}).
factory PlaceData.fromGeoJsonSingle(Map<String, dynamic> data) {
return PlaceData.fromGeoJsonFeature(data);
}
}
class PlacesRepository {
final ApiClient _apiClient;
final AppDatabase _db;
PlacesRepository(this._apiClient, this._db);
/// Get POIs in a bounding box via GET /api/pois?bbox=...
Future<List<PlaceData>> getPoisInBbox({
required double minLon,
required double minLat,
required double maxLon,
required double maxLat,
String? category,
int limit = 100,
}) async {
final params = <String, dynamic>{
'bbox': '$minLon,$minLat,$maxLon,$maxLat',
'limit': limit,
};
if (category != null) {
params['category'] = category;
}
final data = await _apiClient.get('/api/pois', queryParameters: params);
final features = (data['features'] as List?) ?? [];
return features
.map((f) => PlaceData.fromGeoJsonFeature(f as Map<String, dynamic>))
.toList();
}
/// Get a single POI by type and ID via GET /api/pois/{osm_type}/{osm_id}.
Future<PlaceData> getPoiDetail(String osmType, int osmId) async {
final data = await _apiClient.get('/api/pois/$osmType/$osmId');
return PlaceData.fromGeoJsonSingle(data as Map<String, dynamic>);
}
// -----------------------------------------------------------------------
// Favorites (local Drift DB)
// -----------------------------------------------------------------------
Stream<List<Favorite>> watchFavorites() {
return _db.watchAllFavorites();
}
Future<List<Favorite>> getAllFavorites() {
return _db.getAllFavorites();
}
Future<int> addFavorite(PlaceData place) {
return _db.addFavorite(FavoritesCompanion.insert(
osmId: Value(place.osmId),
osmType: Value(place.osmType),
name: place.name,
latitude: place.latitude,
longitude: place.longitude,
addressJson: Value(
place.address != null ? jsonEncode(place.address) : null,
),
createdAt: DateTime.now().millisecondsSinceEpoch ~/ 1000,
updatedAt: DateTime.now().millisecondsSinceEpoch ~/ 1000,
));
}
Future<void> removeFavorite(int id) {
return _db.deleteFavorite(id);
}
Future<Favorite?> isFavorited(String osmType, int osmId) {
return _db.findFavoriteByOsm(osmType, osmId);
}
}
final placesRepositoryProvider = Provider<PlacesRepository>((ref) {
return PlacesRepository(
ref.watch(apiClientProvider),
ref.watch(appDatabaseProvider),
);
});