WT 1.1.14+2 sales page update

This commit is contained in:
bossanyit 2021-05-05 21:01:03 +02:00
parent c901b07bf8
commit 3137bc2330
30 changed files with 1008 additions and 207 deletions

View File

@ -330,7 +330,7 @@
"Progressindicator for the tests":"Where do you stand achieving your tests?",
"Progressindicator_desc":"<p>It shows which muscle group test did your achieved already.</p><h2>When do you reach the 100% test round?</h2><br/><p>If you have sucessfully tested one of the <strong>base</strong> exercises of each muscle group</p>",
"Unleash Your Development Now!":"Unleash Your Development Now!",
"Unleash Your Development Now!":"Commit To Your Development",
"Learn about your development, enjoy AI-driven predictions of all of your skills and bodyparts.":"Learn about your development, enjoy AI-driven predictions of all of your skills and bodyparts.",
"Subscription Conditions":"Subscription Conditions",
"Payment will be charged to your account. Subscription automatically renews unless auto-renew is turned off at least 24 hours before the end of the current period":"Payment will be charged to your account. Subscription automatically renews unless auto-renew is turned off at least 24 hours before the end of the current period",
@ -471,6 +471,13 @@
"Please take a short tour in the app":"Please take a short tour in the app",
"No Login":"No Login",
"You will skip the login.":"You will skip the login.",
"The app functionalitity will be restricted, but please take a tour!":"The app functionalitity will be restricted, but please take a tour!"
"The app functionalitity will be restricted, but please take a tour!":"The app functionalitity will be restricted, but please take a tour!",
"Activating the basic tutorial":"Activating the basic tutorial",
"Basic Tutorial":"Basic Tutorial",
"Activate":"Activate",
"Try free for 3 days!":"Try it without risk for 3 days! In this period you can cancel any time without lasting your account.",
"View other alternatives":"View other alternatives"
}

View File

@ -463,5 +463,12 @@
"Please take a short tour in the app":"Kérlek tégy egy rövid túrát az applikációban",
"No Login":"Bejelentkezés kimaradt",
"You will skip the login.":"Átugrod a bejelentkezést.",
"The app functionalitity will be restricted, but please take a tour!":"Az applikációt korlátozottan tudod így használni, de tégy egy túrát!"
"The app functionalitity will be restricted, but please take a tour!":"Az applikációt korlátozottan tudod így használni, de tégy egy túrát!",
"Activating the basic tutorial":"Bemutató aktiválása",
"Basic Tutorial":"Alap bemutató",
"Activate":"Aktivál",
"Try free for 3 days!":"Próbáld ki kockázat nélkül 3 napig! Ebben az időszakban bármikor lemondhatod, anélkül, hogy megterhelnénk a számládat.",
"View other alternatives":"Megnézek egy másik lehetőséget"
}

View File

