diff --git a/asset/menu/close_reverse_grip_pull_ups.jpg b/asset/menu/close_reverse_grip_pull_ups.jpg new file mode 100644 index 0000000..8552029 Binary files /dev/null and b/asset/menu/close_reverse_grip_pull_ups.jpg differ diff --git a/asset/menu/dumbbell_presses.jpg b/asset/menu/dumbbell_presses.jpg new file mode 100644 index 0000000..23d13fd Binary files /dev/null and b/asset/menu/dumbbell_presses.jpg differ diff --git a/asset/menu/seated_leg_curls.jpg b/asset/menu/seated_leg_curls.jpg new file mode 100644 index 0000000..c9ca760 Binary files /dev/null and b/asset/menu/seated_leg_curls.jpg differ diff --git a/asset/menu/smith_machine_front_press.jpg b/asset/menu/smith_machine_front_press.jpg new file mode 100644 index 0000000..1bf3f10 Binary files /dev/null and b/asset/menu/smith_machine_front_press.jpg differ diff --git a/asset/menu/straight_arm_pulldown.jpg b/asset/menu/straight_arm_pulldown.jpg new file mode 100644 index 0000000..182b95f Binary files /dev/null and b/asset/menu/straight_arm_pulldown.jpg differ diff --git a/i18n/en.json b/i18n/en.json index b1a10e3..305716b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -280,7 +280,10 @@ "Unleash your potential with WorkoutTest Premium!": "Unleash your potential with WorkoutTest Premium!", "feature is reachable after you finished": "feature is reachable after you finished", "100% test circles": "100% test rounds", - "Keep testing": "Keep Testing", + "week": "week", + "100% completed training": "100% completed training", + "after": "after", + "Keep testing": "Keep Training", "Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.": "Enjoy also this premium fetaure to show all old evaluation data of your successful exercises.", "Please define your Exercise Plan": "Please define your Exercise Plan", "Go to: 'Training Plan' - 'Edit My Custom Plan'": "Go to: 'Training Plan' - 'Edit My Custom Plan'", @@ -463,8 +466,6 @@ "because only that way can we show you your exercises, results and evaluations.": "because only that way can we show you your exercises, results and evaluations.", "because only that way can we show you the personalized development diagrams and analysises": "because only that way can we show you the personalized development diagrams and analysises", "because only in that way can you begin to execute a training plan": "because only in that way can you begin to execute a training plan", - "Please try to execute this exercise with exact weight and repeats what is suggested": "Please try to execute this exercise with exact weight and repeats what is suggested", - "Please try to execute this exercise with exact repeats what is suggested": "Please try to execute this exercise with exact repeats what is suggested", "Please take a relative bigger weight and at least 12 times and do your best! MAXIMIZE it!": "Please take a relative bigger weight and at least 12 times and do your best! MAXIMIZE it!", "During the exercise you should try your best:": "During the exercise you should try your best:", "do it with the possible maximum repeats.": "do it with the possible maximum repeats.", @@ -509,5 +510,35 @@ "optimized training plans, customized to your fitness state and strength:": "optimized training plans, customized to your fitness state and strength:", "Soon! Check back later for the plan details": "Soon! Check back later for the plan details", "mins": "mins", - "set": "set" + "set": "set", + "Set / Reps / Weight": "Set / Reps / Weight", + "Weight_": "Weight", + "Got It": "Got It", + "Done!": "Done!", + "Show this tip no more": "Show this tip no more", + "Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions.": "Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions.", + "Please take a middle weight which you are able to do 8-20 times with.": "Please take a middle weight which you are able to do 8-20 times with.", + "Execute your MAXIMUM repeats with it!": "Execute your MAXIMUM repeats with it!", + "Type here your selected weight,": "Type here your selected weight,", + "then here, how many times could you repeat it!": "then here, how many times could you repeat it!", + "After you done, click to the OK button!": "After you done, click to the OK button!", + "If you executed less repeats than given, modify it, and click to OK!": "If you executed less repeats than given, modify it, and click to OK!", + "Please type in a real number!": "Please type in a real number!", + "Exception: Please type in a real number!": "Please type in a real number!", + "If you could do less, then modify and click to OK": "If you could do less, then modify and click to OK", + "Here is your tailored weight,": "Here is your tailored weight,", + "and executed with this number of repeats!": "and execute it with this number of repeats!", + "You can change the weight, if you could not set it in the training room": "You can change the weight, if you could not set it in the training room", + "Exception: Please type your repeats!": "Please type your maximum repeats!", + "For your optimal development we calculated a suitable weight and repeats": "For your optimal development we calculated a suitable weight and repeats", + "It is time to exhaust your muscles": "It is time to really exhaust your muscles", + "Next Exercise": "Next Exercise", + "and execute it with maximum repeats!": "and execute it with maximum repeats!", + "10 days Premium for free": "10 days Premium for free", + "Would you like to try all premium functions for 10 days, without any subscription or bank card data?": "Would you like to try all premium functions for 10 days, without any subscription or bank card data?", + "If you click to 'Yes', all premium functions will be available right now.": "If you click to 'Yes', all premium functions will be available right now.", + "If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.": "If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.", + "Based on your initial data, we will generate the personalized training plan for you.": "Based on your initial data, we will generate the personalized training plan for you.", + "No selected Training Plan": "No selected Training Plan", + "Min. 10 minutes": "Min. 10 minutes" } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index 0043736..3db4518 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -278,6 +278,9 @@ "Unleash your potential with WorkoutTest Premium!": "Bontakoztasd ki az erősségeidet WorkoutTest Prémiummal!", "feature is reachable after you finished": "funkció elérhető számodra, miután teljesítetted", "100% test circles": "100%-os teszt-köröd", + "week": "hét", + "100% completed training": "alatt 100%-osan végrehajtott edzést", + "after": "", "Keep testing": "Folytasd a tesztelést", "Enjoy also this premium feature to show all old evaluation data of your successful exercises.": "Élvezd ezt a prémium funkciót is, amely megjeleníti a korábbi gyakorlatok teljes kiértékelését", "Please define your Exercise Plan": "Kérlek készíts edzéstervet!", @@ -507,5 +510,35 @@ "optimized training plans, customized to your fitness state and strength:": "optiomalizált edzés terveket, amelyeket a te erő- és fitnesz állapododra szabunk:", "Soon! Check back later for the plan details": "Nemsokára! Nézz vissza később, amikor már aktiváltuk az edzésterv részleteit", "mins": "perc", - "set": "sorozat" + "set": "sorozat", + "Set / Reps / Weight": "Sorozat / Ismétlés / Súly", + "Weight_": "Súly", + "Got It": "Értettem!", + "Done!": "Kész!", + "Show this tip no more": "Ne mutasd többet ezt a tippet", + "Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions.": "Ne felejtsd el, hogy a program csak akkor tud számodra megfelelő értéket adni, ha helyesen, és az utasítás szerint végzed el a feladatokat", + "Please take a middle weight which you are able to do 8-20 times with.": "Vegyél egy közepes súlyt, amivel 8-20 ismétlésre vagy képes.", + "Execute your MAXIMUM repeats with it!": "Végezz vele MAXIMUM ismétlést", + "Type here your selected weight,": "Írd be ide a kiválasztott súlyt,", + "then here, how many times could you repeat it!": "majd ide, hogy mennyi ismétlés sikerült belőle!", + "After you done, click to the OK button!": "Ha elvégezted a gyakorlatot, kattints az OK gombra!", + "If you executed less repeats than given, modify it, and click to OK!": "Ha kevesebb sikerült, akkor írd be és utána nyomj OK-t!", + "Please type in a real number!": "Kérlek írj be egy számot eredményként", + "Exception: Please type in a real number!": "Kérlek írj be egy számot eredményként", + "If you could do less, then modify and click to OK": "Ha kevesebb sikerült, írd be és kattints az OK-ra", + "Here is your tailored weight,": "Itt van a rádszabott súly,", + "and executed with this number of repeats!": "ennyi ismétlést végezz!", + "You can change the weight, if you could not set it in the training room": "Változtasd meg a súlyt, ha éppen nincs ennyi az edzőteremben", + "Exception: Please type your repeats!": "Kérlek írd be a maximum ismétlésed számát!", + "For your optimal development we calculated a suitable weight and repeats": "A fejlődésed érdekében meghatároztuk Neked a megfelelő súlyt és ismétlésszámot", + "It is time to exhaust your muscles": "Itt az idő, hogy igazán lemerítsd az izmaidat.", + "Next Exercise": "Következő", + "and execute it with maximum repeats!": "és hajtsd végre maximális ismétléssel!", + "10 days Premium for free": "10 nap Prémium előfizetés díjmentesen", + "Would you like to try all premium functions for 10 days, without any subscription or bank card data?": "Szeretnéd kipróbálni 10 napig az összes prémium funkciót, előfizetés és bankkártya-adatok megadása nélkül?", + "If you click to 'Yes', all premium functions will be available right now.": "Ha az 'Igen'-re kattintasz, azonnal eléred az összes prémium funkciót, edzést.", + "If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.": "Ha a 'Nem'-re kattintasz, használhatod az összes alapfunkciót, de elveszted a lehetőséget, hogy a prémium funkciókat ingyen kipróbáld.", + "Based on your initial data, we will generate the personalized training plan for you.": "A megadott adataid alapján most személyre szabott edzéstervet generálunk neked.", + "No selected Training Plan": "Nincs kiválasztott edzésterved", + "Min. 10 minutes": "Minimum 10 perc" } \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2c0b680..edaa373 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -388,7 +388,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -531,7 +531,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -566,7 +566,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 811fead..4f1e1fd 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,97 +1,97 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - WorkoutTest - CFBundlePackageType - APPL - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - aitrainer.page.link - CFBundleURLSchemes - - wt001 - fb584181112271127 - - - - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - FacebookAdvertiserIDCollectionEnabled - TRUE - FacebookAppID - 584181112271127 - FacebookAutoLogAppEventsEnabled - TRUE - FacebookDisplayName - Workout Test - FirebaseAppDelegateProxyEnabled - NO - LSApplicationQueriesSchemes - - https - http - fbapi - fbapi20130214 - fbapi20130410 - fbapi20130702 - fbapi20131010 - fbapi20131219 - fbapi20140410 - fbapi20140116 - fbapi20150313 - fbapi20150629 - fbapi20160328 - fb-messenger-share-api - fbauth2 - fbshareextension - - LSMinimumSystemVersion - 11.0.0 - LSRequiresIPhoneOS - - NSAppTransportSecurity + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + WorkoutTest + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleURLTypes + - NSAllowsArbitraryLoads - + CFBundleTypeRole + Editor + CFBundleURLName + aitrainer.page.link + CFBundleURLSchemes + + wt001 + fb584181112271127 + - UIBackgroundModes - - remote-notification - - UILaunchStoryboardName - Launch Screen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + FacebookAdvertiserIDCollectionEnabled + TRUE + FacebookAppID + 584181112271127 + FacebookAutoLogAppEventsEnabled + TRUE + FacebookDisplayName + Workout Test + FirebaseAppDelegateProxyEnabled + NO + LSApplicationQueriesSchemes + + https + http + fbapi + fbapi20130214 + fbapi20130410 + fbapi20130702 + fbapi20131010 + fbapi20131219 + fbapi20140410 + fbapi20140116 + fbapi20150313 + fbapi20150629 + fbapi20160328 + fb-messenger-share-api + fbauth2 + fbshareextension + + LSMinimumSystemVersion + 11.0.0 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + Launch Screen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + diff --git a/lib/bloc/customer_change/customer_change_bloc.dart b/lib/bloc/customer_change/customer_change_bloc.dart index c599e9b..5123564 100644 --- a/lib/bloc/customer_change/customer_change_bloc.dart +++ b/lib/bloc/customer_change/customer_change_bloc.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/sport.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/repository/mautic_repository.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -29,8 +30,8 @@ class CustomerChangeBloc extends Bloc weight = this.customerRepository.getWeight() == 0 ? 60 : this.customerRepository.getWeight(); height = this.customerRepository.getHeight() == 0 ? 170 : this.customerRepository.getHeight(); - // selectedSport = customerRepository.getSport(); - //print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString()); + selectedFitnessItem = customerRepository.fitnessLevel; + selectedSport = customerRepository.getSport(); } Sport? selectedSport; @@ -95,6 +96,7 @@ class CustomerChangeBloc extends Bloc } else if (event is CustomerSportChange) { yield CustomerChangeLoading(); selectedSport = event.sport; + print("Selected Sport $selectedSport"); yield CustomerDataChanged(); } else if (event is CustomerSaveFitness) { yield CustomerChangeLoading(); @@ -137,6 +139,8 @@ class CustomerChangeBloc extends Bloc } await customerRepository.saveCustomer(); + MauticRepository mauticRepository = MauticRepository(customerRepository: customerRepository); + await mauticRepository.sendMauticDataChange(); Cache().initBadges(); yield CustomerSaveSuccess(); } else { @@ -151,10 +155,6 @@ class CustomerChangeBloc extends Bloc bool validation() { if (customerRepository.customer == null) throw Exception("Customer object not defined"); return true; - /* return (emailValidation(customerRepository.customer!.email) == null) && - (passwordValidation(customerRepository.customer!.password) == null) && - (nameValidation(customerRepository.customer!.firstname) == null) && - (nameValidation(customerRepository.customer!.name) == null); */ } String? emailValidation(String? email) { diff --git a/lib/bloc/exercise_control/exercise_control_bloc.dart b/lib/bloc/exercise_control/exercise_control_bloc.dart index 44f176a..a7cb02a 100644 --- a/lib/bloc/exercise_control/exercise_control_bloc.dart +++ b/lib/bloc/exercise_control/exercise_control_bloc.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:aitrainer_app/bloc/timer/timer_bloc.dart'; +import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -26,7 +27,6 @@ class ExerciseControlBloc extends Bloc controlList = []; + @override ExerciseControlBloc({required this.exerciseRepository, required this.readonly, required this.timerBloc}) : super(ExerciseControlInitial()) { @@ -53,29 +55,29 @@ class ExerciseControlBloc extends Bloc 25) { quantity = 35; } - origQuantity = quantity; exerciseRepository.setUnitQuantity(unitQuantity); exerciseRepository.setQuantity(quantity); + for (int i = 0; i < 3; i++) { + Exercise exercise = Exercise(); + exercise.quantity = quantity; + exercise.unitQuantity = unitQuantity; + controlList.add(exercise); + } + timerBloc.add(TimerStart(duration: 300)); } @override Stream mapEventToState(ExerciseControlEvent event) async* { try { - /* if (event is ExerciseControlLoad) { - yield ExerciseControlLoading(); - step = 1; - yield ExerciseControlReady(); - } else */ if (event is ExerciseControlQuantityChange) { - //yield ExerciseControlLoading(); if (event.step == step) { exerciseRepository.setQuantity(event.quantity); quantity = event.quantity; + controlList[step - 1].quantity = quantity; } - //yield ExerciseControlReady(); } else if (event is ExerciseControlUnitQuantityChange) { yield ExerciseControlLoading(); print("event step ${event.step} quantity ${event.quantity}"); @@ -85,7 +87,8 @@ class ExerciseControlBloc extends Bloc with Logg final CustomerRepository customerRepository; final MenuBloc menuBloc; late AnimationController bmiAnimationController; - double quantity = 12; - double unitQuantity = 30; + double quantity = -1; + double unitQuantity = -1; double bmi = 0; double bmr = 0; double goalBMI = 0; @@ -148,6 +148,10 @@ class ExerciseNewBloc extends Bloc with Logg yield ExerciseNewReady(); } else if (event is ExerciseNewSubmit) { yield ExerciseNewLoading(); + if (quantity == -1 || unitQuantity == -1) { + yield ExerciseNewReady(); + throw Exception("Please type in a real number"); + } exerciseRepository.end = DateTime.now(); await exerciseRepository.addExercise(); // exerciseRepository.initExercise(); diff --git a/lib/bloc/login/login_bloc.dart b/lib/bloc/login/login_bloc.dart index 69ed7c0..90457a4 100644 --- a/lib/bloc/login/login_bloc.dart +++ b/lib/bloc/login/login_bloc.dart @@ -2,8 +2,11 @@ import 'dart:async'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/repository/mautic_repository.dart'; import 'package:aitrainer_app/repository/split_test_respository.dart'; +import 'package:aitrainer_app/repository/training_plan_repository.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:aitrainer_app/util/enums.dart'; @@ -19,7 +22,7 @@ part 'login_state.dart'; class LoginBloc extends Bloc with Trans { final AccountBloc accountBloc; final UserRepository userRepository; - final CustomerRepository customerRepository = CustomerRepository(); + late CustomerRepository? customerRepository; final SplitTestRepository splitTestRepository = SplitTestRepository(); final BuildContext context; final bool isRegistration; @@ -30,7 +33,12 @@ class LoginBloc extends Bloc with Trans { Color testColor = Colors.green[800]!; bool emailCheckbox = true; - LoginBloc({required this.accountBloc, required this.userRepository, required this.context, required this.isRegistration}) + LoginBloc( + {required this.accountBloc, + required this.userRepository, + required this.context, + required this.isRegistration, + this.customerRepository}) : super(LoginInitial()) { String colorString = splitTestRepository.getSplitTestValue("registration_skip"); if (colorString == "red") { @@ -40,6 +48,9 @@ class LoginBloc extends Bloc with Trans { if (emailCheckboxString == "0") { emailCheckbox = false; } + if (customerRepository == null) { + customerRepository = CustomerRepository(); + } } @override @@ -87,56 +98,44 @@ class LoginBloc extends Bloc with Trans { yield LoginSuccess(); } else if (event is RegistrationSubmit) { yield LoginLoading(); - /* if (!this.dataPolicyAllowed) { - throw Exception("Please accept our data policy"); - } */ + final String? validationError = validate(); if (validationError != null) { yield LoginError(message: validationError); } else { await userRepository.addUser(); accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!)); - customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0; - await saveCustomer(); - Track().track(TrackingEvent.registration, eventValue: "email"); + customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0; + await afterRegistration("email"); Cache().setLoginType(LoginType.email); yield LoginSuccess(); } } else if (event is RegistrationFB) { yield LoginLoading(); - /* if (!this.dataPolicyAllowed) { - throw Exception("Please accept our data policy"); - } */ + Cache().setLoginType(LoginType.fb); await userRepository.addUserFB(); accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!)); - customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0; - await saveCustomer(); - Track().track(TrackingEvent.registration, eventValue: "FB"); + customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0; + await afterRegistration("FB"); yield LoginSuccess(); } else if (event is RegistrationGoogle) { yield LoginLoading(); - /* if (!this.dataPolicyAllowed) { - throw Exception("Please accept our data policy"); - } */ + Cache().setLoginType(LoginType.google); await userRepository.addUserGoogle(); accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!)); - customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0; - await saveCustomer(); - Track().track(TrackingEvent.registration, eventValue: "Google"); + customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0; + await afterRegistration("Google"); yield LoginSuccess(); } else if (event is RegistrationApple) { yield LoginLoading(); - /* if (!this.dataPolicyAllowed) { - throw Exception("Please accept our data policy"); - } */ + Cache().setLoginType(LoginType.apple); await userRepository.addUserApple(); accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!)); - customerRepository.customer!.emailSubscription = emailSubscription == true ? 1 : 0; - await saveCustomer(); - Track().track(TrackingEvent.registration, eventValue: "Apple"); + customerRepository!.customer!.emailSubscription = emailSubscription == true ? 1 : 0; + await afterRegistration("Apple"); yield LoginSuccess(); } else if (event is DataProtectionClicked) { @@ -162,10 +161,23 @@ class LoginBloc extends Bloc with Trans { } } - Future saveCustomer() async { - customerRepository.customer = Cache().userLoggedIn!; - customerRepository.customer!.dataPolicyAllowed = 1; - await customerRepository.saveCustomer(); + Future afterRegistration(String event) async { + Customer tempCustomer = customerRepository!.customer!; + customerRepository!.customer = Cache().userLoggedIn!; + customerRepository!.customer!.fitnessLevel = tempCustomer.fitnessLevel; + customerRepository!.customer!.goal = tempCustomer.goal; + customerRepository!.customer!.sex = tempCustomer.sex; + customerRepository!.customer!.dataPolicyAllowed = 1; + customerRepository!.customer!.emailSubscription = 1; + customerRepository!.customer!.syncedDate = DateTime.now(); + customerRepository!.setCustomerProperty("Weight", tempCustomer.getProperty("Weight")); + customerRepository!.setCustomerProperty("Height", tempCustomer.getProperty("Height")); + await customerRepository!.saveCustomer(); + MauticRepository mauticRepository = MauticRepository(customerRepository: customerRepository!); + await mauticRepository.sendMauticSubscription(); + TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository(); + trainingPlanRepository.generateTrainingPlan(); + Track().track(TrackingEvent.registration, eventValue: event); } String? emailValidation(String? email) { diff --git a/lib/bloc/training_plan/training_plan_bloc.dart b/lib/bloc/training_plan/training_plan_bloc.dart index c4f1210..047f809 100644 --- a/lib/bloc/training_plan/training_plan_bloc.dart +++ b/lib/bloc/training_plan/training_plan_bloc.dart @@ -7,6 +7,8 @@ import 'package:aitrainer_app/model/customer_training_plan_details.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; +import 'package:aitrainer_app/model/training_plan.dart'; +import 'package:aitrainer_app/model/training_plan_detail.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/training_plan_repository.dart'; import 'package:aitrainer_app/service/exercise_service.dart'; @@ -29,6 +31,9 @@ class TrainingPlanBloc extends Bloc { CustomerTrainingPlan? _myPlan; CustomerTrainingPlanDetails? _myDetail; + TrainingPlan? _myTrainingPlan; + final List trainingPlanDayNames = []; + bool started = false; final List dayNames = []; bool restarting = false; @@ -38,6 +43,9 @@ class TrainingPlanBloc extends Bloc { CustomerTrainingPlan? getMyPlan() => this._myPlan; setMyPlan(CustomerTrainingPlan? myPlan) => this._myPlan = myPlan; + TrainingPlan? getMyTrainingPlan() => this._myTrainingPlan; + setMyTrainingPlan(TrainingPlan? myTrainingPlan) => this._myTrainingPlan = myTrainingPlan; + CustomerTrainingPlanDetails? getMyDetail() => this._myDetail; setMyDetail(CustomerTrainingPlanDetails? value) => this._myDetail = value; @@ -88,6 +96,10 @@ class TrainingPlanBloc extends Bloc { yield TrainingPlanReady(); } else if (event is TrainingPlanSaveExercise) { yield TrainingPlanLoading(); + if (event.detail.repeats == -1) { + yield TrainingPlanReady(); + throw Exception("Please type your repeats!"); + } if (event.detail.weight == -3) { print("DropSet"); event.detail.state = ExercisePlanDetailState.finished; @@ -293,6 +305,83 @@ class TrainingPlanBloc extends Bloc { getActiveDayIndex(); } + void activateTrainingPlanDays() { + if (_myTrainingPlan == null || _myTrainingPlan!.details == null) { + return; + } + + trainingPlanDayNames.clear(); + + String dayName = "."; + _myTrainingPlan!.details!.forEach((element) { + if (element.day != null && element.day != dayName) { + trainingPlanDayNames.add(element.day!); + dayName = element.day!; + } + }); + + if (trainingPlanDayNames.length == 0) { + dayName = ""; + trainingPlanDayNames.add(dayName); + } + } + + List trainingPlanDetailSummary(TrainingPlan plan, String dayName) { + List details = []; + TrainingPlanDetail? prev; + plan.details!.forEach((element) { + if (prev == null || element.exerciseTypeId != prev!.exerciseTypeId) { + if (element.day! == dayName) { + element.summary = getSummary(element); + details.add(element); + } + prev = element; + } + }); + return details; + } + + List getAllTrainingPlanDetailsSameExercise(TrainingPlanDetail detail) { + List list = []; + getMyTrainingPlan()!.details!.forEach((element) { + if (detail.exerciseTypeId == element.exerciseTypeId) { + list.add(element); + } + }); + return list; + } + + String getSummary(TrainingPlanDetail detail) { + String summary = ""; + String set = "1"; + + set = detail.set.toString() + "/ "; + List details = getAllTrainingPlanDetailsSameExercise(detail); + int index = 0; + + String quantities = ""; + + details.forEach((element) { + String delimiter = ", "; + if (index == 0) { + delimiter = ""; + } + if (element.repeats == -1) { + quantities += delimiter + " MAX "; + } else { + quantities += delimiter + "${element.repeats}"; + } + + index++; + }); + + //quantities += " / ? kg"; + + summary = quantities; + + return summary; + } + void addExtraExerciseType(String name, String dayName) { if (Cache().getExerciseTypes() == null) { return; diff --git a/lib/library/super_tooltip.dart b/lib/library/super_tooltip.dart index d9b8f61..d6b08ca 100644 --- a/lib/library/super_tooltip.dart +++ b/lib/library/super_tooltip.dart @@ -307,6 +307,60 @@ class SuperTooltip { isOpen = true; } + void showBox(BuildContext targetContext) { + final renderBox = targetContext.findRenderObject() as RenderBox; + var size = renderBox.size; + print("Size $size"); + if (containsBackgroundOverlay) { + _backGroundOverlay = OverlayEntry( + builder: (context) => _AnimationWrapper( + builder: (context, opacity) => AnimatedOpacity( + opacity: opacity, + duration: const Duration(milliseconds: 600), + child: GestureDetector( + onTap: () { + if (dismissOnTapOutside) { + close(); + } + }, + child: Container( + decoration: ShapeDecoration( + shape: _ShapeOverlay( + touchThrougArea, touchThroughAreaShape, touchThroughAreaCornerRadius, outsideBackgroundColor))), + ), + ), + )); + } + + _ballonOverlay = OverlayEntry( + builder: (context) => _AnimationWrapper( + builder: (context, opacity) => Positioned( + left: left, //offset.dx, + top: top, + width: size.width > maxWidth! ? maxWidth : size.width, + child: AnimatedOpacity( + duration: Duration( + milliseconds: 300, + ), + opacity: opacity, + child: Stack( + fit: StackFit.passthrough, + children: [_buildPopUp(), _buildCloseButton()], + ), + )), + )); + + var overlays = []; + + if (containsBackgroundOverlay) { + overlays.add(_backGroundOverlay!); + } + overlays.add(_ballonOverlay!); + + Overlay.of(targetContext)!.insertAll(overlays); + isOpen = true; + } + Widget _buildPopUp() { return Positioned( child: Container( @@ -675,6 +729,122 @@ class _PopupBallonLayoutDelegate extends SingleChildLayoutDelegate { } } +class _PopupBallonLayoutDelegateBox extends SingleChildLayoutDelegate { + final double? _minWidth; + final double? _maxWidth; + final double? _minHeight; + final double? _maxHeight; + final double? _top; + final double? _bottom; + final double? _left; + final double? _right; + final double? _outSidePadding; + + _PopupBallonLayoutDelegateBox({ + double? minWidth, + double? maxWidth, + double? minHeight, + double? maxHeight, + double? outSidePadding, + double? top, + double? bottom, + double? left, + double? right, + }) : _minWidth = minWidth, + _maxWidth = maxWidth, + _minHeight = minHeight, + _maxHeight = maxHeight, + _top = top, + _bottom = bottom, + _left = left, + _right = right, + _outSidePadding = outSidePadding; + + @override + Offset getPositionForChild(Size size, Size childSize) { + double? calcLeftMostXtoTarget() { + double? leftMostXtoTarget; + if (_left != null) { + leftMostXtoTarget = _left; + } else if (_right != null) { + leftMostXtoTarget = max( + size.topLeft(Offset.zero).dx + _outSidePadding!, size.topRight(Offset.zero).dx - _outSidePadding! - childSize.width - _right!); + } + return leftMostXtoTarget; + } + + double? calcTopMostYtoTarget() { + double? topmostYtoTarget; + if (_top != null) { + topmostYtoTarget = _top!; + } else if (_bottom != null) { + topmostYtoTarget = max(size.topLeft(Offset.zero).dy + _outSidePadding!, + size.bottomRight(Offset.zero).dy - _outSidePadding! - childSize.height - _bottom!); + } + return topmostYtoTarget; + } + + return new Offset(calcLeftMostXtoTarget()!, _top!); + } + + @override + BoxConstraints getConstraintsForChild(BoxConstraints constraints) { + // print("ParentConstraints: $constraints"); + + var calcMinWidth = _minWidth ?? 0.0; + var calcMaxWidth = _maxWidth ?? double.infinity; + var calcMinHeight = _minHeight ?? 0.0; + var calcMaxHeight = _maxHeight ?? double.infinity; + + void calcMinMaxWidth() { + if (_left != null && _right != null) { + calcMaxWidth = constraints.maxWidth - (_left! + _right!); + } else if ((_left != null && _right == null) || (_left == null && _right != null)) { + // make sure that the sum of left, right + maxwidth isn't bigger than the screen width. + var sideDelta = (_left ?? 0.0) + (_right ?? 0.0) + _outSidePadding!; + if (calcMaxWidth > constraints.maxWidth - sideDelta) { + calcMaxWidth = constraints.maxWidth - sideDelta; + } + } else { + if (calcMaxWidth > constraints.maxWidth - 2 * _outSidePadding!) { + calcMaxWidth = constraints.maxWidth - 2 * _outSidePadding!; + } + } + } + + void calcMinMaxHeight() { + if (_top != null && _bottom != null) { + calcMaxHeight = constraints.maxHeight - (_top! + _bottom!); + } else if ((_top != null && _bottom == null) || (_top == null && _bottom != null)) { + // make sure that the sum of top, bottom + maxHeight isn't bigger than the screen Height. + var sideDelta = (_top ?? 0.0) + (_bottom ?? 0.0) + _outSidePadding!; + if (calcMaxHeight > constraints.maxHeight - sideDelta) { + calcMaxHeight = constraints.maxHeight - sideDelta; + } + } else { + if (calcMaxHeight > constraints.maxHeight - 2 * _outSidePadding!) { + calcMaxHeight = constraints.maxHeight - 2 * _outSidePadding!; + } + } + } + + var childConstraints = new BoxConstraints( + minWidth: calcMinWidth > calcMaxWidth ? calcMaxWidth : calcMinWidth, + maxWidth: calcMaxWidth, + minHeight: calcMinHeight > calcMaxHeight ? calcMaxHeight : calcMinHeight, + maxHeight: calcMaxHeight); + + // print("Child constraints: $childConstraints"); + + return childConstraints; + } + + @override + bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) { + return false; + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// class _BubbleShape extends ShapeBorder { diff --git a/lib/main.dart b/lib/main.dart index c823fb2..0f97e89 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,7 +41,6 @@ import 'package:aitrainer_app/view/test_set_edit.dart'; import 'package:aitrainer_app/view/test_set_execute.dart'; import 'package:aitrainer_app/view/test_set_new.dart'; import 'package:aitrainer_app/view/training_plan_activate_page.dart'; -import 'package:aitrainer_app/view/training_plan_execute_page.dart'; import 'package:aitrainer_app/view/training_plan_exercise.dart'; import 'package:aitrainer_app/widgets/home.dart'; import 'package:aitrainer_app/library/facebook_app_events/facebook_app_events.dart'; @@ -180,8 +179,8 @@ Future main() async { BlocProvider( create: (BuildContext context) => TestSetExecuteBloc(), ), - /* BlocProvider( - create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())), */ + BlocProvider( + create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())), BlocProvider(create: (context) { final MenuBloc menuBloc = BlocProvider.of(context); return TrainingPlanBloc(menuBloc: menuBloc, trainingPlanRepository: TrainingPlanRepository()); @@ -283,7 +282,6 @@ class WorkoutTestApp extends StatelessWidget { 'myTrainingPlanCustom': (context) => TrainingPlanCustomPage(), 'myTrainingPlanCustomAdd': (context) => TrainingPlanCustomAddPage(), 'myTrainingPlanActivate': (context) => TrainingPlanActivatePage(), - 'myTrainingPlanExecute2': (context) => TrainingPlanExecutePage(), 'myTrainingPlanExecute': (context) => TrainingPlanExecute(), 'myTrainingPlanExercise': (context) => TrainingPlanExercise(), 'myTrainingEvaluation': (context) => TrainingEvaluationPage(), diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 9c68999..b4b712a 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -71,7 +71,11 @@ enum ActivityDone { tutorialBasicLegPress, tutorialDevelopment, isExerciseLogSeen, - isMuscleDevelopmentSeen + isMuscleDevelopmentSeen, + isBodyTypeSeen, + exerciseSaveTestTip, + exerciseSaveTrainingTip, + exerciseSaveTestsetTip } extension ActivityDoneExt on ActivityDone { @@ -641,8 +645,8 @@ class Cache with Logging { } if (this._exercises == null || this._exercises!.isEmpty) { setBadge("home", true); - setBadge("Muscle Build / Shape Toning", true); - setBadge("Cardio", true); + setBadge("Custom Tests", true); + setBadge("Start Training", true); } if (customerRepository.getHeight() == 0) { setBadge("BMI", true); @@ -726,6 +730,8 @@ class Cache with Logging { sharedPreferences.setBool(activity.toStr(), true); } + bool isActivityDone(ActivityDone activity) => activitiesDone[activity.toStr()] == true; + List? get evaluations => this._evaluations; set evaluations(List? value) => this._evaluations = value; diff --git a/lib/model/customer.dart b/lib/model/customer.dart index 4aa19d6..df20a9c 100644 --- a/lib/model/customer.dart +++ b/lib/model/customer.dart @@ -24,6 +24,7 @@ class Customer { DateTime? dateChange; int? emailSubscription; int? sportId; + DateTime? syncedDate; DateTime? trialDate; LinkedHashMap properties = LinkedHashMap(); @@ -71,6 +72,7 @@ class Customer { this.dataPolicyAllowed = json['dataPolicyAllowed']; this.emailSubscription = json['emailSubscription']; this.sportId = json['sportId']; + this.syncedDate = json['syncedDate'] == null ? null : DateTime.parse(json['syncedDate']); this.trialDate = json['trialDate'] == null ? null : DateTime.parse(json['trialDate']); this.dateAdd = json['dateAdd'] == null ? DateTime.parse("0000-00-00") : DateTime.parse(json['dateAdd']); @@ -96,9 +98,13 @@ class Customer { "dateChange": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateChange!), "emailSubscription": this.emailSubscription, "sportId": this.sportId, - "trialDate": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.trialDate!), + "syncedDate": this.syncedDate == null ? null : DateFormat('yyyy-MM-dd HH:mm:ss').format(this.syncedDate!), + "trialDate": this.trialDate == null ? null : DateFormat('yyyy-MM-dd HH:mm:ss').format(this.trialDate!), }; + @override + String toString() => this.toJson().toString(); + double getProperty(String propertyName) { if (this.properties[propertyName] == null) { return 0; diff --git a/lib/model/customer_training_plan_details.dart b/lib/model/customer_training_plan_details.dart index dfcb7f9..75c9653 100644 --- a/lib/model/customer_training_plan_details.dart +++ b/lib/model/customer_training_plan_details.dart @@ -59,7 +59,7 @@ class CustomerTrainingPlanDetails { this.exerciseTypeId = json['exerciseTypeId']; this.set = json['set']; this.repeats = json['repeats'] == "null" ? -1 : json['repeats']; - this.weight = json['weight']; + this.weight = json['weight'] == "null" ? 0 : json['weight']; this.restingTime = json['restingTime']; this.parallel = json['parallel'] == "false" ? false diff --git a/lib/model/faq.dart b/lib/model/faq.dart index 3e15174..2f4dd2c 100644 --- a/lib/model/faq.dart +++ b/lib/model/faq.dart @@ -1,7 +1,5 @@ import 'dart:collection'; -import 'package:aitrainer_app/util/app_language.dart'; - class Faq { late int faqId; late String name; diff --git a/lib/model/mautic.dart b/lib/model/mautic.dart new file mode 100644 index 0000000..aa212f9 --- /dev/null +++ b/lib/model/mautic.dart @@ -0,0 +1,40 @@ +class Mautic { + late int formId; + String? firstname; + String? lastname; + String? email; + String? fitnessLevel; + String? goal; + int? databaseId; + String? subscriptionDate; + String? language; + + Map toJson() => { + "formId": this.formId, + "firstname": this.firstname, + "lastname": this.lastname, + "email": this.email, + "fitnessLevel": this.fitnessLevel, + "goal": this.goal, + "databaseId": this.databaseId, + "subscriptionDate": this.subscriptionDate, + "language": this.language + }; + + String toForm() { + String form = "mauticform[formId]=${this.formId}"; + form += this.email == null ? "" : "&mauticform[email]=${this.email}"; + form += this.lastname == null ? "" : "&mauticform[f_name]=${this.lastname}"; + form += this.firstname == null ? "" : "&mauticform[firstname]=${this.firstname}"; + form += this.fitnessLevel == null ? "" : "&mauticform[fitness_level]=${this.fitnessLevel}"; + form += this.goal == null ? "" : "&mauticform[goal]=${this.goal}"; + form += this.subscriptionDate == null ? "" : "&mauticform[subscribed]=${this.subscriptionDate}"; + form += this.databaseId == null ? "" : "&mauticform[database_id]=${this.databaseId}"; + form += this.language == null ? "" : "&mauticform[language]=${this.language}"; + + return form; + } + + @override + String toString() => this.toJson().toString(); +} diff --git a/lib/model/sport.dart b/lib/model/sport.dart index fc41fd2..31a94c7 100644 --- a/lib/model/sport.dart +++ b/lib/model/sport.dart @@ -1,13 +1,21 @@ +import 'dart:collection'; + class Sport { late int sportId; late String name; - late String sportNameTranslation; + + HashMap nameTranslations = HashMap(); Sport.fromJson(Map json) { this.sportId = json['sportId']; this.name = json['name']; - this.sportNameTranslation = - json['translations'] != null && (json['translations']).length > 0 ? json['translations'][0]['sportName'] : this.name; + + nameTranslations['en'] = name; + if (json['translations'] != null && json['translations'].length > 0) { + json['translations'].forEach((translation) { + nameTranslations[translation['languageCode']] = translation['sportName']; + }); + } } Map toJson() => { diff --git a/lib/model/training_plan_detail.dart b/lib/model/training_plan_detail.dart index bc299d8..58ae838 100644 --- a/lib/model/training_plan_detail.dart +++ b/lib/model/training_plan_detail.dart @@ -10,6 +10,7 @@ class TrainingPlanDetail { bool? parallel; int? dayId; String? day; + String? summary; TrainingPlanDetail.fromJson(Map json) { this.trainingPlanDetailId = json['trainingPlanDetailId']; @@ -35,6 +36,7 @@ class TrainingPlanDetail { "parallel": this.parallel, "dayId": this.dayId, "day": this.day, + "summary": this.summary }; @override diff --git a/lib/repository/mautic_repository.dart b/lib/repository/mautic_repository.dart new file mode 100644 index 0000000..f00136b --- /dev/null +++ b/lib/repository/mautic_repository.dart @@ -0,0 +1,49 @@ +import 'package:intl/intl.dart'; +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/mautic.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/service/mautic.dart'; +import 'package:aitrainer_app/util/app_language.dart'; + +class MauticRepository { + final CustomerRepository customerRepository; + + const MauticRepository({required this.customerRepository}); + + Future sendMauticSubscription() async { + Mautic mautic = Mautic(); + mautic.formId = 2; + mautic.databaseId = Cache().userLoggedIn!.customerId!; + mautic.firstname = customerRepository.customer!.firstname == null ? "" : customerRepository.customer!.firstname!; + mautic.lastname = customerRepository.customer!.name == null ? "" : customerRepository.customer!.name!; + mautic.email = customerRepository.customer!.email == null ? "" : customerRepository.customer!.email!; + if (mautic.email == null || mautic.email!.contains("privaterelay.appleid.com")) { + return; + } + mautic.fitnessLevel = customerRepository.customer!.fitnessLevel == null ? "" : customerRepository.customer!.fitnessLevel!; + mautic.goal = customerRepository.customer!.goal == null ? "" : customerRepository.customer!.goal!; + mautic.subscriptionDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()); + mautic.language = AppLanguage().appLocal.languageCode; + + await MauticApi().sendMauticForm(mautic); + + customerRepository.customer!.syncedDate = DateTime.now(); + await customerRepository.saveCustomer(); + } + + Future sendMauticDataChange() async { + Mautic mautic = Mautic(); + mautic.formId = 3; + mautic.databaseId = Cache().userLoggedIn!.customerId!; + mautic.firstname = customerRepository.customer!.firstname == null ? "" : customerRepository.customer!.firstname!; + mautic.lastname = customerRepository.customer!.name == null ? "" : customerRepository.customer!.name!; + mautic.email = customerRepository.customer!.email == null ? "" : customerRepository.customer!.email!; + if (mautic.email == null || mautic.email!.contains("privaterelay.appleid.com")) { + return; + } + mautic.fitnessLevel = customerRepository.customer!.fitnessLevel == null ? "" : customerRepository.customer!.fitnessLevel!; + mautic.goal = customerRepository.customer!.goal == null ? "" : customerRepository.customer!.goal!; + + await MauticApi().sendMauticForm(mautic); + } +} diff --git a/lib/repository/training_plan_repository.dart b/lib/repository/training_plan_repository.dart index 640e1f6..0072069 100644 --- a/lib/repository/training_plan_repository.dart +++ b/lib/repository/training_plan_repository.dart @@ -63,6 +63,7 @@ class TrainingPlanRepository { TrainingPlan? trainingPlan = this.getTrainingPlanById(trainingPlanId); if (trainingPlan == null || trainingPlan.details == null) { + print("trainingPlan null"); return null; } @@ -274,14 +275,26 @@ class TrainingPlanRepository { bool isWoman = Cache().userLoggedIn!.sex == "w"; - if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) { - trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("beginner_man"); - } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) { - trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("beginner_split"); - } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) { - trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("man_routine4"); + if (Cache().userLoggedIn!.goal == "shape_forming") { + if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("man_routine1"); + } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("man_routine3"); + } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("man_routine4"); + } else { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("man_routine2") : getTrainingPlanByInternalName("man_routine2"); + } } else { - trainingPlanId = isWoman ? getTrainingPlanByInternalName("5day") : getTrainingPlanByInternalName("5day"); + if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("beginner_man"); + } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("man_foundation"); + } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("basic_mass_building"); + } else { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("man_routine2") : getTrainingPlanByInternalName("mass_building"); + } } print("Generated plan $trainingPlanId fitness ${Cache().userLoggedIn!.fitnessLevel} - ${FitnessState.beginner}"); @@ -290,6 +303,7 @@ class TrainingPlanRepository { CustomerTrainingPlan? customerTrainingPlan = activateTrainingPlan(trainingPlanId); if (customerTrainingPlan != null) { Cache().myTrainingPlan = customerTrainingPlan; + Cache().saveMyTrainingPlan(); } } } diff --git a/lib/service/mautic.dart b/lib/service/mautic.dart new file mode 100644 index 0000000..3abab2e --- /dev/null +++ b/lib/service/mautic.dart @@ -0,0 +1,29 @@ +import 'dart:io'; + +import 'package:aitrainer_app/model/mautic.dart'; +import 'package:aitrainer_app/service/logging.dart'; + +class MauticApi with Logging { + final String mauticUrl = "https://mautic.aitrainer.app/form/submit?formId="; + + Future sendMauticForm(Mautic model) async { + final String body = model.toForm(); + log(" ===== mautic subscription:" + body); + HttpClient client = new HttpClient(); + + String url = mauticUrl + model.formId.toString(); + + var uri = Uri.parse(url); + final HttpClientRequest request = await client.postUrl(uri); + request.headers.set('Content-Type', 'application/x-www-form-urlencoded'); + request.headers.set('cache-control', 'no-cache'); + + request.write(body); + HttpClientResponse result = await request.close(); + client.close(); + if (!(result.statusCode == 200 || result.statusCode == 302)) { + trace("mautic response: ${result.statusCode}"); + throw Exception("Network error, try again later!"); + } + } +} diff --git a/lib/util/enums.dart b/lib/util/enums.dart index 4277ff0..0660e94 100644 --- a/lib/util/enums.dart +++ b/lib/util/enums.dart @@ -57,7 +57,8 @@ enum TrackingEvent { training_plan_execute, training_plan_finished, training_plan_custom, - trial + trial, + feedback_email } T enumFromString(Iterable values, String value) { @@ -103,3 +104,11 @@ extension ExerciseTypeTrainingPlanStateExt on ExerciseTypeTrainingPlanState { bool equalsTo(ExerciseTypeTrainingPlanState state) => this.toString() == state.toString(); bool equalsStringTo(String state) => this.toStr() == state; } + +enum ExerciseSaveType { test, training, test_set } + +extension ExerciseSaveTypeExt on ExerciseSaveType { + String toStr() => this.toString().split(".").last; + bool equalsTo(ExerciseSaveType type) => this.toString() == type.toString(); + bool equalsStringTo(String type) => this.toStr() == type; +} diff --git a/lib/util/track.dart b/lib/util/track.dart index 5b0dd7a..46115b9 100644 --- a/lib/util/track.dart +++ b/lib/util/track.dart @@ -5,6 +5,7 @@ import 'package:aitrainer_app/service/tracking_service.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/model/tracking.dart' as model; import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flurry_data/flurry_data.dart'; import 'package:flutter_uxcam/flutter_uxcam.dart'; @@ -21,7 +22,7 @@ class Track with Logging { void track(TrackingEvent event, {String eventValue = ""}) { analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue}); if (!isInDebugMode) { - FlurryData.logEvent(event.toString()); + FlurryData.logEvent(event.enumToString()); // Smartlook.setGlobalEventProperty(event.toString(), eventValue, false); FlutterUxcam.logEventWithProperties(event.enumToString(), {"value": eventValue}); model.Tracking tracking = model.Tracking(); @@ -32,6 +33,7 @@ class Track with Logging { tracking.eventValue = eventValue; } tracking.dateAdd = DateTime.now(); + FirebaseMessaging.instance.subscribeToTopic(event.enumToString()); TrackingApi().saveTracking(tracking); } } diff --git a/lib/view/account.dart b/lib/view/account.dart index 72b46c3..fbc7c8d 100644 --- a/lib/view/account.dart +++ b/lib/view/account.dart @@ -65,7 +65,7 @@ class AccountPage extends StatelessWidget with Trans { return ListView(padding: EdgeInsets.only(top: 35), children: [ ListTile( leading: Common.badgedIcon(Colors.grey, Icons.perm_identity, "personalData"), //Icon(Icons.perm_identity), - subtitle: Text(t("Profile")), + subtitle: Text(t("Profile") + " " + t("and") + " " + t("Sport")), title: TextButton( child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(customerName, style: TextStyle(color: Colors.blue)), @@ -108,7 +108,7 @@ class AccountPage extends StatelessWidget with Trans { ), ListTile( leading: Common.badgedIcon(Colors.grey, Icons.perm_contact_cal, "FitnessLevel"), //Icon(Icons.perm_contact_cal), - subtitle: Text(t("Activity") + " " + t("and") + " " + t("Sport")), + subtitle: Text(t("Activity")), title: TextButton( child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(fitnessLevel, style: TextStyle(color: Colors.blue)), diff --git a/lib/view/customer_bodytype_animation.dart b/lib/view/customer_bodytype_animation.dart index ad90ae5..8a31e82 100644 --- a/lib/view/customer_bodytype_animation.dart +++ b/lib/view/customer_bodytype_animation.dart @@ -7,6 +7,7 @@ import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; +import 'package:aitrainer_app/widgets/app_bar_min.dart'; import 'package:aitrainer_app/widgets/dialog_html.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -34,9 +35,13 @@ class _CustomerBodyTypeAnimationPageState extends State with Trans { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } else if (state is CustomerSaveSuccess) { - Navigator.of(context).pop(); - Navigator.of(context).pushNamed("customerSexPage", arguments: changeBloc.customerRepository); + if (fulldata) { + Navigator.of(context).pop(); + } else { + Navigator.of(context).popAndPushNamed("customerSexPage", arguments: changeBloc.customerRepository); + } } }, builder: (context, state) { @@ -103,15 +103,15 @@ class _CustomerFitnessPageState extends State with Trans { changeBloc.add(CustomerSave()), } }, - backgroundColor: Color(0xffb4f500), + backgroundColor: Colors.orange[600], icon: Icon( CustomIcon.save, - color: Colors.black, + color: Colors.white, size: 26, ), label: Text( fulldata ? t("Save") : t("Next"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ), ), ); @@ -141,46 +141,19 @@ class _CustomerFitnessPageState extends State with Trans { SizedBox( height: h, ), - getButton("Beginner", "I am beginner", FitnessState.beginner), SizedBox( height: h, ), getButton("Intermediate", "I am intermediate", FitnessState.intermediate), - SizedBox( height: h, ), getButton("Advanced", "I am advanced", FitnessState.advanced), - SizedBox( height: h, ), getButton("Professional", "I am professional", FitnessState.professional), - - /* Divider(), - Text( - t("Your Primary Sport") + ":", - textAlign: TextAlign.center, - style: GoogleFonts.archivoBlack( - color: Colors.orange, - fontSize: 20, - ), - ), */ - //getSport(changeBloc), - /* Divider(), - ElevatedButton( - style: ElevatedButton.styleFrom( - onPrimary: Colors.white, - primary: Colors.orange, - ), - child: Text(fulldata ? t("Save") : t("Next")), - onPressed: () => { - changeBloc.add(CustomerSave()), - Navigator.of(context).pop(), - if (!fulldata) {Navigator.of(context).pushNamed("customerBodyTypePage", arguments: customerRepository)} - }, - ) */ ], ), ); @@ -233,98 +206,6 @@ class _CustomerFitnessPageState extends State with Trans { side: BorderSide(width: 4, color: Colors.white24), borderRadius: BorderRadius.circular(12), ); - //return return returnCode; } - - Widget getSport(CustomerChangeBloc bloc) { - Sport? selected = bloc.selectedSport; - return Container( - padding: EdgeInsets.only(left: 65, right: 65), - child: DropdownSearch( - dropdownSearchDecoration: InputDecoration( - contentPadding: EdgeInsets.only(left: 15, top: 5, bottom: 5), - labelText: t("Sport"), - labelStyle: GoogleFonts.inter(fontSize: 16, color: Colors.indigo), - //fillColor: Colors.black38, - filled: false, - border: OutlineInputBorder( - gapPadding: 2.0, - borderRadius: BorderRadius.circular(12.0), - borderSide: BorderSide(color: Colors.blue, width: 0.4), - ), - ), - mode: Mode.MENU, - compareFn: (Sport? i, Sport? s) { - if (i == null || s == null) { - return false; - } else { - return i.sportId == s.sportId; - } - }, - showSelectedItem: true, - selectedItem: selected, - itemAsString: (data) => t(data!.sportNameTranslation), - onChanged: (data) { - bloc.add(CustomerSportChange(sport: data!)); - }, - dropdownBuilder: _customDropDownItem, - popupItemBuilder: _customMenuBuilder, - popupBarrierColor: Colors.white10, - //popupBackgroundColor: Colors.yellow, - items: Cache().getSports(), - dropDownButton: Icon( - Icons.arrow_drop_down, - color: Colors.indigo, - ), - )); - //items: FitnessItem().toList())); - } - - Widget _customMenuBuilder(BuildContext context, Sport? sport, bool isSelected) { - return Container( - decoration: !isSelected - ? BoxDecoration(color: Colors.grey[300]) - : BoxDecoration( - border: Border.all(color: Colors.blue), - borderRadius: BorderRadius.circular(12), - color: Colors.grey[100], - ), - child: ListTile( - selected: isSelected, - title: Text( - t(sport!.sportNameTranslation), - style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]), - ), - subtitle: Text( - t(sport.name), - style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]), - ), - ), - ); - } - - Widget _customDropDownItem(BuildContext context, Sport? item, String itemDesignation) { - return Container( - child: (item == null) - ? ListTile( - contentPadding: EdgeInsets.all(0), - title: Text( - t("No item selected"), - style: GoogleFonts.inter(fontSize: 14, color: Colors.blue[600]), - ), - ) - : ListTile( - contentPadding: EdgeInsets.all(0), - title: Text( - t(item.sportNameTranslation), - style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]), - ), - subtitle: Text( - t(item.name), - style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]), - ), - ), - ); - } } diff --git a/lib/view/customer_goal_page.dart b/lib/view/customer_goal_page.dart index 2e00963..06c0fce 100644 --- a/lib/view/customer_goal_page.dart +++ b/lib/view/customer_goal_page.dart @@ -81,7 +81,7 @@ class _CustomerGoalPage extends State with Trans { customerRepository = CustomerRepository(); } PreferredSizeWidget _bar = AppBarMin( - back: false, + back: true, ); if (!fulldata) { _bar = AppBarProgress(max: 14, min: 0); @@ -107,7 +107,11 @@ class _CustomerGoalPage extends State with Trans { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } else if (state is CustomerSaveSuccess) { - Navigator.of(context).pushNamed("customerFitnessPage", arguments: changeBloc.customerRepository); + if (fulldata) { + Navigator.of(context).pop(); + } else { + Navigator.of(context).popAndPushNamed("customerFitnessPage", arguments: changeBloc.customerRepository); + } } }, builder: (context, state) { @@ -136,15 +140,15 @@ class _CustomerGoalPage extends State with Trans { changeBloc.add(CustomerSave()), } }, - backgroundColor: Color(0xffb4f500), + backgroundColor: Colors.orange[600], icon: Icon( CustomIcon.save, - color: Colors.black, + color: Colors.white, size: 26, ), label: Text( fulldata ? t("Save") : t("Next"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ), ), ); diff --git a/lib/view/customer_height_page.dart b/lib/view/customer_height_page.dart index b8d9410..afba844 100644 --- a/lib/view/customer_height_page.dart +++ b/lib/view/customer_height_page.dart @@ -64,8 +64,11 @@ class _CustomerHeightPageState extends State with Trans { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } else if (state is CustomerSaveSuccess) { - Navigator.of(context).pop(); - Navigator.of(context).pushNamed("registration", arguments: changeBloc.customerRepository); + if (fulldata) { + Navigator.of(context).pop(); + } else { + Navigator.of(context).popAndPushNamed("registration", arguments: changeBloc.customerRepository); + } } }, builder: (context, state) { @@ -76,15 +79,15 @@ class _CustomerHeightPageState extends State with Trans { )), floatingActionButton: FloatingActionButton.extended( onPressed: () => changeBloc.add(CustomerSaveHeight()), - backgroundColor: Color(0xffb4f500), + backgroundColor: Colors.orange[600], icon: Icon( CustomIcon.save, - color: Colors.black, + color: Colors.white, size: 26, ), label: Text( fulldata ? t("Save") : t("Finish"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ), ), ); diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart index 13bce29..5ce0475 100644 --- a/lib/view/customer_modify_page.dart +++ b/lib/view/customer_modify_page.dart @@ -3,7 +3,10 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; +import 'package:aitrainer_app/library/dropdown_search/dropdown_search.dart'; import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/sport.dart'; +import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar_min.dart'; @@ -385,10 +388,119 @@ class CustomerModifyPage extends StatelessWidget with Trans { }, ), Divider(), + Divider(), + Text( + t("Your Primary Sport") + ":", + textAlign: TextAlign.center, + style: GoogleFonts.archivoBlack( + color: Colors.orange, + fontSize: 20, + ), + ), + getSport(customerBloc), + Divider(), ], ), ), ), ); } + + Widget getSport(CustomerChangeBloc bloc) { + Sport? selected = bloc.selectedSport; + + return Container( + padding: EdgeInsets.only(left: 65, right: 65), + child: DropdownSearch( + dropdownSearchDecoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 15, top: 5, bottom: 5), + labelText: t("Sport"), + labelStyle: GoogleFonts.inter(fontSize: 16, color: Colors.indigo), + //fillColor: Colors.black38, + filled: false, + border: OutlineInputBorder( + gapPadding: 2.0, + borderRadius: BorderRadius.circular(12.0), + borderSide: BorderSide(color: Colors.blue, width: 0.4), + ), + ), + mode: Mode.MENU, + compareFn: (Sport? i, Sport? s) { + if (i == null || s == null) { + return false; + } else { + return i.sportId == s.sportId; + } + }, + showSelectedItem: true, + selectedItem: selected, + itemAsString: (data) => data!.nameTranslations[AppLanguage().appLocal.toString()] != null + ? data.nameTranslations[AppLanguage().appLocal.toString()]! + : data.name, + onChanged: (data) { + bloc.add(CustomerSportChange(sport: data!)); + }, + dropdownBuilder: _customDropDownItem, + popupItemBuilder: _customMenuBuilder, + popupBarrierColor: Colors.white10, + //popupBackgroundColor: Colors.yellow, + items: Cache().getSports(), + dropDownButton: Icon( + Icons.arrow_drop_down, + color: Colors.indigo, + ), + )); + //items: FitnessItem().toList())); + } + + Widget _customMenuBuilder(BuildContext context, Sport? sport, bool isSelected) { + return Container( + decoration: !isSelected + ? BoxDecoration(color: Colors.grey[300]) + : BoxDecoration( + border: Border.all(color: Colors.blue), + borderRadius: BorderRadius.circular(12), + color: Colors.grey[100], + ), + child: ListTile( + selected: isSelected, + title: Text( + sport!.nameTranslations[AppLanguage().appLocal.toString()] != null + ? sport.nameTranslations[AppLanguage().appLocal.toString()]! + : sport.name, + style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]), + ), + subtitle: Text( + sport.name, + style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]), + ), + ), + ); + } + + Widget _customDropDownItem(BuildContext context, Sport? item, String itemDesignation) { + return Container( + child: (item == null) + ? ListTile( + contentPadding: EdgeInsets.all(0), + title: Text( + t("No item selected"), + style: GoogleFonts.inter(fontSize: 14, color: Colors.blue[600]), + ), + ) + : ListTile( + contentPadding: EdgeInsets.all(0), + title: Text( + item.nameTranslations[AppLanguage().appLocal.toString()] != null + ? item.nameTranslations[AppLanguage().appLocal.toString()]! + : item.name, + style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]), + ), + subtitle: Text( + item.name, + style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]), + ), + ), + ); + } } diff --git a/lib/view/customer_sex_page.dart b/lib/view/customer_sex_page.dart index 5cef9d8..55a9178 100644 --- a/lib/view/customer_sex_page.dart +++ b/lib/view/customer_sex_page.dart @@ -64,8 +64,11 @@ class _CustomerSexPageState extends State with Trans { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } else if (state is CustomerSaveSuccess) { - Navigator.of(context).pop(); - Navigator.of(context).pushNamed("customerWeightPage", arguments: changeBloc.customerRepository); + if (fulldata) { + Navigator.of(context).pop(); + } else { + Navigator.of(context).popAndPushNamed("customerWeightPage", arguments: changeBloc.customerRepository); + } } }, builder: (context, state) { @@ -84,15 +87,15 @@ class _CustomerSexPageState extends State with Trans { onPressed: () => { changeBloc.add(CustomerSaveSex()), }, - backgroundColor: Color(0xffb4f500), + backgroundColor: Colors.orange[600], icon: Icon( CustomIcon.save, - color: Colors.black, + color: Colors.white, size: 26, ), label: Text( fulldata ? t("Save") : t("Next"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ), ), ); diff --git a/lib/view/customer_weight_page.dart b/lib/view/customer_weight_page.dart index 25ee9cd..1f5bd2d 100644 --- a/lib/view/customer_weight_page.dart +++ b/lib/view/customer_weight_page.dart @@ -1,7 +1,6 @@ import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; -import 'package:aitrainer_app/util/app_localization.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar_min.dart'; @@ -65,8 +64,11 @@ class _CustomerWeightPageState extends State with Trans { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); } else if (state is CustomerSaveSuccess) { - Navigator.of(context).pop(); - Navigator.of(context).pushNamed("customerHeightPage", arguments: changeBloc.customerRepository); + if (fulldata) { + Navigator.of(context).pop(); + } else { + Navigator.of(context).popAndPushNamed("customerHeightPage", arguments: changeBloc.customerRepository); + } } }, builder: (context, state) { @@ -85,15 +87,15 @@ class _CustomerWeightPageState extends State with Trans { onPressed: () => { changeBloc.add(CustomerSaveWeight()), }, - backgroundColor: Color(0xffb4f500), + backgroundColor: Colors.orange[600], icon: Icon( CustomIcon.save, - color: Colors.black, + color: Colors.white, size: 26, ), label: Text( fulldata ? t("Save") : t("Next"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ), ), ); diff --git a/lib/view/customer_welcome_page.dart b/lib/view/customer_welcome_page.dart index 3f37c60..cf9b164 100644 --- a/lib/view/customer_welcome_page.dart +++ b/lib/view/customer_welcome_page.dart @@ -1,4 +1,3 @@ -import 'package:aitrainer_app/library/button_animations.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar_min.dart'; import 'package:flutter/material.dart'; diff --git a/lib/view/exercise_control_page.dart b/lib/view/exercise_control_page.dart index 9b13066..5c42e3f 100644 --- a/lib/view/exercise_control_page.dart +++ b/lib/view/exercise_control_page.dart @@ -231,7 +231,7 @@ class _ExerciseControlPage extends State with Trans { Divider(), numberPickForm(exerciseBloc, 3), SizedBox( - height: 80, + height: 120, ) ]), )), @@ -263,12 +263,10 @@ class _ExerciseControlPage extends State with Trans { } Widget numberPickForm(ExerciseControlBloc exerciseBloc, int step) { - final String strTimes = step == 2 ? exerciseBloc.origQuantity.toStringAsFixed(0) : "max."; - //print("step $step, quantity ${exerciseBloc.origQuantity}"); + final String strTimes = step == 2 ? exerciseBloc.controlList[step - 1].quantity!.toStringAsFixed(0) : "max."; String title = (step + 1).toString() + "/4 " + t("Control Exercise:"); LinkedHashMap args = LinkedHashMap(); - final TutorialBloc tutorialBloc = BlocProvider.of(context); List listWidgets = [ Text( @@ -294,7 +292,7 @@ class _ExerciseControlPage extends State with Trans { children: [ TextSpan(text: t("Please repeat with ")), TextSpan( - text: exerciseBloc.unitQuantity.toStringAsFixed(0) + + text: exerciseBloc.controlList[step - 1].unitQuantity!.toStringAsFixed(0) + " " + exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!, style: GoogleFonts.inter( @@ -324,7 +322,7 @@ class _ExerciseControlPage extends State with Trans { NumberPickerWidget( minValue: 0, maxValue: 200, - initalValue: exerciseBloc.origQuantity.round(), + initalValue: exerciseBloc.controlList[step - 1].quantity!.round(), unit: t("reps"), color: Colors.yellow[50]!, onChange: (value) => {exerciseBloc.add(ExerciseControlQuantityChange(quantity: value.toDouble(), step: step))}), @@ -335,12 +333,6 @@ class _ExerciseControlPage extends State with Trans { onSurface: Colors.blueAccent, ), onPressed: () { - if (tutorialBloc.isActive) { - if (!tutorialBloc.checkAction("Save")) { - return; - } - } - exerciseBloc.add(ExerciseControlSubmit(step: step)); if (step == 3) { Navigator.of(context).pop(); @@ -354,7 +346,7 @@ class _ExerciseControlPage extends State with Trans { children: [ Image.asset('asset/icon/gomb_orange_c.png', width: 140, height: 60), Text( - t("Save"), + t("Done!"), style: TextStyle(fontSize: 16, color: Colors.white), ), ], @@ -436,9 +428,9 @@ class _UnitQuantityControlState extends State with Trans { height: 20, ), NumberPickerWidget( - minValue: (widget.exerciseBloc.unitQuantity - 30).round(), - maxValue: (widget.exerciseBloc.unitQuantity + 30).round(), - initalValue: widget.exerciseBloc.unitQuantity.round(), + minValue: (widget.exerciseBloc.controlList[widget.step - 1].unitQuantity! - 30).round(), + maxValue: (widget.exerciseBloc.controlList[widget.step - 1].unitQuantity! + 30).round(), + initalValue: widget.exerciseBloc.controlList[widget.step - 1].unitQuantity!.round(), unit: t("kg"), color: Colors.yellow[50]!, onChange: (value) => { @@ -452,9 +444,9 @@ class _UnitQuantityControlState extends State with Trans { onTap: () => { if (changedValue == null) { - changedValue = widget.exerciseBloc.unitQuantity, + changedValue = widget.exerciseBloc.controlList[widget.step - 1].unitQuantity, }, - //print("Changed new value $changedValue"), + print("Changed new value $changedValue, step ${widget.step}"), widget.exerciseBloc.add(ExerciseControlUnitQuantityChange(quantity: changedValue!.toDouble(), step: widget.step)), Navigator.of(context).pop(), }, diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart index 65a8516..ba9cf0c 100644 --- a/lib/view/exercise_new_page.dart +++ b/lib/view/exercise_new_page.dart @@ -8,6 +8,7 @@ import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; +import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/service/logging.dart'; @@ -17,6 +18,7 @@ import 'package:aitrainer_app/widgets/bmi_widget.dart'; import 'package:aitrainer_app/widgets/bmr_widget.dart'; import 'package:aitrainer_app/widgets/dialog_common.dart'; import 'package:aitrainer_app/widgets/exercise_save.dart'; +import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:aitrainer_app/widgets/size_widget.dart'; import 'package:aitrainer_app/widgets/tutorial_widget.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -55,7 +57,9 @@ class _ExerciseNewPageState extends State with Trans, Logging { title: t("Warning"), descriptions: t(state.message), text: "OK", - onTap: () => Navigator.of(context).pushNamed("login"), + onTap: () => { + Navigator.of(context).pop(), + }, onCancel: () => { Navigator.of(context).pop(), }, @@ -114,10 +118,12 @@ class _ExerciseNewPageState extends State with Trans, Logging { return SizeWidget(exerciseBloc: exerciseBloc); } + WorkoutMenuTree? workoutTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseType.exerciseTypeId); + return Scaffold( appBar: AppBarNav(depth: 1), body: Container( - padding: EdgeInsets.only(top: 10, left: 20, right: 20), + padding: EdgeInsets.only(), decoration: BoxDecoration( image: DecorationImage( image: AssetImage('asset/image/WT_black_background.jpg'), @@ -126,37 +132,38 @@ class _ExerciseNewPageState extends State with Trans, Logging { ), ), child: ExerciseSave( - exerciseName: exerciseBloc.exerciseRepository.exerciseType!.nameTranslation, - exerciseDescription: exerciseBloc.exerciseRepository.exerciseType!.descriptionTranslation, - exerciseTask: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null - ? t("Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!") - : t("Please repeat as much times as you can! MAXIMIZE it!"), - unit: exerciseBloc.exerciseRepository.exerciseType!.unit, - unitQuantityUnit: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit, - hasUnitQuantity: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null, - onQuantityChanged: (value) { - exerciseBloc.add(ExerciseNewQuantityChange(quantity: value)); - }, - onUnitQuantityChanged: (value) => exerciseBloc.add(ExerciseNewQuantityUnitChange(quantity: value)), - //onSubmit: () => confirmationDialog(exerciseBloc, menuBloc), - exerciseTypeId: exerciseType.exerciseTypeId, - )), + exerciseName: exerciseBloc.exerciseRepository.exerciseType!.nameTranslation, + exerciseDescription: exerciseBloc.exerciseRepository.exerciseType!.descriptionTranslation, + exerciseTask: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null + ? t("Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!") + : t("Please repeat as much times as you can! MAXIMIZE it!"), + unit: exerciseBloc.exerciseRepository.exerciseType!.unit, + unitQuantityUnit: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit, + hasUnitQuantity: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null, + onQuantityChanged: (value) { + exerciseBloc.add(ExerciseNewQuantityChange(quantity: value)); + }, + onUnitQuantityChanged: (value) => exerciseBloc.add(ExerciseNewQuantityUnitChange(quantity: value)), + //onSubmit: () => confirmationDialog(exerciseBloc, menuBloc), + exerciseTypeId: exerciseType.exerciseTypeId, + tip: ActivityDone.exerciseSaveTestTip, + menuImage: MenuImage( + imageName: workoutTree!.imageName, + workoutTreeId: workoutTree.id, + radius: 0, + ))), floatingActionButton: FloatingActionButton.extended( onPressed: () => save(exerciseBloc, menuBloc), - backgroundColor: Colors.orange[800], + backgroundColor: Colors.orange[600], icon: Icon( - CustomIcon.save, + CustomIcon.ok_circled, size: 20, ), label: Text( - t("Save"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 12), + t("Done!"), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16), ), ), - /* bottomNavigationBar: BottomBarMultipleExercises( - isSet: false, - exerciseTypeId: exerciseType.exerciseTypeId, - ), */ ); } @@ -195,6 +202,11 @@ class _ExerciseNewPageState extends State with Trans, Logging { return; } + if (bloc.quantity == -1 || bloc.unitQuantity == -1) { + bloc.add(ExerciseNewAddError(message: "Please type in a real number!")); + return; + } + String quantity = bloc.exerciseRepository.exercise!.quantity! % 1 == 0 ? bloc.exerciseRepository.exercise!.quantity!.round().toString() : bloc.exerciseRepository.exercise!.quantity!.toString(); @@ -209,9 +221,9 @@ class _ExerciseNewPageState extends State with Trans, Logging { // ignore: close_sinks final TestSetExecuteBloc executeBloc = BlocProvider.of(context); - final question = bloc.exerciseRepository.exercise!.quantity! == 12.0 - ? "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?" - : "Do you save this exercise with these parameters?"; +/* final question = bloc.exerciseRepository.exercise!.quantity! == 12.0 + ? "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?" */ + final question = "Do you save this exercise with these parameters?"; showCupertinoDialog( useRootNavigator: true, diff --git a/lib/view/mydevelopment_body_page.dart b/lib/view/mydevelopment_body_page.dart index dca6181..f866a83 100644 --- a/lib/view/mydevelopment_body_page.dart +++ b/lib/view/mydevelopment_body_page.dart @@ -37,7 +37,7 @@ class _MyDevelopmentBodyPage extends State with Trans, Co setContext(context); return DialogPremium( unlocked: Cache().hasPurchased, - unlockRound: 2, + unlockRound: 4, function: "My Whole Body Development", unlockedText: null, onTap: () => {Navigator.of(context).pop(), Navigator.of(context).pop()}, diff --git a/lib/view/mydevelopment_page.dart b/lib/view/mydevelopment_page.dart index 5a55dc3..8553ccf 100644 --- a/lib/view/mydevelopment_page.dart +++ b/lib/view/mydevelopment_page.dart @@ -163,7 +163,7 @@ class _MyDevelopmentPage extends State with Trans { builder: (BuildContext context) { return DialogPremium( unlocked: Cache().hasPurchased, - unlockRound: 3, + unlockRound: 12, function: "Predictions", unlockedText: null, onTap: () => {Navigator.of(context).pop()}, diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 1f99566..532dd6c 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/bloc/login/login_bloc.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/training_plan_repository.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; @@ -23,12 +24,23 @@ class RegistrationPage extends StatelessWidget with Trans { @override Widget build(BuildContext context) { final accountBloc = BlocProvider.of(context); + + CustomerRepository? customerRepository; + if (ModalRoute.of(context) != null && ModalRoute.of(context)!.settings.arguments != null) { + customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; + print("CustomerFitness reg ${customerRepository.customer!.toJson()}"); + } + setContext(context); return Scaffold( appBar: AppBarMin(), body: BlocProvider( - create: (context) => - LoginBloc(userRepository: UserRepository(), accountBloc: accountBloc, context: context, isRegistration: true), + create: (context) => LoginBloc( + userRepository: UserRepository(), + accountBloc: accountBloc, + context: context, + isRegistration: true, + customerRepository: customerRepository), child: BlocConsumer(listener: (context, state) { if (state is LoginError) { ScaffoldMessenger.of(context).showSnackBar( @@ -67,7 +79,9 @@ class RegistrationPage extends StatelessWidget with Trans { } }, builder: (context, state) { final loginBloc = BlocProvider.of(context); - + if (customerRepository != null) { + print("Customer data ${customerRepository.customer!.toJson()}"); + } return ModalProgressHUD( child: loadForm(loginBloc, accountBloc), inAsyncCall: state is LoginLoading, diff --git a/lib/view/settings.dart b/lib/view/settings.dart index 9ccd6a0..98d01fd 100644 --- a/lib/view/settings.dart +++ b/lib/view/settings.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart'; @@ -19,8 +17,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:toggle_switch/toggle_switch.dart'; -import 'package:flutter/services.dart'; -import 'dart:async'; +import 'package:mailto/mailto.dart'; +import 'package:url_launcher/url_launcher.dart'; // ignore: must_be_immutable class SettingsPage extends StatelessWidget with Trans { @@ -87,7 +85,7 @@ class SettingsPage extends StatelessWidget with Trans { //getTuturialBasic(settingsBloc), getTermsOfUse(), getPrivacy(), - getFaq(), + mailTo(), getVersion(), //getDevice(settingsBloc), ]); @@ -264,25 +262,40 @@ class SettingsPage extends StatelessWidget with Trans { ); } - Future _printAndCopy(String cmd) async { - print(cmd); - - await Clipboard.setData(ClipboardData(text: cmd)); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Copied to Clipboard')), + ListTile mailTo() { + return ListTile( + leading: Icon(CustomIcon.mail_1), + title: TextButton( + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text("Feedback email", style: TextStyle(color: Colors.indigo)), + Icon( + Icons.arrow_forward_ios, + color: Colors.indigo, + ), + ]), + style: TextButton.styleFrom( + backgroundColor: Colors.white70, + onSurface: Colors.grey, + ), + onPressed: () => { + launchMailto(), + Track().track(TrackingEvent.feedback_email), + }, + ), ); } - void link1() { - String? cmd; - String cmdSuffix; - if (Platform.isIOS) { - cmd = '/usr/bin/xcrun simctl openurl booted'; - } else if (Platform.isAndroid) { - cmd = '\$ANDROID_HOME/platform-tools/adb shell \'am start' - ' -a android.intent.action.VIEW' - ' -c android.intent.category.BROWSABLE -d'; - cmdSuffix = "'"; - } + launchMailto() async { + String from = Cache().userLoggedIn == null ? "" : Cache().userLoggedIn!.customerId!.toStringAsFixed(0); + final mailtoLink = Mailto( + to: ['service@workouttest.com'], + subject: 'Feedback from app: $from', + body: '', + ); + // Convert the Mailto instance into a string. + // Use either Dart's string interpolation + // or the toString() method. + print("Mailto: $mailtoLink"); + await launch('$mailtoLink'); } } diff --git a/lib/view/test_set_new.dart b/lib/view/test_set_new.dart index a8b246b..ce17367 100644 --- a/lib/view/test_set_new.dart +++ b/lib/view/test_set_new.dart @@ -3,13 +3,16 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart'; import 'package:aitrainer_app/bloc/test_set_new/test_set_new_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/model/exercise_type.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/bottom_bar_multiple_exercises.dart'; import 'package:aitrainer_app/widgets/exercise_save.dart'; +import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -29,7 +32,6 @@ class TestSetNew extends StatelessWidget with Trans { return Scaffold( appBar: AppBarNav(depth: 1), body: Container( - padding: EdgeInsets.all(20), decoration: BoxDecoration( image: DecorationImage( image: AssetImage('asset/image/WT_black_background.jpg'), @@ -77,7 +79,7 @@ class TestSetNew extends StatelessWidget with Trans { backgroundColor: Colors.orange[800], icon: Icon(CustomIcon.save), label: Text( - t("Save"), + t("Done"), style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16), ), ), @@ -89,6 +91,8 @@ class TestSetNew extends StatelessWidget with Trans { } Widget getExercises(TestSetNewBloc bloc) { + WorkoutMenuTree? workoutTree = + bloc.executeBloc.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(bloc.exerciseType.exerciseTypeId); return ExerciseSave( exerciseName: bloc.exerciseType.nameTranslation, exerciseDescription: bloc.exerciseType.descriptionTranslation, @@ -103,6 +107,12 @@ class TestSetNew extends StatelessWidget with Trans { }, onUnitQuantityChanged: (value) => bloc.add(TestSetNewChangeQuantityUnit(quantity: value)), exerciseTypeId: bloc.exerciseType.exerciseTypeId, + tip: ActivityDone.exerciseSaveTestsetTip, + menuImage: MenuImage( + imageName: workoutTree!.imageName, + workoutTreeId: workoutTree.id, + radius: 0, + ), /* onSubmit: () { Navigator.of(context).pop(); bloc.add(TestSetNewSubmit()); diff --git a/lib/view/training_plan_activate_page.dart b/lib/view/training_plan_activate_page.dart index 5f146b1..cca0a8a 100644 --- a/lib/view/training_plan_activate_page.dart +++ b/lib/view/training_plan_activate_page.dart @@ -6,6 +6,7 @@ import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/training_plan.dart'; +import 'package:aitrainer_app/model/training_plan_detail.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/util/trans.dart'; @@ -58,7 +59,7 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { title: t("Warning"), descriptions: t(state.message), text: "OK", - onTap: () => Navigator.of(context).pushNamed("login"), + onTap: () => Navigator.of(context).pop(), onCancel: () => { Navigator.of(context).pop(), }, @@ -197,21 +198,45 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { icon: Icon(Icons.list_sharp), color: Colors.blue[800], ), - children: _getChildList(element, bloc), + children: _getDays(element, bloc), ))); }); return listWidget; } - List _getChildList(TrainingPlan plan, TrainingPlanBloc bloc) { + List _getDays(TrainingPlan plan, TrainingPlanBloc bloc) { + List listWidget = []; + + bloc.setMyTrainingPlan(plan); + bloc.activateTrainingPlanDays(); + bloc.trainingPlanDayNames.forEach((value) { + listWidget.add(Container( + margin: const EdgeInsets.only(left: 4.0), + child: TreeViewChild( + startExpanded: true, + parent: TreeviewParentWidget( + text: " * " + t("Training Day") + ": " + value, + fontSize: 16, + color: Colors.white, + icon: Icon(Icons.view_day), + backgroundColor: Colors.white24, + ), + children: _getChildList(plan, bloc, value), + ))); + }); + + return listWidget; + } + + List _getChildList(TrainingPlan plan, TrainingPlanBloc bloc, String dayName) { List list = []; bool restricted = (!plan.free && !Cache().hasPurchased); list.add(Card( margin: EdgeInsets.only(left: 10, top: 5), - color: Colors.white60, + color: Colors.white24, child: Container( padding: EdgeInsets.only(left: 10, right: 10), child: Column(children: [ @@ -262,7 +287,7 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { ), )) : Offstage(), - getPlanDetails(plan, bloc), + getPlanDetails(plan, bloc, dayName), ElevatedButton( style: ElevatedButton.styleFrom( onPrimary: Colors.white, @@ -371,13 +396,17 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { } } - Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc) { + Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc, String dayName) { return SfDataGrid( headerRowHeight: 30, - rowHeight: 60, + rowHeight: 70, + columnWidthMode: ColumnWidthMode.lastColumnFill, + defaultColumnWidth: 50, source: TrainingPlanDetailSource( plan: plan, menuBloc: bloc.menuBloc, + bloc: bloc, + dayName: dayName, onWeightTap: () => { showDialog( context: context, @@ -438,18 +467,23 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { columns: [ GridTextColumn( columnWidthMode: ColumnWidthMode.lastColumnFill, - maximumWidth: 160, + maximumWidth: 130, columnName: 'exerciseImage', label: Container( - color: Colors.green[50], + color: Colors.black38, padding: EdgeInsets.only(left: 8.0), alignment: Alignment.centerLeft, child: Text( t('Exercise'), + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.bold, + ), textAlign: TextAlign.start, overflow: TextOverflow.ellipsis, ))), GridTextColumn( + maximumWidth: 0, visible: false, columnName: 'exerciseName', label: Container( @@ -462,47 +496,40 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { overflow: TextOverflow.ellipsis, ))), GridTextColumn( - maximumWidth: 40, - columnName: 'set', + maximumWidth: 60, + columnName: 'Set', label: Container( - color: Colors.green[50], + color: Colors.black38, padding: EdgeInsets.symmetric(horizontal: 2.0), alignment: Alignment.centerLeft, child: Text( t('Set'), + style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ))), GridTextColumn( - maximumWidth: 50, - columnName: 'repeats', + maximumWidth: 100, + columnWidthMode: ColumnWidthMode.fill, + columnName: 'Repeats', label: Container( - color: Colors.green[50], + color: Colors.black38, padding: EdgeInsets.symmetric(horizontal: 2.0), alignment: Alignment.centerLeft, child: Text( - t('Reps'), + t('Repeats'), + style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ))), GridTextColumn( maximumWidth: 60, - columnName: 'weight', + columnName: 'Weight', label: Container( - color: Colors.green[50], + color: Colors.black38, padding: EdgeInsets.symmetric(horizontal: 2.0), alignment: Alignment.centerLeft, child: Text( - t('Weight'), - overflow: TextOverflow.ellipsis, - ))), - GridTextColumn( - maximumWidth: 50, - columnName: 'day', - label: Container( - color: Colors.green[50], - padding: EdgeInsets.symmetric(horizontal: 8.0), - alignment: Alignment.centerLeft, - child: Text( - t('Day'), + t('Weight_'), + style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ))), ], @@ -512,19 +539,25 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { class TrainingPlanDetailSource extends DataGridSource { final TrainingPlan plan; + final TrainingPlanBloc bloc; final MenuBloc menuBloc; + final dayName; final VoidCallback onDropsetTap; final VoidCallback onWeightTap; final VoidCallback onRepeatTap; TrainingPlanDetailSource({ required this.plan, required this.menuBloc, + required this.bloc, + required this.dayName, required this.onDropsetTap, required this.onWeightTap, required this.onRepeatTap, }) { if (plan.details != null) { - dataGridRows = plan.details!.map((dataGridRow) { + List details = bloc.trainingPlanDetailSummary(plan, dayName); + + dataGridRows = details.map((dataGridRow) { WorkoutMenuTree? menuTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(dataGridRow.exerciseTypeId); if (menuTree == null) { return DataGridRow(cells: []); @@ -539,9 +572,8 @@ class TrainingPlanDetailSource extends DataGridSource { )), DataGridCell(columnName: 'exerciseName', value: menuTree.name), DataGridCell(columnName: 'set', value: dataGridRow.set), - DataGridCell(columnName: 'reps', value: dataGridRow.repeats), + DataGridCell(columnName: 'reps', value: dataGridRow.summary!), DataGridCell(columnName: 'weight', value: dataGridRow.weight), - DataGridCell(columnName: 'day', value: dataGridRow.day), ]); }).toList(); } @@ -557,9 +589,9 @@ class TrainingPlanDetailSource extends DataGridSource { if (row.getCells().isEmpty) { return null; } - String name = row.getCells()[1].value; + String name = row.getCells()[1].value.toString(); return DataGridRowAdapter( - color: Colors.white60, + color: Colors.white12, cells: row.getCells().map((dataGridCell) { return Container( alignment: dataGridCell.columnName == "exerciseImage" ? Alignment.centerLeft : Alignment.centerLeft, @@ -573,8 +605,7 @@ class TrainingPlanDetailSource extends DataGridSource { name, maxLines: 3, overflow: TextOverflow.ellipsis, - style: - GoogleFonts.inter(fontSize: 10, color: Colors.yellow[600], fontWeight: FontWeight.bold, shadows: [ + style: GoogleFonts.inter(fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, shadows: [ Shadow( offset: Offset(2.0, 2.0), blurRadius: 6.0, @@ -590,7 +621,7 @@ class TrainingPlanDetailSource extends DataGridSource { }, child: Icon( CustomIcon.question_circle, - color: Colors.indigo[300], + color: Colors.white, )) : dataGridCell.columnName == "weight" && dataGridCell.value == -3 ? GestureDetector( @@ -608,12 +639,12 @@ class TrainingPlanDetailSource extends DataGridSource { }, child: Icon( CustomIcon.question_circle, - color: Colors.indigo[600], + color: Colors.white, )) : Text(dataGridCell.value.toString(), style: GoogleFonts.inter( fontSize: 14, - color: Colors.indigo, + color: Colors.white, fontWeight: FontWeight.bold, ))); }).toList()); diff --git a/lib/view/training_plan_execute.dart b/lib/view/training_plan_execute.dart index 7ad2875..be0b6b3 100644 --- a/lib/view/training_plan_execute.dart +++ b/lib/view/training_plan_execute.dart @@ -45,8 +45,20 @@ class _TrainingPlanExecuteState extends State with Trans { ), child: BlocConsumer(listener: (context, state) { if (state is TrainingPlanError) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + showDialog( + context: context, + builder: (BuildContext context) { + return DialogCommon( + warning: true, + title: t("Warning"), + descriptions: t(state.message), + text: "OK", + onTap: () => Navigator.of(context).pop(), + onCancel: () => { + Navigator.of(context).pop(), + }, + ); + }); } else if (state is TrainingPlanDayFinished) { bloc.celebrating = false; final HashMap args = HashMap(); @@ -368,7 +380,7 @@ class _ExerciseListState extends State with Trans { bloc.getMyPlan()!.days[widget.dayName] != null && bloc.getMyPlan()!.days[widget.dayName]!.isNotEmpty) { bloc.getMyPlan()!.days[widget.dayName]!.forEach((element) { - if (prev != null && prev!.exerciseTypeId != element.exerciseTypeId) { + if (prev == null || (prev != null && prev!.exerciseTypeId != element.exerciseTypeId)) { tiles.add(GestureDetector( onTap: () => bloc.getNext() != null ? executeExercise(bloc, bloc.getNext()!, context) : Navigator.of(context).pushNamed('home'), diff --git a/lib/view/training_plan_execute_page.dart b/lib/view/training_plan_execute_page.dart deleted file mode 100644 index 0f4bdb2..0000000 --- a/lib/view/training_plan_execute_page.dart +++ /dev/null @@ -1,914 +0,0 @@ -import 'dart:collection'; - -import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart'; -import 'package:aitrainer_app/library/custom_icon_icons.dart'; -import 'package:aitrainer_app/model/customer_training_plan_details.dart'; -import 'package:aitrainer_app/model/exercise_plan_detail.dart'; -import 'package:aitrainer_app/util/app_localization.dart'; -import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar.dart'; -import 'package:aitrainer_app/widgets/dialog_common.dart'; -import 'package:aitrainer_app/widgets/dialog_html.dart'; -import 'package:aitrainer_app/widgets/menu_image.dart'; -import 'package:aitrainer_app/widgets/weight_control.dart'; -import 'package:badges/badges.dart'; -import 'package:extended_tabs/extended_tabs.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:timeline_tile/timeline_tile.dart'; - -// ignore: must_be_immutable -class TrainingPlanExecutePage extends StatefulWidget { - @override - _TrainingPlanExecutePageState createState() => _TrainingPlanExecutePageState(); -} - -class _TrainingPlanExecutePageState extends State with Trans { - final scrollController = ScrollController(); - TrainingPlanBloc? bloc; - - @override - Widget build(BuildContext context) { - final HashMap args = HashMap(); - bloc = BlocProvider.of(context); - bloc!.activateDays(); - setContext(context); - return Scaffold( - appBar: AppBarNav(depth: 0), - body: Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_black_background.jpg'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), - ), - child: BlocConsumer(listener: (context, state) { - if (state is TrainingPlanError) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); - } else if (state is TrainingPlanDayFinished) { - bloc!.celebrating = false; - args["bloc"] = bloc; - args["day"] = bloc!.dayNames[bloc!.activeDayIndex]; - Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args); - } else if (state is TrainingPlanDayReadyToRestart) { - if (!bloc!.celebrating) { - showCupertinoDialog( - useRootNavigator: true, - context: context, - builder: (_) => CupertinoAlertDialog( - title: Text(t("The training is finished")), - content: Column(children: [Divider(), Text(t("Do you want to restart, or select a new Training Plan?"))]), - actions: [ - TextButton( - child: Text(t("New Training Plan"), textAlign: TextAlign.center), - onPressed: () => { - Navigator.pop(context), - Navigator.of(context).popAndPushNamed('myTrainingPlans'), - bloc!.restarting = false, - }), - TextButton( - child: Text(t("Restart")), - onPressed: () { - bloc!.restart(); - Navigator.pop(context); - Navigator.of(context).popAndPushNamed('home'); - }, - ) - ], - )); - } - } - }, builder: (context, state) { - return ModalProgressHUD( - child: ExerciseTabs(bloc: bloc!), - inAsyncCall: state is TrainingPlanLoading, - opacity: 0.5, - color: Colors.black54, - progressIndicator: CircularProgressIndicator(), - ); - }), - ), - floatingActionButton: FloatingActionButton.extended( - onPressed: () => { - args["bloc"] = bloc, - args["day"] = bloc!.dayNames[bloc!.activeDayIndex], - bloc!.getNext() != null - ? _ExerciseListState.executeExercise(bloc!, bloc!.getNext()!, context) - : Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args), - }, - backgroundColor: Colors.orange[800], - icon: Icon(CustomIcon.weight_hanging), - label: Text( - t("Training") + "!", - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16), - ), - ), - ); - } -} - -class ExerciseTabs extends StatefulWidget { - final TrainingPlanBloc bloc; - ExerciseTabs({required this.bloc}); - @override - _ExerciseTabs createState() => _ExerciseTabs(); -} - -class _ExerciseTabs extends State with TickerProviderStateMixin { - late TabController tabController; - - @override - void initState() { - super.initState(); - print("init TAB ${widget.bloc.dayNames.length} index ${widget.bloc.activeDayIndex}"); - tabController = TabController(length: widget.bloc.dayNames.length, vsync: this); - tabController.animateTo(widget.bloc.activeDayIndex, duration: Duration(milliseconds: 300)); - } - - @override - void dispose() { - tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return getTabs(widget.bloc); - } - - Widget getTabs(TrainingPlanBloc bloc) { - return Column(children: [ - ExtendedTabBar( - indicator: BoxDecoration( - color: Colors.black87, - border: Border( - bottom: BorderSide(width: 4.0, color: Colors.blue), - top: BorderSide(width: 4.0, color: Colors.blue), - )), - labelPadding: EdgeInsets.only(left: 5, right: 5), - tabs: getTabNames(), - controller: tabController, - onTap: (index) => bloc.activeDayIndex = index, - ), - Expanded( - child: ExtendedTabBarView( - children: getExerciseLists(), - controller: tabController, - - /// if link is true and current tabbarview over scroll, - /// it will check and scroll ancestor or child tabbarView. - link: true, - - /// cache page count - /// default is 0. - /// if cacheExtent is 1, it has two pages in cache - /// null is infinity, it will cache all pages - cacheExtent: 0, - )), - ]); - } - - List getTabNames() { - List tabs = []; - widget.bloc.dayNames.forEach((element) { - final Widget widget = Container( - //height: 50, - padding: EdgeInsets.only(top: 2, left: 5, right: 5, bottom: 2), - color: Colors.white24, - child: RichText( - textScaleFactor: 0.8, - text: TextSpan( - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - children: [ - TextSpan( - text: AppLocalizations.of(context)!.translate("Training Day") + ": \n", - style: GoogleFonts.inter( - fontSize: 14, - color: Colors.white, - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - )), - TextSpan( - text: element, - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.yellow[400], - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - )), - ]))); - - tabs.add(Tab(child: widget)); - }); - return tabs; - } - - List getExerciseLists() { - List list = []; - widget.bloc.dayNames.forEach((element) { - list.add(ExerciseList(bloc: widget.bloc, dayName: element)); - }); - return list; - } -} - -class ExerciseList extends StatefulWidget { - final TrainingPlanBloc bloc; - final String dayName; - ExerciseList({required this.bloc, required this.dayName}); - - @override - _ExerciseListState createState() => _ExerciseListState(); -} - -class _ExerciseListState extends State with Trans { - final scrollController = ScrollController(); - double offset = 5; - - @override - void initState() { - WidgetsBinding.instance!.addPostFrameCallback((_) { - animate(); - }); - super.initState(); - } - - @override - void didUpdateWidget(ExerciseList page) { - super.didUpdateWidget(page); - WidgetsBinding.instance!.addPostFrameCallback((_) { - animate(); - }); - } - - void animate() { - offset = widget.bloc.getOffset(); - if (scrollController.hasClients) { - scrollController.animateTo(offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn); - } - } - - @override - void dispose() { - scrollController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - setContext(context); - return CustomScrollView(controller: scrollController, slivers: [ - SliverList(delegate: SliverChildListDelegate(getTiles(widget.bloc))), - ]); - } - - List getTiles(TrainingPlanBloc bloc) { - List tiles = []; - tiles.add(getStartTile(bloc)); - tiles.addAll(getExerciseTiles(bloc, context)); - if (bloc.getMyPlan() != null) tiles.add(getEndTile()); - return tiles; - } - - Widget getStartTile(TrainingPlanBloc bloc) { - String startText = ""; - String explainingText = ""; - if (null == bloc.getMyPlan()) { - startText = t("No Active Training Plan"); - explainingText = t("Please select one in the Training menu, or create your custom plan"); - } else { - startText = bloc.isStarted() ? t("Continue your training") : t("Start your training"); - explainingText = bloc.getMyPlan()!.name != null ? bloc.getMyPlan()!.name! : ""; - } - - return TimelineTile( - alignment: TimelineAlign.manual, - lineXY: 0.1, - isFirst: true, - afterLineStyle: const LineStyle( - color: Colors.orange, - thickness: 6, - ), - indicatorStyle: IndicatorStyle( - width: 40, - color: Colors.orange, - padding: const EdgeInsets.all(8), - iconStyle: IconStyle( - color: Colors.white, - iconData: Icons.emoji_flags_rounded, - ), - ), - endChild: Container( - padding: EdgeInsets.only(top: 30), - constraints: const BoxConstraints( - minHeight: 120, - ), - color: Colors.transparent, - child: RichText( - text: TextSpan( - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - children: [ - TextSpan( - text: startText, - style: GoogleFonts.inter( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.yellow[400], - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - )), - TextSpan( - text: "\n", - style: GoogleFonts.inter( - fontSize: 16, - color: Colors.white, - )), - TextSpan( - text: explainingText, - style: GoogleFonts.inter( - fontSize: 16, - color: Colors.white, - )), - ])), - ), - ); - } - - Widget getEndTile() { - return Container( - color: Colors.transparent, - child: TimelineTile( - alignment: TimelineAlign.manual, - lineXY: 0.1, - isLast: true, - beforeLineStyle: const LineStyle( - color: Colors.orange, - thickness: 6, - ), - indicatorStyle: IndicatorStyle( - width: 40, - color: Colors.orange, - padding: const EdgeInsets.all(8), - iconStyle: IconStyle( - color: Colors.white, - iconData: Icons.thumb_up, - ), - ), - endChild: Container( - padding: EdgeInsets.only(top: 50), - constraints: const BoxConstraints( - minHeight: 120, - ), - color: Colors.transparent, - child: RichText( - text: TextSpan( - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - children: [ - TextSpan( - text: "Finish!", - style: GoogleFonts.inter( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.yellow[400], - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - )), - ])), - ), - ), - ); - } - - List getExerciseTiles(TrainingPlanBloc bloc, BuildContext context) { - List tiles = []; - if (bloc.getMyPlan() != null && - bloc.getMyPlan()!.details.isNotEmpty && - bloc.getMyPlan()!.days[widget.dayName] != null && - bloc.getMyPlan()!.days[widget.dayName]!.isNotEmpty) { - bloc.getMyPlan()!.days[widget.dayName]!.forEach((element) { - tiles.add( - /* GestureDetector( - onTap: () => bloc.getNext() != null ? executeExercise(bloc, bloc.getNext()!, context) : Navigator.of(context).pushNamed('home'), - child: */ - ExerciseTile( - bloc: bloc, - detail: element, - )); - }); - } - - return tiles; - } - - static void executeExercise(TrainingPlanBloc bloc, CustomerTrainingPlanDetails detail, BuildContext context) { - CustomerTrainingPlanDetails? next = bloc.getNext(); - - if (next != null) { - String title = ""; - String description = ""; - String description2 = ""; - if (next.exerciseTypeId != detail.exerciseTypeId) { - title = AppLocalizations.of(context)!.translate("Stop!"); - description = AppLocalizations.of(context)!.translate("Please continue with the next exercise in the queue:") + - next.exerciseType!.nameTranslation; - } else { - final HashMap args = HashMap(); - args['exerciseType'] = next.exerciseType; - args['customerTrainingPlanDetails'] = detail; - Navigator.of(context).pushNamed('myTrainingPlanExercise', arguments: args); - return; - } - - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return DialogCommon( - title: title, - descriptions: description, - description2: description2, - text: "OK", - onTap: () => {Navigator.of(context).pop()}, - onCancel: () => {Navigator.of(context).pop()}, - ); - }); - } else { - Navigator.of(context).pushNamed('home'); - } - } -} - -class ExerciseTile extends StatefulWidget { - final TrainingPlanBloc bloc; - final CustomerTrainingPlanDetails detail; - - ExerciseTile({required this.bloc, required this.detail}); - - @override - _ExerciseTileState createState() => _ExerciseTileState(); -} - -class _ExerciseTileState extends State with Trans { - GestureRecognizer? _tapRecognizer; - - @override - void initState() { - _tapRecognizer = TapGestureRecognizer()..onTap = _onPlusMinusWeight; - super.initState(); - } - - @override - void dispose() { - if (_tapRecognizer != null) { - _tapRecognizer!.dispose(); - } - super.dispose(); - } - - void _onPlusMinusWeight() { - showDialog( - context: context, - builder: (BuildContext context) { - return WeightControl( - initialValue: widget.detail.weight != null ? widget.detail.weight! : 30, - onTap: (value) => widget.bloc.add(TrainingPlanWeightChangeRecalculate(detail: widget.detail, weight: value)), - ); - }); - } - - Widget getIndicator(ExercisePlanDetailState state) { - CustomerTrainingPlanDetails? next = widget.bloc.getNext(); - bool actual = false; - if (next != null) { - if (next.exerciseTypeId == widget.detail.exerciseTypeId) { - actual = true; - } - } - if (state.equalsTo(ExercisePlanDetailState.inProgress)) { - return ClipRRect( - borderRadius: BorderRadius.circular(24.0), - child: Container( - color: actual ? Colors.green : Colors.orange, - child: Icon( - CustomIcon.calendar_2, - size: 28, - color: Colors.white, - ))); - } else if (state.equalsTo(ExercisePlanDetailState.finished)) { - return ClipRRect( - borderRadius: BorderRadius.circular(24.0), - child: Container( - color: Colors.white, - child: Icon( - CustomIcon.ok_circled, - size: 40, - color: Colors.green, - ))); - } else if (state.equalsTo(ExercisePlanDetailState.skipped)) { - return ClipRRect( - borderRadius: BorderRadius.circular(24.0), - child: Container( - color: Colors.white, - child: Icon( - CustomIcon.stop_1, - size: 40, - color: Colors.grey, - ))); - } else if (state.equalsTo(ExercisePlanDetailState.extra)) { - return ClipRRect( - borderRadius: BorderRadius.circular(24.0), - child: Container( - color: Colors.white, - child: Icon( - CustomIcon.stopwatch_20, - size: 40, - color: Colors.blue[800], - ))); - } else { - return Image.asset( - "asset/image/pict_reps_volumen_db.png", - ); - } - } - - @override - Widget build(BuildContext context) { - setContext(context); - final ExercisePlanDetailState state = widget.detail.state; - final bool done = state.equalsTo(ExercisePlanDetailState.finished) || state.equalsTo(ExercisePlanDetailState.skipped); - final String countSerie = widget.detail.set.toString(); - final String step = (widget.detail.exercises.length).toString(); - String weight = widget.detail.weight != null ? widget.detail.weight!.toStringAsFixed(1) : "-"; - bool isDrop = false; - if (widget.detail.weight == -3) { - weight = "DROP"; - isDrop = true; - } - String restingTime = widget.detail.restingTime == null ? "" : widget.detail.restingTime!.toStringAsFixed(0); - bool isTest = false; - if (widget.detail.weight != null && widget.detail.weight! == -1) { - weight = "TEST"; - isTest = true; - } - String repeats = widget.detail.repeats!.toString(); - if (widget.detail.repeats! == -1) { - repeats = t("MAX"); - } - final bool extraExercise = widget.detail.exerciseType!.name == "Warming Up" || widget.detail.exerciseType!.name == "Stretching"; - - bool buddyWarning = widget.detail.exerciseType == null ? false : widget.detail.exerciseType!.buddyWarning; - setContext(context); - return Container( - color: Colors.transparent, - child: TimelineTile( - alignment: TimelineAlign.manual, - lineXY: 0.1, - beforeLineStyle: const LineStyle( - color: Color(0xffb4f500), - thickness: 6, - ), - afterLineStyle: const LineStyle( - color: Color(0xffb4f500), - thickness: 6, - ), - indicatorStyle: IndicatorStyle( - width: 40, - height: 40, - indicator: getIndicator(state), - ), - startChild: Container( - child: Column(children: [ - SizedBox( - height: 1, - ), - SizedBox( - height: 55, - ), - done - ? Offstage() - : IconButton( - padding: EdgeInsets.zero, - alignment: Alignment.centerLeft, - icon: Icon( - Icons.skip_next_sharp, - size: 30, - color: Colors.orange[300], - ), - onPressed: () => skip()), - ]), - ), - endChild: Container( - padding: EdgeInsets.only(left: 10), - child: Row(children: [ - Container( - width: 120, - height: 80, - child: Badge( - elevation: 0, - padding: EdgeInsets.all(0), - position: BadgePosition.bottomStart(start: -5), - animationDuration: Duration(milliseconds: 500), - animationType: BadgeAnimationType.slide, - badgeColor: Colors.transparent, - showBadge: true, - badgeContent: IconButton( - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) { - return DialogHTML( - title: widget.detail.exerciseType!.nameTranslation, - htmlData: '

