functional datastructure. Needs to be able to write to storage as well
This commit is contained in:
		| @@ -2,6 +2,7 @@ import 'package:fast_network_navigation/modules/trips_overview.dart'; | ||||
| import 'package:fast_network_navigation/pages/new_trip.dart'; | ||||
| import 'package:fast_network_navigation/pages/tutorial.dart'; | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:fast_network_navigation/utils/load_trips.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| import 'package:fast_network_navigation/pages/overview.dart'; | ||||
| @@ -11,10 +12,13 @@ import 'package:fast_network_navigation/pages/profile.dart'; | ||||
| // A side drawer is used to switch between pages | ||||
| class BasePage extends StatefulWidget { | ||||
|   final String mainScreen; | ||||
|   final String currentMap; | ||||
|   final List<Trip> trips; | ||||
|   final Trip? trip; | ||||
|    | ||||
|   const BasePage({super.key, required this.mainScreen, this.currentMap = "map", this.trips = const []}); | ||||
|   const BasePage({ | ||||
|     super.key, | ||||
|     required this.mainScreen, | ||||
|     this.trip | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<BasePage> createState() => _BasePageState(); | ||||
| @@ -22,12 +26,15 @@ class BasePage extends StatefulWidget { | ||||
|  | ||||
| class _BasePageState extends State<BasePage> { | ||||
|  | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     Widget currentView = const Text("loading..."); | ||||
|     Future<List<Trip>> trips = loadTrips(); | ||||
|     Future<Trip> firstTrip = getFirstTrip(trips); | ||||
|     // Future<Trip> trip = Future(trips[0]); | ||||
|      | ||||
|     if (widget.mainScreen == "map") { | ||||
|       currentView = NavigationOverview(); | ||||
|       currentView = NavigationOverview(trip: firstTrip); | ||||
|     } else if (widget.mainScreen == "tutorial") { | ||||
|       currentView = TutorialPage(); | ||||
|     } else if (widget.mainScreen == "profile") { | ||||
| @@ -40,9 +47,6 @@ class _BasePageState extends State<BasePage> { | ||||
|       appBar: AppBar(title: Text("City Nav")), | ||||
|       body: Center(child: currentView), | ||||
|       drawer: Drawer( | ||||
|         // Add a ListView to the drawer. This ensures the user can scroll | ||||
|         // through the options in the drawer if there isn't enough vertical | ||||
|         // space to fit everything. | ||||
|         child: Column( | ||||
|           children: [ | ||||
|             DrawerHeader( | ||||
| @@ -61,20 +65,10 @@ class _BasePageState extends State<BasePage> { | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: const Text('Start'), | ||||
|               title: const Text('Your Trips'), | ||||
|               leading: const Icon(Icons.map), | ||||
|               selected: widget.mainScreen == "map", | ||||
|               onTap: () { | ||||
|                 Navigator.of(context).push( | ||||
|                   MaterialPageRoute( | ||||
|                     builder: (context) => BasePage(mainScreen: "map") | ||||
|                   ) | ||||
|                 ); | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: const Text('Trip Overview'), | ||||
|               leading: const Icon(Icons.list), | ||||
|               onTap: () {}, | ||||
|               trailing: ElevatedButton( | ||||
|                 onPressed: () { | ||||
|                   Navigator.of(context).push( | ||||
| @@ -86,7 +80,23 @@ class _BasePageState extends State<BasePage> { | ||||
|                 child: const Text('New'), | ||||
|               ), | ||||
|             ), | ||||
|             Expanded(child: TripsOverview()), | ||||
|  | ||||
|             // Adds a ListView to the drawer. This ensures the user can scroll | ||||
|             // through the options in the drawer if there isn't enough vertical | ||||
|             // space to fit everything. | ||||
|             Expanded( | ||||
|               child: TripsOverview(trips: trips), | ||||
|             ), | ||||
|             ElevatedButton( | ||||
|               onPressed: () { | ||||
|                 Navigator.of(context).push( | ||||
|                   MaterialPageRoute( | ||||
|                     builder: (context) => const NewTripPage() | ||||
|                   ) | ||||
|                 ); | ||||
|               }, | ||||
|               child: const Text('Clear trips'), | ||||
|             ), | ||||
|             const Divider(), | ||||
|             ListTile( | ||||
|               title: const Text('How to use'), | ||||
| @@ -101,6 +111,7 @@ class _BasePageState extends State<BasePage> { | ||||
|               }, | ||||
|             ), | ||||
|  | ||||
|             // settings in the bottom of the drawer | ||||
|             ListTile( | ||||
|               title: const Text('Settings'), | ||||
|               leading: const Icon(Icons.settings), | ||||
| @@ -113,7 +124,6 @@ class _BasePageState extends State<BasePage> { | ||||
|                 ); | ||||
|               }, | ||||
|             ), | ||||
|             // settings in the bottom of the drawer | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
| @@ -121,3 +131,9 @@ class _BasePageState extends State<BasePage> { | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| Future<Trip> getFirstTrip (Future<List<Trip>> trips) async { | ||||
|   List<Trip> tripsf = await trips; | ||||
|   return tripsf[0]; | ||||
| } | ||||
| @@ -17,4 +17,3 @@ class App extends StatelessWidget { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| Widget Greeter (ThemeData theme, {bool full = false}) { | ||||
| Widget Greeter(ThemeData theme, {bool full = false}) { | ||||
|   String greeterText = ""; | ||||
|   try { | ||||
|     String cityName = getCityName(); | ||||
|   | ||||
| @@ -32,7 +32,9 @@ class _LandmarkCardState extends State<LandmarkCard> { | ||||
|               // force a fixed width | ||||
|               width: 160, | ||||
|               child: Image.network( | ||||
|                 widget.landmark.imageURL, | ||||
|                 widget.landmark.imageURL!, | ||||
|                 errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined), | ||||
|                 // TODO: make this a switch statement to load a placeholder if null | ||||
|                 // cover the whole container meaning the image will be cropped | ||||
|                 fit: BoxFit.cover, | ||||
|               ), | ||||
|   | ||||
| @@ -1,29 +1,34 @@ | ||||
| import 'dart:collection'; | ||||
|  | ||||
| import 'package:fast_network_navigation/modules/landmark_card.dart'; | ||||
| import 'package:fast_network_navigation/structs/landmark.dart'; | ||||
| import 'package:fast_network_navigation/utils/get_landmarks.dart'; | ||||
|  | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class loadLandmarksOverview extends StatefulWidget { | ||||
|   const loadLandmarksOverview({super.key}); | ||||
| class LandmarksOverview extends StatefulWidget { | ||||
|   final Future<Trip>? trip; | ||||
|   const LandmarksOverview({super.key, this.trip}); | ||||
|  | ||||
|   @override | ||||
|   State<loadLandmarksOverview> createState() => _loadLandmarksOverviewState(); | ||||
|   State<LandmarksOverview> createState() => _LandmarksOverviewState(); | ||||
| } | ||||
|  | ||||
| class _loadLandmarksOverviewState extends State<loadLandmarksOverview> { | ||||
|   final Future<List<Landmark>> _landmarks = fetchLandmarks(); | ||||
| class _LandmarksOverviewState extends State<LandmarksOverview> { | ||||
|   // final Future<List<Landmark>> _landmarks = fetchLandmarks(); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip); | ||||
|     return DefaultTextStyle( | ||||
|       style: Theme.of(context).textTheme.displayMedium!, | ||||
|       textAlign: TextAlign.center, | ||||
|       child: FutureBuilder<List<Landmark>>( | ||||
|       child: FutureBuilder<LinkedList<Landmark>>( | ||||
|         future: _landmarks, | ||||
|         builder: (BuildContext context, AsyncSnapshot<List<Landmark>> snapshot) { | ||||
|         builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) { | ||||
|           List<Widget> children; | ||||
|           if (snapshot.hasData) { | ||||
|             children = [landmarksWithSteps(snapshot.data!)]; | ||||
| @@ -42,13 +47,10 @@ class _loadLandmarksOverviewState extends State<loadLandmarksOverview> { | ||||
|           } else { | ||||
|             children = [Center(child: CircularProgressIndicator())]; | ||||
|           } | ||||
|           return Padding( | ||||
|             padding: const EdgeInsets.all(10), | ||||
|             child: Center( | ||||
|               child: Column( | ||||
|                 mainAxisAlignment: MainAxisAlignment.center, | ||||
|                 children: children, | ||||
|               ), | ||||
|           return Center( | ||||
|             child: Column( | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               children: children, | ||||
|             ), | ||||
|           ); | ||||
|         }, | ||||
| @@ -57,12 +59,12 @@ class _loadLandmarksOverviewState extends State<loadLandmarksOverview> { | ||||
|   } | ||||
| } | ||||
|  | ||||
| Widget landmarksWithSteps(List<Landmark> landmarks) { | ||||
| Widget landmarksWithSteps(LinkedList<Landmark> landmarks) { | ||||
|   List<Widget> children = []; | ||||
|   for (int i = 0; i < landmarks.length; i++) { | ||||
|     children.add(LandmarkCard(landmarks[i])); | ||||
|     if (i < landmarks.length - 1) { | ||||
|       Widget step = stepBetweenLandmarks(landmarks[i], landmarks[i + 1]); | ||||
|   for (Landmark landmark in landmarks) { | ||||
|     children.add(LandmarkCard(landmark)); | ||||
|     if (landmark.next != null) { | ||||
|       Widget step = stepBetweenLandmarks(landmark, landmark.next!); | ||||
|       children.add(step); | ||||
|     } | ||||
|   } | ||||
| @@ -109,4 +111,9 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) { | ||||
|       ], | ||||
|     ), | ||||
|   ); | ||||
| } | ||||
| } | ||||
|  | ||||
| Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async { | ||||
|   Trip tripf = await trip!; | ||||
|   return tripf.landmarks; | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,18 @@ | ||||
| import 'dart:collection'; | ||||
|  | ||||
| import 'package:fast_network_navigation/structs/landmark.dart'; | ||||
| import 'package:fast_network_navigation/utils/get_landmarks.dart'; | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:google_maps_flutter/google_maps_flutter.dart'; | ||||
|  | ||||
| class MapWidget extends StatefulWidget { | ||||
|  | ||||
|   final Future<Trip>? trip; | ||||
|  | ||||
|   MapWidget({ | ||||
|     this.trip | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<MapWidget> createState() => _MapWidgetState(); | ||||
| } | ||||
| @@ -31,7 +40,8 @@ class _MapWidgetState extends State<MapWidget> { | ||||
|  | ||||
|   void drawLandmarks() async { | ||||
|     // (re)draws landmarks on the map | ||||
|     List<Landmark> landmarks = await fetchLandmarks(); | ||||
|     Trip? trip = await widget.trip; | ||||
|     LinkedList<Landmark> landmarks = trip!.landmarks; | ||||
|     setState(() { | ||||
|       for (Landmark landmark in landmarks) { | ||||
|         markers.add(Marker( | ||||
| @@ -55,4 +65,3 @@ class _MapWidgetState extends State<MapWidget> { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,19 +2,21 @@ import 'package:flutter/material.dart'; | ||||
|  | ||||
| import 'package:fast_network_navigation/layout.dart'; | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:fast_network_navigation/utils/get_trips.dart'; | ||||
|  | ||||
|  | ||||
| class TripsOverview extends StatefulWidget { | ||||
|    | ||||
|   const TripsOverview({super.key}); | ||||
|   final Future<List<Trip>> trips; | ||||
|   const TripsOverview({ | ||||
|     super.key, | ||||
|     required this.trips, | ||||
|     }); | ||||
|  | ||||
|   @override | ||||
|   State<TripsOverview> createState() => _TripsOverviewState(); | ||||
| } | ||||
|  | ||||
| class _TripsOverviewState extends State<TripsOverview> { | ||||
|   final Future<List<Trip>> _trips = loadTrips(); | ||||
|   // final Future<List<Trip>> _trips = loadTrips(); | ||||
|  | ||||
|  | ||||
|   Widget listBuild (BuildContext context, AsyncSnapshot<List<Trip>> snapshot) { | ||||
| @@ -23,12 +25,12 @@ class _TripsOverviewState extends State<TripsOverview> { | ||||
|       children = List<Widget>.generate(snapshot.data!.length, (index) { | ||||
|         Trip trip = snapshot.data![index]; | ||||
|         return ListTile( | ||||
|           title: Text("Trip to ${trip.cityName} (${trip.landmarks.length} stops)"), | ||||
|           title: Text("Trip to ${trip.cityName}"), | ||||
|           leading: Icon(Icons.pin_drop), | ||||
|           onTap: () { | ||||
|             Navigator.of(context).push( | ||||
|               MaterialPageRoute( | ||||
|                 builder: (context) => BasePage(mainScreen: "map") //, trip: trip) | ||||
|                 builder: (context) => BasePage(mainScreen: "map", trip: trip) | ||||
|               ) | ||||
|             ); | ||||
|           }, | ||||
| @@ -58,7 +60,7 @@ class _TripsOverviewState extends State<TripsOverview> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return FutureBuilder( | ||||
|       future: _trips, | ||||
|       future: widget.trips, | ||||
|       builder: listBuild, | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import 'package:fast_network_navigation/modules/greeter.dart'; | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:sliding_up_panel/sliding_up_panel.dart'; | ||||
|  | ||||
| @@ -8,6 +9,12 @@ import 'package:fast_network_navigation/modules/map.dart'; | ||||
|  | ||||
|  | ||||
| class NavigationOverview extends StatefulWidget { | ||||
|   final Future<Trip> trip; | ||||
|  | ||||
|   NavigationOverview({ | ||||
|     required this.trip | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<NavigationOverview> createState() => _NavigationOverviewState(); | ||||
| } | ||||
| @@ -23,7 +30,7 @@ class _NavigationOverviewState extends State<NavigationOverview> { | ||||
|         renderPanelSheet: false, | ||||
|         panel: _floatingPanel(theme), | ||||
|         collapsed: _floatingCollapsed(theme), | ||||
|         body: MapWidget() | ||||
|         body: MapWidget(trip: widget.trip) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -58,7 +65,7 @@ class _NavigationOverviewState extends State<NavigationOverview> { | ||||
|           child: Column( | ||||
|             children: <Widget>[ | ||||
|               Greeter(theme, full: true), | ||||
|               loadLandmarksOverview(), | ||||
|               LandmarksOverview(trip: widget.trip), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|   | ||||
| @@ -51,13 +51,7 @@ class ImportanceSliders extends StatefulWidget { | ||||
|  | ||||
| class _ImportanceSlidersState extends State<ImportanceSliders> { | ||||
|  | ||||
|   final UserPreferences _prefs = UserPreferences(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     _prefs.load(); | ||||
|     super.initState(); | ||||
|   } | ||||
|   UserPreferences _prefs = UserPreferences(); | ||||
|  | ||||
|   List<Card> _createSliders() { | ||||
|     List<Card> sliders = []; | ||||
| @@ -89,6 +83,7 @@ class _ImportanceSlidersState extends State<ImportanceSliders> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
|     return Column(children: _createSliders()); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,56 +1,69 @@ | ||||
| class Landmark { | ||||
|   final String name; | ||||
|   final List location; | ||||
|   final LandmarkType type; | ||||
|   final String imageURL; | ||||
|   // final String description; | ||||
|   // final Duration duration; | ||||
|   // final bool visited; | ||||
| import 'dart:collection'; | ||||
| import 'dart:convert'; | ||||
|  | ||||
|   const Landmark({ | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| final class Landmark extends LinkedListEntry<Landmark>{ | ||||
|   // A linked node of a list of Landmarks | ||||
|   final String uuid; | ||||
|   final String name; | ||||
|   final List<double> location; | ||||
|   final LandmarkType type; | ||||
|   final bool? isSecondary; | ||||
|  | ||||
|   // description to be shown in the overview | ||||
|   final String? imageURL; | ||||
|   final String? description; | ||||
|   final Duration? duration; | ||||
|   final bool? visited; | ||||
|  | ||||
|   // Next node | ||||
|   // final Landmark? next; | ||||
|   final Duration? tripTime; | ||||
|  | ||||
|   Landmark({ | ||||
|     required this.uuid, | ||||
|     required this.name, | ||||
|     required this.location, | ||||
|     required this.type, | ||||
|     this.imageURL = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg', | ||||
|     // required this.description, | ||||
|     // required this.duration, | ||||
|     // required this.visited, | ||||
|     this.isSecondary, | ||||
|  | ||||
|     this.imageURL, | ||||
|     this.description, | ||||
|     this.duration, | ||||
|     this.visited, | ||||
|  | ||||
|     // this.next, | ||||
|     this.tripTime, | ||||
|   }); | ||||
|  | ||||
|   factory Landmark.fromJson(Map<String, dynamic> json) { | ||||
|     return switch (json) { | ||||
|       { | ||||
|         'loc': List location, | ||||
|     if (json | ||||
|       case { // automatically match all the non-optionals and cast them to the right type | ||||
|         'uuid': String uuid, | ||||
|         'name': String name, | ||||
|         'type': String type, | ||||
|         // 'description': String description, | ||||
|         // 'duration': int duration, | ||||
|         // 'visited': bool visited | ||||
|  | ||||
|       } => | ||||
|         Landmark( | ||||
|           name: name, | ||||
|           location: location, | ||||
|           type: LandmarkType(name: type) | ||||
|           // description: description, | ||||
|           // duration: Duration(minutes: duration), | ||||
|           // visited: visited | ||||
|         ), | ||||
|       _ => throw const FormatException('Failed to load destination.'), | ||||
|     }; | ||||
|         'location': List<double> location, | ||||
|         'type': LandmarkType type, | ||||
|       }) { | ||||
|         // parse the rest separately, they could be missing | ||||
|         final isSecondary = json['is_secondary'] as bool?; | ||||
|         final imageURL = json['image_url'] as String?; | ||||
|         final description = json['description'] as String?; | ||||
|         final duration = json['duration'] as Duration?; | ||||
|         final visited = json['visited'] as bool?; | ||||
|          | ||||
|         return Landmark( | ||||
|           uuid: uuid, name: name, location: location, type: type, isSecondary: isSecondary, imageURL: imageURL, description: description, duration: duration, visited: visited); | ||||
|     } else { | ||||
|       throw FormatException('Invalid JSON: $json'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|     Map<String, dynamic> toJson() { | ||||
|       return { | ||||
|         'name': name, | ||||
|         'location': location, | ||||
|         'type': type.name, | ||||
|         // 'description': description, | ||||
|         // 'duration': duration.inMinutes, | ||||
|         // 'visited': visited | ||||
|       }; | ||||
|     } | ||||
|   @override | ||||
|   bool operator ==(Object other) { | ||||
|     return other is Landmark && uuid == other.uuid; | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -67,3 +80,12 @@ class LandmarkType { | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| // Helper | ||||
| (Landmark, String?) getLandmarkFromPrefs(SharedPreferences prefs, String uuid) { | ||||
|   String? content = prefs.getString('landmark_$uuid'); | ||||
|   Map<String, dynamic> json = jsonDecode(content!); | ||||
|   String? nextUUID = json['next_uuid']; | ||||
|   return (Landmark.fromJson(json), nextUUID); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										46
									
								
								frontend/lib/structs/linked_landmarks.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								frontend/lib/structs/linked_landmarks.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| // import "package:fast_network_navigation/structs/landmark.dart"; | ||||
|  | ||||
| // class Linked<Landmark> { | ||||
| //   Landmark? head; | ||||
|    | ||||
| //   Linked(); | ||||
|  | ||||
| //   // class methods | ||||
| //   bool get isEmpty => head == null; | ||||
|  | ||||
| //   // Add a new node to the end of the list | ||||
| //   void add(Landmark value) { | ||||
| //     if (isEmpty) { | ||||
| //       // If the list is empty, set the new node as the head | ||||
| //       head = value; | ||||
| //     } else { | ||||
| //       Landmark? current = head; | ||||
| //       while (current!.next != null) { | ||||
| //         // Traverse the list to find the last node | ||||
| //         current = current.next; | ||||
| //       } | ||||
| //       current.next = value; // Set the new node as the next node of the last node | ||||
| //     } | ||||
| //   } | ||||
|  | ||||
| //   // Remove the first node with the given value | ||||
| //   void remove(Landmark value) { | ||||
| //     if (isEmpty) return; | ||||
|  | ||||
| //     // If the value is in the head node, update the head to the next node | ||||
| //     if (head! == value) { | ||||
| //       head = head.next; | ||||
| //       return; | ||||
| //     } | ||||
|  | ||||
| //     var current = head; | ||||
| //     while (current!.next != null) { | ||||
| //       if (current.next! == value) { | ||||
| //         // If the value is found in the next node, skip the next node | ||||
| //         current.next = current.next.next; | ||||
| //         return; | ||||
| //       } | ||||
| //       current = current.next; | ||||
| //     } | ||||
| //   } | ||||
| // } | ||||
| @@ -67,16 +67,16 @@ class UserPreferences { | ||||
|  | ||||
|  | ||||
|   void save() async { | ||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|     SharedPreferences sharedPrefs = await SharedPreferences.getInstance(); | ||||
|     for (SinglePreference pref in preferences) { | ||||
|       prefs.setInt(pref.key, pref.value); | ||||
|       sharedPrefs.setInt(pref.key, pref.value); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void load() async { | ||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|     SharedPreferences sharedPrefs = await SharedPreferences.getInstance(); | ||||
|     for (SinglePreference pref in preferences) { | ||||
|       pref.value = prefs.getInt(pref.key) ?? 0; | ||||
|       pref.value = sharedPrefs.getInt(pref.key) ?? 0; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -5,7 +5,7 @@ class Route { | ||||
|   final String name; | ||||
|   final Duration duration; | ||||
|   final List<Landmark> landmarks; | ||||
|    | ||||
|  | ||||
|   Route({ | ||||
|     required this.name, | ||||
|     required this.duration, | ||||
|   | ||||
| @@ -1,25 +1,49 @@ | ||||
| // 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 'package:fast_network_navigation/structs/landmark.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| class Trip { | ||||
|   final String uuid; | ||||
|   final String cityName; | ||||
|   final List<Landmark> landmarks; | ||||
|   // TODO: cityName should be inferred from coordinates of the Landmarks | ||||
|   final LinkedList<Landmark> landmarks; | ||||
|   // could be empty as well | ||||
|  | ||||
|  | ||||
|   Trip({required this.uuid, required this.cityName, required this.landmarks}); | ||||
|   Trip({ | ||||
|     required this.uuid, | ||||
|     required this.cityName, | ||||
|     required this.landmarks, | ||||
|     }); | ||||
|  | ||||
|   factory Trip.fromJson(Map<String, dynamic> json) { | ||||
|     List<Landmark> landmarks = []; | ||||
|     for (var landmark in json['landmarks']) { | ||||
|       landmarks.add(Landmark.fromJson(landmark)); | ||||
|     } | ||||
|     return Trip( | ||||
|       uuid: json['uuid'], | ||||
|       cityName: json['cityName'], | ||||
|       landmarks: landmarks, | ||||
|       landmarks: LinkedList() | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   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['entry_uuid']; | ||||
|     appendLandmarks(trip.landmarks, prefs, firstUUID); | ||||
|     return trip; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Helper | ||||
|  | ||||
| appendLandmarks(LinkedList<Landmark> landmarks, SharedPreferences prefs, String? firstUUID) { | ||||
|   while (firstUUID != null) { | ||||
|     var (head, nextUUID) = getLandmarkFromPrefs(prefs, firstUUID); | ||||
|     landmarks.add(head); | ||||
|     firstUUID = nextUUID; | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										52
									
								
								frontend/lib/utils/fetch_landmarks.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								frontend/lib/utils/fetch_landmarks.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| // import "package:fast_network_navigation/structs/landmark.dart"; | ||||
| // import 'package:http/http.dart' as http; | ||||
|  | ||||
|  | ||||
| // Future<List<Landmark>> fetchLandmarks() async { | ||||
| //   // final response = await http | ||||
| //   //     .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1')); | ||||
|  | ||||
| //   // if (response.statusCode == 200) { | ||||
| //     // If the server did return a 200 OK response, | ||||
| //     // then parse the JSON. | ||||
| //     List<Landmark> landmarks = [ | ||||
| //       // 48°51′29.6″N 2°17′40.2″E | ||||
| //       Landmark( | ||||
| //         name: "Eiffel Tower", | ||||
| //         location: [48.51296, 2.17402], | ||||
| //         type: LandmarkType(name: "Tower"), | ||||
| //         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg" | ||||
| //         ), | ||||
| //       Landmark( | ||||
| //         name: "Notre Dame Cathedral", | ||||
| //         location: [48.8530, 2.3498], | ||||
| //         type: LandmarkType(name: "Monument"), | ||||
| //         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg" | ||||
| //         ), | ||||
| //       Landmark( | ||||
| //         name: "Louvre palace", | ||||
| //         location: [48.8606, 2.3376], | ||||
| //         type: LandmarkType(name: "Museum"), | ||||
| //         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg" | ||||
| //         ), | ||||
| //       Landmark( | ||||
| //         name: "Pont-des-arts", | ||||
| //         location: [48.5130, 2.2015], | ||||
| //         type: LandmarkType(name: "Bridge"), | ||||
| //         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"), | ||||
| //       Landmark( | ||||
| //         name: "Panthéon", | ||||
| //         location: [48.5046, 2.2046], | ||||
| //         type: LandmarkType(name: "Monument"), | ||||
| //         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG" | ||||
| //         ), | ||||
| //     ]; | ||||
| //     // sleep 10 seconds | ||||
| //     await Future.delayed(Duration(seconds: 5)); | ||||
| //     return landmarks; | ||||
| //   // } else { | ||||
| //   //   // If the server did not return a 200 OK response, | ||||
| //   //   // then throw an exception. | ||||
| //   //   throw Exception('Failed to load destination'); | ||||
| //   // } | ||||
| // } | ||||
| @@ -1,52 +0,0 @@ | ||||
| import "package:fast_network_navigation/structs/landmark.dart"; | ||||
| import 'package:http/http.dart' as http; | ||||
|  | ||||
|  | ||||
| Future<List<Landmark>> fetchLandmarks() async { | ||||
|   // final response = await http | ||||
|   //     .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1')); | ||||
|  | ||||
|   // if (response.statusCode == 200) { | ||||
|     // If the server did return a 200 OK response, | ||||
|     // then parse the JSON. | ||||
|     List<Landmark> landmarks = [ | ||||
|       // 48°51′29.6″N 2°17′40.2″E | ||||
|       Landmark( | ||||
|         name: "Eiffel Tower", | ||||
|         location: [48.51296, 2.17402], | ||||
|         type: LandmarkType(name: "Tower"), | ||||
|         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg" | ||||
|         ), | ||||
|       Landmark( | ||||
|         name: "Notre Dame Cathedral", | ||||
|         location: [48.8530, 2.3498], | ||||
|         type: LandmarkType(name: "Monument"), | ||||
|         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg" | ||||
|         ), | ||||
|       Landmark( | ||||
|         name: "Louvre palace", | ||||
|         location: [48.8606, 2.3376], | ||||
|         type: LandmarkType(name: "Museum"), | ||||
|         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg" | ||||
|         ), | ||||
|       Landmark( | ||||
|         name: "Pont-des-arts", | ||||
|         location: [48.5130, 2.2015], | ||||
|         type: LandmarkType(name: "Bridge"), | ||||
|         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"), | ||||
|       Landmark( | ||||
|         name: "Panthéon", | ||||
|         location: [48.5046, 2.2046], | ||||
|         type: LandmarkType(name: "Monument"), | ||||
|         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG" | ||||
|         ), | ||||
|     ]; | ||||
|     // sleep 10 seconds | ||||
|     await Future.delayed(Duration(seconds: 5)); | ||||
|     return landmarks; | ||||
|   // } else { | ||||
|   //   // If the server did not return a 200 OK response, | ||||
|   //   // then throw an exception. | ||||
|   //   throw Exception('Failed to load destination'); | ||||
|   // } | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:fast_network_navigation/structs/landmark.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| Future<List<Trip>> loadTrips() async { | ||||
|   SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|  | ||||
|   Set<String> keys = prefs.getKeys(); | ||||
|   List<Trip> itineraries = []; | ||||
|   for (String key in keys) { | ||||
|     if (key.startsWith("itinerary_")) { | ||||
|       String json = prefs.getString(key)!; | ||||
|       itineraries.add(Trip.fromJson(jsonDecode(json))); | ||||
|     } | ||||
|   } | ||||
|   itineraries.add(Trip(uuid: "1", cityName: "Paris", landmarks: [ | ||||
|       Landmark(name: "Landmark 1", location: [48.85, 2.35], type: LandmarkType(name: "Type 1")), | ||||
|       Landmark(name: "Landmark 2", location: [48.86, 2.36], type: LandmarkType(name: "Type 2")), | ||||
|       Landmark(name: "Landmark 3", location: [48.75, 2.3], type: LandmarkType(name: "Type 3")), | ||||
|       Landmark(name: "Landmark 4", location: [48.9, 2.4], type: LandmarkType(name: "Type 4")), | ||||
|       Landmark(name: "Landmark 5", location: [48.91, 2.45], type: LandmarkType(name: "Type 5")), | ||||
|     ])); | ||||
|   itineraries.add(Trip(uuid: "2", cityName: "Vienna", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "3", cityName: "London", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "4", cityName: "Madrid", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "5", cityName: "Tokyo", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "6", cityName: "New York", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "7", cityName: "Los Angeles", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "8", cityName: "Zurich", landmarks: [])); | ||||
|   itineraries.add(Trip(uuid: "9", cityName: "Orschwiller", landmarks: [])); | ||||
|  | ||||
|   await Future.delayed(Duration(seconds: 3)); | ||||
|  | ||||
|   return itineraries; | ||||
| } | ||||
							
								
								
									
										44
									
								
								frontend/lib/utils/load_trips.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								frontend/lib/utils/load_trips.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| import 'dart:collection'; | ||||
|  | ||||
| import 'package:fast_network_navigation/structs/linked_landmarks.dart'; | ||||
| import 'package:fast_network_navigation/structs/trip.dart'; | ||||
| import 'package:fast_network_navigation/structs/landmark.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| Future<List<Trip>> loadTrips() async { | ||||
|   SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|  | ||||
|   List<Trip> trips = []; | ||||
|   Set<String> keys = prefs.getKeys(); | ||||
|   for (String key in keys) { | ||||
|     if (key.startsWith('trip_')) { | ||||
|       String uuid = key.replaceFirst('trip_', ''); | ||||
|       trips.add(Trip.fromPrefs(prefs, uuid)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (trips.isEmpty) { | ||||
|     String now = DateTime.now().toString(); | ||||
|     trips.add( | ||||
|       Trip(uuid: '1', cityName: 'Paris (generated $now)', landmarks: LinkedList<Landmark>()) | ||||
|     ); | ||||
|     //   Trip(uuid: "1", cityName: "Paris", landmarks: [ | ||||
|     //     Landmark(name: "Landmark 1", location: [48.85, 2.35], type: LandmarkType(name: "Type 1")), | ||||
|     //     Landmark(name: "Landmark 2", location: [48.86, 2.36], type: LandmarkType(name: "Type 2")), | ||||
|     //     Landmark(name: "Landmark 3", location: [48.75, 2.3], type: LandmarkType(name: "Type 3")), | ||||
|     //     Landmark(name: "Landmark 4", location: [48.9, 2.4], type: LandmarkType(name: "Type 4")), | ||||
|     //     Landmark(name: "Landmark 5", location: [48.91, 2.45], type: LandmarkType(name: "Type 5")), | ||||
|     //   ])); | ||||
|     // trips.add(Trip(uuid: "2", cityName: "Vienna", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "3", cityName: "London", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "4", cityName: "Madrid", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "5", cityName: "Tokyo", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "6", cityName: "New York", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "7", cityName: "Los Angeles", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "8", cityName: "Zurich", landmarks: [])); | ||||
|     // trips.add(Trip(uuid: "9", cityName: "Orschwiller", landmarks: [])); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   return trips; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user