From db95c9a6f71c2324ed22ee394c8b011343174894 Mon Sep 17 00:00:00 2001 From: Bossanyi Tibor Date: Wed, 28 Oct 2020 14:05:31 +0100 Subject: [PATCH] wt1.1.2f fix for 1.1.3 --- asset/image/predictions.jpg | Bin 0 -> 58010 bytes i18n/en.json | 8 +- i18n/hu.json | 3 +- lib/bloc/exercise_add_by_plan_bloc.dart | 81 --------- lib/bloc/reset_password_bloc.dart | 49 ++++++ lib/library/numberpicker.dart | 43 ++++- lib/library/tree_view.dart | 104 +----------- lib/main.dart | 15 +- lib/model/cache.dart | 25 ++- lib/model/customer.dart | 5 +- lib/model/user.dart | 2 + lib/repository/exercise_repository.dart | 12 +- lib/repository/user_repository.dart | 22 ++- lib/service/customer_service.dart | 27 ++- lib/service/firebase_api.dart | 105 ++++++++++++ lib/util/session.dart | 33 ++-- lib/view/exercise_execute_plan_add_page.dart | 21 +-- .../exercise_plan_custom_detail_add_page.dart | 133 ++++++--------- lib/view/login.dart | 49 ++---- lib/view/mydevelopment_page.dart | 156 +++++++++--------- lib/view/myexcercise_plan_page.dart | 39 +++-- lib/view/registration.dart | 30 +--- lib/view/reset_password.dart | 135 +++++++++++++++ lib/widgets/image_button.dart | 17 +- pubspec.lock | 59 ++++--- pubspec.yaml | 9 +- 26 files changed, 672 insertions(+), 510 deletions(-) create mode 100644 asset/image/predictions.jpg delete mode 100644 lib/bloc/exercise_add_by_plan_bloc.dart create mode 100644 lib/bloc/reset_password_bloc.dart create mode 100644 lib/service/firebase_api.dart create mode 100644 lib/view/reset_password.dart diff --git a/asset/image/predictions.jpg b/asset/image/predictions.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6009b11e743b564f03fba0695396d9f46f1b453a GIT binary patch literal 58010 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 { - final ExerciseRepository exerciseRepository; - final ExercisePlanRepository exercisePlanRepository; - final WorkoutMenuTree workoutTree; - final customerId; - Customer customer; - int step = 1; - int countSteps = 1; - - final quantity1Field = TextFieldBloc( - ); - - final unitQuantity1Field = TextFieldBloc( - ); - - ExerciseAddByPlanFormBloc({this.exerciseRepository, this.exercisePlanRepository, this.customerId, this.workoutTree}) { - addFieldBlocs(fieldBlocs: [ - quantity1Field, - unitQuantity1Field, - ]); - - quantity1Field.onValueChanges(onData: (previous, current) async* { - exerciseRepository.setQuantity(current.valueToDouble); - }); - - unitQuantity1Field.onValueChanges(onData: (previous, current) async* { - exerciseRepository.setUnitQuantity(unitQuantity1Field.valueToDouble); - }); - - exerciseRepository.exerciseType = workoutTree.exerciseType; - if ( Cache().userLoggedIn.customerId == customerId) { - customer = Cache().userLoggedIn; - } else if ( Cache().getTrainee().customerId == customerId) { - customer = Cache().getTrainee(); - } - exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType); - exerciseRepository.customer = customer; - countSteps = exercisePlanRepository.getActualPlanDetail().serie; - - unitQuantity1Field.updateInitialValue(exercisePlanRepository.getActualPlanDetail().weightEquation); - quantity1Field.updateInitialValue(exercisePlanRepository.getActualPlanDetail().repeats.toString()); - - } - - @override - void onSubmitting() async { - - try { - emitLoading(progress: 30); - step++; - - exerciseRepository.setUnitQuantity(unitQuantity1Field.valueToDouble); - exerciseRepository.setQuantity(quantity1Field.valueToDouble); - exerciseRepository.exercise.exercisePlanDetailId = - exercisePlanRepository.getActualPlanDetail().exercisePlanDetailId; - exerciseRepository.exercise.unit = workoutTree.exerciseType.unit; - workoutTree.executed = true; - print("On Submitting Add Exercise By Plan " + exerciseRepository.exercise.toJson().toString()); - await exerciseRepository.addExercise(); - - emitSuccess(canSubmitAgain: true); - } on Exception catch (ex) { - emitFailure(failureResponse: ex.toString()); - } - } - - //@override - Future close() { - quantity1Field.close(); - unitQuantity1Field.close(); - - return super.close(); - } -} diff --git a/lib/bloc/reset_password_bloc.dart b/lib/bloc/reset_password_bloc.dart new file mode 100644 index 0000000..5a803f0 --- /dev/null +++ b/lib/bloc/reset_password_bloc.dart @@ -0,0 +1,49 @@ + +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +class ResetPasswordFormBloc extends FormBloc with Common { + final UserRepository userRepository; + + final emailField = TextFieldBloc( + validators: [ + FieldBlocValidators.required, + ], + ); + + ResetPasswordFormBloc({this.userRepository}) { + addFieldBlocs(fieldBlocs: [ + emailField + ]); + + emailField.onValueChanges(onData: (previous, current) async* { + userRepository.setEmail(current.value); + }); + } + + @override + void onSubmitting() async { + try { + emitLoading(progress: 30); + if ( ! validateEmail(userRepository)) { + emailField.addFieldError(EMAIL_ERROR, isPermanent: true); + + emitFailure(failureResponse: EMAIL_ERROR); + } else { + // Emit either Loaded or Error + await userRepository.resetPassword(); + emitSuccess(canSubmitAgain: false); + } + } on Exception catch (ex) { + emitFailure(failureResponse: ex.toString()); + + } + } + + Future close() { + emailField.close(); + return super.close(); + } + +} \ No newline at end of file diff --git a/lib/library/numberpicker.dart b/lib/library/numberpicker.dart index 3dae677..17575d0 100644 --- a/lib/library/numberpicker.dart +++ b/lib/library/numberpicker.dart @@ -305,15 +305,50 @@ class NumberPicker extends StatelessWidget { ? selectedStyle : defaultStyle; + double top = defaultStyle != null && defaultStyle.fontSize != null + ? listViewHeight / 2 - defaultStyle.fontSize / 2 - 15 + : listViewHeight / 2 - 22; + double left = defaultStyle != null && defaultStyle.fontSize != null + ? listViewWidth / 6 - defaultStyle.fontSize / 2 - 10 + : listViewHeight / 2 - 27; + bool isExtra = index == 0 || index == listItemCount - 1; return isExtra ? Container() //empty first and last element : Center( - child: Text( - getDisplayedValue(value), - style: itemStyle, - ), + child: value != selectedIntValue ? + Container( + padding: EdgeInsets.only(top: 15, left: 10, right: 5, bottom: 10), + child: Stack( + children: [ + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: value < selectedIntValue ? Alignment.centerRight : Alignment.centerLeft, + end: value < selectedIntValue ? Alignment.centerLeft : Alignment.centerRight, + colors: [Colors.white12, Colors.black12]), + borderRadius: BorderRadius.circular(8.0), + ), + ), + Positioned( + top: top, + left: left, + child: + Text( + getDisplayedValue(value), + style: itemStyle, + ), + + ), + ], + ) + ) : + + Text( + getDisplayedValue(value), + style: itemStyle, + ), ); }, ), diff --git a/lib/library/tree_view.dart b/lib/library/tree_view.dart index 7cde1a1..a9a832e 100644 --- a/lib/library/tree_view.dart +++ b/lib/library/tree_view.dart @@ -5,7 +5,6 @@ import 'package:aitrainer_app/util/common.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'dart:math' as math; class TreeView extends InheritedWidget { final List children; @@ -99,7 +98,6 @@ class TreeViewChildState extends State with Common, SingleTicker Color _color; double _opacity = 0; -/* List listWidgets; */ AnimationController _controller; Animation sizeAnimation; @@ -107,16 +105,6 @@ class TreeViewChildState extends State with Common, SingleTicker void initState() { super.initState(); isExpanded = widget.startExpanded; - _color = Colors.transparent; - _opacity = 0; - /* listWidgets = List.from(widget.children); */ - - /* _controller = AnimationController( - duration: const Duration(seconds: 1), - vsync: this, - ); - - sizeAnimation = Tween(begin: 0.0, end: 1.0).animate(_controller);*/ } @override @@ -136,105 +124,27 @@ class TreeViewChildState extends State with Common, SingleTicker ), Flexible( child: Container( - /*animation: _controller, - builder: (BuildContext context, Widget child) { - return Transform.scale( - scale: _controller.value, - child: child, - ); - },*/ - /* color: _color, - duration: Duration(seconds: 2), - curve: Curves.easeInOut,*/ - //height: double.infinity, - /*child: isExpanded //animateChildren() - ? Column( + child: + AnimatedSwitcher( + duration: Duration(milliseconds:900), + reverseDuration: Duration(milliseconds:500), + switchInCurve: Curves.easeIn, + child: isExpanded ? Column( mainAxisSize: MainAxisSize.min, children: widget.children, - ) - : Offstage(),*/ - child: - /*AnimatedCrossFade( - firstChild: Column( - mainAxisSize: MainAxisSize.min, - children: widget.children, + ) : Offstage(), ), - secondChild: Offstage(), - duration: Duration(milliseconds:800), - crossFadeState: isExpanded ? CrossFadeState.showFirst : CrossFadeState.showSecond, - )*/ - AnimatedSwitcher( - duration: Duration(milliseconds:900), - reverseDuration: Duration(milliseconds:500), - switchInCurve: Curves.easeIn, - child: isExpanded ? Column( - mainAxisSize: MainAxisSize.min, - children: widget.children, - ) : Offstage(), - ), ), ) ], ); } - /*AnimatedList animateChildren() { - - return AnimatedList( - shrinkWrap: true, - key: listKey, - initialItemCount: listWidgets.length, - itemBuilder: (context, index, animation) { - return slideIt(animation, listWidgets[index]); - }, - ); - } - - Widget slideIt(Animation animation, Widget element) { - - return SizeTransition( - sizeFactor: animation, - axis: Axis.vertical, - child: element, - ); - }*/ - void toggleExpanded() { setState(() { this.isExpanded = !this.isExpanded; _color = isExpanded ? Colors.black12 : Colors.transparent; _opacity = isExpanded ? 1 : 0; - - /* if ( isExpanded == false ) { - _removeAllItems(); - } else { - //_addAllItems(); - listWidgets = List.from(widget.children); - }*/ }); } - - /* void _removeAllItems() { - final int itemCount = widget.children.length; - - for (var i = 0; i < itemCount; i++) { - Widget itemToRemove = listWidgets[0]; - listKey.currentState.removeItem(0, - (BuildContext context, Animation animation) => slideIt(animation, itemToRemove), - duration: const Duration(milliseconds: 250), - ); - - listWidgets.removeAt(0); - } - } - - void _addAllItems() { - final int itemCount = widget.children.length; - - for (var i = 0; i < itemCount; i++) { - listKey.currentState.insertItem(0); - listWidgets.insert(0, widget.children[i]); - } - }*/ - } diff --git a/lib/main.dart b/lib/main.dart index 7782e57..d5d9677 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,16 +25,18 @@ import 'package:aitrainer_app/view/mydevelopment_muscle_page.dart'; import 'package:aitrainer_app/view/mydevelopment_page.dart'; import 'package:aitrainer_app/view/myexcercise_plan_page.dart'; import 'package:aitrainer_app/view/registration.dart'; +import 'package:aitrainer_app/view/reset_password.dart'; import 'package:aitrainer_app/view/settings.dart'; import 'package:aitrainer_app/widgets/home.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_analytics/observer.dart'; +//import 'package:firebase_analytics/firebase_analytics.dart'; +//import 'package:firebase_analytics/observer.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:sentry/sentry.dart'; import 'bloc/account/account_bloc.dart'; import 'bloc/body_development/body_development_bloc.dart'; @@ -157,7 +159,7 @@ class AitrainerApp extends StatelessWidget { @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - final FirebaseAnalytics analytics = FirebaseAnalytics(); + //final FirebaseAnalytics analytics = FirebaseAnalytics(); return MaterialApp( localizationsDelegates: [ // ... app-specific localization delegate[s] here @@ -197,6 +199,7 @@ class AitrainerApp extends StatelessWidget { 'exerciseCustomPage': (context) => CustomExercisePage(), 'exerciseControlPage': (context) => ExerciseControlPage(), 'login': (context) => LoginPage(), + 'resetPassword': (context) => ResetPasswordPage(), 'registration': (context) => RegistrationPage(), 'gdpr': (context) => Gdpr(), 'menu_page': (context) => MenuPage(), @@ -218,13 +221,13 @@ class AitrainerApp extends StatelessWidget { theme: ThemeData( brightness: Brightness.light, //primarySwatch: Colors.transparent, - fontFamily: 'Arial', + //fontFamily: 'Arial', textTheme: TextTheme( - bodyText1: TextStyle(fontSize: 14.0), + bodyText1: GoogleFonts.openSans(textStyle: TextStyle(fontSize: 14.0)), ) ), navigatorObservers: [ - FirebaseAnalyticsObserver(analytics: analytics), + //FirebaseAnalyticsObserver(analytics: analytics), ], home: AitrainerHome(), diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 3436c0e..5fc1f82 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -40,6 +40,7 @@ class Cache { // Keys to store and fetch data from SharedPreferences static final String authTokenKey = 'auth_token'; static final String customerIdKey = 'customer_id'; + static final String firebaseUidKey = 'firebase_uid'; static final String lastStoreDateKey = 'last_date'; static final String isRegisteredKey = 'is_registered'; static final String isLoggedInKey = 'is_logged_in'; @@ -52,6 +53,8 @@ class Cache { String authToken = ""; Customer userLoggedIn; + String firebaseUid; + bool firstLoad = true; List _exerciseTypes; @@ -108,18 +111,24 @@ class Cache { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; - setPreferences(prefs, SharePrefsChange.registration, customer.customerId); + setPreferences(prefs, SharePrefsChange.registration, customer.customerId, Cache().firebaseUid); } afterLogin(Customer customer) async { Future prefs = SharedPreferences.getInstance(); userLoggedIn = customer; - await setPreferences(prefs, SharePrefsChange.login, customer.customerId); + await setPreferences(prefs, SharePrefsChange.login, customer.customerId, Cache().firebaseUid); + } + + afterFirebaseLogin() async { + Future prefs = SharedPreferences.getInstance(); + await setPreferences(prefs, SharePrefsChange.login, userLoggedIn.customerId, Cache().firebaseUid); } logout() async { userLoggedIn = null; + firebaseUid = null; authToken = ""; _trainee = null; _percentExercises = -1; @@ -129,12 +138,13 @@ class Cache { _myExercisesPlanDetails = LinkedHashMap(); print("Trainees is null? " + (_trainee == null).toString() ); Future prefs = SharedPreferences.getInstance(); - await setPreferences(prefs, SharePrefsChange.logout, 0); + await setPreferences(prefs, SharePrefsChange.logout, 0, ""); } setPreferences(Future prefs, SharePrefsChange type, - int customerId) async { + int customerId, + String firebaseUid) async { SharedPreferences sharedPreferences; sharedPreferences = await prefs; @@ -146,19 +156,22 @@ class Cache { sharedPreferences.setInt(Cache.customerIdKey, customerId); sharedPreferences.setBool(Cache.isRegisteredKey, true); sharedPreferences.setBool(Cache.isLoggedInKey, true); + sharedPreferences.setString(Cache.firebaseUidKey, firebaseUid); await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); - exerciseRepository.getExercisesByCustomer(customerId); + await exerciseRepository.getExercisesByCustomer(customerId); } else if ( type == SharePrefsChange.login ) { Cache().startPage = "home"; sharedPreferences.setInt(Cache.customerIdKey, customerId); + sharedPreferences.setString(Cache.firebaseUidKey, firebaseUid); sharedPreferences.setBool(Cache.isLoggedInKey, true); await ExerciseTypeApi().getExerciseTypes(); await ExerciseTreeApi().getExerciseTree(); - exerciseRepository.getExercisesByCustomer(customerId); + await exerciseRepository.getExercisesByCustomer(customerId); } else if ( type == SharePrefsChange.logout ) { sharedPreferences.setBool(Cache.isLoggedInKey, false); sharedPreferences.setInt(Cache.customerIdKey, 0); + sharedPreferences.setString(Cache.firebaseUidKey, null); sharedPreferences.setString(authTokenKey, ""); } } diff --git a/lib/model/customer.dart b/lib/model/customer.dart index 23444a6..5e55dd8 100644 --- a/lib/model/customer.dart +++ b/lib/model/customer.dart @@ -15,6 +15,7 @@ class Customer { int admin; int trainer; int dataPolicyAllowed; + String firebaseUid; Customer({this.customerId, @@ -32,7 +33,8 @@ class Customer { this.weight, this.admin, this.trainer, - this.dataPolicyAllowed + this.dataPolicyAllowed, + this.firebaseUid, }); Customer.fromJson(Map json) { @@ -50,6 +52,7 @@ class Customer { this.weight = json['weight']; this.admin = json['admin']; this.trainer = json['trainer']; + this.firebaseUid = json['firebaseUid']; } Map toJson() => diff --git a/lib/model/user.dart b/lib/model/user.dart index 14cba3c..495dfc6 100644 --- a/lib/model/user.dart +++ b/lib/model/user.dart @@ -2,6 +2,7 @@ String email; String password; int customerId; + String firebaseUid; User({this.customerId, this.email, this.password}); @@ -11,5 +12,6 @@ { "username": email, "password": password, + "firebaseUid": firebaseUid, }; } diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart index 52dd49f..63cf07c 100644 --- a/lib/repository/exercise_repository.dart +++ b/lib/repository/exercise_repository.dart @@ -87,10 +87,14 @@ class ExerciseRepository { Future> getExercisesByCustomer( int customerId ) async { final results = await ExerciseApi().getExercisesByCustomer(customerId); this.exerciseList = results; - if ( customerId == Cache().userLoggedIn.customerId) { - Cache().setExercises(exerciseList); - } else if ( Cache().getTrainee() != null && customerId == Cache().getTrainee().customerId ) { - Cache().setExercisesTrainee(exerciseList); + if ( Cache().userLoggedIn != null ) { + if (customerId == Cache().userLoggedIn.customerId) { + Cache().setExercises(exerciseList); + } else if (Cache().getTrainee() != null && customerId == Cache() + .getTrainee() + .customerId) { + Cache().setExercisesTrainee(exerciseList); + } } return this.exerciseList; } diff --git a/lib/repository/user_repository.dart b/lib/repository/user_repository.dart index 4a7a94a..ab1a0c8 100644 --- a/lib/repository/user_repository.dart +++ b/lib/repository/user_repository.dart @@ -1,5 +1,7 @@ +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/user.dart'; import 'package:aitrainer_app/service/customer_service.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; class UserRepository { User user; @@ -22,11 +24,27 @@ class UserRepository { Future addUser() async { final User modelUser = this.user; - await CustomerApi().addUser(modelUser); + String rc = await FirebaseApi().registerEmail(modelUser.email, modelUser.password); + if ( rc == FirebaseApi.SIGN_IN_OK ) { + modelUser.firebaseUid = Cache().firebaseUid; + await CustomerApi().addUser(modelUser); + } } Future getUser() async { final User modelUser = this.user; - await CustomerApi().getUser(modelUser); + String rc = await FirebaseApi().signInEmail(modelUser.email, modelUser.password); + if ( rc == FirebaseApi.SIGN_IN_NOT_FOUND ) { + rc = await FirebaseApi().registerEmail(modelUser.email, modelUser.password); + } + if ( rc == FirebaseApi.SIGN_IN_OK ) { + await CustomerApi().getUserByEmail(modelUser.email); + Cache().afterFirebaseLogin(); + } + } + + Future resetPassword() async { + final User modelUser = this.user; + await FirebaseApi().resetPassword(modelUser.email); } } diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index 5e2eaa0..03ba511 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -25,6 +25,14 @@ class CustomerApi { body); } + Future updateFirebaseUid(int customerId, String uid) async { + print(" ===== update Firebase uid : " + customerId.toString() + ": " + + uid); + await _client.post( + "customers/update_firebase_uid/" + customerId.toString(), + uid); + } + Future addCustomer(Customer customer) async { String body = JsonEncoder().convert(customer.toJson()); print(" ===== add new customer: " + body); @@ -35,7 +43,7 @@ class CustomerApi { Future addUser(User user) async { String body = JsonEncoder().convert(user.toJson()); - print(" ===== register new user: " + body); + print(" ===== add new user: " + body); final String responseBody = await _client.post( "registration", body); @@ -68,6 +76,23 @@ class CustomerApi { } } + Future getUserByEmail(String email) async { + print(" ===== User getByEmail : " + email); + final String responseBody = await _client.get( + "customers/find_by_email/" + email, + ""); + Customer customer; + try { + customer = Customer.fromJson(jsonDecode(responseBody)); + if ( customer.firebaseUid == null ) { + await this.updateFirebaseUid(customer.customerId, Cache().firebaseUid); + } + Cache().userLoggedIn = customer; + } on FormatException { + throw new Exception(responseBody); + } + } + Future getCustomer(int customerId) async { String body = ""; diff --git a/lib/service/firebase_api.dart b/lib/service/firebase_api.dart new file mode 100644 index 0000000..35333b9 --- /dev/null +++ b/lib/service/firebase_api.dart @@ -0,0 +1,105 @@ +import 'package:aitrainer_app/model/cache.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; + +class FirebaseApi { + static FirebaseApi _instance; + + static final FirebaseAuth auth = FirebaseAuth.instance; + + static const String SIGN_IN_OK = "OK"; + static const String SIGN_IN_NOT_FOUND = "user-not-found"; + static const String SIGN_IN_WRONG_PWD = "wrong-password"; + static const String REGISTER_WEAK_PWD = "weak-password"; + static const String REGISTER_EMAIL_IN_USE = "email-already-in-use"; + + UserCredential userCredential; + + factory FirebaseApi() => _instance ?? FirebaseApi._internal(); + + FirebaseApi._internal() { + _instance = this; + } + + + + // Define an async function to initialize FlutterFire + Future initializeFlutterFire() async { + try { + // Wait for Firebase to initialize and set `_initialized` state to true + await Firebase.initializeApp(); + } catch (e) { + // Set `_error` state to true if Firebase initialization fails + print("Error initializing Firebase"); + } + } + + Future signInEmail(String email, String password) async { + String rc = SIGN_IN_OK; + try { + userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword( + email: email, + password: password + ); + Cache().firebaseUid = userCredential.user.uid; + } on FirebaseAuthException catch (e) { + if (e.code == 'user-not-found') { + print('No user found for that email.'); + rc = SIGN_IN_NOT_FOUND; + } else if (e.code == 'wrong-password') { + print('Wrong password provided for that user.'); + rc = SIGN_IN_WRONG_PWD; + throw Exception("Customer does not exist or the password is wrong"); + } + return e.code; + } + return rc; + } + + Future registerEmail(String email, String password) async { + String rc = SIGN_IN_OK; + try { + userCredential = await FirebaseAuth.instance.createUserWithEmailAndPassword( + email: email, + password: password + ); + Cache().firebaseUid = userCredential.user.uid; + } on FirebaseAuthException catch (e) { + if (e.code == 'weak-password') { + print('The password provided is too weak.'); + rc = REGISTER_WEAK_PWD; + throw Exception("Password too short"); + } else if (e.code == 'email-already-in-use') { + print('The account already exists for that email.'); + throw Exception("Customer exists"); + rc = REGISTER_EMAIL_IN_USE; + } + } catch (e) { + print(e); + throw Exception(e.toString()); + } + + return rc; + } + + Future signInWithFacebook() async { + // Trigger the sign-in flow + final LoginResult result = await FacebookAuth.instance.login(); + + // Create a credential from the access token + final FacebookAuthCredential facebookAuthCredential = + FacebookAuthProvider.credential(result.accessToken.token); + + // Once signed in, return the UserCredential + return await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential); + } + + Future signOut() async { + await FirebaseAuth.instance.signOut(); + } + + Future resetPassword(String email) async { + await FirebaseAuth.instance.sendPasswordResetEmail(email: email); + } +} \ No newline at end of file diff --git a/lib/util/session.dart b/lib/util/session.dart index 60df05d..27b0511 100644 --- a/lib/util/session.dart +++ b/lib/util/session.dart @@ -5,6 +5,7 @@ import 'package:aitrainer_app/service/api.dart'; import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/service/exercise_tree_service.dart'; import 'package:aitrainer_app/service/exercisetype_service.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -31,7 +32,8 @@ class Session { await AppLocalizations.delegate.load(AppLanguage().appLocal); print (" -- Session: fetch token.."); await _fetchToken(_sharedPreferences); - await _initializeFlutterFire(); + print (" -- FireBase init.."); + await FirebaseApi().initializeFlutterFire(); //initDeviceLocale(); // Create the initialization Future outside of `build`: @@ -42,17 +44,6 @@ class Session { } - // Define an async function to initialize FlutterFire - void _initializeFlutterFire() async { - try { - // Wait for Firebase to initialize and set `_initialized` state to true - await Firebase.initializeApp(); - } catch (e) { - // Set `_error` state to true if Firebase initialization fails - print("Error initializing Firebase"); - } - } - Future initDeviceLocale() async { List languages; String currentLocale; @@ -90,10 +81,10 @@ class Session { } else if (responseJson['token'] != null) { prefs.setString(Cache.authTokenKey, responseJson['token']); Cache().authToken = responseJson['token']; + Cache().firebaseUid = prefs.get(Cache.firebaseUidKey); if (prefs.get(Cache.customerIdKey) == null) { print("************** Registration"); // registration - //Navigator.of(context).pushNamed('registration'); prefs.setBool(Cache.isRegisteredKey, true); Cache().startPage = "registration"; } else { @@ -107,13 +98,19 @@ class Session { prefs.get(Cache.isLoggedInKey) == null || prefs.get(Cache.isLoggedInKey) == false) { print("************* Login"); - //Navigator.of(context).pushNamed('login'); Cache().startPage = "login"; } else { - // get API customer - customerId = prefs.getInt(Cache.customerIdKey); - await CustomerApi().getCustomer(customerId); - Cache().startPage = "home"; + + // only + if ( Cache().firebaseUid == null) { + print("************* firebaseUid is null, Login"); + Cache().startPage = "login"; + } else { + // get API customer + customerId = prefs.getInt(Cache.customerIdKey); + await CustomerApi().getCustomer(customerId); + Cache().startPage = "home"; + } } await ExerciseTypeApi().getExerciseTypes(); diff --git a/lib/view/exercise_execute_plan_add_page.dart b/lib/view/exercise_execute_plan_add_page.dart index dc99299..2782cfa 100644 --- a/lib/view/exercise_execute_plan_add_page.dart +++ b/lib/view/exercise_execute_plan_add_page.dart @@ -6,6 +6,7 @@ import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:aitrainer_app/library/numberpicker.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -71,24 +72,7 @@ class _ExerciseExecuteAddPage extends State with Tra autovalidate: true, child: Scaffold( resizeToAvoidBottomInset: true, - appBar: AppBar( - backgroundColor: Colors.black, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(t("Save Exercise"), style: TextStyle(fontSize: 18),), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => Navigator.of(context).pop(), - ), - ), + appBar: AppBarNav(depth: 1), body: Container( width: MediaQuery .of(context) @@ -115,6 +99,7 @@ class _ExerciseExecuteAddPage extends State with Tra child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Text(t("Save Exercise")), Text(exerciseName, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, diff --git a/lib/view/exercise_plan_custom_detail_add_page.dart b/lib/view/exercise_plan_custom_detail_add_page.dart index 27ad4ca..0a76a64 100644 --- a/lib/view/exercise_plan_custom_detail_add_page.dart +++ b/lib/view/exercise_plan_custom_detail_add_page.dart @@ -50,6 +50,8 @@ class _ExercisePlanDetailAddPage extends State with T )); } + + Widget getForm(ExercisePlanCustomAddBloc bloc, WorkoutMenuTree workoutMenuTree) { String exerciseName = ""; if (bloc != null) { @@ -94,25 +96,58 @@ class _ExercisePlanDetailAddPage extends State with T fit: FlexFit.tight, flex: 8, child: - Text(t("Serie")), + Text( + t("Serie"), + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + + ), ), NumberPicker.horizontal( highlightSelectedValue: true, initialValue: bloc.serie.toInt(), - minValue: 0, - maxValue: 200, + itemExtent: 85, + minValue: 1, + maxValue: 20, + //decoration: _decoration, step: 1, onChanged: (value) => { bloc.add(ExercisePlanCustomAddChangeSerie(quantity: value.toDouble())) }, listViewHeight: 80, textStyle: TextStyle(fontSize: 24), - textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold), + textStyleHighlighted: TextStyle(fontSize: 36, color: Colors.orange, fontWeight: FontWeight.bold), //decoration: _decoration, ), ] ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Flexible( + fit: FlexFit.tight, + flex: 8, + child: + Text(t("Repeats"), + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),), + ), + NumberPicker.horizontal( + highlightSelectedValue: true, + initialValue: bloc.quantity.toInt(), + itemExtent: 85, + minValue: 0, + maxValue: 200, + step: 1, + onChanged: (value) => { + bloc.add(ExercisePlanCustomAddChangeQuantity(quantity: value.toDouble())) + }, + listViewHeight: 80, + textStyle: TextStyle(fontSize: 24), + textStyleHighlighted: TextStyle(fontSize: 36, color: Colors.orange, fontWeight: FontWeight.bold), + //decoration: _decoration, + ), + ]), Divider(), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -121,11 +156,14 @@ class _ExercisePlanDetailAddPage extends State with T fit: FlexFit.tight, flex: 8, child: - Text(t("Weight") + " (" + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.unitQuantityUnit + ")"), + Text(t("Weight") + " (" + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.unitQuantityUnit + ")", + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),), ), NumberPicker.horizontal( highlightSelectedValue: true, initialValue: bloc.quantityUnit.toInt(), + //decoration: _decoration, + itemExtent: 85, minValue: 0, maxValue: 650, step: 1, @@ -134,79 +172,19 @@ class _ExercisePlanDetailAddPage extends State with T }, listViewHeight: 80, textStyle: TextStyle(fontSize: 24), - textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold), + textStyleHighlighted: TextStyle(fontSize: 36, color: Colors.orange, fontWeight: FontWeight.bold), //decoration: _decoration, ), ]), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Flexible( - fit: FlexFit.tight, - flex: 8, - child: - Text(t("Repeats")), - ), - NumberPicker.horizontal( - highlightSelectedValue: true, - initialValue: bloc.quantity.toInt(), - minValue: 0, - maxValue: 200, - step: 1, - onChanged: (value) => { - bloc.add(ExercisePlanCustomAddChangeQuantity(quantity: value.toDouble())) - }, - listViewHeight: 80, - textStyle: TextStyle(fontSize: 24), - textStyleHighlighted: TextStyle(fontSize: 40, color: Colors.orange, fontWeight: FontWeight.bold), - //decoration: _decoration, - ), - ]), + + Divider(), - /*TextFieldBlocBuilder( - textFieldBloc: bloc.serieField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: AppLocalizations.of(context).translate("The number of the serie done with"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate("Serie"), - ), - ),*/ - /*TextFieldBlocBuilder( - textFieldBloc: bloc.quantityField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: - AppLocalizations.of(context).translate("The number of the repeats of one serie"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate("Repeats"), - ), - ),*/ - /*TextFieldBlocBuilder( - textFieldBloc: bloc.weightField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: AppLocalizations.of(context).translate("The weight"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate("Weight"), - ), - ),*/ + Text( + bloc.serie.toStringAsFixed(0) + " x " + bloc.quantity.toStringAsFixed(0) + " x " + bloc.quantityUnit.toStringAsFixed(0) + " kg" , + style: TextStyle(fontSize: 28, fontWeight: FontWeight.normal), + ), + Divider(), + Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -217,11 +195,6 @@ class _ExercisePlanDetailAddPage extends State with T onPressed: () => { bloc.add(ExercisePlanCustomAddRemove()), Navigator.of(context).pop(), - /*print("Remove " + bloc.exercisePlanRepository.getActualPlanDetail().exerciseType.name), - bloc.exercisePlanRepository.getActualPlanDetail().change = ExercisePlanDetailChange.delete, - planBloc.add(ExercisePlanRemoveExercise( - exercisePlanDetail: bloc.exercisePlanRepository.getActualPlanDetail() )), - Navigator.of(context).pop(),*/ }, child: Text(t( "Delete")), //Text(AppLocalizations.of(context).translate("Delete"), style: TextStyle(fontSize: 16),) @@ -233,8 +206,6 @@ class _ExercisePlanDetailAddPage extends State with T onPressed: () => { bloc.add(ExercisePlanCustomAddSubmit()), Navigator.of(context).pop(), - /* bloc.submit(), - Navigator.of(context).pop(),*/ }, child: Text( t("Save"), diff --git a/lib/view/login.dart b/lib/view/login.dart index d552e74..abd1b98 100644 --- a/lib/view/login.dart +++ b/lib/view/login.dart @@ -2,12 +2,13 @@ import 'package:aitrainer_app/bloc/login_form_bloc.dart'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_facebook_login/flutter_facebook_login.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import '../library_keys.dart'; @@ -26,13 +27,14 @@ class LoginWidget extends StatefulWidget { State createState() => _LoginWidget(); } -class _LoginWidget extends State with Common { +class _LoginWidget extends State with Common, Trans { final GlobalKey _scaffoldKey = new GlobalKey(); final _formKey = GlobalKey(); @override Widget build(BuildContext context) { final accountBloc = BlocProvider.of(context); + setContext(context); return BlocProvider( create: (context) => LoginFormBloc( userRepository: UserRepository(), @@ -72,7 +74,6 @@ class _LoginWidget extends State with Common { } Widget buildLoginForm(LoginFormBloc formBloc, AccountBloc accountBloc) { - final cWidth = mediaSizeWidth(context); return Form( key: _formKey, @@ -81,17 +82,18 @@ class _LoginWidget extends State with Common { child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 120.0), children: [ - FlatButton( + /* FlatButton( child: new Image.asset( 'asset/image/login_fb.png', width: cWidth * .85, ), onPressed: () => { - _fbLogin(), + //_fbLogin(), + FirebaseApi().signInWithFacebook(), print("Login with FB"), }, ), - Text(AppLocalizations.of(context).translate("OR")), + Text(AppLocalizations.of(context).translate("OR")),*/ Divider(), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -156,10 +158,15 @@ class _LoginWidget extends State with Common { Spacer(flex: 1), new InkWell( child: new Text( - AppLocalizations.of(context).translate('Privacy')), + AppLocalizations.of(context).translate('I forgot the password')), + onTap: () => Navigator.of(context).pushNamed('resetPassword'), + ), + Spacer(flex: 1), + new InkWell( + child: new Text( + AppLocalizations.of(context).translate('Privacy')), onTap: () => Navigator.of(context).pushNamed('gdpr'), ), - Spacer(flex: 2), ]), ])), ); @@ -172,30 +179,4 @@ class _LoginWidget extends State with Common { content: Text(error, style: TextStyle(color: Colors.white)))); } - Future _fbLogin() async { - final FacebookLogin facebookSignIn = new FacebookLogin(); - final FacebookLoginResult result = await facebookSignIn.logIn(['email']); - - switch (result.status) { - case FacebookLoginStatus.loggedIn: - final FacebookAccessToken accessToken = result.accessToken; - showInSnackBar(''' - Logged in! - - Token: ${accessToken.token} - User id: ${accessToken.userId} - Expires: ${accessToken.expires} - Permissions: ${accessToken.permissions} - Declined permissions: ${accessToken.declinedPermissions} - '''); - break; - case FacebookLoginStatus.cancelledByUser: - showInSnackBar('Login cancelled by the user.'); - break; - case FacebookLoginStatus.error: - showInSnackBar('Something went wrong with the login process.\n' - 'Here\'s the error Facebook gave us: ${result.errorMessage}'); - break; - } - } } diff --git a/lib/view/mydevelopment_page.dart b/lib/view/mydevelopment_page.dart index 2736bbb..569011d 100644 --- a/lib/view/mydevelopment_page.dart +++ b/lib/view/mydevelopment_page.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; @@ -22,114 +23,121 @@ class _MyDevelopmentPage extends State with Trans { final CustomerRepository customerRepository = CustomerRepository(); final LinkedHashMap args = LinkedHashMap(); setContext(context); + double mediaWidth = MediaQuery.of(context).size.width; + double imageWidth = (mediaWidth - 45) / 2; + print("Media: " + mediaWidth.toString() + " imageWidth: " + imageWidth.toString()); return Scaffold( - appBar: AppBarNav(depth: 0), - body: Container( - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_menu_dark.png'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), - ), - child: CustomScrollView( - scrollDirection: Axis.vertical, - slivers: - [ - SliverGrid( - delegate: SliverChildListDelegate( - [ + appBar: AppBarNav(depth: 0), + body: Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_menu_dark.png'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + child: CustomScrollView(scrollDirection: Axis.vertical, slivers: [ + SliverGrid( + delegate: SliverChildListDelegate([ ImageButton( + width: imageWidth, textAlignment: Alignment.topCenter, text: t("My Exercise Logs"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4) + ) + ), image: "asset/image/edzesnaplom400400.jpg", - top: 150, left: 10, - onTap:() => this.callBackExerciseLog(exerciseRepository, customerRepository), + onTap: () => this.callBackExerciseLog(exerciseRepository, customerRepository), isLocked: false, ), - ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("My Whole Body Development"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4)), + ), image: "asset/image/testemfejl400x400.jpg", - top: 150, left: 10, - onTap:() => { + onTap: () => { args['customerId'] = Cache().userLoggedIn.customerId, - Navigator.of(context).pushNamed('mydevelopmentBodyPage', - arguments: args) + Navigator.of(context).pushNamed('mydevelopmentBodyPage', arguments: args) }, isLocked: true, ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Development Of Muscles"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/izomcsop400400.jpg", - top: 120, left: 10, - onTap:() => { - Navigator.of(context).pushNamed('mydevelopmentMusclePage', - arguments: args) - }, + onTap: () => {Navigator.of(context).pushNamed('mydevelopmentMusclePage', arguments: args)}, isLocked: true, ), - ImageButton( + width: imageWidth, + left: 10, textAlignment: Alignment.topLeft, text: t("Predictions"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), - image: "asset/menu/2.2.1.1RM.png", - top: 150, - onTap:() => { - - }, + style: GoogleFonts.robotoMono( + textStyle: TextStyle( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), + image: "asset/image/predictions.jpg", + onTap: () => {}, isLocked: true, ), - hiddenWidget(customerRepository, exerciseRepository), - ] - ), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 15.0, - crossAxisSpacing: 15.0, - childAspectRatio: 1.0, - ), - ) - ] - ) - ), - bottomNavigationBar: BottomNavigator(bottomNavIndex: 1)); + ]), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 15.0, + crossAxisSpacing: 15.0, + childAspectRatio: 1.0, + ), + ) + ])), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 1)); } Widget hiddenWidget(CustomerRepository customerRepository, ExerciseRepository exerciseRepository) { final LinkedHashMap args = LinkedHashMap(); if (Cache().getTrainee() != null) { return FlatButton( - padding: EdgeInsets.all(20), - textColor: Colors.white, - color: Colors.black12, - focusColor: Colors.blueAccent, - onPressed: () => - { - args['exerciseRepository'] = exerciseRepository, - args['customerRepository'] = customerRepository, - args['customerId'] = Cache().getTrainee().customerId, - Navigator.of(context).pushNamed('exerciseLogPage', - arguments: args) - }, - child: Text(t("My Trainee's Exercise Logs"), - style: TextStyle(fontSize: 18),) - ); + padding: EdgeInsets.all(20), + textColor: Colors.white, + color: Colors.black12, + focusColor: Colors.blueAccent, + onPressed: () => { + args['exerciseRepository'] = exerciseRepository, + args['customerRepository'] = customerRepository, + args['customerId'] = Cache().getTrainee().customerId, + Navigator.of(context).pushNamed('exerciseLogPage', arguments: args) + }, + child: Text( + t("My Trainee's Exercise Logs"), + style: TextStyle(fontSize: 18), + )); } else { return Container(); } @@ -140,10 +148,6 @@ class _MyDevelopmentPage extends State with Trans { args['exerciseRepository'] = exerciseRepository; args['customerRepository'] = customerRepository; args['customerId'] = Cache().userLoggedIn.customerId; - Navigator.of(context).pushNamed('exerciseLogPage', - arguments: args); + Navigator.of(context).pushNamed('exerciseLogPage', arguments: args); } - } - - diff --git a/lib/view/myexcercise_plan_page.dart b/lib/view/myexcercise_plan_page.dart index cc50e9b..24567de 100644 --- a/lib/view/myexcercise_plan_page.dart +++ b/lib/view/myexcercise_plan_page.dart @@ -7,6 +7,7 @@ import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/image_button.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; class MyExercisePlanPage extends StatefulWidget { @override @@ -20,6 +21,10 @@ class _MyExercisePlanPage extends State with Trans { final LinkedHashMap args = LinkedHashMap(); setContext(context); + double mediaWidth = MediaQuery.of(context).size.width; + double imageWidth = (mediaWidth - 45) / 2; + print("Media: " + mediaWidth.toString() + " imageWidth: " + imageWidth.toString()); + return Scaffold( appBar: AppBarNav(depth: 0), body: Container( @@ -39,10 +44,12 @@ class _MyExercisePlanPage extends State with Trans { delegate: SliverChildListDelegate( [ ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Execute My Selected Training Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_execute.jpg", top: 130, left: 10, @@ -55,12 +62,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Edit My Custom Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_custom.jpg", - top: 150, left: 10, onTap:() => { args['exerciseRepository'] = exerciseRepository, @@ -72,12 +80,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("Suggested Training Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_suggested.jpg", - top: 130, left: 10, onTap:() => { @@ -86,12 +95,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("My Special Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_special.jpg", - top: 150, left: 10, onTap:() => { @@ -100,12 +110,13 @@ class _MyExercisePlanPage extends State with Trans { ), ImageButton( + width: imageWidth, textAlignment: Alignment.topLeft, text: t("My Arnold's Plan"), - style: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, - backgroundColor: Colors.black54.withOpacity(0.4)), + style: GoogleFonts.robotoMono( + textStyle: TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4))), image: "asset/image/exercise_plan_stars.jpg", - top: 120, left: 10, onTap:() => { diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 345ba93..0feda80 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -86,7 +86,7 @@ class _RegistrationWidget extends State with Common { children: [ //Spacer(flex:4), - FlatButton( + /* FlatButton( child: new Image.asset( 'asset/image/login_fb.png', width: cWidth * .85, @@ -96,7 +96,7 @@ class _RegistrationWidget extends State with Common { print("Login with FB"), }, ), - Text(AppLocalizations.of(context).translate("OR")), + Text(AppLocalizations.of(context).translate("OR")),*/ Divider(), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -187,30 +187,4 @@ class _RegistrationWidget extends State with Common { error, style: TextStyle(color: Colors.white)))); } - - /* Future _fbLogin() async { - final FacebookLogin facebookSignIn = new FacebookLogin(); - final FacebookLoginResult result = await facebookSignIn.logIn(['email']); - - switch (result.status) { - case FacebookLoginStatus.loggedIn: - final FacebookAccessToken accessToken = result.accessToken; - showInSnackBar(''' - Logged in! - Token: ${accessToken.token} - User id: ${accessToken.userId} - Expires: ${accessToken.expires} - Permissions: ${accessToken.permissions} - Declined permissions: ${accessToken.declinedPermissions} - '''); - break; - case FacebookLoginStatus.cancelledByUser: - showInSnackBar('Login cancelled by the user.'); - break; - case FacebookLoginStatus.error: - showInSnackBar('Something went wrong with the login process.\n' - 'Here\'s the error Facebook gave us: ${result.errorMessage}'); - break; - } - } */ } diff --git a/lib/view/reset_password.dart b/lib/view/reset_password.dart new file mode 100644 index 0000000..4c452ae --- /dev/null +++ b/lib/view/reset_password.dart @@ -0,0 +1,135 @@ +import 'package:aitrainer_app/bloc/login_form_bloc.dart'; +import 'package:aitrainer_app/bloc/account/account_bloc.dart'; +import 'package:aitrainer_app/bloc/reset_password_bloc.dart'; +import 'package:aitrainer_app/localization/app_localization.dart'; +import 'package:aitrainer_app/repository/user_repository.dart'; +import 'package:aitrainer_app/service/firebase_api.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/splash.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +import '../library_keys.dart'; + +// ignore: must_be_immutable +class ResetPasswordPage extends StatelessWidget with Trans { + final GlobalKey _scaffoldKey = new GlobalKey(); + final _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + setContext(context); + return BlocProvider( + create: (context) => ResetPasswordFormBloc( + userRepository: UserRepository(), + ), + child: Builder(builder: (context) { + final loginBloc = BlocProvider.of(context); + return Scaffold( + key: _scaffoldKey, + body: FormBlocListener( + onSubmitting: (context, state) { + LoadingDialog.show(context); + }, + onSuccess: (context, state) { + LoadingDialog.hide(context); + Navigator.of(context).pop(); + }, + onFailure: (context, state) { + LoadingDialog.hide(context); + showInSnackBar(state.failureResponse); + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_login.png'), + fit: BoxFit.cover, + //height: double.infinity, + //width: double.infinity, + alignment: Alignment.center, + ), + ), + child: buildResetPasswordForm(loginBloc), + ), + ), + ); + })); + } + + Widget buildResetPasswordForm(ResetPasswordFormBloc formBloc) { + + return Form( + key: _formKey, + child: Container( + padding: const EdgeInsets.only(left: 25, right: 100), + child: + ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 120.0), + children: [ + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + new InkWell( + child: new Text( + AppLocalizations.of(context).translate('I forgot the password'), + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 24)), + ), + ], + ), + Divider(), + TextFieldBlocBuilder( + key: LibraryKeys.loginEmailField, + textFieldBloc: formBloc.emailField, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + labelText: 'Email', + ), + ), + Divider( + color: Colors.transparent, + ), + Divider( + color: Colors.transparent, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + new FlatButton( + key: LibraryKeys.loginOKButton, + child: Image.asset('asset/image/WT_OK.png', + width: 100, height: 100), + onPressed: () => { + formBloc.add(SubmitFormBloc()) + }), + ]), + Divider( + color: Colors.transparent, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + new InkWell( + child: new Text( + AppLocalizations.of(context).translate('Login')), + onTap: () => + Navigator.of(context).pushNamed('login'), + ), + Spacer(flex: 1), + ]), + ])), + ); + } + + + void showInSnackBar(String error) { + _scaffoldKey.currentState.showSnackBar(SnackBar( + backgroundColor: Colors.orange, + content: Text(error, style: TextStyle(color: Colors.white)))); + } + +} diff --git a/lib/widgets/image_button.dart b/lib/widgets/image_button.dart index e85d78d..4acb6e3 100644 --- a/lib/widgets/image_button.dart +++ b/lib/widgets/image_button.dart @@ -5,12 +5,12 @@ import 'package:bloc/bloc.dart'; class ImageButton extends StatelessWidget { final String text; - final TextStyle style; + TextStyle style = TextStyle(fontSize: 14); final String image; final double top; final double left; final double height; - final double width; + double width = 180; final bool isShape; final Bloc bloc; final Alignment textAlignment; @@ -30,10 +30,15 @@ class ImageButton extends StatelessWidget { this.textAlignment, this.onTap, @required this.isLocked - }); + }) { + width = width ?? 180; + style = style ?? TextStyle(fontSize: 14, fontFamily: "Roboto Mono"); + } @override Widget build(BuildContext context) { + double top = width - (style.fontSize - 5) * text.length < 0 ? width - 2 * style.fontSize - 10 : width - style.fontSize - 10; + print ("Top: " + top.toStringAsFixed(0) + " length: " + ((style.fontSize - 5) * text.length).toString()); return Stack( //alignment: textAlignment, fit: StackFit.passthrough, @@ -63,11 +68,11 @@ class ImageButton extends StatelessWidget { )] ), Positioned( - top: text.length > 20 ? 140 : 160, + top: top, left: left, child: Container( - height: 200, - width: 180, + height: width - 2 * left, + width: width - 2 * left, child: InkWell( onTap: onTap ?? onTap, child: Text( diff --git a/pubspec.lock b/pubspec.lock index 61507d2..ea93925 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -246,27 +246,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.3.2" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - url: "https://pub.dartlang.org" - source: hosted - version: "6.0.2" - firebase_analytics_platform_interface: - dependency: transitive - description: - name: firebase_analytics_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - firebase_analytics_web: - dependency: transitive - description: - name: firebase_analytics_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.1" firebase_auth: dependency: "direct main" description: @@ -340,13 +319,13 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_facebook_login: + flutter_facebook_auth: dependency: "direct main" description: - name: flutter_facebook_login + name: flutter_facebook_auth url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "0.3.3" flutter_form_bloc: dependency: "direct main" description: @@ -416,6 +395,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" gradient_bottom_navigation_bar: dependency: "direct main" description: @@ -598,6 +584,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.4" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.22" path_provider_linux: dependency: transitive description: @@ -605,6 +598,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+4" path_provider_platform_interface: dependency: transitive description: @@ -632,7 +632,7 @@ packages: name: percent_indicator url: "https://pub.dartlang.org" source: hosted - version: "2.1.7+4" + version: "2.1.8" petitparser: dependency: transitive description: @@ -904,6 +904,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.1+2" + toggle_switch: + dependency: "direct main" + description: + name: toggle_switch + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.8" typed_data: dependency: transitive description: @@ -1004,4 +1011,4 @@ packages: version: "2.2.1" sdks: dart: ">=2.9.0-14.0.dev <3.0.0" - flutter: ">=1.16.0 <2.0.0" + flutter: ">=1.17.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 68e10e2..68a8063 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.0 + google_fonts: ^1.1.1 devicelocale: ^0.3.3 sentry: ^3.0.1 flutter_bloc: ^6.0.6 @@ -33,15 +34,16 @@ dependencies: flutter_form_bloc: ^0.19.0 spider_chart: ^0.1.5 rainbow_color: ^0.1.1 - percent_indicator: ^2.1.7+4 + percent_indicator: ^2.1.8 gradient_bottom_navigation_bar: ^1.0.0+4 fl_chart: ^0.12.0 infinite_listview: ^1.0.1+1 + toggle_switch: ^0.1.8 firebase_core: 0.5.0+1 - firebase_analytics: ^6.0.2 + #firebase_analytics: ^6.0.2 firebase_auth: ^0.18.1+2 - flutter_facebook_login: ^3.0.0 + flutter_facebook_auth: ^0.3.3 mockito: ^4.1.1 @@ -104,6 +106,7 @@ flutter: - asset/image/exercise_plan_execute.jpg - asset/image/exercise_plan_custom.jpg - asset/image/exercise_plan_suggested.jpg + - asset/image/predictions.jpg - asset/menu/1.cardio.png - asset/menu/1.1.aerob.png - asset/menu/1.2.anaerob.png