' + widget.detail.exerciseType!.descriptionTranslation + '

'); - }), - icon: Icon( - Icons.info_outline, - color: Colors.yellow[200], - )), - child: Badge( - elevation: 0, - padding: EdgeInsets.all(0), - position: BadgePosition.topEnd(end: -8), - animationDuration: Duration(milliseconds: 500), - animationType: BadgeAnimationType.slide, - badgeColor: Colors.transparent, - showBadge: buddyWarning, - badgeContent: IconButton( - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) { - return DialogCommon( - warning: true, - text: "Warning", - descriptions: t("Attention!"), - description2: t("The safe and exact execution of this exercise you need a training buddy or a trainer"), - description3: t("Execution at your own risk!"), - onTap: () => Navigator.of(context).pop(), - onCancel: () => Navigator.of(context).pop(), - title: t('Training Buddy'), - ); - }), - icon: Icon( - CustomIcon.exclamation_circle, - color: Colors.red[800], - )), - child: MenuImage( - imageName: widget.bloc.getActualImageName(widget.detail.exerciseType!.exerciseTypeId), - workoutTreeId: widget.bloc.getActualWorkoutTreeId(widget.detail.exerciseType!.exerciseTypeId)!, - radius: 12, - ))), - ), - SizedBox( - width: 10, - ), - Expanded( - child: RichText( - text: TextSpan( - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: done ? Colors.grey[400] : Colors.white, - ), - children: [ - TextSpan( - text: widget.detail.exerciseType!.nameTranslation, - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.bold, - color: done ? Colors.grey[400] : Colors.orange[500], - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - )), - widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise - ? TextSpan( - text: "\n", - ) - : TextSpan(), - widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise - ? TextSpan( - text: t(widget.detail.exerciseType!.unitQuantityUnit!) + ": ", - style: GoogleFonts.inter( - fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)) - : TextSpan(), - widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise - ? TextSpan( - text: t(weight), - style: GoogleFonts.inter( - fontSize: 12, - )) - : TextSpan(), - widget.detail.exerciseType!.unitQuantityUnit != null && !extraExercise && weight != "TEST" && weight != "DROP" - ? TextSpan( - text: " - +", - style: GoogleFonts.archivoBlack( - color: Colors.blue, - fontSize: 16, - ), - recognizer: _tapRecognizer, - mouseCursor: SystemMouseCursors.precise, - ) - : TextSpan(), - TextSpan( - text: "\n", - ), - !extraExercise - ? TextSpan( - text: t(widget.detail.exerciseType!.unit) + ": ", - style: GoogleFonts.inter( - fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)) - : TextSpan(), - !extraExercise - ? TextSpan( - text: repeats, - style: GoogleFonts.inter( - fontSize: 12, - )) - : TextSpan(), - TextSpan( - text: "\n", - ), - !extraExercise - ? TextSpan( - text: t("Set") + ": ", - style: GoogleFonts.inter( - fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)) - : TextSpan(), - !extraExercise - ? TextSpan( - text: step + "/" + countSerie, - style: GoogleFonts.inter( - fontSize: 12, - )) - : TextSpan(), - TextSpan( - text: "\n", - ), - !extraExercise - ? TextSpan( - text: t("Resting time") + ": ", - style: GoogleFonts.inter( - fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)) - : TextSpan(), - !extraExercise - ? TextSpan( - text: restingTime + " " + t("min(s)"), - style: - GoogleFonts.inter(fontSize: 12, color: done ? Colors.grey[100] : Colors.white, fontWeight: FontWeight.bold)) - : TextSpan(), - ]), - )), - isTest - ? Container( - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - GestureDetector( - onTap: () => showDialog( - context: context, - builder: (BuildContext context) { - return DialogCommon( - warning: false, - title: t("Why Test?"), - descriptions: t("This is your first exercise after at least 3 weeks."), - description2: - t("The first exercise will be a test. The following sets will be recalculated base on your test."), - description3: t("This is the most optimal way for your development"), - text: "OK", - onTap: () => Navigator.of(context).pop(), - onCancel: () => { - Navigator.of(context).pop(), - }, - ); - }), - child: Icon( - CustomIcon.question_circle, - color: Colors.yellowAccent[700], - size: 16, - )), - ])) - : isDrop - ? Container( - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - GestureDetector( - onTap: () => showDialog( - context: context, - builder: (BuildContext context) { - return DialogCommon( - warning: false, - title: t("Drop Set"), - descriptions: t( - "Execute at least 3 sets with maximum repeats, without resting time, with decreasing the weight."), - description2: - t("The goal is to completly exhaust your muscle without lifting a ridiculous weight end the end."), - text: "OK", - onTap: () => Navigator.of(context).pop(), - onCancel: () => { - Navigator.of(context).pop(), - }, - ); - }), - child: Icon( - CustomIcon.question_circle, - color: Colors.orange[200], - size: 16, - )), - ])) - : Offstage() - ]), - ), - ), - ); - } - - void skip() { - showCupertinoDialog( - useRootNavigator: true, - context: context, - builder: (_) => CupertinoAlertDialog( - title: Text(t("You want to skip really this exercise?")), - content: Column(children: [ - Divider(), - ]), - actions: [ - TextButton( - child: Text(t("No")), - onPressed: () => { - Navigator.pop(context), - }), - TextButton( - child: Text(t("Yes")), - onPressed: () { - Navigator.pop(context); - widget.bloc.add(TrainingPlanSkipExercise(detail: widget.detail)); - }, - ) - ], - )); - } -} diff --git a/lib/view/training_plan_exercise.dart b/lib/view/training_plan_exercise.dart index c8dba62..de8a641 100644 --- a/lib/view/training_plan_exercise.dart +++ b/lib/view/training_plan_exercise.dart @@ -2,10 +2,12 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer_training_plan_details.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/exercise_save.dart'; +import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -26,7 +28,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans { body: Container( height: double.infinity, width: double.infinity, - padding: EdgeInsets.all(20), + //padding: EdgeInsets.all(20), decoration: BoxDecoration( image: DecorationImage( image: AssetImage("asset/image/WT_black_background.jpg"), @@ -75,7 +77,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans { backgroundColor: Colors.orange[800], icon: Icon(CustomIcon.save), label: Text( - isDropSet ? t("Done") : t("Save"), + t("Done"), style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16), ), ), @@ -92,7 +94,7 @@ class TrainingPlanExercise extends StatelessWidget with Trans { unit: detail.exerciseType!.unit, unitQuantityUnit: detail.exerciseType!.unitQuantityUnit, hasUnitQuantity: detail.exerciseType!.unitQuantityUnit != null, - weight: detail.weight == -1 ? 0 : detail.weight, + weight: detail.weight, repeats: detail.repeats == -1 ? 99 : detail.repeats, set: detail.set, exerciseNr: detail.exercises.length + 1, @@ -100,6 +102,13 @@ class TrainingPlanExercise extends StatelessWidget with Trans { onQuantityChanged: (value) => bloc.add(TrainingPlanRepeatsChange(repeats: value.toInt(), detail: detail)), exerciseTypeId: detail.exerciseType!.exerciseTypeId, originalQuantity: originalQuantity, + tip: ActivityDone.exerciseSaveTrainingTip, + menuImage: MenuImage( + imageName: bloc.getActualImageName(detail.exerciseType!.exerciseTypeId), + workoutTreeId: bloc.getActualWorkoutTreeId(detail.exerciseType!.exerciseTypeId)!, + radius: 0, + filter: false, + ), ); } else { return getDropSet(bloc, detail); diff --git a/lib/view/training_plans_page.dart b/lib/view/training_plans_page.dart index 90436f3..60b1272 100644 --- a/lib/view/training_plans_page.dart +++ b/lib/view/training_plans_page.dart @@ -44,7 +44,7 @@ class MyTrainingPlans extends StatelessWidget with Trans, Logging { title: t("Warning"), descriptions: t(state.message), text: "OK", - onTap: () => Navigator.of(context).pushNamed("login"), + onTap: () => Navigator.of(context).pop(), onCancel: () => { Navigator.of(context).pop(), }, diff --git a/lib/widgets/bmr_widget.dart b/lib/widgets/bmr_widget.dart index 3246869..aca4e21 100644 --- a/lib/widgets/bmr_widget.dart +++ b/lib/widgets/bmr_widget.dart @@ -286,7 +286,7 @@ class _BMRState extends State with Trans { ), ), mode: Mode.MENU, - compareFn: (FitnessState i, FitnessState s) => i.isEqual(s), + compareFn: (FitnessState? i, FitnessState? s) => i!.isEqual(s), showSelectedItem: true, selectedItem: FitnessItem().getItem(fitnessLevel), itemAsString: (data) => t(data!.stateText), @@ -301,7 +301,6 @@ class _BMRState extends State with Trans { color: Colors.yellow[200], ), )); - //items: FitnessItem().toList())); } Widget _customMenuBuilder(BuildContext context, FitnessState? item, bool isSelected) { diff --git a/lib/widgets/dialog_premium.dart b/lib/widgets/dialog_premium.dart index 9f2e85a..188f728 100644 --- a/lib/widgets/dialog_premium.dart +++ b/lib/widgets/dialog_premium.dart @@ -88,7 +88,7 @@ class _DialogPremiumState extends State with Trans { alignment: AlignmentDirectional.topEnd, children: [ Text( - widget.unlocked ? t("Keep testing") : t("Go Premium") + " ", + widget.unlocked ? t("Keep training") : t("Go Premium") + " ", style: GoogleFonts.archivoBlack( fontSize: widget.unlocked ? 20 : 24, color: Colors.yellow[400], @@ -257,11 +257,7 @@ class _DialogPremiumState extends State with Trans { list.add(TextSpan(text: t(" "))); list.add( TextSpan( - text: widget.unlockRound == 1 - ? t("the first") - : widget.unlockRound == 2 - ? t("the second") - : t("the third"), + text: widget.unlockRound.toString() + " " + t("week"), style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.bold, @@ -282,7 +278,7 @@ class _DialogPremiumState extends State with Trans { ), ); list.add(TextSpan(text: t(" "))); - list.add(TextSpan(text: t("100% test circles"))); + list.add(TextSpan(text: t("100% completed training"))); return list; } diff --git a/lib/widgets/dialog_trial.dart b/lib/widgets/dialog_trial.dart index e3a3f8b..9c8a5e5 100644 --- a/lib/widgets/dialog_trial.dart +++ b/lib/widgets/dialog_trial.dart @@ -120,7 +120,7 @@ class _DialogTrialWidgetState extends State with Trans { children: [ Image.asset('asset/icon/gomb_lila_b.png', width: 100, height: 45), Text( - t("Nem"), + t("No"), style: TextStyle(fontSize: 16, color: Colors.white), ), ], @@ -132,7 +132,7 @@ class _DialogTrialWidgetState extends State with Trans { children: [ Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45), Text( - t("Igen"), + t("Yes"), style: TextStyle(fontSize: 16, color: Colors.white), ), ], diff --git a/lib/widgets/exercise_save.dart b/lib/widgets/exercise_save.dart index f715382..893d2a8 100644 --- a/lib/widgets/exercise_save.dart +++ b/lib/widgets/exercise_save.dart @@ -1,21 +1,113 @@ import 'dart:async'; -import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:aitrainer_app/widgets/time_picker.dart'; +import 'package:aitrainer_app/widgets/tutorial_widget.dart'; +import 'package:badges/badges.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:keyboard_actions/keyboard_actions_config.dart'; import 'package:stop_watch_timer/stop_watch_timer.dart'; import 'package:wakelock/wakelock.dart'; - -import 'dialog_common.dart'; import 'dialog_html.dart'; +enum Explanations { intro, introBold, explanationWeight, explanationRepeats, explanationButton, explanationButtonExt } + +extension ExplanationsExt on Explanations { + String toStr() => this.toString().split(".").last; + bool equalsTo(Explanations type) => this.toString() == type.toString(); + bool equalsStringTo(String type) => this.toStr() == type; +} + +class ExplanationExt { + final ActivityDone tip; + final String? unitQuantityUnit; + final double? weight; + final int? repeats; + ExplanationExt({ + required this.tip, + this.unitQuantityUnit, + required this.weight, + required this.repeats, + }); + + String getExplanation(Explanations explanation) { + String expl = ""; + if (this.tip.equalsTo(ActivityDone.exerciseSaveTestTip)) { + if (explanation.equalsTo(Explanations.intro)) { + expl = "Please take a middle weight which you are able to do 8-20 times with."; + } else if (explanation.equalsTo(Explanations.introBold)) { + expl = "Execute your MAXIMUM repeats with it!"; + } else if (explanation.equalsTo(Explanations.explanationWeight)) { + expl = "Type here your selected weight,"; + } else if (explanation.equalsTo(Explanations.explanationRepeats)) { + expl = "then here, how many times could you repeat it!"; + } else if (explanation.equalsTo(Explanations.explanationButton)) { + expl = "After you done, click to the OK button!"; + } else if (explanation.equalsTo(Explanations.explanationButtonExt)) { + expl = ""; + } + } else if (this.tip.equalsTo(ActivityDone.exerciseSaveTrainingTip)) { + if (unitQuantityUnit != null) { + if (weight == -1) { + if (explanation.equalsTo(Explanations.intro)) { + expl = "Please take a middle weight which you are able to do 8-20 times with."; + } else if (explanation.equalsTo(Explanations.introBold)) { + expl = "Execute your MAXIMUM repeats with it!"; + } + } else if (repeats == 99) { + if (explanation.equalsTo(Explanations.intro)) { + expl = "It is time to exhaust your muscles"; + } + } else { + if (explanation.equalsTo(Explanations.intro)) { + expl = "For your optimal development we calculated a suitable weight and repeats"; + } else if (explanation.equalsTo(Explanations.introBold)) { + expl = "You can change the weight, if you could not set it in the training room"; + } + } + if (explanation.equalsTo(Explanations.explanationButtonExt)) { + if (repeats != 99) { + expl = "If you could do less, then modify and click to OK"; + } + } else if (explanation.equalsTo(Explanations.explanationWeight)) { + if (weight == -1 || weight == -2) { + expl = "Type here your selected weight,"; + } else { + expl = "Here is your tailored weight,"; + } + } else if (explanation.equalsTo(Explanations.explanationRepeats)) { + if (repeats == 99) { + expl = "and execute it with maximum repeats!"; + } else { + expl = "and executed with this number of repeats!"; + } + } + } else { + if (repeats == 99) { + if (explanation.equalsTo(Explanations.intro)) { + expl = "Please repeat as much times as you can! MAXIMIZE it!"; + } + } else { + if (explanation.equalsTo(Explanations.intro)) { + expl = "Please try to execute this exercise with exact repeats what is suggested"; + } + } + } + if (explanation.equalsTo(Explanations.explanationButton)) { + expl = "After you done, click to the OK button!"; + } + } else if (this.tip.equalsTo(ActivityDone.exerciseSaveTestsetTip)) {} + + return expl; + } +} + // ignore: must_be_immutable class ExerciseSave extends StatefulWidget { final ValueChanged onQuantityChanged; @@ -33,6 +125,8 @@ class ExerciseSave extends StatefulWidget { final int? set; final int? exerciseNr; final int? originalQuantity; + final MenuImage menuImage; + final ActivityDone? tip; ExerciseSave( {required this.onQuantityChanged, @@ -49,7 +143,9 @@ class ExerciseSave extends StatefulWidget { this.repeats, this.set, this.exerciseNr, - this.originalQuantity}); + this.originalQuantity, + required this.menuImage, + this.tip}); @override _ExerciseSaveState createState() => _ExerciseSaveState(); } @@ -72,12 +168,6 @@ class _ExerciseSaveState extends State with Trans { @override initState() { super.initState(); - _controller1.text = widget.weight == null - ? "0" - : widget.weight! % widget.weight!.round() == 0 - ? widget.weight!.toStringAsFixed(0) - : widget.weight!.toStringAsFixed(1); - _controller2.text = widget.repeats == null ? "12" : widget.repeats!.toStringAsFixed(0); _nodeText1.addListener(() { if (_nodeText1.hasFocus) { _controller1.selection = TextSelection(baseOffset: 0, extentOffset: _controller1.text.length); @@ -98,25 +188,31 @@ class _ExerciseSaveState extends State with Trans { } SchedulerBinding.instance!.addPostFrameCallback((_) { - /* //final TutorialBloc bloc = BlocProvider.of(context); - if (bloc.actualCheck == "directTest") { + _controller1.text = widget.weight == null || widget.weight == -1 + ? "--" + : widget.weight! % widget.weight!.round() == 0 + ? widget.weight!.toStringAsFixed(0) + : widget.weight!.toStringAsFixed(1); + _controller2.text = widget.repeats == null + ? "--" + : widget.repeats! == 99 + ? "MAX" + : widget.repeats!.toStringAsFixed(0); + if (widget.unitQuantityUnit != null && widget.tip != null && Cache().isActivityDone(widget.tip!) == false) { Timer( Duration(milliseconds: 2000), () => { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return DialogCommon( - title: t("Attention"), - descriptions: t(widget.exerciseTask), - text: "OK", - onTap: () => Navigator.of(context).pop(), - onCancel: () => Navigator.of(context).pop(), - ); - }) + TutorialWidget().explanation( + context, + ExplanationWidget( + unitQuantityUnit: widget.unitQuantityUnit, + unit: widget.unit, + tip: widget.tip, + weight: widget.weight, + repeats: widget.repeats, + )), }); - } */ + } }); } @@ -189,82 +285,139 @@ class _ExerciseSaveState extends State with Trans { } Widget getExerciseWidget() { + ExplanationExt expl = ExplanationExt( + tip: widget.tip!, + weight: widget.weight, + repeats: widget.repeats, + unitQuantityUnit: widget.unitQuantityUnit, + ); + return KeyboardActions( config: _buildConfig(context), child: Container( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - widget.exerciseName, - style: GoogleFonts.archivoBlack( - fontWeight: FontWeight.bold, - fontSize: 24, - color: Colors.white, - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], + Stack(alignment: Alignment.bottomLeft, children: [ + Badge( + elevation: 0, + padding: EdgeInsets.all(0), + position: BadgePosition.topEnd(top: 5, end: 5), + animationDuration: Duration(milliseconds: 1500), + animationType: BadgeAnimationType.fade, + badgeColor: Colors.transparent, + showBadge: true, + badgeContent: IconButton( + iconSize: 30, + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return DialogHTML(title: widget.exerciseName, htmlData: '

' + widget.exerciseDescription + '

'); + }), + icon: Icon( + CustomIcon.info_circle, + color: Colors.white, + )), + child: widget.menuImage, ), - overflow: TextOverflow.fade, - maxLines: 4, - softWrap: true, - textAlign: TextAlign.center, - ), - SizedBox( - height: 15, - ), - InkWell( - child: Text( - t("Exercise descripton") + " »", - style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[200]), + Container( + padding: EdgeInsets.only(left: 10, bottom: 10, right: 10), + child: Text( + widget.exerciseName, + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + fontSize: 24, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + overflow: TextOverflow.fade, + maxLines: 4, + softWrap: true, + )) + ]), + ListTile( + leading: IconButton( + iconSize: 30, + onPressed: () => { + if (widget.unitQuantityUnit != null) + { + TutorialWidget().explanation( + context, + ExplanationWidget( + unitQuantityUnit: widget.unitQuantityUnit, + unit: widget.unit, + tip: widget.tip, + weight: widget.weight, + repeats: widget.repeats, + )) + } + }, + icon: Icon( + CustomIcon.info_circle, + color: Colors.orange[100], + )), + subtitle: Text( + t(expl.getExplanation(Explanations.intro)), + style: GoogleFonts.inter( + fontSize: 14, + color: Colors.white, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + maxLines: 3, + textAlign: TextAlign.left, + overflow: TextOverflow.fade, + softWrap: true, ), - onTap: () => { - showDialog( - context: context, - builder: (BuildContext context) { - return DialogHTML(title: widget.exerciseName, htmlData: '

' + widget.exerciseDescription + '

'); - }) - }, - ), - Divider( - color: Colors.transparent, - ), - Text( - t(widget.exerciseTask), - style: GoogleFonts.inter( - fontSize: 14, - color: Colors.orange, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 6.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - ), - maxLines: 3, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: true, - ), - Divider( - color: Colors.transparent, ), + expl.getExplanation(Explanations.introBold).length > 0 + ? Text( + t(expl.getExplanation(Explanations.introBold)), + style: GoogleFonts.inter( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + maxLines: 3, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: true, + ) + : Offstage(), widget.unit == "second" ? Text( getTimeGoal(widget.originalQuantity), @@ -289,29 +442,60 @@ class _ExerciseSaveState extends State with Trans { ) : Offstage(), columnQuantityUnit(), - Divider( - color: Colors.transparent, - ), columnQuantity(), Divider( color: Colors.transparent, ), - widget.hasUnitQuantity - ? Text( - widget.set == null || widget.exerciseNr == null - ? t("Step") + ": " + "1/4" - : t("Step") + ": " + "${widget.exerciseNr}/${widget.set}", - style: GoogleFonts.inter( - fontSize: 22, - color: Colors.white, - fontWeight: FontWeight.bold, - ), - maxLines: 3, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: true, - ) - : Offstage(), + Text( + t(expl.getExplanation(Explanations.explanationButton)), + style: GoogleFonts.inter( + fontSize: 14, + color: Colors.white, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + maxLines: 3, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: true, + ), + Padding( + padding: const EdgeInsets.only(left: 35, right: 35), + child: Text( + t(expl.getExplanation(Explanations.explanationButtonExt)), + style: GoogleFonts.inter( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + maxLines: 3, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: true, + )), Divider( color: Colors.transparent, ), @@ -476,3 +660,168 @@ class _ExerciseSaveState extends State with Trans { return row; } } + +class ExplanationWidget extends StatefulWidget { + final String? unitQuantityUnit; + final String unit; + final ActivityDone? tip; + final double? weight; + final int? repeats; + const ExplanationWidget({ + Key? key, + this.unitQuantityUnit, + required this.unit, + this.tip, + this.weight, + this.repeats, + }) : super(key: key); + + @override + _ExplanationWidgetState createState() => _ExplanationWidgetState(); +} + +class _ExplanationWidgetState extends State with Trans { + bool _selected = false; + + @override + Widget build(BuildContext context) { + ExplanationExt expl = ExplanationExt( + tip: widget.tip!, + weight: widget.weight, + repeats: widget.repeats, + unitQuantityUnit: widget.unitQuantityUnit, + ); + setContext(context); + return Material( + color: Colors.transparent, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10, left: 10, right: 10), + child: Text(t(expl.getExplanation(Explanations.intro)), + maxLines: 5, + textAlign: TextAlign.center, + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ))), + Padding( + padding: const EdgeInsets.only(top: 10, left: 10, right: 10), + child: Text(t(expl.getExplanation(Explanations.introBold)), + maxLines: 5, + textAlign: TextAlign.center, + style: GoogleFonts.inter( + color: Colors.orange, + fontWeight: FontWeight.bold, + fontSize: 16, + ))), + Stack(children: [ + Padding( + padding: const EdgeInsets.only(top: 13, left: 18, right: 18), + child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + TextFormField( + decoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), + labelText: t(widget.unitQuantityUnit!), + labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]), + fillColor: Colors.black38, + filled: true, + border: OutlineInputBorder( + gapPadding: 8.0, + borderRadius: BorderRadius.circular(12.0), + borderSide: BorderSide(color: Colors.white12, width: 0.4), + ), + ), + initialValue: ".", + keyboardType: TextInputType.numberWithOptions(decimal: true), + textInputAction: TextInputAction.done, + style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.transparent), + onChanged: (value) {}), + ])), + Container( + padding: EdgeInsets.only(top: 35, left: 35, right: 35), + child: Text( + t(expl.getExplanation(Explanations.explanationWeight)), + style: GoogleFonts.archivoBlack(fontSize: 23, color: Colors.yellow[300]), + )), + ]), + Stack(children: [ + Padding( + padding: const EdgeInsets.only(top: 10, left: 15, right: 15), + child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + TextFormField( + decoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), + labelText: t(widget.unit), + labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]), + fillColor: Colors.black38, + filled: true, + border: OutlineInputBorder( + gapPadding: 8.0, + borderRadius: BorderRadius.circular(12.0), + borderSide: BorderSide(color: Colors.white12, width: 0.4), + ), + ), + initialValue: ".", + keyboardType: TextInputType.numberWithOptions(decimal: true), + textInputAction: TextInputAction.done, + style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.transparent), + onChanged: (value) {}), + ])), + Container( + padding: EdgeInsets.only(top: 25, left: 35, right: 35), + child: Text( + t(expl.getExplanation(Explanations.explanationRepeats)), + style: GoogleFonts.archivoBlack(fontSize: 23, color: Colors.yellow[300]), + )), + ]), + Padding( + padding: const EdgeInsets.only(top: 10, left: 5, right: 5), + child: Text( + t("Don't forget, the app can give you only the right values, if you execute the task regurarly and after the exact instructions."), + maxLines: 5, + textAlign: TextAlign.center, + style: GoogleFonts.inter( + color: Colors.white, + fontSize: 14, + ))), + Divider(), + GestureDetector( + onTap: () => { + TutorialWidget().close(), + if (_selected && widget.tip != null) + { + Cache().setActivityDonePrefs(widget.tip!), + } + }, + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45), + Text( + t("Got It"), + style: TextStyle(fontSize: 20, color: Colors.white), + ), + ], + )), + Theme( + data: ThemeData(unselectedWidgetColor: Colors.white38), + child: CheckboxListTile( + value: _selected, + onChanged: (bool? checked) { + setState(() { + _selected = checked!; + }); + }, + checkColor: Colors.white, + activeColor: Colors.orange[600], + controlAffinity: ListTileControlAffinity.leading, + title: Text( + t("Show this tip no more"), + style: GoogleFonts.inter(color: Colors.grey), + ))) + ], + )); + } +} diff --git a/lib/widgets/menu_page_widget.dart b/lib/widgets/menu_page_widget.dart index b69cf1b..4132f1c 100644 --- a/lib/widgets/menu_page_widget.dart +++ b/lib/widgets/menu_page_widget.dart @@ -83,16 +83,16 @@ class _MenuPageWidgetState extends State with Trans, Logging { context: context, builder: (BuildContext context) { return DialogTrialWidget( - title: "10 days Premium for free", - description: "Would you like to try all premium functions for 10 days, without any subscription or bank card data?", + title: t("10 days Premium for free"), + description: t("Would you like to try all premium functions for 10 days, without any subscription or bank card data?"), widget: Column(children: [ Text( - "If you click to 'Yes', all premium functions will be available right now.", + t("If you click to 'Yes', all premium functions will be available right now."), style: GoogleFonts.inter(color: Colors.white), ), Divider(), Text( - "If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.", + t("If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free."), style: GoogleFonts.inter(color: Colors.white), ), ]), diff --git a/lib/widgets/tutorial_widget.dart b/lib/widgets/tutorial_widget.dart index f17e9ea..4318820 100644 --- a/lib/widgets/tutorial_widget.dart +++ b/lib/widgets/tutorial_widget.dart @@ -25,6 +25,46 @@ class TutorialWidget with Trans, Logging { } } + Widget explanationFrame(Widget widget) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24), + boxShadow: [BoxShadow(color: Colors.black, offset: Offset(0, 10), blurRadius: 10)], + image: DecorationImage( + image: AssetImage('asset/image/WT_plainblack_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + child: widget, + ); + } + + void explanation(BuildContext context, Widget widget) { + tooltip = SuperTooltip( + top: 120, + left: 20, + right: 20, + backgroundColor: Colors.black87, + popupDirection: TooltipDirection.up, + maxWidth: 330, + borderColor: Colors.transparent, + borderWidth: 1.0, + minimumOutSidePadding: 20, + //snapsFarAwayVertically: false, + showCloseButton: ShowCloseButton.outside, + closeButtonColor: Colors.grey, + dismissOnTapOutside: true, + outsideBackgroundColor: Colors.black.withOpacity(0.6), + hasShadow: true, + touchThrougArea: null, + //onClose: () => bloc.add(TutorialFinished()), + custom: true, + content: explanationFrame(widget)); + + tooltip!.showBox(context); + } + void tip(BuildContext context) { final TutorialBloc bloc = BlocProvider.of(context); if (bloc.action == null) { diff --git a/pubspec.lock b/pubspec.lock index 84233de..4cde0b3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -707,6 +707,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + mailto: + dependency: "direct main" + description: + name: mailto + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" matcher: dependency: transitive description: @@ -1294,12 +1301,12 @@ packages: source: hosted version: "3.5.1" url_launcher: - dependency: transitive + dependency: "direct main" description: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.3" + version: "6.0.9" url_launcher_linux: dependency: transitive description: @@ -1320,7 +1327,7 @@ packages: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.4" url_launcher_web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0594500..f3dd53d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,7 +58,9 @@ dependencies: upgrader: ^3.5.1 web_browser: ^0.5.0 flutter_fadein: ^2.0.0 - + mailto: ^2.0.0 + url_launcher: ^6.0.9 + firebase_core: ^1.5.0 firebase_analytics: ^8.1.0 firebase_messaging: ^10.0.0 @@ -280,6 +282,7 @@ flutter: - asset/menu/close_grip_front_lat_pulldown.jpg - asset/menu/close_grip_pull_ups.jpg - asset/menu/close_reverse_grip_lat_pulldown.jpg + - asset/menu/close_reverse_grip_pull_ups.jpg - asset/menu/concentration.jpg - asset/menu/cooper.jpg - asset/menu/crisscross.jpg @@ -294,6 +297,7 @@ flutter: - asset/menu/donkey_calf_raises.jpg - asset/menu/dumbbell_alternate_bicep_curl.jpg - asset/menu/dumbell_bench_presses.jpg + - asset/menu/dumbbell_presses.jpg - asset/menu/ez_bar_burl.jpg - asset/menu/flyes.jpg - asset/menu/forward_raise.jpg @@ -358,6 +362,7 @@ flutter: - asset/menu/seated_dumbbell_curl.jpg - asset/menu/seated_dumbbell_shoulder_press.jpg - asset/menu/seated_lateral_raises.jpg + - asset/menu/seated_leg_curls.jpg - asset/menu/seated_triceps_extension.jpg - asset/menu/shrugs.jpg - asset/menu/side_plank.jpg @@ -372,6 +377,7 @@ flutter: - asset/menu/sizes.jpg - asset/menu/smith_machine_chest_press.jpg - asset/menu/smith_machine_squats.jpg + - asset/menu/smith_machine_front_press.jpg - asset/menu/squats_with_kettlebell.jpg - asset/menu/squat_jump_weight.jpg - asset/menu/squat_jump.jpg @@ -388,6 +394,7 @@ flutter: - asset/menu/standing_single_arm_lateral_raises.jpg - asset/menu/standing_triceps_extension.jpg - asset/menu/stiff_legged_deadlift.jpg + - asset/menu/straight_arm_pulldown.jpg - asset/menu/straight-arm_rope_pull-down.jpg - asset/menu/stretching.jpg - asset/menu/t_bar_rows.jpg