diff --git a/frontend/.github/workflows/build_app_android.yaml b/frontend/.github/workflows/build_app_android.yaml index 451cfbb..074e835 100644 --- a/frontend/.github/workflows/build_app_android.yaml +++ b/frontend/.github/workflows/build_app_android.yaml @@ -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 diff --git a/frontend/lib/modules/current_trip_error_message.dart b/frontend/lib/modules/current_trip_error_message.dart new file mode 100644 index 0000000..880da7a --- /dev/null +++ b/frontend/lib/modules/current_trip_error_message.dart @@ -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 createState() => _CurrentTripErrorMessageState(); +} + +class _CurrentTripErrorMessageState extends State { + @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, + ), + ], + ) + ); +} diff --git a/frontend/lib/modules/current_trip_greeter.dart b/frontend/lib/modules/current_trip_greeter.dart index 6a2118a..7655b73 100644 --- a/frontend/lib/modules/current_trip_greeter.dart +++ b/frontend/lib/modules/current_trip_greeter.dart @@ -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 createState() => _GreeterState(); + State createState() => _CurrentTripGreeterState(); } -class _GreeterState extends State { - - 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 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 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 { @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 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 + ); + } + } + ) + ); + } \ No newline at end of file diff --git a/frontend/lib/modules/current_trip_loading_indicator.dart b/frontend/lib/modules/current_trip_loading_indicator.dart new file mode 100644 index 0000000..7d61b2a --- /dev/null +++ b/frontend/lib/modules/current_trip_loading_indicator.dart @@ -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 createState() => _CurrentTripLoadingIndicatorState(); +} + +class _CurrentTripLoadingIndicatorState extends State { + @override + Widget build(BuildContext context) => Center( + child: FutureBuilder( + future: widget.trip.cityName, + builder: (BuildContext context, AsyncSnapshot 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, + ], + ); + } + ) + ); +} \ No newline at end of file diff --git a/frontend/lib/modules/current_trip_panel.dart b/frontend/lib/modules/current_trip_panel.dart index cd73f28..45ecdcc 100644 --- a/frontend/lib/modules/current_trip_panel.dart +++ b/frontend/lib/modules/current_trip_panel.dart @@ -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 { 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 { 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}'), - ), - ], - ); } } ); diff --git a/frontend/lib/pages/current_trip.dart b/frontend/lib/pages/current_trip.dart index 834b3f6..8967f6c 100644 --- a/frontend/lib/pages/current_trip.dart +++ b/frontend/lib/pages/current_trip.dart @@ -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 { 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)), diff --git a/frontend/lib/utils/fetch_trip.dart b/frontend/lib/utils/fetch_trip.dart index a497f2e..e6c1f2c 100644 --- a/frontend/lib/utils/fetch_trip.dart +++ b/frontend/lib/utils/fetch_trip.dart @@ -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) {