From 0ca3b71c030c3c1888f9d0e4730280f86c55744b Mon Sep 17 00:00:00 2001 From: bossanyit Date: Sat, 5 Jun 2021 15:39:12 +0200 Subject: [PATCH] WT 1.1.18+4 A/B Test sales page --- i18n/en.json | 10 +- i18n/hu.json | 12 +- ios/Runner.xcodeproj/project.pbxproj | 6 +- .../exercise_execute_plan_bloc.dart | 59 ---- .../exercise_execute_plan_event.dart | 21 -- .../exercise_execute_plan_state.dart | 31 -- .../exercise_execute_plan_add_bloc.dart | 99 ------ .../exercise_execute_plan_add_event.dart | 33 -- .../exercise_execute_plan_add_state.dart | 31 -- lib/bloc/login/login_bloc.dart | 16 +- lib/bloc/menu/menu_bloc.dart | 19 +- lib/bloc/sales/sales_bloc.dart | 154 +++++----- .../training_plan/training_plan_bloc.dart | 28 +- lib/main.dart | 6 +- lib/model/cache.dart | 15 +- lib/model/customer_training_plan.dart | 4 - lib/model/customer_training_plan_details.dart | 13 +- lib/model/product_test.dart | 39 --- lib/model/split_test.dart | 36 +++ lib/push_notifications.dart | 3 +- lib/repository/customer_repository.dart | 22 -- lib/repository/description_repository.dart | 10 +- lib/repository/split_test_respository.dart | 63 ++++ lib/repository/training_plan_repository.dart | 35 +++ lib/service/exercise_service.dart | 3 + lib/service/firebase_api.dart | 34 ++- lib/service/package_service.dart | 11 +- lib/service/product_test_service.dart | 23 -- lib/util/common.dart | 17 +- lib/view/exercise_execute_page.dart | 227 -------------- lib/view/exercise_execute_plan_add_page.dart | 284 ------------------ lib/view/exercise_log_page.dart | 2 +- lib/view/mydevelopment_page.dart | 53 ++-- lib/view/registration.dart | 5 +- lib/view/sales_page.dart | 278 ++++++++++------- lib/view/training_plan_activate_page.dart | 113 +++++-- lib/view/training_plan_custom.dart | 2 +- lib/view/training_plan_custom_add.dart | 11 +- lib/view/training_plans_page.dart | 32 +- lib/widgets/app_bar.dart | 4 +- lib/widgets/app_bar_min.dart | 13 +- lib/widgets/bmi_widget.dart | 18 +- lib/widgets/bmr_widget.dart | 25 +- lib/widgets/dialog_premium.dart | 2 +- lib/widgets/exercise_save.dart | 20 +- lib/widgets/input_dialog_widget.dart | 9 +- lib/widgets/victory_widget.dart | 4 +- pubspec.yaml | 2 +- 48 files changed, 734 insertions(+), 1223 deletions(-) delete mode 100644 lib/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart delete mode 100644 lib/bloc/exercise_execute_plan/exercise_execute_plan_event.dart delete mode 100644 lib/bloc/exercise_execute_plan/exercise_execute_plan_state.dart delete mode 100644 lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart delete mode 100644 lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_event.dart delete mode 100644 lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_state.dart delete mode 100644 lib/model/product_test.dart create mode 100644 lib/model/split_test.dart create mode 100644 lib/repository/split_test_respository.dart delete mode 100644 lib/service/product_test_service.dart delete mode 100644 lib/view/exercise_execute_page.dart delete mode 100644 lib/view/exercise_execute_plan_add_page.dart diff --git a/i18n/en.json b/i18n/en.json index 84b92b7..67d6789 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -454,5 +454,13 @@ "Do you want to override it with": "Do you want to override it with", "Calculated Weight": "Calculated Weight", "The weight is based on your previuos tests - if they exist.": "The weight is based on your previuos tests - if they exist.", - "If it does not exist, your very first exercise will be a test.": "If it does not exist, your very first exercise will be a test." + "If it does not exist, your very first exercise will be a test.": "If it does not exist, your very first exercise will be a test.", + "Tap on the button below the reach all premium content!": "Tap on the button below the reach all premium content!", + "Enjoy also this premium feature": "Enjoy also this premium feature", + "to activate all available training programs.": "to activate all available training programs.", + "because only that way can we generated the training plan for you.": "because only that way can we generate the personalized training plan for you.", + "This is a premium function": "This is a premium function", + "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" } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index ec76d92..f476ed1 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -279,7 +279,7 @@ "feature is reachable after you finished": "funkció elérhető számodra, miután teljesítetted", "100% test circles": "100%-os teszt-köröd", "Keep testing": "Folytasd a tesztelést", - "Enjoy also this premium fetaure 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", + "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!", "Go to: 'Training Plan' - 'Edit My Custom Plan'": "Menj a 'Edzéstervem' - 'Egyéni edzésterv' menübe", "Jump there »": "Vigyél oda »", @@ -452,5 +452,13 @@ "Do you want to override it with": "Ezt az edzéstervet akarod ezentúl használni?", "Calculated Weight": "Kalkulált súly", "The weight is based on your previuos tests - if they exist.": "A súlyt kikalkuláltuk az előző teszted alapján - ha létezik.", - "If it does not exist, your very first exercise will be a test.": "Ha nem létezik 3 hétnél korábbi teszt, akkor a legelső gyakorlatod egy teszt lesz." + "If it does not exist, your very first exercise will be a test.": "Ha nem létezik 3 hétnél korábbi teszt, akkor a legelső gyakorlatod egy teszt lesz.", + "Tap on the button below the reach all premium content!": "Kattints a gombra, hogy hozzáférhess a prémium funkciókhoz!", + "Enjoy also this premium feature": "Élvezd ezt a prémium funkciót is, ", + "to activate all available training programs.": "amellyel aktiválhatod az összes haladó tréning programot.", + "because only that way can we generated the training plan for you.": "mert csak így tudjuk neked a személyre szabott edzéstervet generálni.", + "This is a premium function": "Ez egy prémium funkció", + "because only that way can we show you your exercises, results and evaluations.": "mert csak így tudjuk neked megmutatni a korábbi gyakorlataidat, eredményeket és kiértékeléseket.", + "because only that way can we show you the personalized development diagrams and analysises": "mert csak így tudjuk neked megmutatni a személyre szabott diagramokat és analíziseket.", + "because only in that way can you begin to execute a training plan": "mert csak így tudod elkezdeni az edzésterved végrehajtását" } \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 93efb97..9e39a62 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 = 3; + CURRENT_PROJECT_VERSION = 4; 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 = 3; + CURRENT_PROJECT_VERSION = 4; 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 = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/lib/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart b/lib/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart deleted file mode 100644 index 3311622..0000000 --- a/lib/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:async'; -import 'package:aitrainer_app/model/exercise_type.dart'; -import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/repository/exercise_plan_repository.dart'; -import 'package:aitrainer_app/repository/workout_tree_repository.dart'; -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:meta/meta.dart'; - -part 'exercise_execute_plan_event.dart'; - -part 'exercise_execute_plan_state.dart'; - -class ExerciseExecutePlanBloc extends Bloc { - final WorkoutTreeRepository menuTreeRepository; - final ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository(); - int? customerId; - int selectedNumber = 0; - - @override - ExerciseExecutePlanBloc({required this.menuTreeRepository}) : super(ExerciseByPlanStateInitial()); - - Future getData() async { - exercisePlanRepository.setCustomerId(customerId!); - await exercisePlanRepository.getLastExercisePlan(); - await exercisePlanRepository.getExercisePlanDetails(); - menuTreeRepository.sortedTree.clear(); - menuTreeRepository.sortByMuscleType(); - - menuTreeRepository.sortedTree.forEach((key, value) { - List listWorkoutTree = value; - listWorkoutTree.forEach((workoutTree) { - workoutTree.selected = false; - if (exercisePlanRepository.getExercisePlanDetailSize() > 0) { - if (exercisePlanRepository.getExercisePlanDetailByExerciseId(workoutTree.exerciseTypeId) != null) { - workoutTree.selected = true; - this.selectedNumber++; - } - } - }); - }); - } - - @override - Stream mapEventToState(ExerciseExecutePlanEvent event) async* { - try { - if (event is ExerciseByPlanLoad) { - yield ExerciseByPlanLoading(); - await this.getData(); - yield ExerciseByPlanReady(); - } else if (event is AddExerciseByPlanEvent) { - yield ExerciseByPlanLoading(); - yield ExerciseByPlanReady(); - } - } on Exception catch (e) { - yield ExerciseByPlanError(message: e.toString()); - } - } -} diff --git a/lib/bloc/exercise_execute_plan/exercise_execute_plan_event.dart b/lib/bloc/exercise_execute_plan/exercise_execute_plan_event.dart deleted file mode 100644 index ff8e83a..0000000 --- a/lib/bloc/exercise_execute_plan/exercise_execute_plan_event.dart +++ /dev/null @@ -1,21 +0,0 @@ -part of 'exercise_execute_plan_bloc.dart'; - -@immutable -abstract class ExerciseExecutePlanEvent extends Equatable { - const ExerciseExecutePlanEvent(); - - @override - List get props => []; -} - -class AddExerciseByPlanEvent extends ExerciseExecutePlanEvent { - final ExerciseType exerciseType; - const AddExerciseByPlanEvent({required this.exerciseType}); - - @override - List get props => [exerciseType]; -} - -class ExerciseByPlanLoad extends ExerciseExecutePlanEvent { - const ExerciseByPlanLoad(); -} diff --git a/lib/bloc/exercise_execute_plan/exercise_execute_plan_state.dart b/lib/bloc/exercise_execute_plan/exercise_execute_plan_state.dart deleted file mode 100644 index 3ecbf68..0000000 --- a/lib/bloc/exercise_execute_plan/exercise_execute_plan_state.dart +++ /dev/null @@ -1,31 +0,0 @@ -part of 'exercise_execute_plan_bloc.dart'; - -@immutable -abstract class ExerciseExecutePlanState extends Equatable { - const ExerciseExecutePlanState(); - - @override - List get props => []; -} - -class ExerciseByPlanStateInitial extends ExerciseExecutePlanState { - const ExerciseByPlanStateInitial(); -} - -class ExerciseByPlanLoading extends ExerciseExecutePlanState { - const ExerciseByPlanLoading(); -} - -// updated screen -class ExerciseByPlanReady extends ExerciseExecutePlanState { - const ExerciseByPlanReady(); -} - -// error splash screen -class ExerciseByPlanError extends ExerciseExecutePlanState { - final String message; - const ExerciseByPlanError({required this.message}); - - @override - List get props => [message]; -} diff --git a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart b/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart deleted file mode 100644 index 4e04269..0000000 --- a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:async'; -import 'package:aitrainer_app/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart'; -import 'package:aitrainer_app/model/cache.dart'; -import 'package:aitrainer_app/model/customer.dart'; -import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/repository/exercise_plan_repository.dart'; -import 'package:aitrainer_app/repository/exercise_repository.dart'; -import 'package:aitrainer_app/util/enums.dart'; -import 'package:aitrainer_app/util/track.dart'; -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:meta/meta.dart'; - -part 'exercise_execute_plan_add_event.dart'; - -part 'exercise_execute_plan_add_state.dart'; - -class ExerciseExecutePlanAddBloc extends Bloc { - final ExerciseRepository exerciseRepository; - final ExercisePlanRepository exercisePlanRepository; - final WorkoutMenuTree workoutTree; - final ExerciseExecutePlanBloc planBloc; - final int customerId; - - late Customer customer; - int step = 1; - int countSteps = 1; - - double? quantity; - double? unitQuantity; - - double scrollOffset = 0; - - @override - ExerciseExecutePlanAddBloc( - {required this.exerciseRepository, - required this.exercisePlanRepository, - required this.customerId, - required this.workoutTree, - required this.planBloc}) - : super(ExerciseExecutePlanAddInitial()); - - void init() { - exerciseRepository.exerciseType = workoutTree.exerciseType; - if (Cache().userLoggedIn!.customerId == customerId) { - customer = Cache().userLoggedIn!; - } else if (Cache().getTrainee()!.customerId == customerId) { - customer = Cache().getTrainee()!; - } - exercisePlanRepository.setActualPlanDetailByExerciseType(workoutTree.exerciseType!); - exerciseRepository.customer = customer; - countSteps = exercisePlanRepository.getActualPlanDetail()!.serie!; - if (exercisePlanRepository.getActualPlanDetail()!.weightEquation == null) { - unitQuantity = 0.0; - } else { - unitQuantity = double.parse(exercisePlanRepository.getActualPlanDetail()!.weightEquation!); - } - quantity = exercisePlanRepository.getActualPlanDetail()!.repeats!.toDouble(); - - exerciseRepository.setQuantity(quantity!); - exerciseRepository.setUnitQuantity(unitQuantity!); - } - - @override - Stream mapEventToState(ExerciseExecutePlanAddEvent event) async* { - try { - if (event is ExerciseExecutePlanAddLoad) { - yield ExerciseExecutePlanAddLoading(); - init(); - Track().track(TrackingEvent.my_exercise_plan_execute_open); - yield ExerciseExecutePlanAddReady(); - } else if (event is ExerciseExecutePlanAddChangeQuantity) { - yield ExerciseExecutePlanAddLoading(); - quantity = event.quantity; - exerciseRepository.setQuantity(quantity!); - yield ExerciseExecutePlanAddReady(); - } else if (event is ExerciseExecutePlanAddChangeUnitQuantity) { - yield ExerciseExecutePlanAddLoading(); - unitQuantity = event.quantity; - exerciseRepository.setUnitQuantity(unitQuantity!); - yield ExerciseExecutePlanAddReady(); - } else if (event is ExerciseExecutePlanAddSubmit) { - yield ExerciseExecutePlanAddLoading(); - exerciseRepository.exercise!.exercisePlanDetailId = exercisePlanRepository.getActualPlanDetail()!.exercisePlanDetailId; - exerciseRepository.exercise!.unit = workoutTree.exerciseType!.unit; - workoutTree.executed = true; - await exerciseRepository.addExercise(); - exerciseRepository.initExercise(); - Track().track(TrackingEvent.my_exercise_plan_execute_save); - step++; - scrollOffset = step * 200.0; - planBloc.add(ExerciseByPlanLoad()); - yield ExerciseExecutePlanAddReady(); - } - } on Exception catch (e) { - yield ExerciseExecutePlanAddError(message: e.toString()); - } - } -} diff --git a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_event.dart b/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_event.dart deleted file mode 100644 index 6b84666..0000000 --- a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_event.dart +++ /dev/null @@ -1,33 +0,0 @@ -part of 'exercise_execute_plan_add_bloc.dart'; - -@immutable -abstract class ExerciseExecutePlanAddEvent extends Equatable { - const ExerciseExecutePlanAddEvent(); - - @override - List get props => []; -} - -class ExerciseExecutePlanAddLoad extends ExerciseExecutePlanAddEvent { - const ExerciseExecutePlanAddLoad(); -} - -class ExerciseExecutePlanAddChangeQuantity extends ExerciseExecutePlanAddEvent { - final double quantity; - const ExerciseExecutePlanAddChangeQuantity({required this.quantity}); - - @override - List get props => [quantity]; -} - -class ExerciseExecutePlanAddChangeUnitQuantity extends ExerciseExecutePlanAddEvent { - final double quantity; - const ExerciseExecutePlanAddChangeUnitQuantity({required this.quantity}); - - @override - List get props => [quantity]; -} - -class ExerciseExecutePlanAddSubmit extends ExerciseExecutePlanAddEvent { - const ExerciseExecutePlanAddSubmit(); -} diff --git a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_state.dart b/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_state.dart deleted file mode 100644 index 3401da1..0000000 --- a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_state.dart +++ /dev/null @@ -1,31 +0,0 @@ -part of 'exercise_execute_plan_add_bloc.dart'; - -@immutable -abstract class ExerciseExecutePlanAddState extends Equatable { - const ExerciseExecutePlanAddState(); - - @override - List get props => []; -} - -class ExerciseExecutePlanAddInitial extends ExerciseExecutePlanAddState { - const ExerciseExecutePlanAddInitial(); -} - -class ExerciseExecutePlanAddLoading extends ExerciseExecutePlanAddState { - const ExerciseExecutePlanAddLoading(); -} - -// updated screen -class ExerciseExecutePlanAddReady extends ExerciseExecutePlanAddState { - const ExerciseExecutePlanAddReady(); -} - -// error splash screen -class ExerciseExecutePlanAddError extends ExerciseExecutePlanAddState { - final String message; - const ExerciseExecutePlanAddError({required this.message}); - - @override - List get props => [message]; -} diff --git a/lib/bloc/login/login_bloc.dart b/lib/bloc/login/login_bloc.dart index 0479f04..53b078f 100644 --- a/lib/bloc/login/login_bloc.dart +++ b/lib/bloc/login/login_bloc.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/repository/split_test_respository.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:aitrainer_app/util/enums.dart'; @@ -19,14 +20,27 @@ class LoginBloc extends Bloc with Trans { final AccountBloc accountBloc; final UserRepository userRepository; final CustomerRepository customerRepository = CustomerRepository(); + final SplitTestRepository splitTestRepository = SplitTestRepository(); final BuildContext context; final bool isRegistration; bool dataPolicyAllowed = false; bool emailSubscription = false; bool obscure = true; + Color testColor = Colors.green[800]!; + bool emailCheckbox = true; + LoginBloc({required this.accountBloc, required this.userRepository, required this.context, required this.isRegistration}) - : super(LoginInitial()); + : super(LoginInitial()) { + String colorString = splitTestRepository.getSplitTestValue("registration_skip"); + if (colorString == "red") { + testColor = Colors.red[800]!; + } + String emailCheckboxString = splitTestRepository.getSplitTestValue("email_checkbox"); + if (emailCheckboxString == "0") { + emailCheckbox = false; + } + } @override Stream mapEventToState( diff --git a/lib/bloc/menu/menu_bloc.dart b/lib/bloc/menu/menu_bloc.dart index a1ab9b3..1381724 100644 --- a/lib/bloc/menu/menu_bloc.dart +++ b/lib/bloc/menu/menu_bloc.dart @@ -66,7 +66,7 @@ class MenuBloc extends Bloc with Trans, Logging { workoutItem = event.item; if (workoutItem != null) { - setAbility(workoutItem!.nameEnglish); + setAbility(workoutItem!.internalName); } final LinkedHashMap branch = menuTreeRepository.getBranch(event.parent); @@ -80,7 +80,7 @@ class MenuBloc extends Bloc with Trans, Logging { LinkedHashMap branch; if (workoutItem != null) { - setAbility(workoutItem!.nameEnglish); + setAbility(workoutItem!.internalName); branch = menuTreeRepository.getBranch(workoutItem!.parent); await getImages(branch); } @@ -92,7 +92,7 @@ class MenuBloc extends Bloc with Trans, Logging { workoutItem = menuTreeRepository.getParentItem(parent); if (workoutItem != null) { - setAbility(workoutItem!.nameEnglish); + setAbility(workoutItem!.internalName); } final LinkedHashMap branch = menuTreeRepository.getBranch(workoutItem!.parent); await getImages(branch); @@ -119,22 +119,19 @@ class MenuBloc extends Bloc with Trans, Logging { void setAbility(String name) { switch (name) { - case "Muscle Build / Shape Toning": + case "one_rep_max": ability = ExerciseAbility.oneRepMax; break; - case "Endurance": - ability = ExerciseAbility.endurance; - break; - case "Cardio": + case "cardio": ability = ExerciseAbility.running; break; - case "Test Center": + case "test_center": ability = ExerciseAbility.mini_test_set; break; - case "Training Plans": + case "training_plans": ability = ExerciseAbility.training; break; - case "My Body": + case "my_body": ability = ExerciseAbility.none; break; } diff --git a/lib/bloc/sales/sales_bloc.dart b/lib/bloc/sales/sales_bloc.dart index a20ba25..1f02350 100644 --- a/lib/bloc/sales/sales_bloc.dart +++ b/lib/bloc/sales/sales_bloc.dart @@ -1,12 +1,11 @@ import 'dart:async'; import 'dart:io'; -import 'dart:math' as math; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/product.dart'; -import 'package:aitrainer_app/model/product_test.dart'; import 'package:aitrainer_app/model/purchase.dart'; import 'package:aitrainer_app/repository/description_repository.dart'; +import 'package:aitrainer_app/repository/split_test_respository.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/service/purchase_service.dart'; import 'package:aitrainer_app/util/enums.dart'; @@ -14,37 +13,43 @@ import 'package:aitrainer_app/util/purchases.dart'; import 'package:aitrainer_app/util/track.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:purchases_flutter/offering_wrapper.dart'; part 'sales_event.dart'; part 'sales_state.dart'; class SalesBloc extends Bloc with Logging { - List? tests = []; List product2Display = []; + List productText2Display = ["WorkoutTest annual", "WorkoutTest montly"]; + final SplitTestRepository splitTestRepository = SplitTestRepository(); + int productSet = -1; final DescriptionRepository descriptionRepository = DescriptionRepository(); SalesBloc() : super(SalesInitial()); String? salesText; + String? premiumFunctions = ""; - String salesButtonText = "

