Merge pull request 'UI and UX fixes to make new trips (and the errors it might have less confusing)' (#26) from fix/frontend/greeter-ui into main
Reviewed-on: #26
This commit is contained in:
		| @@ -41,9 +41,8 @@ jobs: | ||||
|  | ||||
|       - name: Load secrets from github | ||||
|         run: | | ||||
|           echo "${{ secrets.ANDROID_SECRET_PROPERTIES }}" > secrets.properties | ||||
|           echo "${{ secrets.ANDROID_GOOGLE_PLAY_JSON }}" > fastlane/google-key.json | ||||
|           # decode the base64 encoded google key | ||||
|           echo "${{ secrets.ANDROID_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties | ||||
|           echo "${{ secrets.ANDROID_GOOGLE_PLAY_JSON_BASE64 }}" | base64 -d > fastlane/google-key.json | ||||
|           echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > release.keystore | ||||
|         working-directory: android | ||||
|  | ||||
|   | ||||
							
								
								
									
										37
									
								
								frontend/lib/modules/current_trip_error_message.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								frontend/lib/modules/current_trip_error_message.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:auto_size_text/auto_size_text.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class CurrentTripErrorMessage extends StatefulWidget { | ||||
|   final Trip trip; | ||||
|   const CurrentTripErrorMessage({ | ||||
|     super.key, | ||||
|     required this.trip, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<CurrentTripErrorMessage> createState() => _CurrentTripErrorMessageState(); | ||||
| } | ||||
|  | ||||
| class _CurrentTripErrorMessageState extends State<CurrentTripErrorMessage> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) => Center( | ||||
|     child: Row( | ||||
|       mainAxisAlignment: MainAxisAlignment.center, | ||||
|       children: [ | ||||
|         const Icon( | ||||
|           Icons.error_outline, | ||||
|           color: Colors.red, | ||||
|           size: 50, | ||||
|         ), | ||||
|         const Padding( | ||||
|           padding: EdgeInsets.only(left: 10), | ||||
|         ), | ||||
|         AutoSizeText( | ||||
|           'Error: ${widget.trip.errorDescription}', | ||||
|           maxLines: 3, | ||||
|         ), | ||||
|       ], | ||||
|     ) | ||||
|   ); | ||||
| } | ||||
| @@ -1,111 +1,50 @@ | ||||
| import 'dart:developer'; | ||||
|  | ||||
| import 'package:anyway/constants.dart'; | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:auto_size_text/auto_size_text.dart'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:anyway/pages/current_trip.dart'; | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
|  | ||||
| class Greeter extends StatefulWidget { | ||||
|  | ||||
| class CurrentTripGreeter extends StatefulWidget { | ||||
|   final Trip trip; | ||||
|  | ||||
|   Greeter({ | ||||
|   CurrentTripGreeter({ | ||||
|     super.key, | ||||
|     required this.trip, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<Greeter> createState() => _GreeterState(); | ||||
|   State<CurrentTripGreeter> createState() => _CurrentTripGreeterState(); | ||||
| } | ||||
|  | ||||
|  | ||||
| class _GreeterState extends State<Greeter> { | ||||
|    | ||||
|   Widget greeterBuilder (BuildContext context, Widget? child) { | ||||
|     final Shader textGradient = APP_GRADIENT.createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0)); | ||||
|     TextStyle greeterStyle = TextStyle( | ||||
|       foreground: Paint()..shader = textGradient, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       fontSize: 26 | ||||
|     ); | ||||
|  | ||||
|     Widget topGreeter; | ||||
|  | ||||
|     if (widget.trip.uuid != 'pending') { | ||||
|       topGreeter = FutureBuilder( | ||||
|         future: widget.trip.cityName, | ||||
|         builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|           if (snapshot.hasData) { | ||||
|             return AutoSizeText( | ||||
|               maxLines: 1, | ||||
|               'Welcome to ${snapshot.data}!', | ||||
|               style: greeterStyle | ||||
|             ); | ||||
|           } else if (snapshot.hasError) { | ||||
|             log('Error while fetching city name'); | ||||
|             return AutoSizeText( | ||||
|               maxLines: 1, | ||||
|               'Welcome to your trip!', | ||||
|               style: greeterStyle | ||||
|             ); | ||||
|           } else { | ||||
|             return AutoSizeText( | ||||
|               maxLines: 1, | ||||
|               'Welcome to ...', | ||||
|               style: greeterStyle | ||||
|             ); | ||||
|           } | ||||
|         } | ||||
|       ); | ||||
|     } else { | ||||
|       // still awaiting the trip | ||||
|       // We can hopefully infer the city name from the cityName future | ||||
|       // Show a linear loader at the bottom and an info message above | ||||
|       topGreeter = Column( | ||||
|         mainAxisAlignment: MainAxisAlignment.end, | ||||
|         children: [ | ||||
|           FutureBuilder( | ||||
|             future: widget.trip.cityName, | ||||
|             builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|               if (snapshot.hasData) { | ||||
|                 return AutoSizeText( | ||||
|                   maxLines: 1, | ||||
|                   'Generating your trip to ${snapshot.data}...', | ||||
|                   style: greeterStyle | ||||
|                 ); | ||||
|               } else if (snapshot.hasError) { | ||||
|                 // the exact error is shown in the central part of the trip overview. No need to show it here | ||||
|                 return AutoSizeText( | ||||
|                   maxLines: 1, | ||||
|                   'Error while loading trip.', | ||||
|                   style: greeterStyle | ||||
|                   ); | ||||
|               } | ||||
|               return AutoSizeText( | ||||
|                   maxLines: 1, | ||||
|                   'Generating your trip...', | ||||
|                   style: greeterStyle | ||||
|                   ); | ||||
|             } | ||||
|           ), | ||||
|           Padding( | ||||
|             padding: EdgeInsets.all(5), | ||||
|             child: const LinearProgressIndicator() | ||||
|           ) | ||||
|         ] | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return Center( | ||||
|       child: topGreeter, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|  | ||||
| class _CurrentTripGreeterState extends State<CurrentTripGreeter> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListenableBuilder( | ||||
|       listenable: widget.trip, | ||||
|       builder: greeterBuilder, | ||||
|     ); | ||||
|   } | ||||
|   Widget build(BuildContext context) => Center( | ||||
|     child: FutureBuilder( | ||||
|       future: widget.trip.cityName, | ||||
|       builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|         if (snapshot.hasData) { | ||||
|           return AutoSizeText( | ||||
|             maxLines: 1, | ||||
|             'Welcome to ${snapshot.data}!', | ||||
|             style: greeterStyle | ||||
|           ); | ||||
|         } else if (snapshot.hasError) { | ||||
|           return AutoSizeText( | ||||
|             maxLines: 1, | ||||
|             'Welcome to your trip!', | ||||
|             style: greeterStyle | ||||
|           ); | ||||
|         } else { | ||||
|           return AutoSizeText( | ||||
|             maxLines: 1, | ||||
|             'Welcome to ...', | ||||
|             style: greeterStyle | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|     ) | ||||
|   ); | ||||
|  | ||||
| } | ||||
							
								
								
									
										60
									
								
								frontend/lib/modules/current_trip_loading_indicator.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								frontend/lib/modules/current_trip_loading_indicator.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:auto_size_text/auto_size_text.dart'; | ||||
|  | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:anyway/pages/current_trip.dart'; | ||||
|  | ||||
| class CurrentTripLoadingIndicator extends StatefulWidget { | ||||
|   final Trip trip; | ||||
|   const CurrentTripLoadingIndicator({ | ||||
|     super.key, | ||||
|     required this.trip, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<CurrentTripLoadingIndicator> createState() => _CurrentTripLoadingIndicatorState(); | ||||
| } | ||||
|  | ||||
| class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicator> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) => Center( | ||||
|     child: FutureBuilder( | ||||
|       future: widget.trip.cityName, | ||||
|       builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|         Widget greeter; | ||||
|         Widget loadingIndicator = const Padding( | ||||
|           padding: EdgeInsets.only(top: 10), | ||||
|           child: CircularProgressIndicator() | ||||
|         ); | ||||
|  | ||||
|         if (snapshot.hasData) { | ||||
|           greeter = AutoSizeText( | ||||
|             maxLines: 1, | ||||
|             'Generating your trip to ${snapshot.data}...', | ||||
|             style: greeterStyle, | ||||
|           ); | ||||
|         } else if (snapshot.hasError) { | ||||
|           // the exact error is shown in the central part of the trip overview. No need to show it here | ||||
|           greeter = AutoSizeText( | ||||
|             maxLines: 1, | ||||
|             'Error while loading trip.', | ||||
|             style: greeterStyle, | ||||
|             ); | ||||
|         } else { | ||||
|           greeter = AutoSizeText( | ||||
|             maxLines: 1, | ||||
|             'Generating your trip...', | ||||
|             style: greeterStyle, | ||||
|             ); | ||||
|         } | ||||
|         return Column( | ||||
|           mainAxisAlignment: MainAxisAlignment.center, | ||||
|           children: [ | ||||
|             greeter, | ||||
|             loadingIndicator, | ||||
|           ], | ||||
|         ); | ||||
|       } | ||||
|     ) | ||||
|   ); | ||||
| } | ||||
| @@ -1,4 +1,6 @@ | ||||
| import 'package:anyway/constants.dart'; | ||||
| import 'package:anyway/modules/current_trip_error_message.dart'; | ||||
| import 'package:anyway/modules/current_trip_loading_indicator.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| @@ -28,16 +30,36 @@ class _CurrentTripPanelState extends State<CurrentTripPanel> { | ||||
|     return ListenableBuilder( | ||||
|       listenable: widget.trip, | ||||
|       builder: (context, child) { | ||||
|         if (widget.trip.uuid != 'pending' && widget.trip.uuid != 'error') { | ||||
|         if (widget.trip.uuid == 'error') { | ||||
|           return Align( | ||||
|               alignment: Alignment.topCenter, | ||||
|               child: SizedBox( | ||||
|                 // reuse the exact same height as the panel has when collapsed | ||||
|                 // this way the greeter will be centered when the panel is collapsed | ||||
|                 height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20, | ||||
|                 child: CurrentTripErrorMessage(trip: widget.trip) | ||||
|               ), | ||||
|             ); | ||||
|         } else if (widget.trip.uuid == 'pending') { | ||||
|             return Align( | ||||
|               alignment: Alignment.topCenter, | ||||
|               child: SizedBox( | ||||
|                 // reuse the exact same height as the panel has when collapsed | ||||
|                 // this way the greeter will be centered when the panel is collapsed | ||||
|                 height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20, | ||||
|                 child: CurrentTripLoadingIndicator(trip: widget.trip), | ||||
|               ), | ||||
|             ); | ||||
|         } else { | ||||
|           return ListView( | ||||
|             controller: widget.controller, | ||||
|             padding: const EdgeInsets.only(bottom: 30, left: 5, right: 5), | ||||
|             padding: const EdgeInsets.only(bottom: 30), | ||||
|             children: [ | ||||
|               SizedBox( | ||||
|                 // reuse the exact same height as the panel has when collapsed | ||||
|                 // this way the greeter will be centered when the panel is collapsed | ||||
|                 height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20, | ||||
|                 child: Greeter(trip: widget.trip), | ||||
|                 child: CurrentTripGreeter(trip: widget.trip), | ||||
|               ), | ||||
|  | ||||
|               const Padding(padding: EdgeInsets.only(top: 10)), | ||||
| @@ -53,28 +75,6 @@ class _CurrentTripPanelState extends State<CurrentTripPanel> { | ||||
|               Center(child: saveButton(widget.trip)), | ||||
|             ], | ||||
|           ); | ||||
|         } else if(widget.trip.uuid == 'pending') { | ||||
|           return SizedBox( | ||||
|             // reuse the exact same height as the panel has when collapsed | ||||
|             // this way the greeter will be centered when the panel is collapsed | ||||
|             height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20, | ||||
|             child: Greeter(trip: widget.trip) | ||||
|           ); | ||||
|         } else { | ||||
|           return Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             children: [ | ||||
|               const Icon( | ||||
|                 Icons.error_outline, | ||||
|                 color: Colors.red, | ||||
|                 size: 50, | ||||
|               ), | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.only(left: 10), | ||||
|                 child: Text('Error: ${widget.trip.errorDescription}'), | ||||
|               ), | ||||
|             ], | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|     ); | ||||
|   | ||||
| @@ -6,6 +6,12 @@ import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:anyway/modules/current_trip_map.dart'; | ||||
| import 'package:anyway/modules/current_trip_panel.dart'; | ||||
|  | ||||
| final Shader textGradient = APP_GRADIENT.createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0)); | ||||
| TextStyle greeterStyle = TextStyle( | ||||
|   foreground: Paint()..shader = textGradient, | ||||
|   fontWeight: FontWeight.bold, | ||||
|   fontSize: 26 | ||||
| ); | ||||
|  | ||||
|  | ||||
| class TripPage extends StatefulWidget { | ||||
| @@ -35,7 +41,7 @@ class _TripPageState extends State<TripPage> { | ||||
|         maxHeight: MediaQuery.of(context).size.height * TRIP_PANEL_MAX_HEIGHT, | ||||
|         // padding in this context is annoying: it offsets the notion of vertical alignment. | ||||
|         // children that want to be centered vertically need to have their size adjusted by 2x the padding | ||||
|         padding: const EdgeInsets.only(top: 10), | ||||
|         padding: const EdgeInsets.all(10.0), | ||||
|         // Panel snapping should not be disabled because it significantly improves the user experience | ||||
|         // panelSnapping: false | ||||
|         borderRadius: const BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), | ||||
|   | ||||
| @@ -38,10 +38,18 @@ fetchTrip( | ||||
|   String dataString = jsonEncode(data); | ||||
|   log(dataString); | ||||
|  | ||||
|   final response = await dio.post( | ||||
|     "/trip/new", | ||||
|     data: data | ||||
|   ); | ||||
|   late Response response; | ||||
|   try { | ||||
|      response = await dio.post( | ||||
|       "/trip/new", | ||||
|       data: data | ||||
|     ); | ||||
|   } catch (e) { | ||||
|     trip.updateUUID("error"); | ||||
|     trip.updateError(e.toString()); | ||||
|     log(e.toString()); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // handle errors | ||||
|   if (response.statusCode != 200) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user