WT 1.1.18+4 A/B Test sales page
This commit is contained in:
parent
d5deaf48a9
commit
0ca3b71c03
10
i18n/en.json
10
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"
|
||||
}
|
12
i18n/hu.json
12
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"
|
||||
}
|
@ -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 = (
|
||||
|
@ -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<ExerciseExecutePlanEvent, ExerciseExecutePlanState> {
|
||||
final WorkoutTreeRepository menuTreeRepository;
|
||||
final ExercisePlanRepository exercisePlanRepository = ExercisePlanRepository();
|
||||
int? customerId;
|
||||
int selectedNumber = 0;
|
||||
|
||||
@override
|
||||
ExerciseExecutePlanBloc({required this.menuTreeRepository}) : super(ExerciseByPlanStateInitial());
|
||||
|
||||
Future<void> getData() async {
|
||||
exercisePlanRepository.setCustomerId(customerId!);
|
||||
await exercisePlanRepository.getLastExercisePlan();
|
||||
await exercisePlanRepository.getExercisePlanDetails();
|
||||
menuTreeRepository.sortedTree.clear();
|
||||
menuTreeRepository.sortByMuscleType();
|
||||
|
||||
menuTreeRepository.sortedTree.forEach((key, value) {
|
||||
List<WorkoutMenuTree> 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<ExerciseExecutePlanState> 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());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
part of 'exercise_execute_plan_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ExerciseExecutePlanEvent extends Equatable {
|
||||
const ExerciseExecutePlanEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class AddExerciseByPlanEvent extends ExerciseExecutePlanEvent {
|
||||
final ExerciseType exerciseType;
|
||||
const AddExerciseByPlanEvent({required this.exerciseType});
|
||||
|
||||
@override
|
||||
List<Object> get props => [exerciseType];
|
||||
}
|
||||
|
||||
class ExerciseByPlanLoad extends ExerciseExecutePlanEvent {
|
||||
const ExerciseByPlanLoad();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
part of 'exercise_execute_plan_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ExerciseExecutePlanState extends Equatable {
|
||||
const ExerciseExecutePlanState();
|
||||
|
||||
@override
|
||||
List<Object> 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<Object> get props => [message];
|
||||
}
|
@ -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<ExerciseExecutePlanAddEvent, ExerciseExecutePlanAddState> {
|
||||
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<ExerciseExecutePlanAddState> 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());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
part of 'exercise_execute_plan_add_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ExerciseExecutePlanAddEvent extends Equatable {
|
||||
const ExerciseExecutePlanAddEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class ExerciseExecutePlanAddLoad extends ExerciseExecutePlanAddEvent {
|
||||
const ExerciseExecutePlanAddLoad();
|
||||
}
|
||||
|
||||
class ExerciseExecutePlanAddChangeQuantity extends ExerciseExecutePlanAddEvent {
|
||||
final double quantity;
|
||||
const ExerciseExecutePlanAddChangeQuantity({required this.quantity});
|
||||
|
||||
@override
|
||||
List<Object> get props => [quantity];
|
||||
}
|
||||
|
||||
class ExerciseExecutePlanAddChangeUnitQuantity extends ExerciseExecutePlanAddEvent {
|
||||
final double quantity;
|
||||
const ExerciseExecutePlanAddChangeUnitQuantity({required this.quantity});
|
||||
|
||||
@override
|
||||
List<Object> get props => [quantity];
|
||||
}
|
||||
|
||||
class ExerciseExecutePlanAddSubmit extends ExerciseExecutePlanAddEvent {
|
||||
const ExerciseExecutePlanAddSubmit();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
part of 'exercise_execute_plan_add_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ExerciseExecutePlanAddState extends Equatable {
|
||||
const ExerciseExecutePlanAddState();
|
||||
|
||||
@override
|
||||
List<Object> 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<Object> get props => [message];
|
||||
}
|
@ -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<LoginEvent, LoginState> 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<LoginState> mapEventToState(
|
||||
|
@ -66,7 +66,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> with Trans, Logging {
|
||||
workoutItem = event.item;
|
||||
|
||||
if (workoutItem != null) {
|
||||
setAbility(workoutItem!.nameEnglish);
|
||||
setAbility(workoutItem!.internalName);
|
||||
}
|
||||
final LinkedHashMap<String, WorkoutMenuTree> branch = menuTreeRepository.getBranch(event.parent);
|
||||
|
||||
@ -80,7 +80,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> with Trans, Logging {
|
||||
|
||||
LinkedHashMap<String, WorkoutMenuTree> 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<MenuEvent, MenuState> with Trans, Logging {
|
||||
workoutItem = menuTreeRepository.getParentItem(parent);
|
||||
|
||||
if (workoutItem != null) {
|
||||
setAbility(workoutItem!.nameEnglish);
|
||||
setAbility(workoutItem!.internalName);
|
||||
}
|
||||
final LinkedHashMap<String, WorkoutMenuTree> branch = menuTreeRepository.getBranch(workoutItem!.parent);
|
||||
await getImages(branch);
|
||||
@ -119,22 +119,19 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> 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;
|
||||
}
|
||||
|
@ -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<SalesEvent, SalesState> with Logging {
|
||||
List<ProductTest>? tests = [];
|
||||
List<Product> product2Display = [];
|
||||
List<String> 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 = "<h1>Workout Test Monthly</h1><p>localizedPrice</p><p><small>cancel any time</small></p>";
|
||||
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<SalesEvent, SalesState> 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<SalesEvent, SalesState> 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<SalesEvent, SalesState> with Logging {
|
||||
}
|
||||
|
||||
void getProductSet() {
|
||||
int productId = 0;
|
||||
//this.tests = Cache().productTests;
|
||||
List<Product>? 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;
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
//ProductTest productTest = ProductTest();
|
||||
|
||||
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;
|
||||
log("ProductSet: " + productSet.toString());
|
||||
}
|
||||
|
||||
log("ProductSet: $productSet");
|
||||
|
||||
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<SalesEvent, SalesState> 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();
|
||||
}
|
||||
}
|
||||
|
@ -92,11 +92,16 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
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<TrainingPlanEvent, TrainingPlanState> {
|
||||
}
|
||||
} 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<TrainingPlanEvent, TrainingPlanState> {
|
||||
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<TrainingPlanEvent, TrainingPlanState> {
|
||||
}
|
||||
activeDayIndex++;
|
||||
}
|
||||
print("Active Day Index: $activeDayIndex");
|
||||
|
||||
if (activeDayIndex >= dayNames.length) {
|
||||
activeDayIndex = 0;
|
||||
this.add(TrainingPlanGoToRestart());
|
||||
@ -481,4 +486,17 @@ class TrainingPlanBloc extends Bloc<TrainingPlanEvent, TrainingPlanState> {
|
||||
|
||||
return value.toStringAsFixed(0);
|
||||
}
|
||||
|
||||
bool existsAddedExerciseTypeInTree(String name) {
|
||||
bool exists = false;
|
||||
final List<WorkoutMenuTree>? listWorkoutTree = menuBloc.menuTreeRepository.sortedTree[name];
|
||||
if (listWorkoutTree != null) {
|
||||
listWorkoutTree.forEach((element) {
|
||||
if (element.exerciseType!.trainingPlanState.equalsTo(ExerciseTypeTrainingPlanState.added)) {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
|
@ -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<void> 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(),
|
||||
|
@ -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<Sport>? _sports;
|
||||
List<wt_product.Product>? _products;
|
||||
List<Purchase> _purchases = [];
|
||||
List<ProductTest>? _productTests;
|
||||
List<SplitTest> _splitTests = [];
|
||||
|
||||
List<ExercisePlanTemplate> _exercisePlanTemplates = [];
|
||||
|
||||
ExercisePlan? activeExercisePlan;
|
||||
@ -242,7 +243,7 @@ class Cache with Logging {
|
||||
Map<String, dynamic> 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<Purchase> get purchases => _purchases;
|
||||
setPurchases(List<Purchase> value) => _purchases = value;
|
||||
|
||||
// ignore: unnecessary_getters_setters
|
||||
List<ProductTest>? get productTests => _productTests;
|
||||
// ignore: unnecessary_getters_setters
|
||||
set productTests(List<ProductTest>? value) => _productTests = value;
|
||||
|
||||
Future<void> initCustomer(int customerId) async {
|
||||
log(" *** initCustomer");
|
||||
await PackageApi().getCustomerPackage(customerId);
|
||||
@ -738,4 +734,7 @@ class Cache with Logging {
|
||||
|
||||
List<CustomerTrainingPlan>? getCustomerTrainingPlans() => this._customerTrainingPlans;
|
||||
setCustomerTrainingPlans(value) => this._customerTrainingPlans = value;
|
||||
|
||||
List<SplitTest> getSplitTests() => this._splitTests;
|
||||
setSplitTests(value) => this._splitTests = value;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<Exercise> 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<String, dynamic> toJsonWithExercises() {
|
||||
final Map<String, dynamic> 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;
|
||||
|
@ -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<String, dynamic> 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
36
lib/model/split_test.dart
Normal file
36
lib/model/split_test.dart
Normal file
@ -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<String, dynamic> 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();
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ class PushNotificationsManager with Logging {
|
||||
static final PushNotificationsManager _instance = PushNotificationsManager._();
|
||||
|
||||
Future<void> 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");
|
||||
}
|
||||
}
|
||||
|
@ -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<List<ProductTest>> getProductTests() async {
|
||||
List<ProductTest> 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<void> addProductTest(ProductTest productTest) async {
|
||||
await ProductTestApi().saveProductTest(productTest);
|
||||
}
|
||||
|
||||
void setMediaDimensions(double width, double height) {
|
||||
this.mediaHeight = height;
|
||||
this.mediaWidth = width;
|
||||
|
@ -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<Description>? descriptions;
|
||||
@ -12,10 +15,13 @@ class DescriptionRepository {
|
||||
String descriptionText = "";
|
||||
if (descriptions != null) {
|
||||
this.descriptions!.forEach((element) {
|
||||
print("Desc ${element.name} - $name");
|
||||
if (element.name == name) {
|
||||
if (AppLanguage().appLocal == Locale('en')) {
|
||||
descriptionText = element.description;
|
||||
} else {
|
||||
descriptionText = element.descriptionTranslation != null ? element.descriptionTranslation! : element.description;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return descriptionText;
|
||||
|
63
lib/repository/split_test_respository.dart
Normal file
63
lib/repository/split_test_respository.dart
Normal file
@ -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<String> sourceElements = element.source!.split(".");
|
||||
if (sourceElements.length > 1) {
|
||||
final String modelName = sourceElements[0];
|
||||
if (modelName == "description") {
|
||||
testValue = descriptionRepository.getDescriptionByName(element.testValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return testValue;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ class ExerciseApi with Logging {
|
||||
}
|
||||
|
||||
Future<void> deleteExercise(Exercise exercise) async {
|
||||
if (exercise.exerciseId == null) {
|
||||
return;
|
||||
}
|
||||
int exerciseId = exercise.exerciseId!;
|
||||
log(" ===== delete exercise: " + exerciseId.toString());
|
||||
await _client.post("exercises/" + exerciseId.toString(), "");
|
||||
|
@ -291,21 +291,25 @@ class FirebaseApi with logging.Logging {
|
||||
|
||||
Future<void> setupRemoteConfig() async {
|
||||
initializeFlutterFire();
|
||||
final RemoteConfig remoteConfig = RemoteConfig.instance;
|
||||
RemoteConfig? remoteConfig;
|
||||
try {
|
||||
remoteConfig = RemoteConfig.instance;
|
||||
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
||||
fetchTimeout: const Duration(seconds: 10),
|
||||
minimumFetchInterval: const Duration(seconds: 1),
|
||||
fetchTimeout: const Duration(seconds: 60),
|
||||
minimumFetchInterval: const Duration(hours: 6),
|
||||
));
|
||||
await remoteConfig.setDefaults(<String, dynamic>{
|
||||
'sales_page_text': '0',
|
||||
'sales_page_bkg': 'dark',
|
||||
'sales_page_offer': 'monthly',
|
||||
'please_log_in': '',
|
||||
});
|
||||
|
||||
RemoteConfigValue(null, ValueSource.valueStatic);
|
||||
Cache().setRemoteConfig(remoteConfig);
|
||||
|
||||
Map config = remoteConfig.getAll();
|
||||
print("RemoteConfig sales_page_text Value : ${config['sales_page_text'].asString()}");
|
||||
} on Exception catch (e) {
|
||||
print('Unable to fetch remote config. Cached or default values will be used: $e');
|
||||
if (remoteConfig != null) {
|
||||
await remoteConfig.setDefaults(<String, dynamic>{
|
||||
'sales_page_text_a': '',
|
||||
'product_set_2': '',
|
||||
});
|
||||
Cache().setRemoteConfig(remoteConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<TrainingPlan>? plans = json.map((plan) => TrainingPlan.fromJson(plan)).toList();
|
||||
|
||||
Cache().setTrainingPlans(plans);
|
||||
} else if (headRecord[0] == "SplitTests") {
|
||||
final Iterable json = jsonDecode(headRecord[1]);
|
||||
final List<SplitTest>? 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<Exercise> exercises = json.map((exerciseType) => Exercise.fromJson(exerciseType)).toList();
|
||||
Cache().setExercises(exercises);
|
||||
} else if (headRecord[0] == "ProductTest") {
|
||||
final Iterable json = jsonDecode(headRecord[1]);
|
||||
final List<ProductTest> productTests = json.map((productTest) => ProductTest.fromJson(productTest)).toList();
|
||||
Cache().productTests = productTests;
|
||||
} else if (headRecord[0] == "Purchase") {
|
||||
final Iterable json = jsonDecode(headRecord[1]);
|
||||
final List<Purchase> purchases = json.map((purchase) => Purchase.fromJson(purchase)).toList();
|
||||
|
@ -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<List<ProductTest>> getProductTestByCustomer(int customerId) async {
|
||||
final body = await _client.get("product_test/customer/" + customerId.toString(), "");
|
||||
final Iterable json = jsonDecode(body);
|
||||
final List<ProductTest> productTests = json.map((productTest) => ProductTest.fromJson(productTest)).toList();
|
||||
Cache().productTests = productTests;
|
||||
return productTests;
|
||||
}
|
||||
|
||||
Future<void> saveProductTest(ProductTest productTest) async {
|
||||
String body = JsonEncoder().convert(productTest.toJson());
|
||||
log(" ===== saving productTest:" + body);
|
||||
await _client.post("product_test/", body);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<ExerciseExecutePage> with Trans {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
// 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<ExerciseExecutePlanBloc>(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<ExerciseExecutePlanBloc>(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<ExerciseExecutePlanBloc, ExerciseExecutePlanState>(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<Widget> nodeExercisePlan(ExerciseExecutePlanBloc bloc) {
|
||||
List<Widget> 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<Widget> _getChildList(List<WorkoutMenuTree> listWorkoutTree, ExerciseExecutePlanBloc bloc) {
|
||||
List<Widget> 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);
|
||||
}
|
||||
}
|
@ -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<ExerciseExecutePlanAddPage> 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<ExerciseExecutePlanAddBloc, ExerciseExecutePlanAddState>(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<ExerciseExecutePlanAddBloc>(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: <Widget>[
|
||||
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>[
|
||||
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<Column> repeatExercises(ExerciseExecutePlanAddBloc exerciseBloc) {
|
||||
List<Column> 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>[
|
||||
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<Widget> getButton(int step, ExerciseExecutePlanAddBloc exerciseBloc) {
|
||||
List<Widget> 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;
|
||||
}
|
||||
}
|
@ -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()},
|
||||
|
@ -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<MyDevelopmentPage> 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<MyDevelopmentPage> 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(),
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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,12 +82,124 @@ 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>[
|
||||
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.orange[600],
|
||||
fontSize: FontSize(16),
|
||||
textAlign: TextAlign.center,
|
||||
padding: const EdgeInsets.all(12),
|
||||
),
|
||||
"li": Style(
|
||||
color: Colors.white,
|
||||
fontSize: FontSize(16),
|
||||
padding: const EdgeInsets.only(left: 10, bottom: 10, right: 8),
|
||||
//before: "*",
|
||||
textShadow: <Shadow>[
|
||||
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.orange[600],
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: FontSize(24),
|
||||
textAlign: TextAlign.center,
|
||||
textShadow: <Shadow>[
|
||||
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.orange[400],
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: FontSize.larger,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(4),
|
||||
),
|
||||
},
|
||||
), // final Color bgrColor = Color(0xffb4f500);
|
||||
//final Color bgrColorEnd = Colors.blue;
|
||||
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),
|
||||
@ -95,121 +209,83 @@ class SalesPage extends StatelessWidget with Trans, Logging {
|
||||
"li": Style(
|
||||
color: Colors.white,
|
||||
fontSize: FontSize(14),
|
||||
padding: const EdgeInsets.only(left: 20, bottom: 10, right: 8),
|
||||
padding: const EdgeInsets.only(left: 5, bottom: 10, right: 5),
|
||||
//before: "*",
|
||||
display: Display.LIST_ITEM),
|
||||
"h2": Style(
|
||||
color: Colors.yellow[600],
|
||||
color: Colors.blue[600],
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: FontSize(24),
|
||||
fontSize: FontSize(16),
|
||||
textAlign: TextAlign.center,
|
||||
//padding: const EdgeInsets.all(4),
|
||||
/* textShadow: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(3.0, 3.0),
|
||||
blurRadius: 4.0,
|
||||
color: Colors.black54,
|
||||
),
|
||||
], */
|
||||
),
|
||||
"h1": Style(
|
||||
color: Colors.yellow[400],
|
||||
color: Colors.blue[400],
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: FontSize.larger,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(4),
|
||||
),
|
||||
},
|
||||
), // final Color bgrColor = Color(0xffb4f500);
|
||||
//final Color bgrColorEnd = Colors.blue;
|
||||
AnimatedButton(
|
||||
child: Html(
|
||||
data: bloc.salesButtonText,
|
||||
), // final Color bgr
|
||||
)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
|
||||
getTrialDescription(),
|
||||
Html(
|
||||
data: bloc.premiumFunctions,
|
||||
//Optional parameters:
|
||||
style: {
|
||||
"p": Style(
|
||||
color: Colors.black,
|
||||
fontSize: FontSize(16),
|
||||
padding: const EdgeInsets.all(4),
|
||||
textAlign: TextAlign.center,
|
||||
textShadow: [
|
||||
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>[
|
||||
Shadow(
|
||||
offset: Offset(2.0, 2.0),
|
||||
blurRadius: 6.0,
|
||||
blurRadius: 4.0,
|
||||
color: Colors.black54,
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
"li": Style(
|
||||
color: Colors.white,
|
||||
fontSize: FontSize(14),
|
||||
padding: const EdgeInsets.only(left: 10, bottom: 10),
|
||||
before: "*",
|
||||
padding: const EdgeInsets.only(left: 10, bottom: 3, right: 10),
|
||||
),
|
||||
"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),
|
||||
fontSize: FontSize(16),
|
||||
textAlign: TextAlign.center,
|
||||
textShadow: [
|
||||
textShadow: <Shadow>[
|
||||
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),
|
||||
),
|
||||
},
|
||||
),
|
||||
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),
|
||||
],
|
||||
),
|
||||
|
||||
getTrialDescription(),
|
||||
//Divider(),
|
||||
AnimatedButton(
|
||||
child: Html(
|
||||
data: "<p>" + t("View other alternatives") + "</p>",
|
||||
//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]!,
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 30,
|
||||
|
@ -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<Widget> _getChildList(TrainingPlan plan, TrainingPlanBloc bloc) {
|
||||
List<Widget> list = [];
|
||||
|
||||
bool restricted = (!plan.free && !Cache().hasPurchased);
|
||||
|
||||
list.add(Card(
|
||||
margin: EdgeInsets.only(left: 10, top: 5),
|
||||
color: Colors.white60,
|
||||
@ -236,6 +239,58 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
),
|
||||
child: Text(t("Start")),
|
||||
onPressed: () {
|
||||
if (Cache().userLoggedIn == null) {
|
||||
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 generated the training plan for you."),
|
||||
text: "OK",
|
||||
onTap: () => Navigator.of(context).popAndPushNamed("login"),
|
||||
onCancel: () => {
|
||||
Navigator.of(context).pop(),
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
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,
|
||||
@ -270,12 +325,6 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans {
|
||||
} else {
|
||||
bloc.add(TrainingPlanActivate(trainingPlanId: plan.trainingPlanId));
|
||||
}
|
||||
},
|
||||
)
|
||||
]),
|
||||
)));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
Widget getPlanDetails(TrainingPlan plan, TrainingPlanBloc bloc) {
|
||||
|
@ -123,7 +123,7 @@ class _ExercisePlanCustomPage extends State<TrainingPlanCustomPage> 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),
|
||||
)));
|
||||
|
@ -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<TrainingPlanCustomAddPage> 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<TrainingPlanCustomAddPage> 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<TrainingPlanCustomAddPage> 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: <Widget>[
|
||||
Text(t('Save The Exercise To The Training Plan'),
|
||||
|
@ -111,19 +111,37 @@ class MyTrainingPlans extends StatelessWidget with Trans, Logging {
|
||||
left: 5,
|
||||
textColor: color,
|
||||
onTap: () {
|
||||
if (Cache().userLoggedIn != null) {
|
||||
// if (Cache().userLoggedIn != null) {
|
||||
if (route == "myTrainingPlanActivate") {
|
||||
HashMap<String, dynamic> 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<TrainingPlanBloc>(context);
|
||||
bloc.setMyPlan(Cache().myTrainingPlan);
|
||||
Navigator.of(context).pushNamed(route);
|
||||
} else {
|
||||
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,
|
||||
);
|
||||
|
@ -156,7 +156,9 @@ class _AppBarNav extends State<AppBarNav> 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(
|
||||
|
@ -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<AppBarMin> 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();
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -313,9 +313,12 @@ class _BMIState extends State<BMI> with Trans {
|
||||
textInputAction: TextInputAction.done,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
|
||||
onChanged: (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<BMI> with Trans {
|
||||
textInputAction: TextInputAction.done,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
|
||||
onChanged: (value) => {
|
||||
if (value.isNotEmpty)
|
||||
{
|
||||
value = value.replaceFirst(",", "."),
|
||||
value = value.replaceAll(RegExp(r'[^0-9.]'), ""),
|
||||
widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value))),
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -217,9 +217,12 @@ class _BMRState extends State<BMR> with Trans {
|
||||
textInputAction: TextInputAction.done,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
|
||||
onChanged: (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<BMR> with Trans {
|
||||
textInputAction: TextInputAction.done,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
|
||||
onChanged: (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<BMR> with Trans {
|
||||
textInputAction: TextInputAction.done,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
|
||||
onChanged: (value) => {
|
||||
if (value.isNotEmpty)
|
||||
{
|
||||
value = value.replaceFirst(",", "."),
|
||||
value = value.replaceAll(RegExp(r'[^0-9.]'), ""),
|
||||
widget.exerciseBloc.add(ExerciseNewWeightChange(value: double.parse(value))),
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -162,7 +162,7 @@ class _DialogPremiumState extends State<DialogPremium> 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: [
|
||||
|
@ -281,10 +281,12 @@ class _ExerciseSaveState extends State<ExerciseSave> 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));
|
||||
}
|
||||
}),
|
||||
]));
|
||||
}
|
||||
@ -395,9 +397,11 @@ class _ExerciseSaveState extends State<ExerciseSave> with Trans {
|
||||
textInputAction: TextInputAction.next,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]),
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty) {
|
||||
value = value.replaceFirst(",", ".");
|
||||
value = value.replaceAll(RegExp(r'[^0-9.]'), "");
|
||||
widget.onQuantityChanged(double.parse(value));
|
||||
}
|
||||
},
|
||||
),
|
||||
]));
|
||||
|
@ -157,9 +157,12 @@ class _InputDialogState<Event> extends State<InputDialog<Event>> with Trans {
|
||||
textInputAction: TextInputAction.done,
|
||||
style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.yellow[300]),
|
||||
onChanged: (value) => {
|
||||
if (value.isNotEmpty)
|
||||
{
|
||||
value = value.replaceFirst(",", "."),
|
||||
value = value.replaceAll(RegExp(r'[^0-9.]'), ""),
|
||||
this.inputValue = double.parse(value),
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -81,9 +81,7 @@ class _VictoryState extends State<Victory> {
|
||||
void initState() {
|
||||
animation.start();
|
||||
animation.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
setState(() {});
|
||||
}
|
||||
if (status == AnimationStatus.completed) {}
|
||||
});
|
||||
|
||||
super.initState();
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user