Workout Test Monthly

localizedPrice

cancel any time

"; - Product? offeredProduct; - - Product? getProductByName(String name) { - Product? product; - if (product2Display.isNotEmpty) { - product2Display.forEach((element) { - if (element.type == name) { - product = element; - salesButtonText = salesButtonText.replaceFirst(RegExp(r'localizedPrice'), product!.localizedPrice!); - print("Localized Price ${product!.localizedPrice!} - Text: $salesButtonText"); - } - }); + void init() async { + if (Cache().userLoggedIn == null) { + throw Exception("Please log in"); } - return product; + salesText = splitTestRepository.getSplitTestValue("sales_page_text_a"); + if (salesText == null || salesText!.isEmpty) { + salesText = descriptionRepository.getDescriptionByName("sales_page_text_a"); + } + print("sales Text: $salesText"); + getProductsTexts(); + + premiumFunctions = descriptionRepository.getDescriptionByName("premium_functions"); + if (premiumFunctions == null || premiumFunctions!.isEmpty) { + premiumFunctions = ""; + } + + await RevenueCatPurchases().getOfferings(); + this.getProductSet(); + Track().track(TrackingEvent.sales_page); } @override @@ -53,33 +58,11 @@ class SalesBloc extends Bloc with Logging { ) async* { try { if (event is SalesLoad) { + log(" -- start SalesLoad"); yield SalesLoading(); - log("Load Sales"); - if (Cache().userLoggedIn == null) { - throw Exception("Please log in"); - } - - String descriptionName = "sales_page_text"; - RemoteConfig? remoteConfig = Cache().remoteConfig; - if (remoteConfig != null) { - remoteConfig.fetchAndActivate(); - Map config = remoteConfig.getAll(); - RemoteConfigValue? value = config['sales_page_text_a']; - if (value != null) { - log("RemoteConfig sales_page_text value: ${value.asString()}"); - if (value.asString() == "1") { - descriptionName = "sales_page_text_a"; - } - } - } - await RevenueCatPurchases().getOfferings(); - this.getProductSet(); - salesText = descriptionRepository.getDescriptionByName(descriptionName); - log(salesText!); - salesButtonText = descriptionRepository.getDescriptionByName("sales_button_monthly"); - offeredProduct = getProductByName("wt_sub_2_3"); - Track().track(TrackingEvent.sales_page); + init(); yield SalesReady(); + log(" -- finish SalesLoad"); } else if (event is SalesPurchase) { if (Cache().hasPurchased) { throw Exception("You have already a successfull subscription"); @@ -104,28 +87,34 @@ class SalesBloc extends Bloc with Logging { } else { yield SalesError(message: "No selected product"); } - } else if (event is SalesChangeSubscription) { - yield SalesLoading(); - print("offered product .. $offeredProduct"); - if (offeredProduct != null) { - if (offeredProduct!.type == "wt_sub_2_3") { - print("go yearly"); - salesButtonText = descriptionRepository.getDescriptionByName("sales_button_yearly"); - offeredProduct = getProductByName("wt_sub_2_1"); - } else { - print("go monthly"); - salesButtonText = descriptionRepository.getDescriptionByName("sales_button_monthly"); - offeredProduct = getProductByName("wt_sub_2_3"); - } - } - - yield SalesReady(); } } on Exception catch (ex) { yield SalesError(message: ex.toString()); } } + void getProductsTexts() { + Product product; + if (product2Display.isNotEmpty) { + String salesButtonText; + product2Display.forEach((element) { + product = element; + if (product.sort == 3) { + salesButtonText = descriptionRepository.getDescriptionByName("sales_button_monthly"); + productText2Display[1] = salesButtonText.replaceFirst(RegExp(r'localizedPrice'), product.localizedPrice!); + } else if (product.sort == 1) { + salesButtonText = descriptionRepository.getDescriptionByName("sales_button_yearly"); + productText2Display[0] = salesButtonText.replaceFirst(RegExp(r'localizedPrice'), product.localizedPrice!); + } + }); + } + print("product Text $productText2Display"); + + splitTestRepository.getSplitTestValue("product_set_2"); + + return; + } + Product? getSelectedProduct(int productId) { Product? prod; for (var product in this.product2Display) { @@ -161,38 +150,37 @@ class SalesBloc extends Bloc with Logging { } void getProductSet() { - int productId = 0; - //this.tests = Cache().productTests; List? products = Cache().products; if (products == null) { return; } - /* if (tests != null && tests!.isEmpty) { - var rand = math.Random.secure(); - productSet = rand.nextInt(5) + 1; - } else { - trace("Previous ProductTest: " + tests![0].toJson().toString()); - productId = tests![0].productId; - for (var elem in products) { - final Product product = elem; - if (product.productId == productId) { - productSet = product.productSet; - break; - } - } - } */ + String productSetString = splitTestRepository.getSplitTestValue("product_set_2"); + log("ProductSetString: $productSetString"); + try { + productSet = int.parse(productSetString); + } on Exception catch (e) { + log("Define the right productset!"); + productSet = 2; + } - //ProductTest productTest = ProductTest(); + log("ProductSet: $productSet"); - productSet = 2; - log("ProductSet: " + productSet.toString()); for (var elem in products) { Product product = elem; if (product.productSet == productSet) { - productId = product.productId; - final String platformProductId = Platform.isAndroid ? product.productIdAndroid! : product.productIdIos!; + String? platformProductId; + if (product.productIdAndroid == null || product.productIdIos == null) { + log("Define the product ID for the different Platforms!!"); + } else { + platformProductId = Platform.isAndroid ? product.productIdAndroid! : product.productIdIos!; + } + + if (platformProductId == null) { + log("Not defined platform product id!!"); + platformProductId = ""; + } product.localizedPrice = getLocalizedPrice(platformProductId, product); log("product with localized price: $product"); product2Display.add(product); @@ -203,10 +191,6 @@ class SalesBloc extends Bloc with Logging { return a.sort < b.sort ? -1 : 1; }); - //productTest.productId = productId; - //productTest.customerId = Cache().userLoggedIn!.customerId!; - //productTest.dateView = DateTime.now(); - //ProductTestApi().saveProductTest(productTest); - //Cache().productTests.add(productTest); + this.getProductsTexts(); } } diff --git a/lib/bloc/training_plan/training_plan_bloc.dart b/lib/bloc/training_plan/training_plan_bloc.dart index d2a537c..652112d 100644 --- a/lib/bloc/training_plan/training_plan_bloc.dart +++ b/lib/bloc/training_plan/training_plan_bloc.dart @@ -92,11 +92,16 @@ class TrainingPlanBloc extends Bloc { event.detail.state = ExercisePlanDetailState.inProgress; } + // recalculate the weight to the original planned repeats + if (event.detail.isTest && event.detail.exercises.length == 1) { + trainingPlanRepository.recalculateDetail(_myPlan!.trainingPlanId!, event.detail); + } + exercise.trainingPlanDetailsId = _myPlan!.trainingPlanId; // save Exercise - await ExerciseApi().addExercise(exercise); - Cache().addExercise(exercise); + Exercise savedExercise = await ExerciseApi().addExercise(exercise); + Cache().addExercise(savedExercise); Cache().myTrainingPlan = _myPlan; await Cache().saveMyTrainingPlan(); @@ -108,7 +113,6 @@ class TrainingPlanBloc extends Bloc { } } else if (event is TrainingPlanSkipExercise) { yield TrainingPlanLoading(); - print("Skipping ${event.detail.exerciseTypeId}"); event.detail.state = ExercisePlanDetailState.skipped; Cache().myTrainingPlan = _myPlan; await Cache().saveMyTrainingPlan(); @@ -378,7 +382,8 @@ class TrainingPlanBloc extends Bloc { return 0; } if (_myPlan == null || _myPlan!.details.isEmpty) { - throw Exception("No defined Training Plan"); +// throw Exception("No defined Training Plan"); + return 0; } if (dayNames.isEmpty || dayNames.length == 1) { @@ -402,7 +407,7 @@ class TrainingPlanBloc extends Bloc { } activeDayIndex++; } - print("Active Day Index: $activeDayIndex"); + if (activeDayIndex >= dayNames.length) { activeDayIndex = 0; this.add(TrainingPlanGoToRestart()); @@ -481,4 +486,17 @@ class TrainingPlanBloc extends Bloc { return value.toStringAsFixed(0); } + + bool existsAddedExerciseTypeInTree(String name) { + bool exists = false; + final List? listWorkoutTree = menuBloc.menuTreeRepository.sortedTree[name]; + if (listWorkoutTree != null) { + listWorkoutTree.forEach((element) { + if (element.exerciseType!.trainingPlanState.equalsTo(ExerciseTypeTrainingPlanState.added)) { + exists = true; + } + }); + } + return exists; + } } diff --git a/lib/main.dart b/lib/main.dart index 95e7f14..c2bfaf4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,8 +18,6 @@ import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_welcome_page.dart'; import 'package:aitrainer_app/view/evaluation_page.dart'; import 'package:aitrainer_app/view/exercise_control_page.dart'; -import 'package:aitrainer_app/view/exercise_execute_page.dart'; -import 'package:aitrainer_app/view/exercise_execute_plan_add_page.dart'; import 'package:aitrainer_app/view/exercise_log_page.dart'; import 'package:aitrainer_app/view/exercise_plan_custom_page.dart'; import 'package:aitrainer_app/view/exercise_plan_custom_detail_add_page.dart'; @@ -198,8 +196,8 @@ Future initThirdParty() async { if (!isInDebugMode) { await Flurry.initialize(androidKey: "JNYCTCWBT34FM3J8TV36", iosKey: "3QBG7BSMGPDH24S8TRQP", enableLog: true); FlutterUxcam.optIntoSchematicRecordings(); - PushNotificationsManager().init(); } + PushNotificationsManager().init(); } class WorkoutTestApp extends StatelessWidget { @@ -258,8 +256,6 @@ class WorkoutTestApp extends StatelessWidget { 'exerciseLogPage': (context) => ExerciseLogPage(), 'exercisePlanCustomPage': (context) => ExercisePlanCustomPage(), 'exercisePlanDetailAdd': (context) => ExercisePlanDetailAddPage(), - 'exerciseExecutePlanPage': (context) => ExerciseExecutePage(), - 'exerciseExecuteAddPage': (context) => ExerciseExecutePlanAddPage(), 'mydevelopmentMusclePage': (context) => MyDevelopmentMusclePage(), 'mydevelopmentBodyPage': (context) => MyDevelopmentBodyPage(), 'mydevelopmentSizesPage': (context) => SizesDevelopmentPage(), diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 813a921..cdbbe16 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -14,9 +14,9 @@ import 'package:aitrainer_app/model/faq.dart'; import 'package:aitrainer_app/model/model_change.dart'; import 'package:aitrainer_app/model/product.dart' as wt_product; import 'package:aitrainer_app/model/product.dart'; -import 'package:aitrainer_app/model/product_test.dart'; import 'package:aitrainer_app/model/property.dart'; import 'package:aitrainer_app/model/purchase.dart'; +import 'package:aitrainer_app/model/split_test.dart'; import 'package:aitrainer_app/model/sport.dart'; import 'package:aitrainer_app/model/training_plan.dart'; import 'package:aitrainer_app/model/tutorial.dart'; @@ -136,7 +136,8 @@ class Cache with Logging { List? _sports; List? _products; List _purchases = []; - List? _productTests; + List _splitTests = []; + List _exercisePlanTemplates = []; ExercisePlan? activeExercisePlan; @@ -242,7 +243,7 @@ class Cache with Logging { Map map; try { map = JsonDecoder().convert(savedTrainingPlanJson); - print("Training plan: $savedTrainingPlanJson"); + //print("Training plan: $savedTrainingPlanJson"); this.myTrainingPlan = CustomerTrainingPlan.fromJsonWithDetails(map); } on Exception catch (e) { print(e.toString()); @@ -662,11 +663,6 @@ class Cache with Logging { List get purchases => _purchases; setPurchases(List value) => _purchases = value; - // ignore: unnecessary_getters_setters - List? get productTests => _productTests; - // ignore: unnecessary_getters_setters - set productTests(List? value) => _productTests = value; - Future initCustomer(int customerId) async { log(" *** initCustomer"); await PackageApi().getCustomerPackage(customerId); @@ -738,4 +734,7 @@ class Cache with Logging { List? getCustomerTrainingPlans() => this._customerTrainingPlans; setCustomerTrainingPlans(value) => this._customerTrainingPlans = value; + + List getSplitTests() => this._splitTests; + setSplitTests(value) => this._splitTests = value; } diff --git a/lib/model/customer_training_plan.dart b/lib/model/customer_training_plan.dart index 8dca970..835a071 100644 --- a/lib/model/customer_training_plan.dart +++ b/lib/model/customer_training_plan.dart @@ -57,10 +57,6 @@ class CustomerTrainingPlan { jsonDetails = jsonDetails.replaceAllMapped(RegExp(r'([0-9]{4}\-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})'), (Match m) => "\"${m[0]}\""); - // jsonDetails = jsonDetails.replaceAll(r'\"null\"', 'null'); - // jsonDetails = jsonDetails.replaceAll(r'\"false\"', 'false'); - // jsonDetails = jsonDetails.replaceAll(r'\"true\"', 'true'); - print("detail: $jsonDetails"); Iterable iterable = jsonDecode(jsonDetails); diff --git a/lib/model/customer_training_plan_details.dart b/lib/model/customer_training_plan_details.dart index ba7531f..21f993e 100644 --- a/lib/model/customer_training_plan_details.dart +++ b/lib/model/customer_training_plan_details.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; @@ -9,6 +7,9 @@ class CustomerTrainingPlanDetails { /// customerTrainingPlanDetails int? customerTrainingPlanDetailsId; + /// trainingPlanDetailsId + int? trainingPlanDetailsId; + /// exerciseTypeId int? exerciseTypeId; @@ -32,6 +33,8 @@ class CustomerTrainingPlanDetails { List exercises = []; + bool isTest = false; + CustomerTrainingPlanDetails(); CustomerTrainingPlanDetails.fromJson(Map json) { @@ -49,6 +52,7 @@ class CustomerTrainingPlanDetails { this.customerTrainingPlanDetailsId = json['customerTrainingPlanDetailsId'] == "null" || json['customerTrainingPlanDetailsId'] == null ? 0 : json['customerTrainingPlanDetailsId']; + this.trainingPlanDetailsId = json['trainingPlanDetailsId']; this.exerciseTypeId = json['exerciseTypeId']; this.set = json['set']; this.repeats = json['repeats'] == "null" ? -1 : json['repeats']; @@ -79,6 +83,7 @@ class CustomerTrainingPlanDetails { } else { this.state = ExercisePlanDetailState.start; } + this.isTest = json['isTest'] == "true" ? true : false; this.exerciseType = Cache().getExerciseTypeById(exerciseTypeId!); } @@ -98,7 +103,8 @@ class CustomerTrainingPlanDetails { Map toJsonWithExercises() { final Map jsonMap = { - //"customerTrainingPlanDetailsId": this.customerTrainingPlanDetailsId, + "customerTrainingPlanDetailsId": this.customerTrainingPlanDetailsId, + "trainingPlanDetailsId": this.trainingPlanDetailsId, "exerciseTypeId": this.exerciseTypeId, "set": this.set, "repeats": this.repeats, @@ -107,6 +113,7 @@ class CustomerTrainingPlanDetails { "parallel": this.parallel, 'exercises': exercises.isEmpty ? [].toString() : exercises.map((exercise) => exercise.toJson()).toList().toString(), 'state': this.state.toStr(), + "isTest": this.isTest, }; if (this.day != null && this.day!.isNotEmpty) { jsonMap["day"] = this.day; diff --git a/lib/model/product_test.dart b/lib/model/product_test.dart deleted file mode 100644 index ce1de3b..0000000 --- a/lib/model/product_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:intl/intl.dart'; - -class ProductTest { - late int? productTestId; - late int customerId; - late int productId; - - late DateTime dateView; - late bool purchaseClick; - - ProductTest(); - - ProductTest.fromJson(Map json) { - this.productTestId = json['productTestId']; - this.customerId = json['customerId']; - this.productId = json['productId']; - this.dateView = DateTime.parse(json['dateView']); - this.purchaseClick = json['purchaseClick']; - } - - Map toJson() { - if (productTestId != null) { - return { - "productTestId": productTestId, - "customerId": customerId, - "productId": productId, - "dateView": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateView), - "purchaseClick": purchaseClick - }; - } else { - return { - "customerId": customerId, - "productId": productId, - "dateView": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateView), - "purchaseClick": purchaseClick - }; - } - } -} diff --git a/lib/model/split_test.dart b/lib/model/split_test.dart new file mode 100644 index 0000000..a2917c1 --- /dev/null +++ b/lib/model/split_test.dart @@ -0,0 +1,36 @@ +class SplitTest { + late int testId; + late String name; + late String remoteConfigKey; + late String remoteConfigValue; + late String testValue; + String? source; + late bool active; + DateTime? validTo; + + SplitTest.fromJson(Map json) { + this.testId = json['testId']; + this.name = json['name']; + this.remoteConfigKey = json['remoteConfigKey']; + this.remoteConfigValue = json['remoteConfigValue']; + this.testValue = json['testValue']; + this.source = json['source']; + this.active = json['active']; + this.validTo = json['validTo'] == null ? null : DateTime.parse(json['validTo']); + } + + @override + String toString() { + Map json = { + 'productId': this.testId, + 'name': this.name, + 'remoteConfigKey': this.remoteConfigKey, + 'remoteConfigValue': this.remoteConfigValue, + 'testValue': this.testValue, + 'source': this.source, + 'active': this.active, + 'validTo': validTo, + }; + return json.toString(); + } +} diff --git a/lib/push_notifications.dart b/lib/push_notifications.dart index f4d7797..882ea3d 100644 --- a/lib/push_notifications.dart +++ b/lib/push_notifications.dart @@ -10,6 +10,7 @@ class PushNotificationsManager with Logging { static final PushNotificationsManager _instance = PushNotificationsManager._(); Future init() async { + log(" --- Firebase Messagein init.."); const AndroidNotificationChannel channel = AndroidNotificationChannel( 'high_importance_channel', // id 'High Importance Notifications', // title @@ -28,6 +29,6 @@ class PushNotificationsManager with Logging { sound: true, ); String? token = await FirebaseMessaging.instance.getToken(); - print("FirebaseMessaging tokne $token"); + log("FirebaseMessaging token $token"); } } diff --git a/lib/repository/customer_repository.dart b/lib/repository/customer_repository.dart index bd71ee4..0425300 100644 --- a/lib/repository/customer_repository.dart +++ b/lib/repository/customer_repository.dart @@ -3,17 +3,14 @@ import 'dart:collection'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/customer_property.dart'; -import 'package:aitrainer_app/model/product_test.dart'; import 'package:aitrainer_app/model/property.dart'; import 'package:aitrainer_app/model/purchase.dart'; import 'package:aitrainer_app/model/sport.dart'; import 'package:aitrainer_app/repository/property_repository.dart'; import 'package:aitrainer_app/service/customer_service.dart'; import 'package:aitrainer_app/service/logging.dart'; -import 'package:aitrainer_app/service/product_test_service.dart'; import 'package:aitrainer_app/service/purchase_service.dart'; import 'package:aitrainer_app/util/enums.dart'; -import 'package:aitrainer_app/util/not_found_exception.dart'; class GenderItem { GenderItem(this.dbValue, this.name); @@ -354,25 +351,6 @@ class CustomerRepository with Logging { await PurchaseApi().savePurchase(purchase); } - Future> getProductTests() async { - List tests = []; - try { - int customerId = Cache().userLoggedIn!.customerId!; - tests = await ProductTestApi().getProductTestByCustomer(customerId); - } on NotFoundException catch (_) { - log("Product Tests not found"); - Cache().productTests = tests; - } on Exception catch (ex) { - log(ex.toString()); - Cache().productTests = tests; - } - return tests; - } - - Future addProductTest(ProductTest productTest) async { - await ProductTestApi().saveProductTest(productTest); - } - void setMediaDimensions(double width, double height) { this.mediaHeight = height; this.mediaWidth = width; diff --git a/lib/repository/description_repository.dart b/lib/repository/description_repository.dart index b00b3f8..f8c8529 100644 --- a/lib/repository/description_repository.dart +++ b/lib/repository/description_repository.dart @@ -1,5 +1,8 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/description.dart'; +import 'package:aitrainer_app/util/app_language.dart'; + +import 'package:flutter/material.dart'; class DescriptionRepository { List? descriptions; @@ -12,9 +15,12 @@ class DescriptionRepository { String descriptionText = ""; if (descriptions != null) { this.descriptions!.forEach((element) { - print("Desc ${element.name} - $name"); if (element.name == name) { - descriptionText = element.descriptionTranslation != null ? element.descriptionTranslation! : element.description; + if (AppLanguage().appLocal == Locale('en')) { + descriptionText = element.description; + } else { + descriptionText = element.descriptionTranslation != null ? element.descriptionTranslation! : element.description; + } } }); } diff --git a/lib/repository/split_test_respository.dart b/lib/repository/split_test_respository.dart new file mode 100644 index 0000000..43d7a38 --- /dev/null +++ b/lib/repository/split_test_respository.dart @@ -0,0 +1,63 @@ +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/repository/description_repository.dart'; +import 'package:aitrainer_app/service/logging.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; + +class SplitTestRepository with Logging { + final RemoteConfig? _remoteConfig = Cache().remoteConfig; + final DescriptionRepository descriptionRepository = DescriptionRepository(); + + String getSplitTestValue(String remoteConfigKey) { + String testValue = ""; + + if (_remoteConfig != null) { + _remoteConfig!.fetchAndActivate(); + Map configs = _remoteConfig!.getAll(); + RemoteConfigValue? value = configs[remoteConfigKey]; + if (value != null) { + log("A/B Test RemoteConfig $remoteConfigKey value: ${value.asString()}"); + final String remoteConfigValue = value.asString(); + testValue = this.getSplitTestValueByRemoteConfig(remoteConfigKey, remoteConfigValue); + } else { + log("RemoteConfig value $remoteConfigKey is null!!"); + } + } else { + log(" !! remoteConfig isnull"); + } + + return testValue; + } + + String getSource(String remoteConfigKey) { + String source = ""; + + return source; + } + + String getSplitTestValueByRemoteConfig(String key, String value) { + String testValue = ""; + if (Cache().getSplitTests().isEmpty) { + log("Splittests empty"); + return testValue; + } + + Cache().getSplitTests().forEach((element) { + if (element.remoteConfigKey == key && element.remoteConfigValue == value && element.active) { + testValue = element.testValue; + log("A/B Test testValue: $testValue"); + + if (element.source != null && element.source!.isNotEmpty) { + final List sourceElements = element.source!.split("."); + if (sourceElements.length > 1) { + final String modelName = sourceElements[0]; + if (modelName == "description") { + testValue = descriptionRepository.getDescriptionByName(element.testValue); + } + } + } + } + }); + + return testValue; + } +} diff --git a/lib/repository/training_plan_repository.dart b/lib/repository/training_plan_repository.dart index 1d9294d..311fa16 100644 --- a/lib/repository/training_plan_repository.dart +++ b/lib/repository/training_plan_repository.dart @@ -65,8 +65,11 @@ class TrainingPlanRepository { } // 3 calculate weights + int index = 0; trainingPlan.details!.forEach((elem) { CustomerTrainingPlanDetails detail = CustomerTrainingPlanDetails(); + detail.customerTrainingPlanDetailsId = ++index; + detail.trainingPlanDetailsId = elem.trainingPlanDetailId; detail.exerciseTypeId = elem.exerciseTypeId; detail.repeats = elem.repeats; detail.set = elem.set; @@ -111,6 +114,7 @@ class TrainingPlanRepository { double weight = -1; if (Cache().getExercises() == null) { detail.weight = weight; + detail.isTest = true; return detail; } @@ -124,6 +128,7 @@ class TrainingPlanRepository { if (lastExercise1RM == null || lastExercise1RM!.unitQuantity == null) { detail.weight = weight; + detail.isTest = true; return detail; } @@ -138,4 +143,34 @@ class TrainingPlanRepository { detail.weight = weight; return detail; } + + CustomerTrainingPlanDetails recalculateDetail(int trainingPlanId, CustomerTrainingPlanDetails detail) { + CustomerTrainingPlanDetails recalculatedDetail = detail; + + // 1. get original repeats + + // 1a get original plan + TrainingPlan? plan = getTrainingPlanById(trainingPlanId); + if (plan == null) { + return recalculatedDetail; + } + + // 1.b get the original detail's repeat + int originalRepeats = detail.repeats!; + plan.details!.forEach((element) { + if (element.trainingPlanDetailId == detail.trainingPlanDetailsId) { + print("element $element"); + originalRepeats = element.repeats; + } + }); + + // 2 get recalculated repeats + + recalculatedDetail.weight = + Common.calculateWeigthByChangedQuantity(detail.weight!, detail.repeats!.toDouble(), originalRepeats.toDouble()); + print("recalculated repeats for $originalRepeats: ${recalculatedDetail.weight}"); + recalculatedDetail.repeats = originalRepeats; + + return recalculatedDetail; + } } diff --git a/lib/service/exercise_service.dart b/lib/service/exercise_service.dart index 28542c7..9a71846 100644 --- a/lib/service/exercise_service.dart +++ b/lib/service/exercise_service.dart @@ -33,6 +33,9 @@ class ExerciseApi with Logging { } Future deleteExercise(Exercise exercise) async { + if (exercise.exerciseId == null) { + return; + } int exerciseId = exercise.exerciseId!; log(" ===== delete exercise: " + exerciseId.toString()); await _client.post("exercises/" + exerciseId.toString(), ""); diff --git a/lib/service/firebase_api.dart b/lib/service/firebase_api.dart index 3c15bd9..0ae3f0b 100644 --- a/lib/service/firebase_api.dart +++ b/lib/service/firebase_api.dart @@ -291,21 +291,25 @@ class FirebaseApi with logging.Logging { Future setupRemoteConfig() async { initializeFlutterFire(); - final RemoteConfig remoteConfig = RemoteConfig.instance; - await remoteConfig.setConfigSettings(RemoteConfigSettings( - fetchTimeout: const Duration(seconds: 10), - minimumFetchInterval: const Duration(seconds: 1), - )); - await remoteConfig.setDefaults({ - 'sales_page_text': '0', - 'sales_page_bkg': 'dark', - 'sales_page_offer': 'monthly', - 'please_log_in': '', - }); - RemoteConfigValue(null, ValueSource.valueStatic); - Cache().setRemoteConfig(remoteConfig); + RemoteConfig? remoteConfig; + try { + remoteConfig = RemoteConfig.instance; + await remoteConfig.setConfigSettings(RemoteConfigSettings( + fetchTimeout: const Duration(seconds: 60), + minimumFetchInterval: const Duration(hours: 6), + )); - Map config = remoteConfig.getAll(); - print("RemoteConfig sales_page_text Value : ${config['sales_page_text'].asString()}"); + RemoteConfigValue(null, ValueSource.valueStatic); + Cache().setRemoteConfig(remoteConfig); + } on Exception catch (e) { + print('Unable to fetch remote config. Cached or default values will be used: $e'); + if (remoteConfig != null) { + await remoteConfig.setDefaults({ + 'sales_page_text_a': '', + 'product_set_2': '', + }); + Cache().setRemoteConfig(remoteConfig); + } + } } } diff --git a/lib/service/package_service.dart b/lib/service/package_service.dart index 14e8918..89e2c71 100644 --- a/lib/service/package_service.dart +++ b/lib/service/package_service.dart @@ -15,9 +15,9 @@ import 'package:aitrainer_app/model/exercise_tree_parents.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/model/faq.dart'; import 'package:aitrainer_app/model/product.dart'; -import 'package:aitrainer_app/model/product_test.dart'; import 'package:aitrainer_app/model/property.dart'; import 'package:aitrainer_app/model/purchase.dart'; +import 'package:aitrainer_app/model/split_test.dart'; import 'package:aitrainer_app/model/training_plan.dart'; import 'package:aitrainer_app/model/tutorial.dart'; import 'package:aitrainer_app/service/api.dart'; @@ -92,6 +92,11 @@ class PackageApi { final List? plans = json.map((plan) => TrainingPlan.fromJson(plan)).toList(); Cache().setTrainingPlans(plans); + } else if (headRecord[0] == "SplitTests") { + final Iterable json = jsonDecode(headRecord[1]); + final List? tests = json.map((test) => SplitTest.fromJson(test)).toList(); + //print("A/B tests: $tests"); + Cache().setSplitTests(tests); } }); @@ -156,10 +161,6 @@ class PackageApi { final Iterable json = jsonDecode(headRecord[1]); final List exercises = json.map((exerciseType) => Exercise.fromJson(exerciseType)).toList(); Cache().setExercises(exercises); - } else if (headRecord[0] == "ProductTest") { - final Iterable json = jsonDecode(headRecord[1]); - final List productTests = json.map((productTest) => ProductTest.fromJson(productTest)).toList(); - Cache().productTests = productTests; } else if (headRecord[0] == "Purchase") { final Iterable json = jsonDecode(headRecord[1]); final List purchases = json.map((purchase) => Purchase.fromJson(purchase)).toList(); diff --git a/lib/service/product_test_service.dart b/lib/service/product_test_service.dart deleted file mode 100644 index a96b524..0000000 --- a/lib/service/product_test_service.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:aitrainer_app/model/cache.dart'; -import 'package:aitrainer_app/model/product_test.dart'; -import 'package:aitrainer_app/service/logging.dart'; -import 'dart:convert'; -import 'api.dart'; - -class ProductTestApi with Logging { - final APIClient _client = new APIClient(); - - Future> getProductTestByCustomer(int customerId) async { - final body = await _client.get("product_test/customer/" + customerId.toString(), ""); - final Iterable json = jsonDecode(body); - final List productTests = json.map((productTest) => ProductTest.fromJson(productTest)).toList(); - Cache().productTests = productTests; - return productTests; - } - - Future saveProductTest(ProductTest productTest) async { - String body = JsonEncoder().convert(productTest.toJson()); - log(" ===== saving productTest:" + body); - await _client.post("product_test/", body); - } -} diff --git a/lib/util/common.dart b/lib/util/common.dart index 76b7a18..e190bbe 100644 --- a/lib/util/common.dart +++ b/lib/util/common.dart @@ -139,6 +139,9 @@ mixin Common { } static normalizeDecimal(String value) { + if (value.isEmpty) { + return 0; + } value = value.replaceFirst(",", "."); value = value.replaceAll(RegExp(r'[^0-9.]'), ""); return value; @@ -193,7 +196,6 @@ mixin Common { static int calculateQuantityByChangedWeight(double initialRM, double weight, double repeat) { final double rmWendler = weight * repeat * 0.0333 + weight; final double rmOconner = weight * (1 + repeat / 40); - //print("Weight: $weight oneRepQuantity: $repeat, $rmWendler, Oconner: $rmOconner"); final double repeatWendler = (rmWendler - weight) / 0.0333 / weight; final double repeatOconner = (rmOconner / weight - 1) * 40; @@ -201,4 +203,17 @@ mixin Common { //print("Initial 1RM: $initialRM Weight: $weight repeatWendler: $repeatWendler repeat Oconner: $repeatOconner. NEW REPEAT: $newRepeat"); return newRepeat; } + + static double calculateWeigthByChangedQuantity(double weight, double repeat, double changedRepeats) { + final double rmWendler = weight * repeat * 0.0333 + weight; + final double rmOconner = weight * (1 + repeat / 40); + final double initialRM = (rmWendler + rmOconner) / 2; + + final double weightWendler = rmWendler / (changedRepeats * 0.0333 + 1); + final double weightOconner = rmOconner / (1 + changedRepeats / 40); + final double newWeight = ((weightWendler + weightOconner) / 2); + print( + "Initial 1RM: $initialRM repeat: $repeat changedRepeat: $changedRepeats Weight: $weight weightWendler: $weightWendler weight Oconner: $weightOconner. NEW WEIGHT: $newWeight"); + return newWeight; + } } diff --git a/lib/view/exercise_execute_page.dart b/lib/view/exercise_execute_page.dart deleted file mode 100644 index af92f1d..0000000 --- a/lib/view/exercise_execute_page.dart +++ /dev/null @@ -1,227 +0,0 @@ -import 'dart:collection'; -import 'package:aitrainer_app/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart'; -import 'package:aitrainer_app/model/cache.dart'; -import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/library/tree_view.dart'; -import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar.dart'; -import 'package:aitrainer_app/widgets/bottom_nav.dart'; -import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; -import 'package:flutter/cupertino.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'; - -class ExerciseExecutePage extends StatefulWidget { - @override - _ExerciseExecutePage createState() => _ExerciseExecutePage(); -} - -class _ExerciseExecutePage extends State with Trans { - final GlobalKey _scaffoldKey = new GlobalKey(); - // ignore: close_sinks - late ExerciseExecutePlanBloc bloc; - - @override - void initState() { - super.initState(); - - /// We require the initializers to run after the loading screen is rendered - SchedulerBinding.instance!.addPostFrameCallback((_) { - BlocProvider.of(context).add(ExerciseByPlanLoad()); - }); - } - - @override - Widget build(BuildContext context) { - LinkedHashMap arguments = ModalRoute.of(context)!.settings.arguments as LinkedHashMap; - final int customerId = arguments['customerId']; - bloc = BlocProvider.of(context); - bloc.customerId = customerId; - setContext(context); - - return Scaffold( - key: _scaffoldKey, - appBar: AppBarNav(depth: 1), - body: Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - image: DecorationImage( - image: customerId == Cache().userLoggedIn!.customerId - ? AssetImage('asset/image/WT_black_background.jpg') - : AssetImage('asset/image/WT_light_background.jpg'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), - ), - child: BlocConsumer(listener: (context, state) { - if (state is ExerciseByPlanError) { - //LoadingDialog.hide(context); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - state.message, - ), - backgroundColor: Colors.orange, - )); - } else if (state is ExerciseByPlanLoading) { - //LoadingDialog.show(context); - } - }, builder: (context, state) { - if (state is ExerciseByPlanStateInitial || state is ExerciseByPlanLoading) { - return Container(); - } else if (state is ExerciseByPlanReady) { - //LoadingDialog.hide(context); - return exerciseWidget(bloc); - } else { - return exerciseWidget(bloc); - } - })), - bottomNavigationBar: BottomNavigator(bottomNavIndex: 2), - ); - } - - Widget exerciseWidget(ExerciseExecutePlanBloc bloc) { - return TreeView( - startExpanded: false, - children: nodeExercisePlan(bloc), - ); - } - - List nodeExercisePlan(ExerciseExecutePlanBloc bloc) { - List exerciseTypes = []; - Card explanation = Card( - color: Colors.white38, - child: Container( - padding: EdgeInsets.only(left: 10, right: 5, top: 12, bottom: 8), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Row( - children: [ - Icon( - Icons.info, - color: Colors.orangeAccent, - ), - Text(" "), - Flexible( - child: Text( - t("Execute your active Exercise Plan!"), - style: GoogleFonts.archivoBlack( - fontSize: 20, - ), - maxLines: 2, - ), - ), - ], - ), - Divider( - color: Colors.transparent, - ), - Text( - t("Select the muscle type and tap on the exercise. One the next page enter the weight and repeat."), - style: TextStyle(fontSize: 12, fontWeight: FontWeight.normal), - ), - ], - ))); - - exerciseTypes.add(explanation); - - if (bloc.selectedNumber == 0) { - exerciseTypes.add(Container( - child: Center( - child: Text( - t("Please define your Exercise Plan"), - style: GoogleFonts.inter(color: Colors.white), - )))); - exerciseTypes.add(Container( - child: Center( - child: Text( - t("Go to: 'Training Plan' - 'Edit My Custom Plan'"), - style: GoogleFonts.inter(color: Colors.white), - )))); - exerciseTypes.add(Container( - child: Center( - child: InkWell( - onTap: () { - final LinkedHashMap args = LinkedHashMap(); - args['customerId'] = Cache().userLoggedIn!.customerId; - Navigator.of(context).pop(); - Navigator.of(context).pushNamed('exercisePlanCustomPage', arguments: args); - }, - child: Text( - t("Jump there »"), - style: GoogleFonts.inter(color: Colors.blue[200], decorationStyle: TextDecorationStyle.solid), - ))))); - } else { - bloc.menuTreeRepository.sortedTree.forEach((name, list) { - exerciseTypes.add(Container( - margin: const EdgeInsets.only(left: 4.0), - child: TreeViewChild( - startExpanded: true, - parent: TreeviewParentWidget(text: name), - children: _getChildList(list, bloc), - ))); - }); - } - - return exerciseTypes; - } - - List _getChildList(List listWorkoutTree, ExerciseExecutePlanBloc bloc) { - List list = []; - listWorkoutTree.forEach((element) { - if (element.selected) { - list.add(TreeViewChild( - startExpanded: false, - parent: Card( - margin: EdgeInsets.only(left: 10, top: 5), - color: Colors.white54, - child: Container( - padding: const EdgeInsets.only(left: 5, top: 0, right: 5, bottom: 0), - child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [ - IconButton( - icon: element.executed - ? Icon(Icons.check_box, color: Colors.green[200]) - : Icon( - Icons.indeterminate_check_box, - color: Colors.blue.shade800, - ), - onPressed: () => {addExerciseByPlanEvent(bloc, element)}, - ), - SizedBox(width: 20), - Flexible( - fit: FlexFit.tight, - child: InkWell( - child: Text( - element.name, - textAlign: TextAlign.start, - style: GoogleFonts.inter(fontSize: 17, color: Colors.black), - ), - onTap: () => {addExerciseByPlanEvent(bloc, element)}, - ), - ), - IconButton( - padding: EdgeInsets.all(0), - icon: Icon( - Icons.info, - color: Colors.black12, - ), - onPressed: () {}, - ), - ]), - )), - children: [])); - } - }); - return list; - } - - void addExerciseByPlanEvent(ExerciseExecutePlanBloc bloc, WorkoutMenuTree workoutTree) { - LinkedHashMap args = LinkedHashMap(); - args['blocExerciseByPlan'] = bloc; - args['customerId'] = bloc.customerId; - args['workoutTree'] = workoutTree; - Navigator.of(context).pushNamed("exerciseExecuteAddPage", arguments: args); - } -} diff --git a/lib/view/exercise_execute_plan_add_page.dart b/lib/view/exercise_execute_plan_add_page.dart deleted file mode 100644 index 70bdfe3..0000000 --- a/lib/view/exercise_execute_plan_add_page.dart +++ /dev/null @@ -1,284 +0,0 @@ -import 'dart:collection'; - -import 'package:aitrainer_app/bloc/exercise_execute_plan/exercise_execute_plan_bloc.dart'; -import 'package:aitrainer_app/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart'; -import 'package:aitrainer_app/library/custom_icon_icons.dart'; -import 'package:aitrainer_app/util/app_language.dart'; -import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/repository/exercise_repository.dart'; -import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar.dart'; -import 'package:aitrainer_app/widgets/number_picker.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; - -class ExerciseExecutePlanAddPage extends StatefulWidget { - _ExerciseExecuteAddPage createState() => _ExerciseExecuteAddPage(); -} - -class _ExerciseExecuteAddPage extends State with Trans { - final ScrollController _controller = ScrollController(); - double offset = 0; - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - LinkedHashMap arguments = ModalRoute.of(context)!.settings.arguments as LinkedHashMap; - // ignore: close_sinks - final ExerciseExecutePlanBloc planBloc = arguments['blocExerciseByPlan']; - final int customerId = arguments['customerId']; - final WorkoutMenuTree workoutTree = arguments['workoutTree']; - final ExerciseRepository exerciseRepository = ExerciseRepository(); - setContext(context); - - return BlocProvider( - create: (context) => ExerciseExecutePlanAddBloc( - exerciseRepository: exerciseRepository, - exercisePlanRepository: planBloc.exercisePlanRepository, - customerId: customerId, - workoutTree: workoutTree, - planBloc: planBloc) - ..add(ExerciseExecutePlanAddLoad()), - child: BlocConsumer(listener: (context, state) { - if (state is ExerciseExecutePlanAddError) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); - } - }, builder: (context, state) { - // ignore: close_sinks - final exerciseBloc = BlocProvider.of(context); - if (state is ExerciseExecutePlanAddReady && _controller.hasClients) { - _controller.animateTo(exerciseBloc.scrollOffset, duration: Duration(milliseconds: 300), curve: Curves.easeIn); - } - return ModalProgressHUD( - child: getControlForm(exerciseBloc), - inAsyncCall: state is ExerciseExecutePlanAddLoading, - opacity: 0.5, - color: Colors.black54, - progressIndicator: CircularProgressIndicator(), - ); - })); - } - - Widget getControlForm(ExerciseExecutePlanAddBloc exerciseBloc) { - if (exerciseBloc.exerciseRepository.exerciseType == null || exerciseBloc.quantity == null) { - return Offstage(); - } - String exerciseName = AppLanguage().appLocal == Locale("en") - ? exerciseBloc.exerciseRepository.exerciseType!.name - : exerciseBloc.exerciseRepository.exerciseType!.nameTranslation; - - return Form( - child: Scaffold( - resizeToAvoidBottomInset: true, - appBar: AppBarNav(depth: 1), - body: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_black_background.jpg'), - fit: BoxFit.fill, - alignment: Alignment.center, - ), - ), - child: Container( - padding: const EdgeInsets.only(top: 25, left: 25, right: 25), - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - physics: BouncingScrollPhysics(), - controller: _controller, - child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Text( - t("Save Exercise"), - style: GoogleFonts.inter(fontSize: 16, color: Colors.orange[50]), - ), - Text( - exerciseName, - style: GoogleFonts.archivoBlack( - fontSize: 24, - color: Colors.orange[700], - 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, - ), - ], - ), - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - maxLines: 3, - softWrap: true, - ), - Divider( - color: Colors.transparent, - ), - Divider(), - Column( - children: repeatExercises(exerciseBloc), - ), - Divider(), - ]), - ))), - ), - ); - } - - List repeatExercises(ExerciseExecutePlanAddBloc exerciseBloc) { - List listColumns = []; - for (int i = 0; i < exerciseBloc.countSteps; i++) { - Column col = Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Divider( - color: Colors.transparent, - ), - RichText( - text: TextSpan( - style: GoogleFonts.inter( - fontSize: 16, - fontWeight: FontWeight.normal, - color: Colors.yellow[300], - 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, - ), - ], - ), - children: [ - TextSpan(text: t("Execute the") + " "), - TextSpan( - text: (i + 1).toString() + ". ", - style: GoogleFonts.inter( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.yellow[600], - ), - ), - TextSpan(text: t("set!")) - ]), - ), - Divider( - color: Colors.transparent, - ), - Row(mainAxisAlignment: MainAxisAlignment.start, children: [ - exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit == null - ? Offstage() - : NumberPickerWidget( - minValue: 0, - maxValue: 1000, - fontSize: 16, - initalValue: exerciseBloc.unitQuantity!.toInt(), - unit: t(exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!), - color: Colors.yellow[50]!, - onChange: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeUnitQuantity(quantity: value.toDouble()))}), - NumberPickerWidget( - minValue: 0, - maxValue: 200, - fontSize: 16, - initalValue: exerciseBloc.quantity!.toInt(), - unit: t(exerciseBloc.exerciseRepository.exerciseType!.unit), //t("repeat"), - color: Colors.yellow[50]!, - onChange: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeQuantity(quantity: value.toDouble()))}), - ]), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.all(0), - primary: Colors.white, - onSurface: Colors.blueAccent, - ), - onPressed: () => { - if (exerciseBloc.step == i + 1) {exerciseBloc.add(ExerciseExecutePlanAddSubmit())}, - if (i + 1 == exerciseBloc.countSteps) {Navigator.of(context).pop()} - }, - child: exerciseBloc.step == i + 1 - ? Stack( - alignment: Alignment.center, - children: [ - Image.asset('asset/icon/gomb_orange_c.png', width: 140, height: 60), - Text( - t("Save"), - style: TextStyle(fontSize: 16, color: Colors.white), - ), - ], - ) - : Stack( - alignment: Alignment.center, - children: getButton(i + 1, exerciseBloc), - )), - /* Row(children: [ - NumberPicker.horizontal( - highlightSelectedValue: (i + 1) == exerciseBloc.step, - initialValue: exerciseBloc.quantity.toInt(), - minValue: 0, - maxValue: 200, - step: 1, - textStyle: TextStyle(fontWeight: FontWeight.bold), - textStyleHighlighted: TextStyle(fontSize: 24, color: Colors.deepOrange, fontWeight: FontWeight.bold), - onChanged: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeQuantity(quantity: value.toDouble()))}, - listViewHeight: 80, - //decoration: _decoration, - ), - Text(t("repeat")), - ]), */ - /* RaisedButton( - padding: EdgeInsets.all(0), - textColor: Colors.white, - color: exerciseBloc.step == i + 1 ? Colors.blue : Colors.black26, - focusColor: Colors.blueAccent, - onPressed: () => { - if (exerciseBloc.step == i + 1) {exerciseBloc.add(ExerciseExecutePlanAddSubmit())}, - if (i + 1 == exerciseBloc.countSteps) {Navigator.of(context).pop()} - }, - child: Text( - t("Save"), - style: TextStyle(fontSize: 12), - )), */ - Divider(), - ], - ); - listColumns.add(col); - } - return listColumns; - } - - List getButton(int step, ExerciseExecutePlanAddBloc exerciseBloc) { - List widgets = []; - if (step < exerciseBloc.step) { - widgets.add(Icon( - CustomIcon.check_circle, - color: Color(0xffb4f500), - size: 36, - )); - } else { - widgets.add(Icon( - CustomIcon.question, - color: Colors.grey[700], - size: 36, - )); - } - return widgets; - } -} diff --git a/lib/view/exercise_log_page.dart b/lib/view/exercise_log_page.dart index bfdb327..3a11d82 100644 --- a/lib/view/exercise_log_page.dart +++ b/lib/view/exercise_log_page.dart @@ -220,7 +220,7 @@ class ExerciseLogPage extends StatelessWidget with Trans, Common { return DialogPremium( unlocked: Cache().hasPurchased, unlockRound: 1, - unlockedText: t("Enjoy also this premium fetaure to show all old evaluation data of your successful exercises."), + unlockedText: t("Enjoy also this premium feature to show all old evaluation data of your successful exercises."), function: "My Exercise Logs", onTap: () => {Navigator.of(context).pop()}, onCancel: () => {Navigator.of(context).pop()}, diff --git a/lib/view/mydevelopment_page.dart b/lib/view/mydevelopment_page.dart index 6ea4589..5a55dc3 100644 --- a/lib/view/mydevelopment_page.dart +++ b/lib/view/mydevelopment_page.dart @@ -5,6 +5,7 @@ import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/track.dart'; +import 'package:aitrainer_app/widgets/dialog_common.dart'; import 'package:aitrainer_app/widgets/dialog_premium.dart'; import 'package:badges/badges.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -93,29 +94,27 @@ class _MyDevelopmentPage extends State with Trans { Navigator.of(context).pushNamed('mydevelopmentBodyPage', arguments: args) } else - {} - }, - isLocked: true, - ), - /* ImageButton( - width: imageWidth, - textAlignment: Alignment.topLeft, - text: t("My Sizes Development"), - style: GoogleFonts.robotoMono( - textStyle: TextStyle( - fontSize: 14, color: Colors.white, fontWeight: FontWeight.bold, backgroundColor: Colors.black54.withOpacity(0.4)), - ), - image: "asset/image/testemfejl400x400.jpg", - left: 5, - onTap: () => { - if (Cache().userLoggedIn != null) { - args['customerId'] = Cache().userLoggedIn.customerId, - Navigator.of(context).pushNamed('mydevelopmentSizesPage', arguments: args) + showDialog( + context: context, + builder: (BuildContext context) { + return DialogCommon( + warning: true, + title: t("Warning"), + descriptions: t("Please log in"), + description2: + t("because only that way can we show you the personalized development diagrams and analysises"), + text: "OK", + onTap: () => Navigator.of(context).popAndPushNamed("login"), + onCancel: () => { + Navigator.of(context).pop(), + }, + ); + }) } }, isLocked: true, - ), */ + ), Badge( padding: EdgeInsets.all(8), position: BadgePosition.topEnd(top: -5, end: -3), @@ -221,6 +220,22 @@ class _MyDevelopmentPage extends State with Trans { args['customerRepository'] = customerRepository; args['customerId'] = Cache().userLoggedIn!.customerId; Navigator.of(context).pushNamed('exerciseLogPage', arguments: args); + } else { + showDialog( + context: context, + builder: (BuildContext context) { + return DialogCommon( + warning: true, + title: t("Warning"), + descriptions: t("Please log in"), + description2: t("because only that way can we show you your exercises, results and evaluations."), + text: "OK", + onTap: () => Navigator.of(context).popAndPushNamed("login"), + onCancel: () => { + Navigator.of(context).pop(), + }, + ); + }); } } } diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 10eccd9..ba5bd00 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -104,7 +104,7 @@ class RegistrationPage extends StatelessWidget with Trans { child: Text( t("Skip"), textAlign: TextAlign.right, - style: GoogleFonts.inter(color: Colors.black, decoration: TextDecoration.underline), + style: GoogleFonts.inter(color: loginBloc.testColor, decoration: TextDecoration.underline), )), SizedBox( height: 120, @@ -203,7 +203,7 @@ class RegistrationPage extends StatelessWidget with Trans { color: Colors.transparent, ), getDataProtection(loginBloc), - getEmailSubscription(loginBloc), + loginBloc.emailCheckbox ? getEmailSubscription(loginBloc) : Offstage(), Divider( color: Colors.transparent, ), @@ -220,7 +220,6 @@ class RegistrationPage extends StatelessWidget with Trans { ), ], ), - //Image.asset('asset/icon/gomb_zold_b-1.png', width: 100, height: 100), onPressed: () => {loginBloc.add(RegistrationSubmit())}), ]), Divider( diff --git a/lib/view/sales_page.dart b/lib/view/sales_page.dart index d3975a0..d017c63 100644 --- a/lib/view/sales_page.dart +++ b/lib/view/sales_page.dart @@ -62,6 +62,8 @@ class SalesPage extends StatelessWidget with Trans, Logging { final salesText = bloc.salesText != null ? bloc.salesText! : ""; final String html = salesText; + log("start SalesPageBuild"); + return Container( decoration: BoxDecoration( image: DecorationImage( @@ -80,33 +82,70 @@ class SalesPage extends StatelessWidget with Trans, Logging { "p": Style( color: Colors.white, fontSize: FontSize(16), - padding: const EdgeInsets.only(left: 20, right: 8, bottom: 4), + padding: const EdgeInsets.only(left: 10, right: 8, bottom: 4), + textShadow: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + ], ), "strong": Style( - color: Colors.yellow[600], + color: Colors.orange[600], fontSize: FontSize(16), ), "h3": Style( - color: Colors.yellow[600], + color: Colors.orange[600], fontSize: FontSize(16), textAlign: TextAlign.center, padding: const EdgeInsets.all(12), ), "li": Style( - color: Colors.white, - fontSize: FontSize(14), - padding: const EdgeInsets.only(left: 20, bottom: 10, right: 8), - //before: "*", - display: Display.LIST_ITEM), + color: Colors.white, + fontSize: FontSize(16), + padding: const EdgeInsets.only(left: 10, bottom: 10, right: 8), + //before: "*", + textShadow: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + ], + //display: Display.LIST_ITEM, + ), "h2": Style( - color: Colors.yellow[600], + color: Colors.orange[600], fontWeight: FontWeight.bold, fontSize: FontSize(24), textAlign: TextAlign.center, + textShadow: [ + 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, + ), + ], //padding: const EdgeInsets.all(4), ), "h1": Style( - color: Colors.yellow[400], + color: Colors.orange[400], fontWeight: FontWeight.bold, fontSize: FontSize.larger, alignment: Alignment.center, @@ -115,100 +154,137 @@ class SalesPage extends StatelessWidget with Trans, Logging { }, ), // final Color bgrColor = Color(0xffb4f500); //final Color bgrColorEnd = Colors.blue; - AnimatedButton( - child: Html( - data: bloc.salesButtonText, - //Optional parameters: - style: { - "p": Style( - color: Colors.black, - fontSize: FontSize(16), - padding: const EdgeInsets.all(4), - textAlign: TextAlign.center, - textShadow: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 6.0, - color: Colors.black54, - ) - ], - ), - "li": Style( - color: Colors.white, - fontSize: FontSize(14), - padding: const EdgeInsets.only(left: 10, bottom: 10), - before: "*", - ), - "h2": Style( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: FontSize.larger, - textAlign: TextAlign.center, - textShadow: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 12.0, - color: Colors.black54, - ) - ], - //padding: const EdgeInsets.all(4), - ), - "h1": Style( - color: Colors.yellow[600], - fontWeight: FontWeight.bold, - fontSize: FontSize.xLarge, - alignment: Alignment.center, - padding: const EdgeInsets.all(4), - textAlign: TextAlign.center, - textShadow: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ) - ], - ), - }, - ), - duration: 600, - darkShadow: true, - blurRadius: 12, - animationCurve: Curves.easeIn, - height: 120, - width: 320, - onTap: () => bloc.add(SalesPurchase(productId: bloc.offeredProduct!.productId)), - //color: Color(0xffb4f500), - isMultiColor: true, - colors: [ - Colors.blue, - Color(0xffb4f500), - Color(0xffb4f500), - ], + Container( + padding: EdgeInsets.only(left: 55, right: 55), + child: Text( + t("Tap on the button below the reach all premium content!"), + textAlign: TextAlign.center, + style: GoogleFonts.inter(color: Colors.white, fontSize: 13), + )), + Divider(), + Row( + children: [0, 1].map((idx) { + return Expanded( + flex: 1, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 10), + child: AnimatedButton( + duration: 600, + darkShadow: true, + blurRadius: 12, + animationCurve: Curves.easeIn, + height: 150, + width: 160, + onTap: () => bloc.add(SalesPurchase(productId: bloc.product2Display[idx].productId)), + isMultiColor: true, + colors: [ + //Colors.blue, + //Color(0xffb4f500), + //Color(0xffb4f500), + Colors.white, + Colors.yellow[50]!, + Colors.yellow[300]!, + ], + child: Html( + data: bloc.productText2Display[idx], + //Optional parameters: + style: { + "p": Style( + color: Colors.blue, + fontSize: FontSize(14), + padding: const EdgeInsets.only(left: 5, right: 8, bottom: 5), + textAlign: TextAlign.center, + ), + "strong": Style( + color: Colors.red[800], + fontWeight: FontWeight.bold, + fontSize: FontSize(14), + ), + "h3": Style( + color: Colors.yellow[600], + fontSize: FontSize(16), + textAlign: TextAlign.center, + padding: const EdgeInsets.all(12), + ), + "li": Style( + color: Colors.white, + fontSize: FontSize(14), + padding: const EdgeInsets.only(left: 5, bottom: 10, right: 5), + //before: "*", + display: Display.LIST_ITEM), + "h2": Style( + color: Colors.blue[600], + fontWeight: FontWeight.bold, + fontSize: FontSize(16), + textAlign: TextAlign.center, + /* textShadow: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 4.0, + color: Colors.black54, + ), + ], */ + ), + "h1": Style( + color: Colors.blue[400], + fontWeight: FontWeight.bold, + fontSize: FontSize.larger, + alignment: Alignment.center, + padding: const EdgeInsets.all(4), + ), + }, + ), // final Color bgr + )), + ); + }).toList(), ), getTrialDescription(), - //Divider(), - AnimatedButton( - child: Html( - data: "

