// Represents a collection of landmarks that represent a journey // Different instances of a Trip can be saved and loaded by the user import 'dart:collection'; import 'dart:convert'; import 'dart:developer'; import 'package:anyway/structs/landmark.dart'; import 'package:flutter/foundation.dart'; import 'package:geocoding/geocoding.dart'; import 'package:shared_preferences/shared_preferences.dart'; class Trip with ChangeNotifier { String uuid; int totalTime; LinkedList<Landmark> landmarks; // could be empty as well String? errorDescription; Future<String> get cityName async { List<double>? location = landmarks.firstOrNull?.location; if (GeocodingPlatform.instance == null) { return '$location'; } else if (location == null) { return 'Unknown'; } else{ List<Placemark> placemarks = await placemarkFromCoordinates(location[0], location[1]); return placemarks.first.locality ?? 'Unknown'; } } Trip({ this.uuid = 'pending', this.totalTime = 0, LinkedList<Landmark>? landmarks // a trip can be created with no landmarks, but the list should be initialized anyway }) : landmarks = landmarks ?? LinkedList<Landmark>(); factory Trip.fromJson(Map<String, dynamic> json) { Trip trip = Trip( uuid: json['uuid'], totalTime: json['total_time'], ); return trip; } void loadFromJson(Map<String, dynamic> json) { uuid = json['uuid']; totalTime = json['total_time']; notifyListeners(); } void addLandmark(Landmark landmark) { landmarks.add(landmark); notifyListeners(); } void updateUUID(String newUUID) { uuid = newUUID; notifyListeners(); } void removeLandmark(Landmark landmark) { landmarks.remove(landmark); notifyListeners(); } void updateError(String error) { errorDescription = error; notifyListeners(); } factory Trip.fromPrefs(SharedPreferences prefs, String uuid) { String? content = prefs.getString('trip_$uuid'); Map<String, dynamic> json = jsonDecode(content!); Trip trip = Trip.fromJson(json); String? firstUUID = json['first_landmark_uuid']; log('Loading trip $uuid with first landmark $firstUUID'); LinkedList<Landmark> landmarks = readLandmarks(prefs, firstUUID); trip.landmarks = landmarks; // notifyListeners(); return trip; } Map<String, dynamic> toJson() => { 'uuid': uuid, 'total_time': totalTime, 'first_landmark_uuid': landmarks.first.uuid }; void toPrefs(SharedPreferences prefs){ Map<String, dynamic> json = toJson(); log('Saving trip $uuid : $json'); prefs.setString('trip_$uuid', jsonEncode(json)); for (Landmark landmark in landmarks) { landmarkToPrefs(prefs, landmark, landmark.next); } } } // Helper LinkedList<Landmark> readLandmarks(SharedPreferences prefs, String? firstUUID) { LinkedList<Landmark> landmarks = LinkedList<Landmark>(); while (firstUUID != null) { var (head, nextUUID) = getLandmarkFromPrefs(prefs, firstUUID); landmarks.add(head); firstUUID = nextUUID; } return landmarks; } void removeAllTripsFromPrefs () async { SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.clear(); }