@ -18,74 +18,108 @@ PODS:
- FBSDKLoginKit/Login (= 9.1.0)
- FBSDKLoginKit/Login (9.1.0):
- FBSDKCoreKit (~> 9.1.0)
- Firebase/Analytics (7.3.0):
- Firebase/Analytics (7.11.0):
- Firebase/Core
- Firebase/Auth (7.3.0):
- Firebase/Auth (7.11.0):
- Firebase/CoreOnly
- FirebaseAuth (~> 7.3.0)
- Firebase/Core (7.3.0):
- FirebaseAuth (~> 7.11.0)
- Firebase/Core (7.11.0):
- Firebase/CoreOnly
- FirebaseAnalytics (= 7.3.0)
- Firebase/CoreOnly (7.3.0):
- FirebaseCore (= 7.3.0)
- Firebase/Messaging (7.3.0):
- FirebaseAnalytics (~> 7.11.0)
- Firebase/CoreOnly (7.11.0):
- FirebaseCore (= 7.11.0)
- Firebase/Messaging (7.11.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 7.3.0)
- firebase_analytics (8.0.0-dev.2):
- Firebase/Analytics (= 7.3.0)
- FirebaseMessaging (~> 7.11.0)
- Firebase/RemoteConfig (7.11.0):
- Firebase/CoreOnly
- FirebaseRemoteConfig (~> 7.11.0)
- firebase_analytics (8.0.2):
- Firebase/Analytics (= 7.11.0)
- firebase_core
- Flutter
- firebase_auth (1.0.3):
- Firebase/Auth (= 7.3.0)
- firebase_auth (1.1.2):
- Firebase/Auth (= 7.11.0)
- firebase_core
- Flutter
- firebase_core (1.0.3):
- Firebase/CoreOnly (= 7.3.0)
- firebase_core (1.1.0):
- Firebase/CoreOnly (= 7.11.0)
- Flutter
- firebase_messaging (9.1.1):
- Firebase/Messaging (= 7.3.0)
- firebase_messaging (9.1.3):
- Firebase/Messaging (= 7.11.0)
- firebase_core
- Flutter
- FirebaseAnalytics (7.3.0):
- firebase_remote_config (0.10.0-dev.2):
- Firebase/RemoteConfig (= 7.11.0)
- firebase_core
- Flutter
- FirebaseABTesting (7.11.0):
- FirebaseCore (~> 7.0)
- FirebaseAnalytics (7.11.0):
- FirebaseAnalytics/AdIdSupport (= 7.11.0)
- FirebaseCore (~> 7.0)
- FirebaseInstallations (~> 7.0)
- GoogleAppMeasurement (= 7.3.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.0)
- GoogleUtilities/MethodSwizzler (~> 7.0)
- GoogleUtilities/Network (~> 7.0)
- "GoogleUtilities/NSData+zlib (~> 7.0)"
- nanopb (~> 2.30906.0)
- FirebaseAuth (7.3.0):
- nanopb (~> 2.30908.0)
- FirebaseAnalytics/AdIdSupport (7.11.0):
- FirebaseAnalytics/Base (= 7.11.0)
- FirebaseCore (~> 7.0)
- FirebaseInstallations (~> 7.0)
- GoogleAppMeasurement/AdIdSupport (= 7.11.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.0)
- GoogleUtilities/MethodSwizzler (~> 7.0)
- GoogleUtilities/Network (~> 7.0)
- "GoogleUtilities/NSData+zlib (~> 7.0)"
- nanopb (~> 2.30908.0)
- FirebaseAnalytics/Base (7.11.0):
- FirebaseCore (~> 7.0)
- FirebaseInstallations (~> 7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.0)
- GoogleUtilities/MethodSwizzler (~> 7.0)
- GoogleUtilities/Network (~> 7.0)
- "GoogleUtilities/NSData+zlib (~> 7.0)"
- nanopb (~> 2.30908.0)
- FirebaseAuth (7.11.0):
- FirebaseCore (~> 7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.0)
- GoogleUtilities/Environment (~> 7.0)
- GTMSessionFetcher/Core (~> 1.4)
- FirebaseCore (7.3.0):
- FirebaseCoreDiagnostics (~> 7.0)
- FirebaseCore (7.11.0):
- FirebaseCoreDiagnostics (~> 7.4)
- GoogleUtilities/Environment (~> 7.0)
- GoogleUtilities/Logger (~> 7.0)
- FirebaseCoreDiagnostics (7.3.0):
- GoogleDataTransport (~> 8.0)
- FirebaseCoreDiagnostics (7.11.0):
- GoogleDataTransport (~> 8.4)
- GoogleUtilities/Environment (~> 7.0)
- GoogleUtilities/Logger (~> 7.0)
- nanopb (~> 2.30906.0)
- FirebaseInstallations (7.9.0):
- nanopb (~> 2.30908.0)
- FirebaseInstallations (7.11.0):
- FirebaseCore (~> 7.0)
- GoogleUtilities/Environment (~> 7.0)
- GoogleUtilities/UserDefaults (~> 7.0)
- PromisesObjC (~> 1.2)
- FirebaseInstanceID (7.9.0):
- FirebaseInstanceID (7.11.0):
- FirebaseCore (~> 7.0)
- FirebaseInstallations (~> 7.0)
- GoogleUtilities/Environment (~> 7.0)
- GoogleUtilities/UserDefaults (~> 7.0)
- FirebaseMessaging (7.3.0):
- FirebaseMessaging (7.11.0):
- FirebaseCore (~> 7.0)
- FirebaseInstallations (~> 7.0)
- FirebaseInstanceID (~> 7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.0)
- GoogleUtilities/Environment (~> 7.0)
- GoogleUtilities/Reachability (~> 7.0)
- GoogleUtilities/UserDefaults (~> 7.0)
- FirebaseRemoteConfig (7.11.0):
- FirebaseABTesting (~> 7.0)
- FirebaseCore (~> 7.0)
- FirebaseInstallations (~> 7.0)
- GoogleUtilities/Environment (~> 7.0)
- "GoogleUtilities/NSData+zlib (~> 7.0)"
- flurry (0.0.4):
- Flurry-iOS-SDK/FlurrySDK
- Flutter
@ -110,36 +144,38 @@ PODS:
- google_sign_in (0.0.1):
- Flutter
- GoogleSignIn (~> 5.0)
- GoogleAppMeasurement (7.3.0):
- GoogleAppMeasurement/AdIdSupport (7.11.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.0)
- GoogleUtilities/MethodSwizzler (~> 7.0)
- GoogleUtilities/Network (~> 7.0)
- "GoogleUtilities/NSData+zlib (~> 7.0)"
- nanopb (~> 2.30906.0)
- GoogleDataTransport (8.1.0):
- nanopb (~> 2.30906.0)
- nanopb (~> 2.30908.0)
- GoogleDataTransport (8.4.0):
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
- PromisesObjC (~> 1.2)
- GoogleSignIn (5.0.2):
- AppAuth (~> 1.2)
- GTMAppAuth (~> 1.0)
- GTMSessionFetcher/Core (~> 1.1)
- GoogleUtilities/AppDelegateSwizzler (7.3.1):
- GoogleUtilities/AppDelegateSwizzler (7.4.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (7.3.1):
- GoogleUtilities/Environment (7.4.0):
- PromisesObjC (~> 1.2)
- GoogleUtilities/Logger (7.3.1):
- GoogleUtilities/Logger (7.4.0):
- GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (7.3.1):
- GoogleUtilities/MethodSwizzler (7.4.0):
- GoogleUtilities/Logger
- GoogleUtilities/Network (7.3.1):
- GoogleUtilities/Network (7.4.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.3.1)"
- GoogleUtilities/Reachability (7.3.1):
- "GoogleUtilities/NSData+zlib (7.4.0)"
- GoogleUtilities/Reachability (7.4.0):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (7.3.1):
- GoogleUtilities/UserDefaults (7.4.0):
- GoogleUtilities/Logger
- GTMAppAuth (1.1.0):
- AppAuth/Core (~> 1.4)
@ -151,11 +187,11 @@ PODS:
- GTMSessionFetcher/Core (= 1.5.0)
- modal_progress_hud_nsn (0.0.1):
- Flutter
- nanopb (2.30906.0):
- nanopb/decode (= 2.30906.0)
- nanopb/encode (= 2.30906.0)
- nanopb/decode (2.30906.0)
- nanopb/encode (2.30906.0)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- package_info (0.0.1):
- Flutter
- package_info_plus (0.4.5):
@ -197,6 +233,7 @@ DEPENDENCIES:
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`)
- flurry (from `.symlinks/plugins/flurry/ios`)
- Flutter (from `Flutter`)
- flutter_app_badger (from `.symlinks/plugins/flutter_app_badger/ios`)
@ -223,6 +260,7 @@ SPEC REPOS:
- FBSDKCoreKit
- FBSDKLoginKit
- Firebase
- FirebaseABTesting
- FirebaseAnalytics
- FirebaseAuth
- FirebaseCore
@ -230,6 +268,7 @@ SPEC REPOS:
- FirebaseInstallations
- FirebaseInstanceID
- FirebaseMessaging
- FirebaseRemoteConfig
- Flurry-iOS-SDK
- FMDB
- GoogleAppMeasurement
@ -259,6 +298,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
firebase_remote_config:
:path: ".symlinks/plugins/firebase_remote_config/ios"
flurry:
:path: ".symlinks/plugins/flurry/ios"
Flutter:
@ -304,18 +345,21 @@ SPEC CHECKSUMS:
devicelocale: b22617f40038496deffba44747101255cee005b0
FBSDKCoreKit: a00fe2efd780c195a5e09201bf51c56106245b40
FBSDKLoginKit: d98498c598ec09de657385a9349a1f21119b7f86
Firebase: 26223c695fe322633274198cb19dca8cb7e54416
firebase_analytics: 4c032e04324c47ee6dd7a28bfff831e0ac0d09b6
firebase_auth: c2c27e1081671b02f90981e70dad54722198491f
firebase_core: b5d81dfd4fb2d6f700e67de34d9a633ae325c4e9
firebase_messaging: 7547c99f31466371f9cfcb733d5a1bf32a819872
FirebaseAnalytics: 2580c2d62535ae7b644143d48941fcc239ea897a
FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2
FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972
FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350
FirebaseInstallations: 5e777e6640fa060405cc7632447b6c5ca5af4742
FirebaseInstanceID: 53140c03b9f6136f890d7901399f85a4c90ab2d0
FirebaseMessaging: 68d1bcb14880189558a8ae57167abe0b7e417232
Firebase: c121feb35e4126c0b355e3313fa9b487d47319fd
firebase_analytics: 620e8cc1705feb6b9c40b6127bea9b39e03e3970
firebase_auth: e7065954aa2a7c8ef1a8502fba3009bcdd8fc91a
firebase_core: 84dcd80ac6d29c3d1039071b7306ee99688eb9c7
firebase_messaging: 7aecb08eada5e5cde85b10875141706a8d18b818
firebase_remote_config: f855065886b7d6ccc38144c9a3cecbdc7887f33e
FirebaseABTesting: e66f1f80747792630d9b292966de206d5df9853b
FirebaseAnalytics: cd3bd84d722a24a8923918af8af8e5236f615d77
FirebaseAuth: 5fe4585c2ed847319f0ea68bd1d82c77e49ff9a0
FirebaseCore: 907447d8917a4d3eb0cce2829c5a0ad21d90b432
FirebaseCoreDiagnostics: 68ad972f99206cef818230f3f3179d52ccfb7f8c
FirebaseInstallations: a58d4f72ec5861840b84df489f2668d970df558a
FirebaseInstanceID: ad5135045a498d7775903efd39762d2cdfa1be27
FirebaseMessaging: 163435fb6db065e3b6228f1e577b10ed2cc506d2
FirebaseRemoteConfig: 0ea30de5fb0231df8c1bdcdf3b6c23bdc5066131
flurry: 15b01f664ab1367c62b50291541ea7f78ca85aad
Flurry-iOS-SDK: 6636d30c30f12010e7c7c71d84b443416a168efc
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
@ -326,14 +370,14 @@ SPEC CHECKSUMS:
flutter_uxcam: 87dd981feb200bc81a2f062edb5ca2d16dae595a
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
google_sign_in: 6bd214b9c154f881422f5fe27b66aaa7bbd580cc
GoogleAppMeasurement: 8d3c0aeede16ab7764144b5a4ca8e1d4323841b7
GoogleDataTransport: 116c84c4bdeb76be2a7a46de51244368f9794eab
GoogleAppMeasurement: fd19169c3034975cb934e865e5667bfdce59df7f
GoogleDataTransport: cd9db2180fcecd8da1b561aea31e3e56cf834aa7
GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213
GoogleUtilities: e1d9ed4e544fc32a93e00e721400cbc3f377200d
GoogleUtilities: 284cddc7fffc14ae1907efb6f78ab95c1fccaedc
GTMAppAuth: 197a8dabfea5d665224aa00d17f164fc2248dab9
GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52
modal_progress_hud_nsn: f6fb744cd060653d66ed8f325360ef3650eb2fde
nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c

View File

@ -388,7 +388,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 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 = 1;
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 = 1;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = SFJJBDCU6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (

View File

@ -44,7 +44,7 @@
<string>fbshareextension</string>
</array>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<string>11.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View File

@ -6,6 +6,7 @@ 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/service/logging.dart';
import 'package:aitrainer_app/service/purchase_service.dart';
import 'package:aitrainer_app/util/enums.dart';
@ -13,6 +14,7 @@ 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';
@ -22,8 +24,29 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
List<ProductTest>? tests = [];
List<Product> product2Display = [];
int productSet = -1;
final DescriptionRepository descriptionRepository = DescriptionRepository();
SalesBloc() : super(SalesInitial());
String? salesText;
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");
}
});
}
return product;
}
@override
Stream<SalesState> mapEventToState(
SalesEvent event,
@ -32,9 +55,30 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
if (event is SalesLoad) {
yield SalesLoading();
log("Load Sales");
Track().track(TrackingEvent.sales_page);
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);
yield SalesReady();
} else if (event is SalesPurchase) {
if (Cache().hasPurchased) {
@ -60,6 +104,22 @@ 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());
@ -76,7 +136,7 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
return prod;
}
String getLocalizedPrice(String productId) {
String getLocalizedPrice(String productId, Product product) {
String price = "";
Offering? offering = RevenueCatPurchases().offering;
if (offering != null) {
@ -86,7 +146,7 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
price = package.product.priceString;
if (price.contains(r'HUF')) {
price = price.replaceAll(RegExp(r'HUF'), 'Ft');
price = price.replaceAll(RegExp(r',00'), "");
price = price.replaceAll(RegExp(r'\,00'), "");
}
break;
}
@ -94,18 +154,21 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
} else {
log(" !!! No Offering");
}
if (price.isEmpty) {
price = Platform.isAndroid ? product.priceAndroid.toString() : product.priceIos.toString();
}
return price;
}
void getProductSet() {
int productId = 0;
this.tests = Cache().productTests;
//this.tests = Cache().productTests;
List<Product>? products = Cache().products;
if (products == null) {
return;
}
if (tests != null && tests!.isEmpty) {
/* if (tests != null && tests!.isEmpty) {
var rand = math.Random.secure();
productSet = rand.nextInt(5) + 1;
} else {
@ -118,9 +181,9 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
break;
}
}
}
} */
ProductTest productTest = ProductTest();
//ProductTest productTest = ProductTest();
productSet = 2;
log("ProductSet: " + productSet.toString());
@ -130,7 +193,8 @@ class SalesBloc extends Bloc<SalesEvent, SalesState> with Logging {
if (product.productSet == productSet) {
productId = product.productId;
final String platformProductId = Platform.isAndroid ? product.productIdAndroid! : product.productIdIos!;
product.localizedPrice = getLocalizedPrice(platformProductId);
product.localizedPrice = getLocalizedPrice(platformProductId, product);
log("product with localized price: $product");
product2Display.add(product);
}
}
@ -139,9 +203,9 @@ 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();
//productTest.productId = productId;
//productTest.customerId = Cache().userLoggedIn!.customerId!;
//productTest.dateView = DateTime.now();
//ProductTestApi().saveProductTest(productTest);
//Cache().productTests.add(productTest);
}

View File

@ -15,3 +15,7 @@ class SalesPurchase extends SalesEvent {
final int productId;
const SalesPurchase({required this.productId});
}
class SalesChangeSubscription extends SalesEvent {
const SalesChangeSubscription();
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:aitrainer_app/bloc/settings/settings_bloc.dart';
import 'package:aitrainer_app/service/firebase_api.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/session.dart';
@ -26,6 +27,7 @@ class SessionBloc extends Bloc<SessionEvent, SessionState> with Logging {
// ignore: close_sinks
SettingsBloc settingsBloc = event.settingsBloc;
await session.fetchSessionAndNavigate();
FirebaseApi().setupRemoteConfig();
String lang = AppLanguage().appLocal.languageCode;
log("Change lang to $lang");
settingsBloc.add(SettingsChangeLanguage(language: lang));

View File

@ -12,6 +12,7 @@ part 'tutorial_state.dart';
class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
late String tutorialName;
final double mediaHeightBase = 390;
bool isActive = false;
bool canActivate = false;
@ -46,6 +47,7 @@ class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
setNextStepData(step);
isActive = true;
canActivate = true;
}
});
}
@ -58,8 +60,9 @@ class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
}
if (tutorial != null && tutorial!.steps != null) {
actualText = tutorial!.steps![step].tutorialTextTranslation;
print("Step: $step, text: $actualText");
this.actualCheck = tutorial!.steps![step].checkText;
print("Step: $step, text: $actualCheck");
this.checks = [];
if (this.actualCheck != null) {
@ -78,27 +81,31 @@ class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
calculateHeight();
}
/* else {
this.add(TutorialFinished());
} */
}
bool activateTutorial() {
if (!canActivate) {
print("Tutorial canActivate false");
return false;
}
bool isTutorialDone() {
ActivityDone? activityDone = ActivityDone.tutorialBasic.searchByString(tutorialName);
if (activityDone == null) {
return false;
return true;
}
bool? isActivityDone = Cache().activitiesDone[activityDone.toStr()];
log("isActivityDone? $isActivityDone");
log("$tutorialName isActivityDone? $isActivityDone");
if (isActivityDone == null || isActivityDone == true) {
return true;
}
canActivate = true;
return false;
}
bool activateTutorial() {
if (isTutorialDone()) {
isActive = false;
canActivate = false;
return false;
}
log("Running tutorial $activityDone");
init();
return true;
}
@ -135,20 +142,9 @@ class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
if (action == null) {
return step + 1;
}
/* bool found = false;
if (tutorial != null && tutorial!.steps != null) {
for (var nextStep in tutorial!.steps!) {
print("step $step parent: ${nextStep.action!.parent}");
if (step + 1 == nextStep.step) {
next = nextStep.step!;
found = true;
break;
}
}
} */
step++;
next = step;
print("Next step:! $next");
print("Next step:! $step");
if (step >= tutorial!.steps!.length) {
next = -1;
this.add(TutorialFinished());
@ -172,6 +168,7 @@ class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
menuBloc!.add(MenuCreate());
}
} else if (event is TutorialNext) {
print("Tutorial Next: ${event.text}");
if (tutorial != null) {
yield TutorialLoading();
@ -195,7 +192,6 @@ class TutorialBloc extends Bloc<TutorialEvent, TutorialState> with Logging {
yield TutorialReady();
} else if (event is TutorialStart) {
yield TutorialLoading();
isActive = true;
canActivate = true;
step = 0;
yield TutorialReady();

View File

@ -0,0 +1,304 @@
import 'package:flutter/material.dart';
enum PredefinedThemes {
success,
successOutline,
danger,
dangerOutline,
warning,
warningOutline,
info,
infoOutline,
primary,
primaryOutline,
secondary,
secondaryOutline,
dark,
darkOutline,
light,
}
List definedColors = [
// success
{"color": Color(0xFF5CB85C), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// successOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF5CB85C), "shadowColor": Color(0xFF5CB85C), "blurColor": Color(0xFF5CB85C)},
// danger
{"color": Color(0xFFD9534F), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// dangerOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFFD9534F), "shadowColor": Color(0xFFD9534F), "blurColor": Color(0xFFD9534F)},
// warning
{"color": Color(0xFFF0AD4E), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// warningOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFFF0AD4E), "shadowColor": Color(0xFFF0AD4E), "blurColor": Color(0xFFF0AD4E)},
// info
{"color": Color(0xFF5BC0DE), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// infoOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF5BC0DE), "shadowColor": Color(0xFF5BC0DE), "blurColor": Color(0xFF5BC0DE)},
// primary
{"color": Color(0xFF0275D8), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// primaryOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF0275D8), "shadowColor": Color(0xFF0275D8), "blurColor": Color(0xFF0275D8)},
// secondary
{"color": Color(0xFF6B5B95), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// secondaryOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF6B5B95), "shadowColor": Color(0xFF6B5B95), "blurColor": Color(0xFF6B5B95)},
// dark
{"color": Color(0xFF292B2C), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
// darkOutline
{"color": Color(0xFFFAFAFA), "borderColor": Color(0xFF292B2C), "shadowColor": Color(0xFF292B2C), "blurColor": Color(0xFF292B2C)},
{"color": Color(0xFFF7F7F7), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black},
{"color": Color(0xFFBFBFC4), "borderColor": Colors.black, "shadowColor": Colors.black, "blurColor": Colors.black}
];
class AnimatedButton extends StatefulWidget {
// * @param onTap The function that is called whenever the widget is tapped
final GestureTapCallback onTap;
final Widget child;
// * @param type There are 16 predefined themes from which you can choose from if you dont want to customize the widget yourself
final PredefinedThemes type;
// * @param animationCurve The curve that the animation will follow
final Curve animationCurve;
// * @param enabled To check whether the button is enabbled or not
final bool enabled;
// * @param isMultiColor To check whether the button has multiple color gradient or not
final bool isMultiColor;
// * @param isOutline To check whether the button has a outline or not
final bool isOutline;
// * @param darkShadow To check whether the button has dark or light shadow
final bool darkShadow;
// * @param duration The time that the animation takes in milliseconds
final int duration;
// * @param height The height of the widget
final double height;
// * @param width The width of the widget
final double width;
// * @param blurRadius The radius of the blur effect of the widget
final double blurRadius;
// * @param borderRadius The radius of the borders of the widget
final double borderRadius;
// * @param shadowHeightBottom The height of the shadow and the animation of the widget from the bottom of the child widget
final double shadowHeightBottom;
// * @param shadowHeightLeft The height of the shadow and the animation of the widget from the left of the child widget
final double shadowHeightLeft;
// * @param borderWidth The width of the border of the widget
final double borderWidth;
// * @param borderColor The color of the border of the widget(if type is not null, this will not work, is Outline should be true for this to work)
final Color borderColor;
// * @param blurColor The color of the blur of the widget(if type is not null, this will not work)
final Color blurColor;
// * @param color The color of the widget(if type is not null, this will not work)
final Color color;
// * @param shadowColor The color of the shadow of the widget(if type is not null, this will not work)
final Color? shadowColor;
// * @param colors The list of colors for the gradient for the background of the widget(isMulticolor should be true for this to work)
final List<Color>? colors;
AnimatedButton({
Key? key,
required this.onTap,
required this.child,
this.enabled = true,
this.type = PredefinedThemes.primary,
this.color = Colors.blue,
this.height = 64,
this.colors,
this.isMultiColor = false,
this.darkShadow = true,
this.width = 200,
this.duration = 70,
this.blurRadius = 0,
this.borderRadius = 12,
this.animationCurve = Curves.easeIn,
this.shadowHeightBottom = 4,
this.shadowHeightLeft = 0,
this.isOutline = false,
this.borderColor = Colors.black,
this.borderWidth = 1,
this.blurColor = Colors.black,
this.shadowColor,
}) : assert(child != null),
super(key: key);
@override
_AnimatedButtonState createState() => _AnimatedButtonState(
type: this.type,
color: this.color,
blurColor: this.blurColor,
borderColor: this.borderColor,
);
}
class _AnimatedButtonState extends State<AnimatedButton> {
late double btnPositionBottom;
late double btnPositionTop;
late double btnPositionLeft;
late double btnPositionRight;
PredefinedThemes? type;
Color? color;
Color? shadowColor;
Color? borderColor;
Color? blurColor;
_AnimatedButtonState({
this.type,
this.color,
this.shadowColor,
this.borderColor,
this.blurColor,
});
@override
void initState() {
super.initState();
// check if there is a PredefinedThemes type used in the widget
// if yes, over-ride all the colors of the widget
if (type != null) {
int index = type!.index;
setState(() {
color = definedColors[index]["color"];
shadowColor = definedColors[index]["shadowColor"];
blurColor = definedColors[index]["blurColor"];
borderColor = definedColors[index]["borderColor"];
});
} else {
setState(() {
color = widget.color;
shadowColor = widget.shadowColor;
blurColor = widget.blurColor;
borderColor = widget.borderColor;
});
}
// initialize the top widget with the defined bottom and left height
setState(() {
btnPositionBottom = widget.shadowHeightBottom;
btnPositionLeft = widget.shadowHeightLeft;
});
}
@override
Widget build(BuildContext context) {
final double height = widget.height - btnPositionBottom;
return GestureDetector(
child: Center(
child: Container(
width: widget.width + btnPositionLeft,
height: height + btnPositionBottom,
child: Stack(
children: <Widget>[
Positioned(
bottom: 0,
child: Container(
height: height,
width: widget.width,
decoration: BoxDecoration(
gradient: widget.isMultiColor
? LinearGradient(
colors: shadow(widget.colors!, widget.darkShadow),
)
: null,
color: (widget.shadowColor != null)
? widget.shadowColor
: shadow((widget.isOutline) ? [color!] : [borderColor!], widget.darkShadow),
borderRadius: BorderRadius.all(
Radius.circular(
widget.borderRadius,
),
),
border: widget.isOutline
? Border.all(
color: borderColor!,
width: widget.borderWidth,
)
: null,
boxShadow: <BoxShadow>[
BoxShadow(
color: blurColor!,
blurRadius: widget.blurRadius,
offset: Offset(0.0, 0.0),
),
],
),
),
),
AnimatedPositioned(
curve: widget.animationCurve,
duration: Duration(milliseconds: widget.duration),
bottom: btnPositionBottom,
left: btnPositionLeft,
child: Container(
height: height,
width: widget.width,
decoration: BoxDecoration(
gradient: widget.isMultiColor
? LinearGradient(
colors: widget.colors!,
)
: null,
color: color,
borderRadius: BorderRadius.all(
Radius.circular(
widget.borderRadius,
),
),
border: widget.isOutline
? Border.all(
color: borderColor!,
width: widget.borderWidth,
)
: null,
),
child: Center(
child: widget.child,
),
),
),
],
),
),
),
onTapDown: widget.enabled ? _tapDown : null,
onTapUp: widget.enabled ? _tapUp : null,
onTapCancel: widget.enabled ? _unTap : null,
);
}
void _tapDown(_) {
// when the widget is pressed make both the heights, bottom and left to zero
setState(() {
btnPositionBottom = 0;
btnPositionLeft = 0;
});
}
// when the widget is released after being pressed, it returns to the given heights, bottom and left
void _tapUp(_) => _unTap();
void _unTap() {
setState(() {
btnPositionLeft = widget.shadowHeightLeft;
btnPositionBottom = widget.shadowHeightBottom;
});
widget.onTap();
}
// returns a list of color shades according to the params
// * @param colors list of colors
// * @param darkshadow whether to return a darker shadow or lighter shadow
shadow(List<Color> colors, bool darkShadow) {
List<Color> w = [];
colors.forEach((color) {
double amount = darkShadow ? 0.22 : 0.1;
final hsl = HSLColor.fromColor(color);
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
w.add(hslDark.toColor());
});
return (w.length == 1) ? w[0] : w;
}
}

View File

@ -2,6 +2,7 @@ import 'dart:collection';
import 'dart:convert';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/customer_activity.dart';
import 'package:aitrainer_app/model/description.dart';
import 'package:aitrainer_app/model/evaluation.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
@ -25,6 +26,7 @@ import 'package:aitrainer_app/main.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/env.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flurry/flurry.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:flutter_uxcam/flutter_uxcam.dart';
@ -58,7 +60,14 @@ enum SharePrefsChange {
- is_logged_in
*/
enum ActivityDone { tutorialBasic, tutorialDevelopment, isExerciseLogSeen, isMuscleDevelopmentSeen }
enum ActivityDone {
tutorialBasic,
tutorialBasicChestPress,
tutorialBasicLegPress,
tutorialDevelopment,
isExerciseLogSeen,
isMuscleDevelopmentSeen
}
extension ActivityDoneExt on ActivityDone {
String toStr() => this.toString().split(".").last;
@ -131,6 +140,7 @@ class Cache with Logging {
List<CustomerExerciseDevice>? _customerDevices;
List<CustomerActivity>? _customerActivities;
List<Tutorial>? _tutorials;
List<Description>? _descriptions;
LinkedHashMap<int, ExercisePlanDetail> _myExercisesPlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
@ -141,6 +151,8 @@ class Cache with Logging {
List<Exercise>? _exercisesTrainee;
ExercisePlan? _traineeExercisePlan;
RemoteConfig? remoteConfig;
LinkedHashMap<String, int> _badges = LinkedHashMap();
List? deviceLanguages;
@ -664,4 +676,10 @@ class Cache with Logging {
List<Tutorial>? get tutorials => this._tutorials;
setTutorials(List<Tutorial>? value) => this._tutorials = value;
RemoteConfig? getRemoteConfig() => this.remoteConfig;
setRemoteConfig(RemoteConfig? remoteConfig) => this.remoteConfig = remoteConfig;
List<Description>? getDescriptions() => this._descriptions;
setDescriptions(List<Description>? value) => this._descriptions = value;
}

View File

@ -0,0 +1,40 @@
import 'package:aitrainer_app/util/app_language.dart';
import 'dart:ui';
class Description {
late int descriptionId;
late String name;
late String description;
int? version;
DateTime? validFrom;
DateTime? validTo;
String? descriptionTranslation;
Description.fromJson(Map json) {
this.descriptionId = json['descriptionId'];
this.name = json['name'];
this.description = json['description'];
this.version = json['version'];
this.validFrom = json['validFrom'];
this.validTo = json['validTo'];
if (json['translations'] != null && json['translations'].length > 0) {
this.descriptionTranslation =
AppLanguage().appLocal == Locale('hu') ? json['translations'][0]['descriptionTranslation'] : json['description'];
}
}
Map<String, dynamic> toJson() => {
"descriptionId": this.descriptionId,
"name": this.name,
"description": this.description,
"version": this.version,
"validFrom": this.validFrom,
"validTo": this.validTo,
"descriptionTranslation": this.descriptionTranslation
};
@override
String toString() => this.toJson().toString();
}

View File

@ -46,6 +46,7 @@ class Product {
'productIdAndroid': this.productIdAndroid,
'priceIos': this.priceIos,
'priceAndroid': this.priceAndroid,
'localizedPrice': this.localizedPrice
};
return json.toString();
}

View File

@ -14,6 +14,19 @@ class Tutorial {
if (json['steps'] != null && json['steps'].length > 0) {
steps = json['steps'].map<TutorialStep>((step) => TutorialStep.fromJson(step)).toList();
if (steps != null) {
steps!.sort((a, b) {
if (a.step == null || b.step == null) {
return -1;
} else {
if (a.step! <= b.step!) {
return -1;
} else {
return 1;
}
}
});
}
}
}

View File

@ -71,7 +71,6 @@ class TutorialStep {
this.condition = json['condition'];
if (this.condition != null) {
this.condition = condition!.replaceAll(r'\\', "replace");
print("Json condition $condition");
this.action = TutorialStepAction.fromJson(jsonDecode(condition!));
}

View File

@ -27,5 +27,7 @@ class PushNotificationsManager with Logging {
badge: true,
sound: true,
);
String? token = await FirebaseMessaging.instance.getToken();
print("FirebaseMessaging tokne $token");
}
}

View File

@ -0,0 +1,23 @@
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/description.dart';
class DescriptionRepository {
List<Description>? descriptions;
DescriptionRepository() {
this.descriptions = Cache().getDescriptions();
}
String getDescriptionByName(String name) {
String descriptionText = "";
if (descriptions != null) {
this.descriptions!.forEach((element) {
print("Desc ${element.name} - $name");
if (element.name == name) {
descriptionText = element.descriptionTranslation != null ? element.descriptionTranslation! : element.description;
}
});
}
return descriptionText;
}
}

View File

@ -3,6 +3,7 @@ import 'package:aitrainer_app/service/logging.dart' as logging;
import 'package:apple_sign_in/apple_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
@ -28,7 +29,8 @@ class FirebaseApi with logging.Logging {
Future<void> initializeFlutterFire() async {
try {
// Wait for Firebase to initialize and set `_initialized` state to true
await Firebase.initializeApp();
FirebaseApp app = await Firebase.initializeApp();
this.appleSignInAvailable = await AppleSignIn.isAvailable();
} catch (e) {
// Set `_error` state to true if Firebase initialization fails
@ -286,4 +288,23 @@ class FirebaseApi with logging.Logging {
Future<void> resetPassword(String email) async {
await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
}
Future<void> setupRemoteConfig() async {
initializeFlutterFire();
final RemoteConfig remoteConfig = RemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
minimumFetchInterval: const Duration(seconds: 1),
));
await remoteConfig.setDefaults(<String, dynamic>{
'sales_page_text': '0',
'sales_page_bkg': 'dark',
'sales_page_offer': 'monthly',
});
RemoteConfigValue(null, ValueSource.valueStatic);
Cache().setRemoteConfig(remoteConfig);
Map config = remoteConfig.getAll();
print("RemoteConfig sales_page_text Value : ${config['sales_page_text'].asString()}");
}
}

View File

@ -5,6 +5,7 @@ import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/customer_activity.dart';
import 'package:aitrainer_app/model/customer_exercise_device.dart';
import 'package:aitrainer_app/model/customer_property.dart';
import 'package:aitrainer_app/model/description.dart';
import 'package:aitrainer_app/model/evaluation.dart';
import 'package:aitrainer_app/model/exercise.dart';
import 'package:aitrainer_app/model/exercise_device.dart';
@ -72,8 +73,13 @@ class PackageApi {
} else if (headRecord[0] == "Tutorial") {
final Iterable json = jsonDecode(headRecord[1]);
final List<Tutorial> tutorials = json.map((tutorial) => Tutorial.fromJson(tutorial)).toList();
print("Tutorial: $tutorials");
Cache().setTutorials(tutorials);
} else if (headRecord[0] == "Description") {
final Iterable json = jsonDecode(headRecord[1]);
final List<Description>? descriptions = json.map((description) => Description.fromJson(description)).toList();
//print("Description: $descriptions");
Cache().setDescriptions(descriptions);
}
});

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:aitrainer_app/service/firebase_api.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/util/app_localization.dart';
import 'package:aitrainer_app/service/api.dart';
@ -7,6 +8,8 @@ import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/service/package_service.dart';
import 'package:aitrainer_app/util/purchases.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/services.dart';
import 'package:flutter_app_badger/flutter_app_badger.dart';
import 'package:package_info/package_info.dart';

View File

@ -4,11 +4,13 @@ import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/service/tracking_service.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/model/tracking.dart' as model;
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flurry/flurry.dart';
import 'package:flutter_uxcam/flutter_uxcam.dart';
class Track with Logging {
static final Track _singleton = Track._internal();
static FirebaseAnalytics analytics = FirebaseAnalytics();
factory Track() {
return _singleton;
@ -22,6 +24,7 @@ class Track with Logging {
// Smartlook.setGlobalEventProperty(event.toString(), eventValue, false);
FlutterUxcam.logEventWithProperties(event.enumToString(), {"value": eventValue});
model.Tracking tracking = model.Tracking();
analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue});
tracking.customerId = Cache().userLoggedIn == null ? 0 : Cache().userLoggedIn!.customerId!;
tracking.event = event.enumToString();
if (eventValue.isNotEmpty) {

View File

@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:aitrainer_app/bloc/exercise_control/exercise_control_bloc.dart';
import 'package:aitrainer_app/bloc/timer/timer_bloc.dart';
import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart';
import 'package:aitrainer_app/library/custom_icon_icons.dart';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
@ -267,6 +268,7 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
String title = (step + 1).toString() + "/4 " + t("Control Exercise:");
LinkedHashMap args = LinkedHashMap();
final TutorialBloc tutorialBloc = BlocProvider.of<TutorialBloc>(context);
List<Widget> listWidgets = [
Text(
@ -332,15 +334,20 @@ class _ExerciseControlPage extends State<ExerciseControlPage> with Trans {
primary: Colors.white,
onSurface: Colors.blueAccent,
),
onPressed: () => {
exerciseBloc.add(ExerciseControlSubmit(step: step)),
if (step == 3)
{
Navigator.of(context).pop(),
args['exerciseRepository'] = exerciseBloc.exerciseRepository,
Navigator.of(context).pushNamed('evaluationPage', arguments: args)
}
},
onPressed: () {
if (tutorialBloc.isActive) {
if (!tutorialBloc.checkAction("Save")) {
return;
}
}
exerciseBloc.add(ExerciseControlSubmit(step: step));
if (step == 3) {
Navigator.of(context).pop();
args['exerciseRepository'] = exerciseBloc.exerciseRepository;
Navigator.of(context).pushNamed('evaluationPage', arguments: args);
}
},
child: step == exerciseBloc.step
? Stack(
alignment: Alignment.center,

View File

@ -1,4 +1,5 @@
import 'package:aitrainer_app/bloc/sales/sales_bloc.dart';
import 'package:aitrainer_app/library/button_animations.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar_min.dart';
@ -6,6 +7,8 @@ import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/sales_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html/style.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
@ -22,7 +25,6 @@ class SalesPage extends StatelessWidget with Trans, Logging {
create: (context) => SalesBloc()..add(SalesLoad()),
child: BlocConsumer<SalesBloc, SalesState>(listener: (context, state) {
if (state is SalesError) {
log("Error: " + state.message);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(backgroundColor: Colors.orange, content: Text(t(state.message), style: TextStyle(color: Colors.white))));
} else if (state is SalesSuccessful) {
@ -57,6 +59,9 @@ class SalesPage extends StatelessWidget with Trans, Logging {
}
Widget salesWidget(SalesBloc bloc) {
final salesText = bloc.salesText != null ? bloc.salesText! : "";
final String html = salesText;
return Container(
decoration: BoxDecoration(
image: DecorationImage(
@ -68,55 +73,144 @@ class SalesPage extends StatelessWidget with Trans, Logging {
child: CustomScrollView(scrollDirection: Axis.vertical, slivers: [
SliverList(
delegate: SliverChildListDelegate([
Divider(),
Container(
padding: EdgeInsets.only(left: 65, right: 65),
child: Text(t("Unleash Your Development Now!"),
Html(
data: html,
//Optional parameters:
style: {
"p": Style(
color: Colors.white,
fontSize: FontSize(16),
padding: const EdgeInsets.only(left: 20, right: 8, bottom: 4),
),
"strong": Style(
color: Colors.yellow[600],
fontSize: FontSize(16),
),
"h3": Style(
color: Colors.yellow[600],
fontSize: FontSize(16),
textAlign: TextAlign.center,
padding: const EdgeInsets.all(12),
),
"li": Style(
color: Colors.white,
fontSize: FontSize(14),
padding: const EdgeInsets.only(left: 20, bottom: 10, right: 8),
//before: "*",
display: Display.LIST_ITEM),
"h2": Style(
color: Colors.yellow[600],
fontWeight: FontWeight.bold,
fontSize: FontSize(24),
textAlign: TextAlign.center,
//padding: const EdgeInsets.all(4),
),
"h1": Style(
color: Colors.yellow[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,
//Optional parameters:
style: {
"p": Style(
color: Colors.black,
fontSize: FontSize(16),
padding: const EdgeInsets.all(4),
textAlign: TextAlign.center,
maxLines: 4,
softWrap: true,
style: GoogleFonts.archivoBlack(
fontSize: 30,
color: Colors.white,
shadows: <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,
),
],
))),
Divider(),
Container(
padding: EdgeInsets.only(left: 45, right: 45),
child: Text(t("Learn about your development, enjoy AI-driven predictions of all of your skills and bodyparts."),
textAlign: TextAlign.left,
maxLines: 4,
softWrap: true,
style: GoogleFonts.inter(
fontSize: 16,
color: Colors.white,
))),
SizedBox(
height: 50,
textShadow: [
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black54,
)
],
),
"li": Style(
color: Colors.white,
fontSize: FontSize(14),
padding: const EdgeInsets.only(left: 10, bottom: 10),
before: "*",
),
"h2": Style(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: FontSize.larger,
textAlign: TextAlign.center,
textShadow: [
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 12.0,
color: Colors.black54,
)
],
//padding: const EdgeInsets.all(4),
),
"h1": Style(
color: Colors.yellow[600],
fontWeight: FontWeight.bold,
fontSize: FontSize.xLarge,
alignment: Alignment.center,
padding: const EdgeInsets.all(4),
textAlign: TextAlign.center,
textShadow: [
Shadow(
offset: Offset(5.0, 5.0),
blurRadius: 12.0,
color: Colors.black54,
)
],
),
},
),
duration: 600,
darkShadow: true,
blurRadius: 12,
animationCurve: Curves.easeIn,
height: 120,
width: 320,
onTap: () => bloc.add(SalesPurchase(productId: bloc.offeredProduct!.productId)),
//color: Color(0xffb4f500),
isMultiColor: true,
colors: [
Colors.blue,
Color(0xffb4f500),
Color(0xffb4f500),
],
),
])),
SliverGrid(
delegate: SliverChildListDelegate(getButtons(bloc)),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 0.55,
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]!,
],
),
),
SliverList(
delegate: SliverChildListDelegate([
SizedBox(
height: 30,
),
@ -144,6 +238,35 @@ class SalesPage extends StatelessWidget with Trans, Logging {
]));
}
Widget getTrialDescription() {
final trialText = t("Try free for 3 days!");
return Container(
padding: EdgeInsets.only(left: 55, right: 55),
child: Html(
data: "<p>" + trialText + "</p>",
//Optional parameters:
style: {
"p": Style(
color: Colors.white,
fontSize: FontSize(13),
padding: const EdgeInsets.all(4),
textAlign: TextAlign.center,
textShadow: [
Shadow(
offset: Offset(2.0, 2.0),
blurRadius: 6.0,
color: Colors.black54,
)
],
),
"strong": Style(
color: Colors.yellow[600],
fontSize: FontSize(13),
),
},
));
}
List<Widget> getButtons(SalesBloc bloc) {
List<Widget> buttons = [];

View File

@ -139,7 +139,7 @@ class SettingsPage extends StatelessWidget with Trans {
final TutorialBloc tutorialBloc = BlocProvider.of<TutorialBloc>(context);
return ListTile(
leading: Icon(CustomIcon.question_circle),
subtitle: Text("Activating the basic tutorial"),
subtitle: Text(t("Activating the basic tutorial")),
title: ToggleSwitch(
minWidth: 120.0,
minHeight: 30.0,
@ -151,7 +151,15 @@ class SettingsPage extends StatelessWidget with Trans {
inactiveFgColor: Colors.grey[900],
labels: [t('Basic Tutorial'), t('Activate')],
onToggle: (index) {
settingsBloc.add(SettingsActivateTutorial(activity: ActivityDone.tutorialBasic));
ActivityDone activity = ActivityDone.tutorialBasic;
if (Cache().userLoggedIn != null) {
if (Cache().userLoggedIn!.sex == "m") {
activity = ActivityDone.tutorialBasicChestPress;
} else {
activity = ActivityDone.tutorialBasicLegPress;
}
}
settingsBloc.add(SettingsActivateTutorial(activity: activity));
tutorialBloc.add(TutorialStart());
},
),

View File

@ -9,6 +9,7 @@ import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/widgets/app_bar.dart';
import 'package:aitrainer_app/widgets/dialog_common.dart';
import 'package:aitrainer_app/widgets/dialog_html.dart';
import 'package:aitrainer_app/widgets/menu_image.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/cupertino.dart';
@ -190,12 +191,9 @@ class TestSetEdit extends StatelessWidget with Trans {
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DialogCommon(
return DialogHTML(
title: bloc.templateNameTranslation,
descriptions: bloc.templateDescription,
text: "OK",
onTap: () => {Navigator.of(context).pop()},
onCancel: () => {Navigator.of(context).pop()},
htmlData: bloc.templateDescription,
);
}),
child: Icon(

View File

@ -67,17 +67,25 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
}
Future runDelayedEvent() async {
print("runDelayedEvent start");
bool isFirst = false;
await Future.delayed(Duration(milliseconds: 600), () async {
if (tutorialBloc.isActive == false) {
print("Activate tutorial");
tutorialBloc.canActivate = true;
tutorialBloc.isActive = true;
tutorialBloc.menuBloc = menuBloc;
tutorialBloc.add(TutorialLoad());
tutorialBloc.init();
isFirst = true;
if (Cache().userLoggedIn != null) {
if (Cache().userLoggedIn!.sex == "m") {
tutorialBloc.tutorialName = ActivityDone.tutorialBasicChestPress.toStr();
} else {
tutorialBloc.tutorialName = ActivityDone.tutorialBasicLegPress.toStr();
}
}
if (!tutorialBloc.isTutorialDone()) {
if (tutorialBloc.isActive == false && tutorialBloc.canActivate) {
print("Activate tutorial");
tutorialBloc.canActivate = true;
tutorialBloc.isActive = true;
tutorialBloc.menuBloc = menuBloc;
tutorialBloc.add(TutorialLoad());
tutorialBloc.init();
isFirst = true;
}
}
});
final bool canActivate = tutorialBloc.activateTutorial();
@ -351,6 +359,7 @@ class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
void menuClick(WorkoutMenuTree workoutTree, MenuBloc menuBloc) {
if (tutorialBloc.isActive) {
final String checkText = workoutTree.nameEnglish;
print("Click: tutorial is active $checkText");
if (!tutorialBloc.checkAction(checkText)) {
return;
}

View File

@ -66,7 +66,8 @@ class SalesButton extends StatelessWidget {
fontSize: 1,
)),
child: Container(
child: Center(
child: Text(
"ho") /* Center(
child: Column(
children: [
Divider(
@ -92,7 +93,8 @@ class SalesButton extends StatelessWidget {
child: Text(desc4!, maxLines: 2, style: GoogleFonts.inter(fontSize: 10, color: Colors.red[800])))
],
),
)),
) */
),
)));
}
}

View File

@ -40,10 +40,13 @@ class TutorialWidget with Trans, Logging {
area = bloc.action!.showBubble == true
? Rect.fromLTWH(targetGlobalCenter.dx - bloc.action!.bubbleX, targetGlobalCenter.dy - bloc.action!.bubbleY,
bloc.action!.bubbleWidth.toDouble(), bloc.action!.bubbleHeight.toDouble())
//? Rect.fromLTWH(targetGlobalCenter.dx - 60, targetGlobalCenter.dy + 120, 420, 320)
: null;
}
final double mediaSize = MediaQuery.of(context).size.width;
final double mediaHeight = MediaQuery.of(context).size.width;
final double distortion = mediaHeight / bloc.mediaHeightBase;
double fontSize = 14;
if (mediaSize > 400) {
@ -51,8 +54,16 @@ class TutorialWidget with Trans, Logging {
} else if (mediaSize < 375) {
fontSize = 13;
}
double calculatedTop = bloc.top! / distortion;
//-((1 - distortion) * 100 * (fontSize));
if (calculatedTop < 0) {
calculatedTop = 10;
}
print("Height: $mediaHeight, distortion: $distortion top: ${bloc.top!} calculated $calculatedTop");
tooltip = SuperTooltip(
top: bloc.top,
top: calculatedTop,
left: bloc.left,
backgroundColor: Colors.black.withOpacity(0.7),
popupDirection: bloc.action == null || bloc.action!.direction == "up" ? TooltipDirection.up : TooltipDirection.down,

View File

@ -15,6 +15,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
animated_widgets:
dependency: "direct main"
description:
name: animated_widgets
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.6"
apple_sign_in:
dependency: "direct main"
description:
@ -315,49 +322,49 @@ packages:
name: firebase_analytics
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.0-dev.2"
version: "8.0.2"
firebase_analytics_platform_interface:
dependency: transitive
description:
name: firebase_analytics_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0-dev.0"
version: "2.0.0"
firebase_analytics_web:
dependency: transitive
description:
name: firebase_analytics_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0-dev.0"
version: "0.3.0"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.1.2"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
version: "4.2.0"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
version: "1.1.0"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.1.0"
firebase_core_platform_interface:
dependency: transitive
description:
@ -378,21 +385,35 @@ packages:
name: firebase_messaging
url: "https://pub.dartlang.org"
source: hosted
version: "9.1.1"
version: "9.1.3"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.3"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "1.0.6"
firebase_remote_config:
dependency: "direct main"
description:
name: firebase_remote_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0-dev.2"
firebase_remote_config_platform_interface:
dependency: transitive
description:
name: firebase_remote_config_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0-dev.2"
fixnum:
dependency: transitive
description:
@ -419,6 +440,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_app_badger:
dependency: "direct main"
description:
name: flutter_app_badger
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
flutter_bloc:
dependency: "direct main"
description:
@ -447,6 +475,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.1+1"
flutter_fadein:
dependency: "direct main"
description:
name: flutter_fadein
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
flutter_html:
dependency: "direct main"
description:
@ -506,6 +541,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_uxcam:
dependency: "direct main"
description:
name: flutter_uxcam
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.2"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -707,6 +749,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
package_info_plus_linux:
dependency: transitive
description:
name: package_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
package_info_plus_macos:
dependency: transitive
description:
name: package_info_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
package_info_plus_web:
dependency: transitive
description:
name: package_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
package_info_plus_windows:
dependency: transitive
description:
name: package_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
path:
dependency: transitive
description:
@ -876,12 +960,19 @@ packages:
source: hosted
version: "0.26.0"
sentry:
dependency: "direct main"
dependency: transitive
description:
name: sentry
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
sentry_flutter:
dependency: "direct main"
description:
name: sentry_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
shared_preferences:
dependency: "direct dev"
description:
@ -957,13 +1048,6 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
smartlook:
dependency: "direct main"
description:
name: smartlook
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
source_gen:
dependency: transitive
description:

View File

@ -57,12 +57,14 @@ dependencies:
convex_bottom_bar: ^3.0.0
flutter_app_badger: ^1.2.0
#super_tooltip: ^1.0.1
firebase_core: ^1.0.3
firebase_analytics: ^8.0.0-dev.2
firebase_messaging: ^9.1.1
firebase_core: ^1.1.0
firebase_analytics: ^8.0.2
firebase_messaging: ^9.1.3
flutter_local_notifications: ^5.0.0
firebase_auth: ^1.0.3
firebase_auth: ^1.1.2
firebase_remote_config: ^0.10.0-dev.2
flutter_facebook_auth: ^3.3.2
google_sign_in: ^5.0.1
apple_sign_in: ^0.1.0