" + t("View other alternatives") + "

", - //Optional parameters: - style: { - "p": Style( - color: Colors.black, - fontSize: FontSize(14), - padding: const EdgeInsets.all(4), - textAlign: TextAlign.center, - ), - }, - ), - onTap: () => bloc.add(SalesChangeSubscription()), - width: 320, - blurRadius: 6, - isMultiColor: true, - colors: [ - Colors.white, - Colors.yellow[300]!, - ], + Html( + data: bloc.premiumFunctions, + //Optional parameters: + style: { + "p": Style( + color: Colors.white, + fontSize: FontSize(14), + padding: const EdgeInsets.only(left: 10, right: 8, bottom: 4), + ), + "strong": Style( + color: Colors.orange[600], + fontSize: FontSize(14), + textShadow: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 4.0, + color: Colors.black54, + ), + ], + ), + "li": Style( + color: Colors.white, + fontSize: FontSize(14), + padding: const EdgeInsets.only(left: 10, bottom: 3, right: 10), + ), + "h2": Style( + color: Colors.yellow[600], + fontWeight: FontWeight.bold, + fontSize: FontSize(16), + textAlign: TextAlign.center, + textShadow: [ + 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, + ), + ], + //padding: const EdgeInsets.all(4), + ), + }, ), SizedBox( diff --git a/lib/view/training_plan_activate_page.dart b/lib/view/training_plan_activate_page.dart index c5de826..5c866f2 100644 --- a/lib/view/training_plan_activate_page.dart +++ b/lib/view/training_plan_activate_page.dart @@ -11,6 +11,7 @@ import 'package:aitrainer_app/util/app_language.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_premium.dart'; import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; import 'package:flutter/cupertino.dart'; @@ -192,6 +193,8 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { List _getChildList(TrainingPlan plan, TrainingPlanBloc bloc) { List list = []; + bool restricted = (!plan.free && !Cache().hasPurchased); + list.add(Card( margin: EdgeInsets.only(left: 10, top: 5), color: Colors.white60, @@ -236,48 +239,94 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { ), child: Text(t("Start")), onPressed: () { - if (Cache().myTrainingPlan != null) { - showCupertinoDialog( - useRootNavigator: true, + if (Cache().userLoggedIn == null) { + showDialog( context: context, - builder: (_) => CupertinoAlertDialog( - title: Text(t("You have an active Training Plan")), - content: Column(children: [ - Divider(), - Text( - t("Do you want to override it with"), - style: (TextStyle(color: Colors.blue)), - ), - Text( - plan.nameTranslations[AppLanguage().appLocal.toString()]! + "?", - style: (TextStyle(color: Colors.blue[800], fontWeight: FontWeight.bold)), - ), - ]), - actions: [ - TextButton( - child: Text(t("No")), - onPressed: () => Navigator.pop(context), - ), - TextButton( - child: Text(t("Yes")), - onPressed: () { - Navigator.pop(context); - bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId)); - }, - ) - ], - )); + builder: (BuildContext context) { + return DialogCommon( + warning: true, + title: t("Warning"), + descriptions: t("Please log in"), + description2: t("because only that way can we generated the training plan for you."), + text: "OK", + onTap: () => Navigator.of(context).popAndPushNamed("login"), + onCancel: () => { + Navigator.of(context).pop(), + }, + ); + }); } else { - bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId)); + if (restricted) { + showDialog( + context: context, + builder: (BuildContext context) { + return DialogPremium( + unlocked: Cache().hasPurchased, + unlockRound: 1, + unlockedText: t("Enjoy also this premium feature") + " " + t("to activate all available training programs."), + function: "Training Programs", + onTap: () => {Navigator.of(context).pop()}, + onCancel: () => {Navigator.of(context).pop()}, + ); + }); + } else { + activate(plan, bloc); + } } }, - ) + ), + restricted + ? Container( + padding: EdgeInsets.only(bottom: 8), + child: Text( + t("This is a premium function"), + style: GoogleFonts.inter(color: Colors.blue[700]), + ), + ) + : Offstage(), ]), ))); return list; } + void activate(TrainingPlan plan, TrainingPlanBloc bloc) { + if (Cache().myTrainingPlan != null) { + showCupertinoDialog( + useRootNavigator: true, + context: context, + builder: (_) => CupertinoAlertDialog( + title: Text(t("You have an active Training Plan")), + content: Column(children: [ + Divider(), + Text( + t("Do you want to override it with"), + style: (TextStyle(color: Colors.blue)), + ), + Text( + plan.nameTranslations[AppLanguage().appLocal.toString()]! + "?", + style: (TextStyle(color: Colors.blue[800], fontWeight: FontWeight.bold)), + ), + ]), + actions: [ + TextButton( + child: Text(t("No")), + onPressed: () => Navigator.pop(context), + ), + TextButton( + child: Text(t("Yes")), + onPressed: () { + Navigator.pop(context); + bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId)); + }, + ) + ], + )); + } else { + bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId)); + } + } + Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc) { return SfDataGrid( headerRowHeight: 30, diff --git a/lib/view/training_plan_custom.dart b/lib/view/training_plan_custom.dart index af2d877..7bdcdc0 100644 --- a/lib/view/training_plan_custom.dart +++ b/lib/view/training_plan_custom.dart @@ -123,7 +123,7 @@ class _ExercisePlanCustomPage extends State with Trans { exerciseTypes.add(Container( margin: const EdgeInsets.only(left: 4.0), child: TreeViewChild( - startExpanded: false, + startExpanded: bloc.existsAddedExerciseTypeInTree(name), parent: TreeviewParentWidget(text: name), children: getTiles(list, bloc), ))); diff --git a/lib/view/training_plan_custom_add.dart b/lib/view/training_plan_custom_add.dart index b0b96da..9349b53 100644 --- a/lib/view/training_plan_custom_add.dart +++ b/lib/view/training_plan_custom_add.dart @@ -1,7 +1,6 @@ import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart'; import 'package:aitrainer_app/util/app_language.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:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; @@ -111,6 +110,9 @@ class _ExercisePlanDetailAddPage extends State with T } Widget getForm(TrainingPlanBloc bloc) { + if (bloc.getMyDetail() == null) { + return Offstage(); + } String exerciseName = ""; exerciseName = bloc.getExerciseName(AppLanguage().appLocal); @@ -124,7 +126,10 @@ class _ExercisePlanDetailAddPage extends State with T return Form( child: Scaffold( resizeToAvoidBottomInset: true, - appBar: AppBarMin(back: true), + appBar: AppBarMin( + back: true, + onTap: () => Navigator.of(context).popAndPushNamed("myTrainingPlanCustom"), + ), body: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, @@ -139,7 +144,7 @@ class _ExercisePlanDetailAddPage extends State with T config: _buildConfig(context), child: Container( child: SingleChildScrollView( - padding: const EdgeInsets.only(top: 25, left: 95, right: 95), + padding: EdgeInsets.only(top: 25, left: 95, right: 95), scrollDirection: Axis.vertical, child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text(t('Save The Exercise To The Training Plan'), diff --git a/lib/view/training_plans_page.dart b/lib/view/training_plans_page.dart index f6a24bc..d778eac 100644 --- a/lib/view/training_plans_page.dart +++ b/lib/view/training_plans_page.dart @@ -111,19 +111,37 @@ class MyTrainingPlans extends StatelessWidget with Trans, Logging { left: 5, textColor: color, onTap: () { - if (Cache().userLoggedIn != null) { - if (route == "myTrainingPlanActivate") { - HashMap args = HashMap(); - args['parentName'] = parentName; - Navigator.of(context).pushNamed(route, arguments: args); - } else if (route == "myTrainingPlanExecute") { + // if (Cache().userLoggedIn != null) { + if (route == "myTrainingPlanActivate") { + HashMap args = HashMap(); + args['parentName'] = parentName; + Navigator.of(context).pushNamed(route, arguments: args); + } else if (route == "myTrainingPlanExecute") { + if (Cache().userLoggedIn != null) { final TrainingPlanBloc bloc = BlocProvider.of(context); bloc.setMyPlan(Cache().myTrainingPlan); Navigator.of(context).pushNamed(route); } else { - Navigator.of(context).pushNamed(route); + showDialog( + context: context, + builder: (BuildContext context) { + return DialogCommon( + warning: true, + title: t("Warning"), + descriptions: t("Please log in"), + description2: t("because only in that way can you begin to execute a training plan"), + text: "OK", + onTap: () => Navigator.of(context).pushNamed("login"), + onCancel: () => { + Navigator.of(context).pop(), + }, + ); + }); } + } else { + Navigator.of(context).pushNamed(route); } + // } }, isLocked: false, ); diff --git a/lib/widgets/app_bar.dart b/lib/widgets/app_bar.dart index 482ce6d..b7b8f2d 100644 --- a/lib/widgets/app_bar.dart +++ b/lib/widgets/app_bar.dart @@ -156,7 +156,9 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin, C } int sizeExerciseList = Cache().getExercises() == null ? 0 : Cache().getExercises()!.length; if (sizeExerciseList == 0) { - String text = AppLocalizations.of(context)!.translate("Make your first test"); + String text = Cache().userLoggedIn == null + ? AppLocalizations.of(context)!.translate("Please log in") + : AppLocalizations.of(context)!.translate("Make your first test"); double fontSize = text.length > 24 ? 13 : 16; return Stack(alignment: Alignment.topLeft, children: [ GestureDetector( diff --git a/lib/widgets/app_bar_min.dart b/lib/widgets/app_bar_min.dart index 44ad236..6fde260 100644 --- a/lib/widgets/app_bar_min.dart +++ b/lib/widgets/app_bar_min.dart @@ -9,7 +9,8 @@ import 'package:google_fonts/google_fonts.dart'; // ignore: must_be_immutable class AppBarMin extends StatefulWidget implements PreferredSizeWidget { bool back = false; - AppBarMin({this.back = false}); + VoidCallback? onTap; + AppBarMin({this.back = false, this.onTap}); @override _AppBarNav createState() => _AppBarNav(); @@ -46,9 +47,13 @@ class _AppBarNav extends State with Common { ), leading: IconButton( icon: Icon(Icons.arrow_back, color: widget.back ? Colors.white : Colors.black), - onPressed: () => { - timerBloc.add(TimerEnd(duration: 0)), - if (widget.back) {Navigator.of(context).pop()} + onPressed: () { + timerBloc.add(TimerEnd(duration: 0)); + if (widget.onTap != null) { + widget.onTap!(); + } else if (widget.back) { + Navigator.of(context).pop(); + } }, )); } diff --git a/lib/widgets/bmi_widget.dart b/lib/widgets/bmi_widget.dart index d22c32d..e12b60f 100644 --- a/lib/widgets/bmi_widget.dart +++ b/lib/widgets/bmi_widget.dart @@ -313,9 +313,12 @@ class _BMIState extends State with Trans { textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]), onChanged: (value) => { - value = value.replaceFirst(",", "."), - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value))), + if (value.isNotEmpty) + { + value = value.replaceFirst(",", "."), + value = value.replaceAll(RegExp(r'[^0-9.]'), ""), + widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value))), + } }), ); } else { @@ -397,9 +400,12 @@ class _BMIState extends State with Trans { textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]), onChanged: (value) => { - value = value.replaceFirst(",", "."), - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value))), + if (value.isNotEmpty) + { + value = value.replaceFirst(",", "."), + value = value.replaceAll(RegExp(r'[^0-9.]'), ""), + widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value))), + } }, ), ), diff --git a/lib/widgets/bmr_widget.dart b/lib/widgets/bmr_widget.dart index 9ca0a33..dc28516 100644 --- a/lib/widgets/bmr_widget.dart +++ b/lib/widgets/bmr_widget.dart @@ -217,9 +217,12 @@ class _BMRState extends State with Trans { textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]), onChanged: (value) => { - value = value.replaceFirst(",", "."), - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value))), + if (value.isNotEmpty) + { + value = value.replaceFirst(",", "."), + value = value.replaceAll(RegExp(r'[^0-9.]'), ""), + widget.exerciseBloc.add(ExerciseNewHeightChange(value: double.parse(value))), + } }), ); } else { @@ -248,8 +251,11 @@ class _BMRState extends State with Trans { textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]), onChanged: (value) => { - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - widget.exerciseBloc.add(ExerciseNewBirthyearChange(value: int.parse(value))) + if (value.isNotEmpty) + { + value = value.replaceAll(RegExp(r'[^0-9.]'), ""), + widget.exerciseBloc.add(ExerciseNewBirthyearChange(value: int.parse(value))) + } }), ); } @@ -373,9 +379,12 @@ class _BMRState extends State with Trans { textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]), onChanged: (value) => { - value = value.replaceFirst(",", "."), - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value))), + if (value.isNotEmpty) + { + value = value.replaceFirst(",", "."), + value = value.replaceAll(RegExp(r'[^0-9.]'), ""), + widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value))), + } }, ), ), diff --git a/lib/widgets/dialog_premium.dart b/lib/widgets/dialog_premium.dart index 400fb39..9f2e85a 100644 --- a/lib/widgets/dialog_premium.dart +++ b/lib/widgets/dialog_premium.dart @@ -162,7 +162,7 @@ class _DialogPremiumState extends State with Trans { Align( alignment: Alignment.center, child: GestureDetector( - onTap: () => widget.unlocked ? Navigator.of(context).pop() : Navigator.of(context).pushNamed("salesPage"), + onTap: () => widget.unlocked ? Navigator.of(context).pop() : Navigator.of(context).popAndPushNamed("salesPage"), child: Stack( alignment: Alignment.center, children: [ diff --git a/lib/widgets/exercise_save.dart b/lib/widgets/exercise_save.dart index ec65689..d88c84a 100644 --- a/lib/widgets/exercise_save.dart +++ b/lib/widgets/exercise_save.dart @@ -281,11 +281,13 @@ class _ExerciseSaveState extends State with Trans { keyboardType: TextInputType.numberWithOptions(decimal: true), textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]), - onChanged: (value) => { - value = value.replaceFirst(",", "."), - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - widget.onUnitQuantityChanged!(double.parse(value)), - }), + onChanged: (value) { + if (value.isNotEmpty) { + value = value.replaceFirst(",", "."); + value = value.replaceAll(RegExp(r'[^0-9.]'), ""); + widget.onUnitQuantityChanged!(double.parse(value)); + } + }), ])); } return row; @@ -395,9 +397,11 @@ class _ExerciseSaveState extends State with Trans { textInputAction: TextInputAction.next, style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]), onChanged: (value) { - value = value.replaceFirst(",", "."); - value = value.replaceAll(RegExp(r'[^0-9.]'), ""); - widget.onQuantityChanged(double.parse(value)); + if (value.isNotEmpty) { + value = value.replaceFirst(",", "."); + value = value.replaceAll(RegExp(r'[^0-9.]'), ""); + widget.onQuantityChanged(double.parse(value)); + } }, ), ])); diff --git a/lib/widgets/input_dialog_widget.dart b/lib/widgets/input_dialog_widget.dart index e6964c7..2ed210a 100644 --- a/lib/widgets/input_dialog_widget.dart +++ b/lib/widgets/input_dialog_widget.dart @@ -157,9 +157,12 @@ class _InputDialogState extends State> with Trans { textInputAction: TextInputAction.done, style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]), onChanged: (value) => { - value = value.replaceFirst(",", "."), - value = value.replaceAll(RegExp(r'[^0-9.]'), ""), - this.inputValue = double.parse(value), + if (value.isNotEmpty) + { + value = value.replaceFirst(",", "."), + value = value.replaceAll(RegExp(r'[^0-9.]'), ""), + this.inputValue = double.parse(value), + } }, ), ), diff --git a/lib/widgets/victory_widget.dart b/lib/widgets/victory_widget.dart index f3c55bf..8f002e1 100644 --- a/lib/widgets/victory_widget.dart +++ b/lib/widgets/victory_widget.dart @@ -81,9 +81,7 @@ class _VictoryState extends State { void initState() { animation.start(); animation.addStatusListener((status) { - if (status == AnimationStatus.completed) { - setState(() {}); - } + if (status == AnimationStatus.completed) {} }); super.initState(); diff --git a/pubspec.yaml b/pubspec.yaml index 4166a03..9b3c9b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.18+82 +version: 1.1.18+83 environment: sdk: ">=2.12.0 <3.0.0"