feat(wip): implement trip persistence through a local repository. Include loaded trips in the start page UI
This commit is contained in:
78
frontend/lib/presentation/utils/trip_location_utils.dart
Normal file
78
frontend/lib/presentation/utils/trip_location_utils.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'package:anyway/domain/entities/trip.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
|
||||
class TripLocationUtils {
|
||||
const TripLocationUtils._();
|
||||
|
||||
static List<double>? startCoordinates(Trip trip) {
|
||||
if (trip.landmarks.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final coords = trip.landmarks.first.location;
|
||||
if (coords.length < 2) {
|
||||
return null;
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
|
||||
static Future<TripLocaleInfo> resolveLocaleInfo(Trip trip) async {
|
||||
final coords = startCoordinates(trip);
|
||||
if (coords == null) {
|
||||
return const TripLocaleInfo();
|
||||
}
|
||||
|
||||
if (GeocodingPlatform.instance == null) {
|
||||
final fallbackCity = '${coords[0].toStringAsFixed(2)}, ${coords[1].toStringAsFixed(2)}';
|
||||
return TripLocaleInfo(cityName: fallbackCity, coordinates: coords);
|
||||
}
|
||||
|
||||
try {
|
||||
final placemarks = await placemarkFromCoordinates(coords[0], coords[1]);
|
||||
if (placemarks.isEmpty) {
|
||||
return TripLocaleInfo(coordinates: coords);
|
||||
}
|
||||
final placemark = placemarks.first;
|
||||
final city = placemark.locality ?? placemark.subAdministrativeArea;
|
||||
final country = placemark.country;
|
||||
final isoCountryCode = placemark.isoCountryCode;
|
||||
return TripLocaleInfo(cityName: city, countryName: country, countryCode: isoCountryCode, flagEmoji: _flagEmoji(isoCountryCode), coordinates: coords);
|
||||
} catch (_) {
|
||||
return TripLocaleInfo(coordinates: coords);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> resolveCityName(Trip trip) async {
|
||||
final localeInfo = await resolveLocaleInfo(trip);
|
||||
return localeInfo.cityName ?? 'Unknown';
|
||||
}
|
||||
|
||||
static String? _flagEmoji(String? countryCode) {
|
||||
if (countryCode == null || countryCode.length != 2) {
|
||||
return null;
|
||||
}
|
||||
final upper = countryCode.toUpperCase();
|
||||
final first = upper.codeUnitAt(0);
|
||||
final second = upper.codeUnitAt(1);
|
||||
if (!_isAsciiLetter(first) || !_isAsciiLetter(second)) {
|
||||
return null;
|
||||
}
|
||||
const base = 0x1F1E6;
|
||||
final firstFlag = base + (first - 0x41);
|
||||
final secondFlag = base + (second - 0x41);
|
||||
return String.fromCharCodes([firstFlag, secondFlag]);
|
||||
}
|
||||
|
||||
static bool _isAsciiLetter(int codeUnit) => codeUnit >= 0x41 && codeUnit <= 0x5A;
|
||||
}
|
||||
|
||||
class TripLocaleInfo {
|
||||
const TripLocaleInfo({this.cityName, this.countryName, this.countryCode, this.flagEmoji, this.coordinates});
|
||||
|
||||
final String? cityName;
|
||||
final String? countryName;
|
||||
final String? countryCode;
|
||||
final String? flagEmoji;
|
||||
final List<double>? coordinates;
|
||||
|
||||
bool get hasResolvedCity => cityName != null && cityName!.trim().isNotEmpty && cityName != 'Unknown';
|
||||
}
|
||||
Reference in New Issue
Block a user