diff --git a/android/app/build.gradle b/android/app/build.gradle index 711670d..4e88905 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -78,5 +78,6 @@ dependencies { implementation 'com.google.firebase:firebase-analytics:18.0.0' implementation 'com.facebook.android:facebook-login:5.15.3' implementation 'com.android.support:multidex:1.0.3' + implementation 'com.google.firebase:firebase-messaging:20.1.0' } sourceCompatibility = '1.8' \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8ef85e5..71e1f63 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,41 +1,23 @@ - + - - + + - + - + @@ -44,27 +26,26 @@ + + + + + + + + - - - + - + - - + + + + diff --git a/asset/image/WT_zold.jpg b/asset/image/WT_zold.jpg new file mode 100644 index 0000000..f359eb5 Binary files /dev/null and b/asset/image/WT_zold.jpg differ diff --git a/asset/menu/smith_machine_squats.jpg b/asset/menu/smith_machine_squats.jpg new file mode 100644 index 0000000..63c8c29 Binary files /dev/null and b/asset/menu/smith_machine_squats.jpg differ diff --git a/asset/menu/training_start.jpg b/asset/menu/training_start.jpg new file mode 100644 index 0000000..db77a87 Binary files /dev/null and b/asset/menu/training_start.jpg differ diff --git a/i18n/en.json b/i18n/en.json index 469684c..b1a10e3 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -15,7 +15,7 @@ "Exception: Please accept our data policy": "Please accept our data policy", "Please accept our data protection policy.": "Please accept our data protection policy.", "For more information please click on 'Privacy'": "For more information please click on 'Privacy'", - "SignUp": "SignUp with Email", + "SignUp": "We will generate your training plan for you. Please register in order to save and recall your data.", "SignUpLink": "SignUp", "Privacy": "Privacy", "Change App Language": "Change App Language", @@ -436,7 +436,7 @@ "Do you want to restart, or select a new Training Plan?": "Do you want to restart, or select a new Training Plan?", "New Training Plan": "New Training Plan", "Restart": "Restart", - "Training Day": "Day", + "Training Day": "Training Day", "No Active Training Plan": "No Active Training Plan", "Please select one in the Training menu, or create your custom plan": "Please select one in the Training menu, or create your custom plan", "Continue your training": "Continue your training", @@ -508,5 +508,6 @@ "Reach all basic functions, suggestions and": "Reach all basic functions, suggestions and", "optimized training plans, customized to your fitness state and strength:": "optimized training plans, customized to your fitness state and strength:", "Soon! Check back later for the plan details": "Soon! Check back later for the plan details", - "mins": "mins" + "mins": "mins", + "set": "set" } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index 6ed6cad..0043736 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -11,7 +11,7 @@ "Change Language": "Nyelv", "Password too short": "A jelszó min. 9 karakterből álljon", "Please type an email address": "Kérlek írj be egy email címet", - "SignUp": "Regisztráció", + "SignUp": "A megadott adatok alapján generáljuk az edzéstervedet. Kérlek regisztrálj, hogy az a tervet el tudd menteni és később betölteni.", "SignUpLink": "Regisztráció", "SignUp with Email": "Email Regisztráció", "Exception: Please accept our data policy": "Kérlek fogadd el az adatvédelmi szabályzatunkat", @@ -434,7 +434,7 @@ "Do you want to restart, or select a new Training Plan?": "Újra akarod indítani, vagy inkább egy másikat választasz?", "New Training Plan": "Másik edzésterv", "Restart": "Újraindítom", - "Training Day": "Nap", + "Training Day": "Edzésnap", "No Active Training Plan": "Nincs aktív edzésterv", "Please select one in the Training menu, or create your custom plan": "Kérlek válassz egyet a Tréning menüben, vagy hozd létre a saját egyéni edzésedet", "Continue your training": "Folytasd az edzést", @@ -506,5 +506,6 @@ "Reach all basic functions, suggestions and": "Kattints a regisztrációra, hogy elérd az alap funkciókat, javaslatokat,", "optimized training plans, customized to your fitness state and strength:": "optiomalizált edzés terveket, amelyeket a te erő- és fitnesz állapododra szabunk:", "Soon! Check back later for the plan details": "Nemsokára! Nézz vissza később, amikor már aktiváltuk az edzésterv részleteit", - "mins": "perc" + "mins": "perc", + "set": "sorozat" } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f007b1a..a2b8604 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,61 +4,80 @@ PODS: - AppAuth/ExternalUserAgent (= 1.4.0) - AppAuth/Core (1.4.0) - AppAuth/ExternalUserAgent (1.4.0) - - awesome_notifications (0.0.2): - - Flutter - device_info (0.0.1): - Flutter - devicelocale (0.0.1): - Flutter - - FBSDKCoreKit (9.1.0): - - FBSDKCoreKit/Basics (= 9.1.0) - - FBSDKCoreKit/Core (= 9.1.0) - - FBSDKCoreKit/Basics (9.1.0) - - FBSDKCoreKit/Core (9.1.0): - - FBSDKCoreKit/Basics - - FBSDKLoginKit (9.1.0): - - FBSDKLoginKit/Login (= 9.1.0) - - FBSDKLoginKit/Login (9.1.0): - - FBSDKCoreKit (~> 9.1.0) - - Firebase/Analytics (8.0.0): + - FBAEMKit (11.1.0): + - FBAEMKit/AEM (= 11.1.0) + - FBAEMKit/AEM (11.1.0): + - FBSDKCoreKit_Basics (~> 11.1.0) + - FBSDKCoreKit (11.1.0): + - FBSDKCoreKit/Core (= 11.1.0) + - FBSDKCoreKit/Core (11.1.0): + - FBAEMKit (~> 11.1.0) + - FBSDKCoreKit_Basics (~> 11.1.0) + - FBSDKCoreKit_Basics (11.1.0): + - FBSDKCoreKit_Basics/Basics (= 11.1.0) + - FBSDKCoreKit_Basics/Basics (11.1.0) + - FBSDKLoginKit (11.1.0): + - FBSDKLoginKit/Login (= 11.1.0) + - FBSDKLoginKit/Login (11.1.0): + - FBSDKCoreKit (~> 11.1.0) + - FBSDKCoreKit_Basics (~> 11.1.0) + - Firebase/Analytics (8.5.0): - Firebase/Core - - Firebase/Auth (8.0.0): + - Firebase/Auth (8.5.0): - Firebase/CoreOnly - - FirebaseAuth (~> 8.0.0) - - Firebase/Core (8.0.0): + - FirebaseAuth (~> 8.5.0) + - Firebase/Core (8.5.0): - Firebase/CoreOnly - - FirebaseAnalytics (~> 8.0.0) - - Firebase/CoreOnly (8.0.0): - - FirebaseCore (= 8.0.0) - - Firebase/Messaging (8.0.0): + - FirebaseAnalytics (~> 8.5.0) + - Firebase/CoreOnly (8.5.0): + - FirebaseCore (= 8.5.0) + - Firebase/DynamicLinks (8.5.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 8.0.0) - - Firebase/RemoteConfig (8.0.0): + - FirebaseDynamicLinks (~> 8.5.0) + - Firebase/InAppMessaging (8.5.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 8.0.0) - - firebase_analytics (8.1.0): - - Firebase/Analytics (= 8.0.0) + - FirebaseInAppMessaging (~> 8.5.0-beta) + - Firebase/Messaging (8.5.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 8.5.0) + - Firebase/RemoteConfig (8.5.0): + - Firebase/CoreOnly + - FirebaseRemoteConfig (~> 8.5.0) + - firebase_analytics (8.3.0): + - Firebase/Analytics (= 8.5.0) - firebase_core - Flutter - - firebase_auth (1.2.0): - - Firebase/Auth (= 8.0.0) + - firebase_auth (3.0.2): + - Firebase/Auth (= 8.5.0) - firebase_core - Flutter - - firebase_core (1.2.0): - - Firebase/CoreOnly (= 8.0.0) + - firebase_core (1.5.0): + - Firebase/CoreOnly (= 8.5.0) - Flutter - - firebase_messaging (10.0.0): - - Firebase/Messaging (= 8.0.0) + - firebase_dynamic_links (2.0.8): + - Firebase/DynamicLinks (= 8.5.0) - firebase_core - Flutter - - firebase_remote_config (0.10.0): - - Firebase/RemoteConfig (= 8.0.0) + - firebase_in_app_messaging (0.5.0-8): + - Firebase/InAppMessaging (= 8.5.0) - firebase_core - Flutter - - FirebaseABTesting (8.0.0): + - firebase_messaging (10.0.5): + - Firebase/Messaging (= 8.5.0) + - firebase_core + - Flutter + - firebase_remote_config (0.10.0-4): + - Firebase/RemoteConfig (= 8.5.0) + - firebase_core + - Flutter + - FirebaseABTesting (8.6.0): - FirebaseCore (~> 8.0) - - FirebaseAnalytics (8.0.0): - - FirebaseAnalytics/AdIdSupport (= 8.0.0) + - FirebaseAnalytics (8.5.0): + - FirebaseAnalytics/AdIdSupport (= 8.5.0) - FirebaseCore (~> 8.0) - FirebaseInstallations (~> 8.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.4) @@ -66,51 +85,50 @@ PODS: - GoogleUtilities/Network (~> 7.4) - "GoogleUtilities/NSData+zlib (~> 7.4)" - nanopb (~> 2.30908.0) - - FirebaseAnalytics/AdIdSupport (8.0.0): - - FirebaseAnalytics/Base (= 8.0.0) + - FirebaseAnalytics/AdIdSupport (8.5.0): - FirebaseCore (~> 8.0) - FirebaseInstallations (~> 8.0) - - GoogleAppMeasurement (= 8.0.0) + - GoogleAppMeasurement (= 8.5.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - GoogleUtilities/MethodSwizzler (~> 7.4) - GoogleUtilities/Network (~> 7.4) - "GoogleUtilities/NSData+zlib (~> 7.4)" - nanopb (~> 2.30908.0) - - FirebaseAnalytics/Base (8.0.0): - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - - GoogleUtilities/MethodSwizzler (~> 7.4) - - GoogleUtilities/Network (~> 7.4) - - "GoogleUtilities/NSData+zlib (~> 7.4)" - - nanopb (~> 2.30908.0) - - FirebaseAuth (8.0.0): + - FirebaseAuth (8.5.0): - FirebaseCore (~> 8.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - GoogleUtilities/Environment (~> 7.4) - GTMSessionFetcher/Core (~> 1.5) - - FirebaseCore (8.0.0): + - FirebaseCore (8.5.0): - FirebaseCoreDiagnostics (~> 8.0) - GoogleUtilities/Environment (~> 7.4) - GoogleUtilities/Logger (~> 7.4) - - FirebaseCoreDiagnostics (8.0.0): + - FirebaseCoreDiagnostics (8.6.0): - GoogleDataTransport (~> 9.0) - GoogleUtilities/Environment (~> 7.4) - GoogleUtilities/Logger (~> 7.4) - nanopb (~> 2.30908.0) - - FirebaseInstallations (8.0.0): + - FirebaseDynamicLinks (8.5.0): + - FirebaseCore (~> 8.0) + - FirebaseInAppMessaging (8.5.0-beta): + - FirebaseABTesting (~> 8.0) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/Environment (~> 7.4) + - nanopb (~> 2.30908.0) + - FirebaseInstallations (8.6.0): - FirebaseCore (~> 8.0) - GoogleUtilities/Environment (~> 7.4) - GoogleUtilities/UserDefaults (~> 7.4) - - PromisesObjC (~> 1.2) - - FirebaseMessaging (8.0.0): + - PromisesObjC (< 3.0, >= 1.2) + - FirebaseMessaging (8.5.0): - FirebaseCore (~> 8.0) - FirebaseInstallations (~> 8.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - GoogleUtilities/Environment (~> 7.4) - GoogleUtilities/Reachability (~> 7.4) - GoogleUtilities/UserDefaults (~> 7.4) - - FirebaseRemoteConfig (8.0.0): + - FirebaseRemoteConfig (8.5.0): - FirebaseABTesting (~> 8.0) - FirebaseCore (~> 8.0) - FirebaseInstallations (~> 8.0) @@ -124,12 +142,12 @@ PODS: - flutter_app_badger (0.0.1): - Flutter - flutter_facebook_auth (2.0.0): - - FBSDKCoreKit (~> 9.1.0) - - FBSDKLoginKit (~> 9.1.0) + - FBSDKCoreKit (~> 11.1.0) + - FBSDKLoginKit (~> 11.1.0) - Flutter - flutter_secure_storage (3.3.1): - Flutter - - flutter_uxcam (2.0.0-beta.1): + - flutter_uxcam (2.0.0): - Flutter - UXCam (~> 3.3.4) - FMDB (2.7.5): @@ -138,45 +156,45 @@ PODS: - google_sign_in (0.0.1): - Flutter - GoogleSignIn (~> 5.0) - - GoogleAppMeasurement (8.0.0): - - GoogleAppMeasurement/AdIdSupport (= 8.0.0) + - GoogleAppMeasurement (8.5.0): + - GoogleAppMeasurement/AdIdSupport (= 8.5.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - GoogleUtilities/MethodSwizzler (~> 7.4) - GoogleUtilities/Network (~> 7.4) - "GoogleUtilities/NSData+zlib (~> 7.4)" - nanopb (~> 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (8.0.0): + - GoogleAppMeasurement/AdIdSupport (8.5.0): - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - GoogleUtilities/MethodSwizzler (~> 7.4) - GoogleUtilities/Network (~> 7.4) - "GoogleUtilities/NSData+zlib (~> 7.4)" - nanopb (~> 2.30908.0) - - GoogleDataTransport (9.0.0): + - GoogleDataTransport (9.1.0): - GoogleUtilities/Environment (~> 7.2) - nanopb (~> 2.30908.0) - - PromisesObjC (~> 1.2) + - PromisesObjC (< 3.0, >= 1.2) - GoogleSignIn (5.0.2): - AppAuth (~> 1.2) - GTMAppAuth (~> 1.0) - GTMSessionFetcher/Core (~> 1.1) - - GoogleUtilities/AppDelegateSwizzler (7.4.1): + - GoogleUtilities/AppDelegateSwizzler (7.5.1): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.4.1): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (7.4.1): + - GoogleUtilities/Environment (7.5.1): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.5.1): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.4.1): + - GoogleUtilities/MethodSwizzler (7.5.1): - GoogleUtilities/Logger - - GoogleUtilities/Network (7.4.1): + - GoogleUtilities/Network (7.5.1): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.4.1)" - - GoogleUtilities/Reachability (7.4.1): + - "GoogleUtilities/NSData+zlib (7.5.1)" + - GoogleUtilities/Reachability (7.5.1): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.4.1): + - GoogleUtilities/UserDefaults (7.5.1): - GoogleUtilities/Logger - GTMAppAuth (1.2.2): - AppAuth/Core (~> 1.4) @@ -195,7 +213,7 @@ PODS: - Flutter - path_provider (0.0.1): - Flutter - - PromisesObjC (1.2.12) + - PromisesObjC (2.0.0) - Purchases (3.11.1): - PurchasesCoreSwift (= 3.11.1) - purchases_flutter (3.2.2): @@ -231,12 +249,13 @@ PODS: - Flutter DEPENDENCIES: - - awesome_notifications (from `.symlinks/plugins/awesome_notifications/ios`) - device_info (from `.symlinks/plugins/device_info/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_dynamic_links (from `.symlinks/plugins/firebase_dynamic_links/ios`) + - firebase_in_app_messaging (from `.symlinks/plugins/firebase_in_app_messaging/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`) - flurry_data (from `.symlinks/plugins/flurry_data/ios`) @@ -264,7 +283,9 @@ DEPENDENCIES: SPEC REPOS: trunk: - AppAuth + - FBAEMKit - FBSDKCoreKit + - FBSDKCoreKit_Basics - FBSDKLoginKit - Firebase - FirebaseABTesting @@ -272,6 +293,8 @@ SPEC REPOS: - FirebaseAuth - FirebaseCore - FirebaseCoreDiagnostics + - FirebaseDynamicLinks + - FirebaseInAppMessaging - FirebaseInstallations - FirebaseMessaging - FirebaseRemoteConfig @@ -292,8 +315,6 @@ SPEC REPOS: - UXCam EXTERNAL SOURCES: - awesome_notifications: - :path: ".symlinks/plugins/awesome_notifications/ios" device_info: :path: ".symlinks/plugins/device_info/ios" devicelocale: @@ -304,6 +325,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" + firebase_dynamic_links: + :path: ".symlinks/plugins/firebase_dynamic_links/ios" + firebase_in_app_messaging: + :path: ".symlinks/plugins/firebase_in_app_messaging/ios" firebase_messaging: :path: ".symlinks/plugins/firebase_messaging/ios" firebase_remote_config: @@ -353,38 +378,43 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7 - awesome_notifications: 74462bc8e68b11f8235d78422266886759e9da61 device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 devicelocale: b22617f40038496deffba44747101255cee005b0 - FBSDKCoreKit: a00fe2efd780c195a5e09201bf51c56106245b40 - FBSDKLoginKit: d98498c598ec09de657385a9349a1f21119b7f86 - Firebase: 73c3e3b216ec1ecbc54d2ffdd4670c65c749edb1 - firebase_analytics: 221d3bc4e8f1b5144a4bd4cc6b33790ee51bd543 - firebase_auth: f960df4ddd8cb415859dbc01a02d7859925ddef0 - firebase_core: e4d3efb030a2b2021819f8faa538bb23deb46695 - firebase_messaging: 3b6e0657b21261a57a1cd041fafa713f2aa6923f - firebase_remote_config: 3a6e2db440f0e95baba3dfc3d4118b1a4bc792c4 - FirebaseABTesting: daebc95ec8829607d07dfe5e92dc3285aca29bc4 - FirebaseAnalytics: dcb92c7c9ef4fa7ffac276e8f87bd4fc8c97f1b8 - FirebaseAuth: b8cd992fca5b53dc6eec09e873a3f375f000c5a1 - FirebaseCore: 3f09591d51292843e2a46f18358d60bf4e996255 - FirebaseCoreDiagnostics: a31d987ba0fe16d59886a5dbadc2f1de871f88c8 - FirebaseInstallations: c4aab1005d6547b00a7529777fe52f5d4d45165b - FirebaseMessaging: 1a33b4af3c8042ed6ddacb6c031894af2064bfab - FirebaseRemoteConfig: 055f6b5ba1751547596ded5032c4d5c6054ca501 + FBAEMKit: 5c8a8d08e5b2c79628490784883e0fcc75b12615 + FBSDKCoreKit: 7ccb8b4bb2b5ee2ad94327b774dc23f03509675d + FBSDKCoreKit_Basics: 8f978bce195845f609b0ec6b425949d0d24f525b + FBSDKLoginKit: d65eb587a9eaa89295338fb0bb3b358bde0b7ae4 + Firebase: ff8c73105b90e33e1dc6c8e5445d7adc2ccdc7c1 + firebase_analytics: 3b7d92b8d1a3482f557c201e5e46c2f7fa2644ff + firebase_auth: 214ff86facd807bbb0ccff32f4b2d3865e3bc4f3 + firebase_core: 82d486a6231b636aea229bd471bceca82cbe00a6 + firebase_dynamic_links: 0768a32e69be5b6f9af258f8e072537dff6b8969 + firebase_in_app_messaging: 04572963cf1ef212ac23e188cb0324316e948bf9 + firebase_messaging: 0c5342aa6d92d09429ef67c81a1345189fcb76c9 + firebase_remote_config: cd43874ff082605023b5913bb1d3206452f1ad48 + FirebaseABTesting: c3e48ebf5e7e5c674c5a131c68e941d7921d83dc + FirebaseAnalytics: 96325c1e0acbd2bb805c6a613028b1fe599d6a37 + FirebaseAuth: b152ea261b60eeb9419ae7e5bf34761382b33277 + FirebaseCore: 1c1ca72483b59b17050f5b4cec4fb748425a3901 + FirebaseCoreDiagnostics: 3721920bde3a9a6d5aa093c1d25e9d3e47f694af + FirebaseDynamicLinks: 6e406b3bb669f8c8a63e7254bb63251fa3f88a43 + FirebaseInAppMessaging: ee6cd4397d1e81d34b14f90ec38697dc4ef9fe93 + FirebaseInstallations: 0ede6ffcd215b8f93c19d9b06c1c54e2d4107e98 + FirebaseMessaging: 0705ec705c21705efc51c071fba924c8e25c63e7 + FirebaseRemoteConfig: 693c1f150408e9a727daf4d8c55c7f9c29ef9ad5 Flurry-iOS-SDK: 5831da8fc6bedb31fa1f94aac6fd204d36dd351d flurry_data: 49b7066a283aa41f4306974c1f2d74c61231ad74 Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c flutter_app_badger: 65de4d6f0c34a891df49e6cfb8a1c0496426fa68 - flutter_facebook_auth: 4b170c07b7fce791497093fcc3f134fb215f3f07 + flutter_facebook_auth: 528d51ea1324741b366fa87fa5dfd41016422364 flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec - flutter_uxcam: ab8e5d3954eb448febd581375e2622e9eecb1066 + flutter_uxcam: 5b2418884a3bf41284a888c7ecc50317c8a84727 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a google_sign_in: 6bd214b9c154f881422f5fe27b66aaa7bbd580cc - GoogleAppMeasurement: c6bbc9753d046b5456dd4f940057fbad2c28419e - GoogleDataTransport: 11e3a5f2c190327df1a4a5d7e7ae3d4d5b9c9e4c + GoogleAppMeasurement: 8d10c1c470fcb0e5143ed74fddd164f0a0384800 + GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9 GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213 - GoogleUtilities: f8a43108b38a68eebe8b3540e1f4f2d28843ce20 + GoogleUtilities: 3df19e3c24f7bbc291d8b5809aa6b0d41e642437 GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89 GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 modal_progress_hud_nsn: f6fb744cd060653d66ed8f325360ef3650eb2fde @@ -392,7 +422,7 @@ SPEC CHECKSUMS: package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 Purchases: 6351f9ff6bd514e5ec5aa0f989ea181effa94bf5 purchases_flutter: 627527b070d80cdaf486fabe8b3d1dbe8d5cad92 PurchasesCoreSwift: ee857e4c21e6254b09d7e303a756fcf2b9164408 @@ -411,4 +441,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: f10c0438b63bc24e6bbc207956dc27d16c4408f2 -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.0.beta.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 69649d2..2c0b680 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -396,7 +396,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -405,12 +405,12 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.1.21; + MARKETING_VERSION = 1.1.22; PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -539,7 +539,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -548,13 +548,13 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.1.21; + MARKETING_VERSION = 1.1.22; PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -574,7 +574,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -583,12 +583,12 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.1.21; + MARKETING_VERSION = 1.1.22; PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index b0bb7f3..43c5911 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -10,6 +10,9 @@ import Firebase ) -> Bool { FirebaseApp.configure() GeneratedPluginRegistrant.register(with: self) + if #available(iOS 12.0, *) { + UNUserNotificationCenter.current().delegate = self + } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 3738f71..811fead 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,75 +1,97 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - WorkoutTest - CFBundlePackageType - APPL - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - fb584181112271127 - com.googleusercontent.apps.926782702216-2nsi7d9at3pc5ts8gkobt5697v590kb9 - - - - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - FacebookAppID - 584181112271127 - FacebookDisplayName - Mobile Login - FirebaseAppDelegateProxyEnabled - NO - LSApplicationQueriesSchemes - - https - http - - LSMinimumSystemVersion - 11.0 - LSRequiresIPhoneOS - - NSAppTransportSecurity - NSAllowsArbitraryLoads + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + WorkoutTest + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + aitrainer.page.link + CFBundleURLSchemes + + wt001 + fb584181112271127 + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + FacebookAdvertiserIDCollectionEnabled + TRUE + FacebookAppID + 584181112271127 + FacebookAutoLogAppEventsEnabled + TRUE + FacebookDisplayName + Workout Test + FirebaseAppDelegateProxyEnabled + NO + LSApplicationQueriesSchemes + + https + http + fbapi + fbapi20130214 + fbapi20130410 + fbapi20130702 + fbapi20131010 + fbapi20131219 + fbapi20140410 + fbapi20140116 + fbapi20150313 + fbapi20150629 + fbapi20160328 + fb-messenger-share-api + fbauth2 + fbshareextension + + LSMinimumSystemVersion + 11.0.0 + LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + Launch Screen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + - UIBackgroundModes - - remote-notification - - UILaunchStoryboardName - Launch Screen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 98bdd20..4bd14f7 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -8,6 +8,10 @@ Default + com.apple.developer.associated-domains + + applinks:aitrainer.page.link + com.apple.developer.authentication-services.autofill-credential-provider diff --git a/lib/bloc/customer_change/customer_change_bloc.dart b/lib/bloc/customer_change/customer_change_bloc.dart index 4ec65a7..c599e9b 100644 --- a/lib/bloc/customer_change/customer_change_bloc.dart +++ b/lib/bloc/customer_change/customer_change_bloc.dart @@ -29,8 +29,8 @@ class CustomerChangeBloc extends Bloc weight = this.customerRepository.getWeight() == 0 ? 60 : this.customerRepository.getWeight(); height = this.customerRepository.getHeight() == 0 ? 170 : this.customerRepository.getHeight(); - selectedSport = customerRepository.getSport(); - print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString()); + // selectedSport = customerRepository.getSport(); + //print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString()); } Sport? selectedSport; @@ -41,6 +41,7 @@ class CustomerChangeBloc extends Bloc CustomerChangeEvent event, ) async* { try { + print("Event: $event"); if (event is CustomerLoad) { yield CustomerChangeLoading(); yield CustomerDataChanged(); @@ -63,7 +64,7 @@ class CustomerChangeBloc extends Bloc year = event.year; yield CustomerDataChanged(); } else if (event is CustomerWeightChange) { - yield CustomerChangeLoading(); + //yield CustomerChangeLoading(); customerRepository.setWeight(event.weight); weight = event.weight.toDouble(); yield CustomerDataChanged(); @@ -88,12 +89,43 @@ class CustomerChangeBloc extends Bloc customerRepository.setName(event.name); yield CustomerDataChanged(); } else if (event is CustomerGenderChange) { + yield CustomerChangeLoading(); customerRepository.setSex(event.gender == 0 ? "m" : "w"); yield CustomerDataChanged(); } else if (event is CustomerSportChange) { yield CustomerChangeLoading(); selectedSport = event.sport; yield CustomerDataChanged(); + } else if (event is CustomerSaveFitness) { + yield CustomerChangeLoading(); + if (customerRepository.customer!.fitnessLevel == null) { + throw Exception("Please select your fitness level"); + } + yield CustomerSaveSuccess(); + } else if (event is CustomerSaveGoal) { + yield CustomerChangeLoading(); + if (customerRepository.customer!.goal == null) { + throw Exception("Please select your goal"); + } + yield CustomerSaveSuccess(); + } else if (event is CustomerSaveSex) { + yield CustomerChangeLoading(); + if (customerRepository.customer!.sex == null) { + throw Exception("Please select your biologial gender"); + } + yield CustomerSaveSuccess(); + } else if (event is CustomerSaveWeight) { + yield CustomerChangeLoading(); + if (customerRepository.customer!.getProperty("Weight") == null) { + throw Exception("Please select your weight"); + } + yield CustomerSaveSuccess(); + } else if (event is CustomerSaveHeight) { + yield CustomerChangeLoading(); + if (customerRepository.customer!.getProperty("Height") == null) { + throw Exception("Please select your height"); + } + yield CustomerSaveSuccess(); } else if (event is CustomerSave) { yield CustomerSaving(); if (validation()) { diff --git a/lib/bloc/customer_change/customer_change_event.dart b/lib/bloc/customer_change/customer_change_event.dart index 26d47c9..0445406 100644 --- a/lib/bloc/customer_change/customer_change_event.dart +++ b/lib/bloc/customer_change/customer_change_event.dart @@ -114,3 +114,23 @@ class CustomerLoad extends CustomerChangeEvent { class CustomerSave extends CustomerChangeEvent { const CustomerSave(); } + +class CustomerSaveGoal extends CustomerChangeEvent { + const CustomerSaveGoal(); +} + +class CustomerSaveFitness extends CustomerChangeEvent { + const CustomerSaveFitness(); +} + +class CustomerSaveSex extends CustomerChangeEvent { + const CustomerSaveSex(); +} + +class CustomerSaveWeight extends CustomerChangeEvent { + const CustomerSaveWeight(); +} + +class CustomerSaveHeight extends CustomerChangeEvent { + const CustomerSaveHeight(); +} diff --git a/lib/bloc/menu/menu_bloc.dart b/lib/bloc/menu/menu_bloc.dart index 1381724..9c8ed10 100644 --- a/lib/bloc/menu/menu_bloc.dart +++ b/lib/bloc/menu/menu_bloc.dart @@ -1,13 +1,17 @@ import 'dart:async'; import 'dart:collection'; +import 'package:intl/intl.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_device_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/workout_tree_repository.dart'; import 'package:aitrainer_app/service/logging.dart'; +import 'package:aitrainer_app/util/enums.dart'; +import 'package:aitrainer_app/util/track.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -111,6 +115,22 @@ class MenuBloc extends Bloc with Trans, Logging { listFilterDevice.remove(deviceId); } yield MenuReady(); + } else if (event is MenuStartTrial) { + yield MenuLoading(); + final DateTime start = event.start; + CustomerRepository customerRepository = CustomerRepository(); + customerRepository.customer = Cache().userLoggedIn; + customerRepository.customer!.trialDate = start; + Cache().userLoggedIn!.trialDate = start; + + customerRepository.saveCustomer(); + + if (DateTime.now().difference(start).inHours < 1) { + Cache().hasPurchased = true; + log("Trial mode on!"); + Track().track(TrackingEvent.trial, eventValue: DateFormat('yyyy-MM-dd HH:mm:ss').format(start)); + } + yield MenuReady(); } } on Exception catch (ex) { yield MenuError(message: ex.toString()); @@ -134,6 +154,9 @@ class MenuBloc extends Bloc with Trans, Logging { case "my_body": ability = ExerciseAbility.none; break; + case "training_execute": + ability = ExerciseAbility.training_execute; + break; } log("Ability: " + ability.toString() + " name: " + name); } diff --git a/lib/bloc/menu/menu_event.dart b/lib/bloc/menu/menu_event.dart index 56b33a3..9516ba0 100644 --- a/lib/bloc/menu/menu_event.dart +++ b/lib/bloc/menu/menu_event.dart @@ -56,4 +56,15 @@ class MenuRecreateTree extends MenuEvent { class MenuFilterExerciseType extends MenuEvent { final int deviceId; const MenuFilterExerciseType({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class MenuStartTrial extends MenuEvent { + final DateTime start; + const MenuStartTrial({required this.start}); + + @override + List get props => [start]; } diff --git a/lib/bloc/session/session_bloc.dart b/lib/bloc/session/session_bloc.dart index b2b940c..47a4ce1 100644 --- a/lib/bloc/session/session_bloc.dart +++ b/lib/bloc/session/session_bloc.dart @@ -13,6 +13,7 @@ part 'session_state.dart'; class SessionBloc extends Bloc with Logging { final Session session; + StreamSubscription? _sub; SessionBloc({required this.session}) : super(SessionInitial()); @@ -41,6 +42,7 @@ class SessionBloc extends Bloc with Logging { @override Future close() async { await this.close(); + _sub?.cancel(); super.close(); } } diff --git a/lib/bloc/training_evaluation/training_evaluation_bloc.dart b/lib/bloc/training_evaluation/training_evaluation_bloc.dart index 1dcfb51..e3a14e4 100644 --- a/lib/bloc/training_evaluation/training_evaluation_bloc.dart +++ b/lib/bloc/training_evaluation/training_evaluation_bloc.dart @@ -59,10 +59,12 @@ class TrainingEvaluationBloc extends Bloc { try { if (event is TrainingPlanActivate) { yield TrainingPlanLoading(); - _myPlan = await trainingPlanRepository.activateTrainingPlan(event.trainingPlanId); + _myPlan = trainingPlanRepository.activateTrainingPlan(event.trainingPlanId); _myPlan!.type = CustomerTrainingPlanType.template; menuBloc.menuTreeRepository.sortedTree.forEach((name, list) { @@ -72,7 +72,6 @@ class TrainingPlanBloc extends Bloc { event.detail.repeats = Common.reCalculateRepeatsByChangedWeight(event.detail.weight!, event.detail.repeats!.toDouble(), event.weight); event.detail.weight = event.weight; - print(" weight: ${event.weight} new repeats: ${event.detail.repeats}"); yield TrainingPlanReady(); } else if (event is TrainingPlanRepeatsChange) { @@ -102,13 +101,12 @@ class TrainingPlanBloc extends Bloc { exercise.unitQuantity = event.detail.weight; exercise.dateAdd = DateTime.now(); event.detail.exercises.add(exercise); - if (event.detail.exercises.length >= event.detail.set!) { + if (this.isAllDetailsSameExerciseFinished(event.detail)) { event.detail.state = ExercisePlanDetailState.finished; } else if (event.detail.exercises.length >= 0) { event.detail.state = ExercisePlanDetailState.inProgress; } // recalculate the weight to the original planned repeats for the next details - if (exercise.unitQuantity != null && exercise.unitQuantity! > 0) { for (var nextDetail in _myPlan!.details) { double weightFromPlan = trainingPlanRepository.getOriginalWeight(this.getMyPlan()!.trainingPlanId!, nextDetail); @@ -357,6 +355,23 @@ class TrainingPlanBloc extends Bloc { return workoutTree.imageName; } + int getStep(CustomerTrainingPlanDetails detail) { + List details = getAllDetailsSameExercise(detail); + int step = 0; + int indexElement = 0; + details.forEach((element) { + if (indexElement == 0) { + step = element.exercises.length; + } else { + step += element.exercises.length; + } + indexElement++; + }); + + //print("STEP: $step "); + return step; + } + CustomerTrainingPlanDetails? getNext() { if (_myPlan == null || _myPlan!.details.isEmpty) { return null; @@ -373,12 +388,16 @@ class TrainingPlanBloc extends Bloc { break; } else { final int step = detail.exercises.length; - if (step < minStep && !detail.state.equalsTo(ExercisePlanDetailState.skipped) && day == detail.day) { + if (step < minStep && + step < detail.set! && + !isAllDetailsSameExerciseFinished(detail) && + !detail.state.equalsTo(ExercisePlanDetailState.skipped) && + day == detail.day) { next = detail; minStep = step; - if (detail.parallel != true) { - break; - } + //if (detail.parallel != true) { + break; + //} } } } @@ -423,18 +442,23 @@ class TrainingPlanBloc extends Bloc { int indexInProgress = 0; int indexInStart = 0; final String day = dayNames[this.activeDayIndex]; + CustomerTrainingPlanDetails? prev; for (var detail in _myPlan!.days[day]!) { - if (detail.state == ExercisePlanDetailState.inProgress) { + //print("Offset detail $detail"); + if (detail.state == ExercisePlanDetailState.inProgress || detail.state == ExercisePlanDetailState.start) { + prev = detail; break; } - if (detail.state == ExercisePlanDetailState.start) { - break; + + if (prev != null && prev.exerciseTypeId != detail.exerciseTypeId && detail.state != ExercisePlanDetailState.extra) { + //print(" --- offset + 1"); + indexInStart++; + indexInProgress++; } - indexInStart++; - indexInProgress++; + prev = detail; } int index = indexInStart > indexInProgress ? indexInStart : indexInProgress; - offset = index * 80; + offset = (index) * 270; print("Offset: $offset day: $day ($indexInStart, $indexInProgress)"); return offset; } @@ -573,4 +597,23 @@ class TrainingPlanBloc extends Bloc { } return exists; } + + List getAllDetailsSameExercise(CustomerTrainingPlanDetails detail) { + List list = []; + getMyPlan()!.details.forEach((element) { + if (detail.exerciseTypeId == element.exerciseTypeId) { + list.add(element); + } + }); + return list; + } + + bool isAllDetailsSameExerciseFinished(CustomerTrainingPlanDetails detail) { + bool allFinished = true; + List list = getAllDetailsSameExercise(detail); + for (var listDetail in list) { + allFinished = allFinished && listDetail.exercises.length >= listDetail.set!; + } + return allFinished; + } } diff --git a/lib/main.dart b/lib/main.dart index dd86d8d..c823fb2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,10 @@ import 'package:aitrainer_app/view/customer_bodytype_animation.dart'; import 'package:aitrainer_app/view/customer_exercise_device.dart'; import 'package:aitrainer_app/view/customer_fitness_page.dart'; import 'package:aitrainer_app/view/customer_goal_page.dart'; +import 'package:aitrainer_app/view/customer_height_page.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart'; +import 'package:aitrainer_app/view/customer_sex_page.dart'; +import 'package:aitrainer_app/view/customer_weight_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'; @@ -23,6 +26,7 @@ import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/exercise_new_page.dart'; import 'package:aitrainer_app/view/training_plan_custom.dart'; import 'package:aitrainer_app/view/training_plan_custom_add.dart'; +import 'package:aitrainer_app/view/training_plan_execute.dart'; import 'package:aitrainer_app/view/training_plans_page.dart'; import 'package:aitrainer_app/view/mydevelopment_body_page.dart'; import 'package:aitrainer_app/view/mydevelopment_muscle_page.dart'; @@ -146,7 +150,6 @@ Future main() async { } print(" -- FireBase init.."); - await FirebaseApi().initializeFlutterFire(); runApp(MultiBlocProvider( providers: [ @@ -177,8 +180,8 @@ Future main() async { BlocProvider( create: (BuildContext context) => TestSetExecuteBloc(), ), - BlocProvider( - create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())), + /* BlocProvider( + create: (BuildContext context) => TutorialBloc(tutorialName: ActivityDone.tutorialExecuteFirstTest.toStr())), */ BlocProvider(create: (context) { final MenuBloc menuBloc = BlocProvider.of(context); return TrainingPlanBloc(menuBloc: menuBloc, trainingPlanRepository: TrainingPlanRepository()); @@ -196,6 +199,7 @@ Future initThirdParty() async { await FlurryData.initialize(androidKey: "JNYCTCWBT34FM3J8TV36", iosKey: "3QBG7BSMGPDH24S8TRQP", enableLog: true); FlutterUxcam.optIntoSchematicRecordings(); } + await FirebaseApi().initializeFlutterFire(); } class WorkoutTestApp extends StatelessWidget { @@ -217,6 +221,7 @@ class WorkoutTestApp extends StatelessWidget { //facebookAppEvents.setAdvertiserTracking(enabled: true); initThirdParty(); + return MaterialApp( localizationsDelegates: [ // ... app-specific localization delegate[s] here @@ -249,6 +254,9 @@ class WorkoutTestApp extends StatelessWidget { 'customerModifyPage': (context) => CustomerModifyPage(), 'customerGoalPage': (context) => CustomerGoalPage(), 'customerFitnessPage': (context) => CustomerFitnessPage(), + 'customerSexPage': (context) => CustomerSexPage(), + 'customerWeightPage': (context) => CustomerWeightPage(), + 'customerHeightPage': (context) => CustomerHeightPage(), 'customerBodyTypePage': (context) => CustomerBodyTypeAnimationPage(), 'customerWelcomePage': (context) => CustomerWelcomePage(), 'customerExerciseDevicePage': (context) => CustomerExerciseDevicePage(), @@ -275,7 +283,8 @@ class WorkoutTestApp extends StatelessWidget { 'myTrainingPlanCustom': (context) => TrainingPlanCustomPage(), 'myTrainingPlanCustomAdd': (context) => TrainingPlanCustomAddPage(), 'myTrainingPlanActivate': (context) => TrainingPlanActivatePage(), - 'myTrainingPlanExecute': (context) => TrainingPlanExecutePage(), + 'myTrainingPlanExecute2': (context) => TrainingPlanExecutePage(), + 'myTrainingPlanExecute': (context) => TrainingPlanExecute(), 'myTrainingPlanExercise': (context) => TrainingPlanExercise(), 'myTrainingEvaluation': (context) => TrainingEvaluationPage(), }, diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 75166be..9c68999 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -186,8 +186,8 @@ class Cache with Logging { Cache._internal() { String testEnv = EnvironmentConfig.test_env; this.testEnvironment = testEnv; + print("testEnv $testEnv"); if (testEnv == "1") { - print("testEnv $testEnv"); baseUrl = baseUrlTest; liveServer = false; } @@ -234,6 +234,17 @@ class Cache with Logging { sharedPreferences.setString(Cache.myTrainingPlanKey, myTrainingPlanJson); } + Future deleteMyTrainingPlan() async { + if (myTrainingPlan == null) { + return; + } + + Future prefs = SharedPreferences.getInstance(); + SharedPreferences sharedPreferences; + sharedPreferences = await prefs; + sharedPreferences.remove(Cache.myTrainingPlanKey); + } + Future getMyTrainingPlan() async { Future prefs = SharedPreferences.getInstance(); SharedPreferences sharedPreferences; @@ -744,4 +755,21 @@ class Cache with Logging { List getTrainingPlanDays() => this._trainingPlanDays; setTrainingPlanDays(value) => this._trainingPlanDays = value; + + bool canTrial() { + if (Cache().userLoggedIn == null) { + return false; + } + for (var element in _purchases) { + if (element.customerId == Cache().userLoggedIn!.customerId) { + return false; + } + } + + if (userLoggedIn!.trialDate != null) { + return false; + } + + return true; + } } diff --git a/lib/model/customer.dart b/lib/model/customer.dart index edd4a33..4aa19d6 100644 --- a/lib/model/customer.dart +++ b/lib/model/customer.dart @@ -24,6 +24,7 @@ class Customer { DateTime? dateChange; int? emailSubscription; int? sportId; + DateTime? trialDate; LinkedHashMap properties = LinkedHashMap(); @@ -70,6 +71,7 @@ class Customer { this.dataPolicyAllowed = json['dataPolicyAllowed']; this.emailSubscription = json['emailSubscription']; this.sportId = json['sportId']; + this.trialDate = json['trialDate'] == null ? null : DateTime.parse(json['trialDate']); this.dateAdd = json['dateAdd'] == null ? DateTime.parse("0000-00-00") : DateTime.parse(json['dateAdd']); this.dateChange = json['dateChange'] == null ? DateTime.parse("0000-00-00") : DateTime.parse(json['dateChange']); @@ -93,7 +95,8 @@ class Customer { "dateAdd": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateAdd!), "dateChange": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.dateChange!), "emailSubscription": this.emailSubscription, - "sportId": this.sportId + "sportId": this.sportId, + "trialDate": DateFormat('yyyy-MM-dd HH:mm:ss').format(this.trialDate!), }; double getProperty(String propertyName) { diff --git a/lib/model/exercise_ability.dart b/lib/model/exercise_ability.dart index 215b046..a73d32b 100644 --- a/lib/model/exercise_ability.dart +++ b/lib/model/exercise_ability.dart @@ -1,4 +1,4 @@ -enum ExerciseAbility { oneRepMax, endurance, running, mini_test_set, paralell_test, training, none } +enum ExerciseAbility { oneRepMax, endurance, running, mini_test_set, paralell_test, training, training_execute, none } extension ExerciseAbilityExt on ExerciseAbility { String enumToString() => this.toString().split(".").last; diff --git a/lib/model/workout_menu_tree.dart b/lib/model/workout_menu_tree.dart index 370e072..cd1943c 100644 --- a/lib/model/workout_menu_tree.dart +++ b/lib/model/workout_menu_tree.dart @@ -75,6 +75,7 @@ class WorkoutMenuTree { "is1RM": is1RM.toString(), "isRunning": isRunning.toString(), "sort": sort, + "internalName": internalName, }; } diff --git a/lib/repository/customer_repository.dart b/lib/repository/customer_repository.dart index 0425300..f31024c 100644 --- a/lib/repository/customer_repository.dart +++ b/lib/repository/customer_repository.dart @@ -168,7 +168,7 @@ class CustomerRepository with Logging { if (this.customer!.properties[propertyName] == null) { this.customer!.properties[propertyName] = CustomerProperty( propertyId: propertyRepository.getPropertyByName("Height")!.propertyId, - customerId: this.customer!.customerId!, + customerId: this.customer!.customerId == null ? 0 : this.customer!.customerId!, propertyValue: value, dateAdd: DateTime.now()); } else { diff --git a/lib/repository/training_plan_repository.dart b/lib/repository/training_plan_repository.dart index 975efa9..640e1f6 100644 --- a/lib/repository/training_plan_repository.dart +++ b/lib/repository/training_plan_repository.dart @@ -4,9 +4,10 @@ import 'package:aitrainer_app/model/customer_training_plan_details.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/model/exercise_tree.dart'; +import 'package:aitrainer_app/model/fitness_state.dart'; import 'package:aitrainer_app/model/training_plan.dart'; import 'package:aitrainer_app/repository/training_plan_day_repository.dart'; -import 'package:aitrainer_app/service/training_plan_service.dart'; +import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/util/common.dart'; class TrainingPlanRepository { @@ -40,14 +41,14 @@ class TrainingPlanRepository { /// 2. calculate customer_training_plan_details weights / repleats /// 3. create new customer_training_plan - Future activateTrainingPlan(int trainingPlanId) async { + CustomerTrainingPlan? activateTrainingPlan(int trainingPlanId) { print(" **** Activate Plan: $trainingPlanId"); // 1. deactivate if (Cache().getCustomerTrainingPlans() != null) { Cache().getCustomerTrainingPlans()!.forEach((plan) { plan.active = false; if (plan.customerTrainingPlanId != null) { - TrainingPlanApi().updateCustomerTrainingPlan(plan, plan.customerTrainingPlanId!); + //TrainingPlanApi().updateCustomerTrainingPlan(plan, plan.customerTrainingPlanId!); } }); } @@ -58,7 +59,7 @@ class TrainingPlanRepository { plan.active = true; plan.status = "open"; plan.dateAdd = DateTime.now(); - plan.name = getTrainingPlanById(trainingPlanId)!.nameTranslations["hu"]; + plan.name = getTrainingPlanById(trainingPlanId)!.nameTranslations[AppLanguage().appLocal.toString()]; TrainingPlan? trainingPlan = this.getTrainingPlanById(trainingPlanId); if (trainingPlan == null || trainingPlan.details == null) { @@ -138,6 +139,23 @@ class TrainingPlanRepository { return plan; } + int? getTrainingPlanByInternalName(String internalName) { + int? id; + if (Cache().getTrainingPlans() == null) { + return id; + } + + for (var trainingPlan in Cache().getTrainingPlans()!) { + print("internal ${trainingPlan.internalName}"); + if (trainingPlan.internalName == internalName) { + id = trainingPlan.trainingPlanId; + break; + } + } + + return id; + } + CustomerTrainingPlanDetails getCalculatedWeightRepeats(int exerciseTypeId, CustomerTrainingPlanDetails detail) { double weight = -1; if (Cache().getExercises() == null) { @@ -247,4 +265,32 @@ class TrainingPlanRepository { return recalculatedDetail; } + + void generateTrainingPlan() { + int? trainingPlanId; + if (Cache().userLoggedIn == null) { + return; + } + + bool isWoman = Cache().userLoggedIn!.sex == "w"; + + if (Cache().userLoggedIn!.fitnessLevel == FitnessState.beginner) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner") : getTrainingPlanByInternalName("beginner_man"); + } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.intermediate) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_beginner_split") : getTrainingPlanByInternalName("beginner_split"); + } else if (Cache().userLoggedIn!.fitnessLevel == FitnessState.advanced) { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("woman_advanced") : getTrainingPlanByInternalName("man_routine4"); + } else { + trainingPlanId = isWoman ? getTrainingPlanByInternalName("5day") : getTrainingPlanByInternalName("5day"); + } + + print("Generated plan $trainingPlanId fitness ${Cache().userLoggedIn!.fitnessLevel} - ${FitnessState.beginner}"); + + if (trainingPlanId != null) { + CustomerTrainingPlan? customerTrainingPlan = activateTrainingPlan(trainingPlanId); + if (customerTrainingPlan != null) { + Cache().myTrainingPlan = customerTrainingPlan; + } + } + } } diff --git a/lib/service/exercise_tree_service.dart b/lib/service/exercise_tree_service.dart index 8a511cc..e828a95 100644 --- a/lib/service/exercise_tree_service.dart +++ b/lib/service/exercise_tree_service.dart @@ -30,6 +30,7 @@ class ExerciseTreeApi with Logging { Future buildImage(String imageUrl, int treeId) async { String assetImage = 'asset/menu/' + imageUrl.substring(7); + print("asset image $assetImage"); return await rootBundle.load(assetImage).then((value) { return assetImage; }).catchError((_) { diff --git a/lib/service/firebase_api.dart b/lib/service/firebase_api.dart index f75ba91..e8cef5e 100644 --- a/lib/service/firebase_api.dart +++ b/lib/service/firebase_api.dart @@ -4,12 +4,10 @@ import 'package:crypto/crypto.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/service/logging.dart' as logging; import 'package:sign_in_with_apple/sign_in_with_apple.dart'; -import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_remote_config/firebase_remote_config.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; import 'package:google_sign_in/google_sign_in.dart'; @@ -37,7 +35,8 @@ class FirebaseApi with logging.Logging { await Firebase.initializeApp(); this.appleSignInAvailable = await SignInWithApple.isAvailable(); - AwesomeNotifications().initialize( + + /* AwesomeNotifications().initialize( // set the icon to null if you want to use the default app icon null, [ @@ -55,7 +54,7 @@ class FirebaseApi with logging.Logging { // This is very important to not harm the user experience AwesomeNotifications().requestPermissionToSendNotifications(); } - }); + }); */ await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( alert: true, // Required to display a heads up notification @@ -71,32 +70,6 @@ class FirebaseApi with logging.Logging { } } - Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - print('Handling a background message: ${message.messageId}'); - - if (!StringUtils.isNullOrEmpty(message.notification?.title, considerWhiteSpaceAsEmpty: true) || - !StringUtils.isNullOrEmpty(message.notification?.body, considerWhiteSpaceAsEmpty: true)) { - print('message also contained a notification: ${message.notification}'); - - String? imageUrl; - imageUrl ??= message.notification!.android?.imageUrl; - imageUrl ??= message.notification!.apple?.imageUrl; - - Map notificationAdapter = { - NOTIFICATION_CHANNEL_KEY: 'basic_channel', - NOTIFICATION_ID: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_ID] ?? message.messageId ?? math.Random().nextInt(2147483647), - NOTIFICATION_TITLE: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_TITLE] ?? message.notification?.title, - NOTIFICATION_BODY: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_BODY] ?? message.notification?.body, - NOTIFICATION_LAYOUT: StringUtils.isNullOrEmpty(imageUrl) ? 'Default' : 'BigPicture', - NOTIFICATION_BIG_PICTURE: imageUrl - }; - - AwesomeNotifications().createNotificationFromJsonData(notificationAdapter); - } else { - AwesomeNotifications().createNotificationFromJsonData(message.data); - } - } - Future signInEmail(String? email, String? password) async { if (email == null) { throw Exception("Please type an email address"); @@ -395,7 +368,7 @@ class FirebaseApi with logging.Logging { } Future setupRemoteConfig() async { - initializeFlutterFire(); + //initializeFlutterFire(); RemoteConfig? remoteConfig; try { remoteConfig = RemoteConfig.instance; @@ -420,3 +393,35 @@ class FirebaseApi with logging.Logging { } } } + +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + print('Handling a background message ${message.messageId}'); +} + +/* Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + print('Handling a background message: ${message.messageId}'); + + if (!StringUtils.isNullOrEmpty(message.notification?.title, considerWhiteSpaceAsEmpty: true) || + !StringUtils.isNullOrEmpty(message.notification?.body, considerWhiteSpaceAsEmpty: true)) { + print('message also contained a notification: ${message.notification}'); + + String? imageUrl; + imageUrl ??= message.notification!.android?.imageUrl; + imageUrl ??= message.notification!.apple?.imageUrl; + + Map notificationAdapter = { + NOTIFICATION_CHANNEL_KEY: 'basic_channel', + NOTIFICATION_ID: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_ID] ?? message.messageId ?? math.Random().nextInt(2147483647), + NOTIFICATION_TITLE: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_TITLE] ?? message.notification?.title, + NOTIFICATION_BODY: message.data[NOTIFICATION_CONTENT]?[NOTIFICATION_BODY] ?? message.notification?.body, + NOTIFICATION_LAYOUT: StringUtils.isNullOrEmpty(imageUrl) ? 'Default' : 'BigPicture', + NOTIFICATION_BIG_PICTURE: imageUrl + }; + + AwesomeNotifications().createNotificationFromJsonData(notificationAdapter); + } else { + AwesomeNotifications().createNotificationFromJsonData(message.data); + } +} */ diff --git a/lib/util/enums.dart b/lib/util/enums.dart index 66e56db..4277ff0 100644 --- a/lib/util/enums.dart +++ b/lib/util/enums.dart @@ -57,6 +57,7 @@ enum TrackingEvent { training_plan_execute, training_plan_finished, training_plan_custom, + trial } T enumFromString(Iterable values, String value) { diff --git a/lib/util/purchases.dart b/lib/util/purchases.dart index aac6fae..0c7f6c2 100644 --- a/lib/util/purchases.dart +++ b/lib/util/purchases.dart @@ -46,7 +46,9 @@ class RevenueCatPurchases with Logging { log("Purchaserinfo not reachable " + e.toString()); } } - if (Cache().userLoggedIn!.admin == 1) { + bool inTrial = Cache().userLoggedIn!.trialDate != null && DateTime.now().difference(Cache().userLoggedIn!.trialDate!).inDays < 10; + log("Trial mode: $inTrial date: ${Cache().userLoggedIn!.trialDate}"); + if (Cache().userLoggedIn!.admin == 1 || inTrial) { Cache().hasPurchased = true; } } diff --git a/lib/util/track.dart b/lib/util/track.dart index 76c5993..5b0dd7a 100644 --- a/lib/util/track.dart +++ b/lib/util/track.dart @@ -19,12 +19,13 @@ class Track with Logging { Track._internal(); void track(TrackingEvent event, {String eventValue = ""}) { + analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue}); if (!isInDebugMode) { FlurryData.logEvent(event.toString()); // 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}); + //analytics.logEvent(name: event.enumToString(), parameters: {"value": eventValue}); tracking.customerId = Cache().userLoggedIn == null ? 0 : Cache().userLoggedIn!.customerId!; tracking.event = event.enumToString(); if (eventValue.isNotEmpty) { diff --git a/lib/view/account.dart b/lib/view/account.dart index 00976c0..72b46c3 100644 --- a/lib/view/account.dart +++ b/lib/view/account.dart @@ -12,6 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:firebase_in_app_messaging/firebase_in_app_messaging.dart'; // ignore: must_be_immutable class AccountPage extends StatelessWidget with Trans { @@ -151,6 +152,7 @@ class AccountPage extends StatelessWidget with Trans { ), devices(context, accountBloc), loginOut(context, accountBloc), + //messaging(), //getMyTrainees(context, accountBloc), ]); } @@ -180,6 +182,25 @@ class AccountPage extends StatelessWidget with Trans { return element; } + ListTile messaging() { + FirebaseInAppMessaging fiam = FirebaseInAppMessaging(); + ListTile element = ListTile( + leading: Icon(Icons.message), + title: TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.white38, + onSurface: Colors.grey, + ), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text(t("Trigger message"), style: TextStyle(color: Colors.purple)), + ]), + onPressed: () => fiam.triggerEvent("mydevelopment"), + ), + ); + + return element; + } + ListTile loginOut(BuildContext context, AccountBloc accountBloc) { ListTile element = ListTile(); diff --git a/lib/view/customer_fitness_page.dart b/lib/view/customer_fitness_page.dart index 6c26d57..ced02ff 100644 --- a/lib/view/customer_fitness_page.dart +++ b/lib/view/customer_fitness_page.dart @@ -1,6 +1,7 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/sport.dart'; import 'package:aitrainer_app/util/app_localization.dart'; @@ -14,6 +15,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import '../bloc/customer_change/customer_change_bloc.dart'; import '../library/dropdown_search/dropdown_search.dart'; @@ -31,11 +33,13 @@ class CustomerFitnessPage extends StatefulWidget { class _CustomerFitnessPageState extends State with Trans { String? selected; bool fulldata = false; + late CustomerChangeBloc changeBloc; + late double cWidth; @override Widget build(BuildContext context) { setContext(context); - final double cWidth = MediaQuery.of(context).size.width * 0.75; + cWidth = MediaQuery.of(context).size.width * 0.75; CustomerRepository customerRepository; dynamic args = ModalRoute.of(context)!.settings.arguments; if (args is HashMap && args['personal_data'] != null) { @@ -49,186 +53,122 @@ class _CustomerFitnessPageState extends State with Trans { back: true, ); if (!fulldata) { - _bar = AppBarProgress(max: 50, min: 26); + _bar = AppBarProgress(max: 30, min: 15); } + final double h = 27; return Scaffold( - appBar: _bar, - body: BlocProvider( + appBar: _bar, + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_plainblack_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + height: double.infinity, + width: double.infinity, + child: BlocProvider( create: (context) => CustomerChangeBloc(customerRepository: customerRepository), - child: Builder(builder: (context) { - // ignore: close_sinks - CustomerChangeBloc changeBloc = BlocProvider.of(context); - selected = changeBloc.selectedFitnessItem; - if (selected == null) { - selected = FitnessState.beginner; - } - return SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Container( - padding: EdgeInsets.only(bottom: 200), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('asset/image/WT_light_background.jpg'), - fit: BoxFit.cover, - alignment: Alignment.center, - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Divider(), - Wrap( - //runAlignment: WrapAlignment.center, - alignment: WrapAlignment.center, - children: [ - Text( - t("Your Fitness State"), - textAlign: TextAlign.center, - style: GoogleFonts.archivoBlack( - color: Colors.orange, - fontSize: 30, - fontWeight: FontWeight.w900, - ), - ) - ]), - Divider(), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.all(10.0), - shape: getShape(changeBloc, FitnessState.beginner), - ), - child: Container( - width: cWidth, - child: Column( - children: [ - Text(t("Beginner"), - textWidthBasis: TextWidthBasis.longestLine, - style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900)), - Text( - t("I am beginner"), - style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100), - ), - ], - )), - onPressed: () => { - setState(() { - selected = FitnessState.beginner; - changeBloc.add(CustomerFitnessChange(fitness: selected!)); - }), - }), - Divider(), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.all(10.0), - shape: getShape(changeBloc, FitnessState.intermediate), - ), - child: Container( - width: cWidth, - child: Column( - children: [ - InkWell( - child: Text( - t("Intermediate"), - style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900), - ), - highlightColor: Colors.white, - ), - InkWell( - child: Text( - t("I am intermediate"), - style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100), - ), - highlightColor: Colors.white, - ), - ], - ), - ), - onPressed: () => { - setState(() { - selected = FitnessState.intermediate; - changeBloc.add(CustomerFitnessChange(fitness: selected!)); - print(selected); - }), - }), - Divider(), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.all(10.0), - shape: getShape(changeBloc, FitnessState.advanced), - ), - child: Container( - width: cWidth, - child: Column( - children: [ - InkWell( - child: Text( - t("Advanced"), - style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900), - ), - highlightColor: Colors.white, - ), - InkWell( - child: Text( - t("I am advanced"), - style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100), - ), - highlightColor: Colors.white, - ), - ], - ), - ), - onPressed: () => { - setState(() { - selected = FitnessState.advanced; - changeBloc.add(CustomerFitnessChange(fitness: selected!)); - print(selected); - }), - }), - Divider(), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.all(10.0), - shape: getShape(changeBloc, FitnessState.professional), - ), - child: Container( - width: cWidth, - child: Column( - children: [ - InkWell( - child: Text( - AppLocalizations.of(context)!.translate("Professional"), - style: TextStyle(color: Colors.blue, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900), - ), - highlightColor: Colors.white, - ), - InkWell( - child: Text( - AppLocalizations.of(context)!.translate("I am professional"), - style: TextStyle(color: Colors.black, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100), - ), - highlightColor: Colors.white, - ), - ], - ), - ), - onPressed: () => { - setState(() { - selected = FitnessState.professional; - changeBloc.add(CustomerFitnessChange(fitness: selected!)); - print(selected); - }), - }), - Divider(), - Text( - t("Your Primary Sport") + ":", - textAlign: TextAlign.center, - style: GoogleFonts.archivoBlack( - color: Colors.orange, - fontSize: 20, - ), + child: BlocConsumer( + listener: (context, state) { + if (state is CustomerSaveError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + } else if (state is CustomerSaveSuccess) { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed("customerSexPage", arguments: changeBloc.customerRepository); + } + }, + builder: (context, state) { + changeBloc = BlocProvider.of(context); + return ModalProgressHUD( + child: getPage(), + inAsyncCall: state is CustomerChangeLoading, + opacity: 0.5, + color: Colors.black54, + progressIndicator: CircularProgressIndicator(), + ); + }, + ), + )), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => { + if (!fulldata) + { + changeBloc.add(CustomerSaveFitness()), + } + else + { + changeBloc.add(CustomerSave()), + } + }, + backgroundColor: Color(0xffb4f500), + icon: Icon( + CustomIcon.save, + color: Colors.black, + size: 26, + ), + label: Text( + fulldata ? t("Save") : t("Next"), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + ), + ), + ); + } + + Widget getPage() { + final double h = 27; + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: h, + ), + Wrap(alignment: WrapAlignment.center, children: [ + Text( + t("Your Fitness State"), + textAlign: TextAlign.center, + style: GoogleFonts.archivoBlack( + color: Colors.white, + fontSize: 30, + fontWeight: FontWeight.w900, + ), + ) + ]), + SizedBox( + height: h, + ), + + getButton("Beginner", "I am beginner", FitnessState.beginner), + SizedBox( + height: h, + ), + getButton("Intermediate", "I am intermediate", FitnessState.intermediate), + + SizedBox( + height: h, + ), + getButton("Advanced", "I am advanced", FitnessState.advanced), + + SizedBox( + height: h, + ), + getButton("Professional", "I am professional", FitnessState.professional), + + /* Divider(), + Text( + t("Your Primary Sport") + ":", + textAlign: TextAlign.center, + style: GoogleFonts.archivoBlack( + color: Colors.orange, + fontSize: 20, ), - getSport(changeBloc), - Divider(), + ), */ + //getSport(changeBloc), + /* Divider(), ElevatedButton( style: ElevatedButton.styleFrom( onPrimary: Colors.white, @@ -240,22 +180,58 @@ class _CustomerFitnessPageState extends State with Trans { Navigator.of(context).pop(), if (!fulldata) {Navigator.of(context).pushNamed("customerBodyTypePage", arguments: customerRepository)} }, - ) - ], - ), + ) */ + ], + ), + ); + } + + TextButton getButton(String title, String desc, String state) { + return TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.all(10.0), + shape: getShape(changeBloc, state), + ), + child: Container( + width: cWidth, + child: Column( + children: [ + InkWell( + child: Text( + AppLocalizations.of(context)!.translate(title), + style: TextStyle(color: Colors.white, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900), ), - ); - }))); + highlightColor: Colors.white, + ), + InkWell( + child: Text( + AppLocalizations.of(context)!.translate(desc), + style: TextStyle(color: Colors.white, fontSize: 20, fontFamily: 'Arial', fontWeight: FontWeight.w100), + ), + highlightColor: Colors.white, + ), + ], + ), + ), + onPressed: () => { + changeBloc.add(CustomerFitnessChange(fitness: state)), + }); } dynamic getShape(CustomerChangeBloc changeBloc, String fitnessLevel) { String? selected = changeBloc.selectedFitnessItem; + dynamic returnCode = (selected == fitnessLevel) ? RoundedRectangleBorder( - side: BorderSide(width: 4, color: Colors.orange), + side: BorderSide( + width: 4, + color: Color(0xffb4f500), + ), + borderRadius: BorderRadius.circular(12), ) : RoundedRectangleBorder( - side: BorderSide(width: 1, color: Colors.blue), + side: BorderSide(width: 4, color: Colors.white24), + borderRadius: BorderRadius.circular(12), ); //return return returnCode; diff --git a/lib/view/customer_goal_page.dart b/lib/view/customer_goal_page.dart index 483ac3e..2e00963 100644 --- a/lib/view/customer_goal_page.dart +++ b/lib/view/customer_goal_page.dart @@ -10,6 +10,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; enum Goals { gain_muscle, weight_loss, endurance, muscle_endurance, flexibility, gain_strength, explosiveness, shape_forming } @@ -69,18 +70,21 @@ class _CustomerGoalPage extends State with Trans { CustomerRepository customerRepository; dynamic args = ModalRoute.of(context)!.settings.arguments; - if (args is HashMap && args['personal_data'] != null) { - fulldata = args['personal_data']; - customerRepository = args['bloc']; + if (args != null) { + if (args is HashMap && args['personal_data'] != null) { + fulldata = args['personal_data']; + customerRepository = args['bloc']; + } else { + customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; + } } else { - customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; + customerRepository = CustomerRepository(); } - PreferredSizeWidget _bar = AppBarMin( - back: true, + back: false, ); if (!fulldata) { - _bar = AppBarProgress(max: 50, min: 26); + _bar = AppBarProgress(max: 14, min: 0); } return Scaffold( @@ -88,7 +92,7 @@ class _CustomerGoalPage extends State with Trans { body: Container( decoration: BoxDecoration( image: DecorationImage( - image: AssetImage('asset/image/WT_light_background.jpg'), + image: AssetImage('asset/image/WT_plainblack_background.jpg'), fit: BoxFit.cover, alignment: Alignment.center, ), @@ -97,75 +101,120 @@ class _CustomerGoalPage extends State with Trans { width: double.infinity, child: BlocProvider( create: (context) => CustomerChangeBloc(customerRepository: customerRepository), - child: Builder(builder: (context) { - changeBloc = BlocProvider.of(context); - - return SingleChildScrollView( - child: Center( - child: Column( - children: [ - Divider(), - Wrap(alignment: WrapAlignment.center, children: [ - Text( - t("Set Your Primary Goal"), - maxLines: 2, - textAlign: TextAlign.center, - style: GoogleFonts.archivoBlack( - color: Colors.orange, - fontSize: 30, - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black87, - ), - ], - ), - ), - ]), - Divider(), - getItem(changeBloc, Goals.gain_muscle), - Divider(), - getItem(changeBloc, Goals.weight_loss), - Divider(), - getItem(changeBloc, Goals.shape_forming), - Divider(), - getItem(changeBloc, Goals.endurance), - Divider(), - getItem(changeBloc, Goals.gain_strength), - Divider(), - getItem(changeBloc, Goals.muscle_endurance), - Divider(), - getItem(changeBloc, Goals.flexibility), - Divider(), - getItem(changeBloc, Goals.explosiveness), - Divider(), - ], - ), - )); - }), + child: BlocConsumer( + listener: (context, state) { + if (state is CustomerSaveError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + } else if (state is CustomerSaveSuccess) { + Navigator.of(context).pushNamed("customerFitnessPage", arguments: changeBloc.customerRepository); + } + }, + builder: (context, state) { + changeBloc = BlocProvider.of(context); + return ModalProgressHUD( + child: getPage(), + inAsyncCall: state is CustomerChangeLoading, + opacity: 0.5, + color: Colors.black54, + progressIndicator: CircularProgressIndicator(), + ); + }, + ), ), ), floatingActionButton: FloatingActionButton.extended( onPressed: () => { - //changingViewModel.saveCustomer(), - changeBloc.add(CustomerSave()), - Navigator.of(context).pop(), - if (!fulldata) {Navigator.of(context).pushNamed("customerFitnessPage", arguments: changeBloc.customerRepository)} + print("Fulldata: $fulldata bloc $changeBloc"), + if (!fulldata) + { + print("Savegoal"), + changeBloc.add(CustomerSaveGoal()), + } + else + { + changeBloc.add(CustomerSave()), + } }, - backgroundColor: Colors.orange[800], + backgroundColor: Color(0xffb4f500), icon: Icon( CustomIcon.save, - size: 20, + color: Colors.black, + size: 26, ), label: Text( fulldata ? t("Save") : t("Next"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 12), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), ), ), ); } + Widget getPage() { + final double h = 27; + return SingleChildScrollView( + child: Center( + child: Column( + children: [ + Divider(), + Wrap(alignment: WrapAlignment.center, children: [ + Text( + t("Set Your Primary Goal"), + maxLines: 2, + textAlign: TextAlign.center, + style: GoogleFonts.archivoBlack( + color: Colors.white, + fontSize: 30, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 3.0, + color: Colors.black87, + ), + ], + ), + ), + ]), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.gain_muscle), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.weight_loss), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.shape_forming), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.endurance), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.gain_strength), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.muscle_endurance), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.flexibility), + SizedBox( + height: h, + ), + getItem(changeBloc, Goals.explosiveness), + SizedBox( + height: h, + ), + ], + ), + )); + } + Widget getItem(CustomerChangeBloc changeBloc, Goals goal) { return Stack(alignment: Alignment.bottomLeft, children: [ TextButton( @@ -188,7 +237,7 @@ class _CustomerGoalPage extends State with Trans { child: Text( t(goal.description(goal)), style: GoogleFonts.archivoBlack( - color: Colors.yellow[300], + color: Colors.white, fontSize: 28, shadows: [ Shadow( @@ -204,13 +253,18 @@ class _CustomerGoalPage extends State with Trans { } dynamic getShape(CustomerChangeBloc customerBloc, String goal) { - if (customerBloc.customerRepository.goal == null) return null; + dynamic baseCode = RoundedRectangleBorder( + side: BorderSide(width: 2, color: Colors.white24), + borderRadius: BorderRadius.circular(12), + ); + if (customerBloc.customerRepository.goal == null) return baseCode; String selectedGoal = customerBloc.customerRepository.goal!; dynamic returnCode = (selectedGoal == goal) ? RoundedRectangleBorder( - side: BorderSide(width: 4, color: Colors.red), + side: BorderSide(width: 6, color: Color(0xffb4f500)), + borderRadius: BorderRadius.circular(12), ) - : null; + : baseCode; //return return returnCode; } diff --git a/lib/view/customer_height_page.dart b/lib/view/customer_height_page.dart new file mode 100644 index 0000000..b8d9410 --- /dev/null +++ b/lib/view/customer_height_page.dart @@ -0,0 +1,178 @@ +import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; + +import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar_min.dart'; +import 'package:aitrainer_app/widgets/app_bar_progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'package:syncfusion_flutter_gauges/gauges.dart'; + +import '../bloc/customer_change/customer_change_bloc.dart'; + +// ignore: must_be_immutable +class CustomerHeightPage extends StatefulWidget { + late _CustomerHeightPageState _state; + + _CustomerHeightPageState createState() { + _state = _CustomerHeightPageState(); + return _state; + } +} + +class _CustomerHeightPageState extends State with Trans { + String? selected; + bool fulldata = false; + late CustomerChangeBloc changeBloc; + late double cWidth; + + @override + Widget build(BuildContext context) { + setContext(context); + + final CustomerRepository customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; + + PreferredSizeWidget _bar = AppBarMin( + back: true, + ); + if (!fulldata) { + _bar = AppBarProgress(max: 75, min: 60); + } + + return Scaffold( + appBar: _bar, + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_plainblack_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + height: double.infinity, + width: double.infinity, + child: BlocProvider( + create: (context) => CustomerChangeBloc(customerRepository: customerRepository), + child: BlocConsumer( + listener: (context, state) { + if (state is CustomerSaveError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + } else if (state is CustomerSaveSuccess) { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed("registration", arguments: changeBloc.customerRepository); + } + }, + builder: (context, state) { + changeBloc = BlocProvider.of(context); + return getPage(); + }, + ), + )), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => changeBloc.add(CustomerSaveHeight()), + backgroundColor: Color(0xffb4f500), + icon: Icon( + CustomIcon.save, + color: Colors.black, + size: 26, + ), + label: Text( + fulldata ? t("Save") : t("Finish"), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + ), + ), + ); + } + + Widget getPage() { + final double h = 27; + cWidth = MediaQuery.of(context).size.width * 0.75; + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: h, + ), + Wrap(alignment: WrapAlignment.center, children: [ + Text( + t("What is your height?"), + textAlign: TextAlign.center, + maxLines: 2, + style: GoogleFonts.archivoBlack( + color: Colors.white, + fontSize: 30, + fontWeight: FontWeight.w900, + ), + ) + ]), + SizedBox( + height: h, + ), + getButton(), + ], + ), + ); + } + + Widget getButton() { + double mediaWidth = MediaQuery.of(context).size.width * .4; + double mediaHeight = MediaQuery.of(context).size.height * .4; + return Row(children: [ + changeBloc.customerRepository.customer!.sex == "m" + ? Image.asset( + "asset/image/test_picto_m.png", + height: mediaHeight, + width: mediaWidth, + ) + : Image.asset( + "asset/image/test_picto_w.png", + height: mediaHeight, + width: mediaWidth, + ), + SfLinearGauge( + minimum: 140, + maximum: 220, + markerPointers: [ + LinearWidgetPointer( + value: changeBloc.height, + offset: 55, + position: LinearElementPosition.inside, + markerAlignment: LinearMarkerAlignment.center, + child: Container( + height: 25, + width: 55, + color: Colors.transparent, + child: Text(changeBloc.height.toString(), + style: GoogleFonts.inter( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Color(0xffb4f500), + )), + ), + ), + LinearShapePointer( + height: 25, + width: 55, + color: Color(0xffb4f500), + value: changeBloc.height, + onValueChanged: (value) => { + changeBloc.add(CustomerHeightChange(height: value.toInt())), + }, + ), + ], + orientation: LinearGaugeOrientation.vertical, + majorTickStyle: LinearTickStyle(length: 20, color: Colors.white), + axisLabelStyle: TextStyle(fontSize: 12.0, color: Colors.white), + axisTrackStyle: + LinearAxisTrackStyle(color: Colors.cyan, edgeStyle: LinearEdgeStyle.bothFlat, thickness: 1.0, borderColor: Colors.white)) + ]); + } +} diff --git a/lib/view/customer_sex_page.dart b/lib/view/customer_sex_page.dart new file mode 100644 index 0000000..5cef9d8 --- /dev/null +++ b/lib/view/customer_sex_page.dart @@ -0,0 +1,185 @@ +import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; + +import 'package:aitrainer_app/util/app_localization.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar_min.dart'; +import 'package:aitrainer_app/widgets/app_bar_progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; + +import '../bloc/customer_change/customer_change_bloc.dart'; + +// ignore: must_be_immutable +class CustomerSexPage extends StatefulWidget { + late _CustomerSexPageState _state; + + _CustomerSexPageState createState() { + _state = _CustomerSexPageState(); + return _state; + } +} + +class _CustomerSexPageState extends State with Trans { + String? selected; + bool fulldata = false; + late CustomerChangeBloc changeBloc; + late double cWidth; + + @override + Widget build(BuildContext context) { + setContext(context); + + final CustomerRepository customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; + + PreferredSizeWidget _bar = AppBarMin( + back: true, + ); + if (!fulldata) { + _bar = AppBarProgress(max: 45, min: 30); + } + + return Scaffold( + appBar: _bar, + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_plainblack_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + height: double.infinity, + width: double.infinity, + child: BlocProvider( + create: (context) => CustomerChangeBloc(customerRepository: customerRepository), + child: BlocConsumer( + listener: (context, state) { + if (state is CustomerSaveError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + } else if (state is CustomerSaveSuccess) { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed("customerWeightPage", arguments: changeBloc.customerRepository); + } + }, + builder: (context, state) { + changeBloc = BlocProvider.of(context); + return ModalProgressHUD( + child: getPage(), + inAsyncCall: state is CustomerChangeLoading, + opacity: 0.5, + color: Colors.black54, + progressIndicator: CircularProgressIndicator(), + ); + }, + ), + )), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => { + changeBloc.add(CustomerSaveSex()), + }, + backgroundColor: Color(0xffb4f500), + icon: Icon( + CustomIcon.save, + color: Colors.black, + size: 26, + ), + label: Text( + fulldata ? t("Save") : t("Next"), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + ), + ), + ); + } + + Widget getPage() { + final double h = 27; + cWidth = MediaQuery.of(context).size.width * 0.75; + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: h, + ), + Wrap(alignment: WrapAlignment.center, children: [ + Text( + t("What is your biological sex?"), + textAlign: TextAlign.center, + maxLines: 2, + style: GoogleFonts.archivoBlack( + color: Colors.white, + fontSize: 30, + fontWeight: FontWeight.w900, + ), + ) + ]), + SizedBox( + height: h, + ), + getButton("Man", "", "m"), + SizedBox( + height: h, + ), + getButton("Woman", "", "w"), + ], + ), + ); + } + + TextButton getButton(String title, String desc, String state) { + return TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.all(10.0), + shape: getShape(changeBloc, state), + ), + child: Container( + width: cWidth, + child: Column( + children: [ + InkWell( + child: ListTile( + leading: Icon( + state == "m" ? Icons.male_outlined : Icons.female, + color: Color(0xffb4f500), + size: 60, + ), + title: Text( + AppLocalizations.of(context)!.translate(title), + style: TextStyle(color: Colors.white, fontSize: 32, fontFamily: 'Arial', fontWeight: FontWeight.w900), + ), + )), + ], + ), + ), + onPressed: () => { + print("Sex $state"), + changeBloc.add(CustomerGenderChange(gender: state == "m" ? 0 : 1)), + }); + } + + dynamic getShape(CustomerChangeBloc changeBloc, String sex) { + String? selected = changeBloc.customerRepository.customer!.sex; + dynamic returnCode = (selected == sex) + ? RoundedRectangleBorder( + side: BorderSide( + width: 4, + color: Color(0xffb4f500), + ), + borderRadius: BorderRadius.circular(12), + ) + : RoundedRectangleBorder( + side: BorderSide(width: 4, color: Colors.white24), + borderRadius: BorderRadius.circular(12), + ); + + return returnCode; + } +} diff --git a/lib/view/customer_weight_page.dart b/lib/view/customer_weight_page.dart new file mode 100644 index 0000000..25ee9cd --- /dev/null +++ b/lib/view/customer_weight_page.dart @@ -0,0 +1,147 @@ +import 'package:aitrainer_app/bloc/customer_change/customer_change_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; + +import 'package:aitrainer_app/util/app_localization.dart'; +import 'package:aitrainer_app/repository/customer_repository.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar_min.dart'; +import 'package:aitrainer_app/widgets/app_bar_progress.dart'; +import 'package:aitrainer_app/widgets/number_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; + +import '../bloc/customer_change/customer_change_bloc.dart'; + +// ignore: must_be_immutable +class CustomerWeightPage extends StatefulWidget { + late _CustomerWeightPageState _state; + + _CustomerWeightPageState createState() { + _state = _CustomerWeightPageState(); + return _state; + } +} + +class _CustomerWeightPageState extends State with Trans { + String? selected; + bool fulldata = false; + late CustomerChangeBloc changeBloc; + late double cWidth; + + @override + Widget build(BuildContext context) { + setContext(context); + + final CustomerRepository customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; + + PreferredSizeWidget _bar = AppBarMin( + back: true, + ); + if (!fulldata) { + _bar = AppBarProgress(max: 60, min: 45); + } + + return Scaffold( + appBar: _bar, + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_plainblack_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + height: double.infinity, + width: double.infinity, + child: BlocProvider( + create: (context) => CustomerChangeBloc(customerRepository: customerRepository), + child: BlocConsumer( + listener: (context, state) { + if (state is CustomerSaveError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + } else if (state is CustomerSaveSuccess) { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed("customerHeightPage", arguments: changeBloc.customerRepository); + } + }, + builder: (context, state) { + changeBloc = BlocProvider.of(context); + return ModalProgressHUD( + child: getPage(), + inAsyncCall: state is CustomerChangeLoading, + opacity: 0.5, + color: Colors.black54, + progressIndicator: CircularProgressIndicator(), + ); + }, + ), + )), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => { + changeBloc.add(CustomerSaveWeight()), + }, + backgroundColor: Color(0xffb4f500), + icon: Icon( + CustomIcon.save, + color: Colors.black, + size: 26, + ), + label: Text( + fulldata ? t("Save") : t("Next"), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.black), + ), + ), + ); + } + + Widget getPage() { + final double h = 27; + cWidth = MediaQuery.of(context).size.width * 0.75; + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: h, + ), + Wrap(alignment: WrapAlignment.center, children: [ + Text( + t("What is your weight?"), + textAlign: TextAlign.center, + maxLines: 2, + style: GoogleFonts.archivoBlack( + color: Colors.white, + fontSize: 30, + fontWeight: FontWeight.w900, + ), + ) + ]), + SizedBox( + height: h, + ), + getButton(), + ], + ), + ); + } + + Widget getButton() { + return NumberPickerWidget( + minValue: 40, + maxValue: 150, + fontSize: 30, + diameterRatio: 1.1, + itemExtent: 40, + fontWeight: FontWeight.w700, + initalValue: changeBloc.weight.toInt(), + unit: t("kg"), + color: Color(0xffb4f500), + onChange: (value) => changeBloc.add(CustomerWeightChange(weight: value))); + } +} diff --git a/lib/view/customer_welcome_page.dart b/lib/view/customer_welcome_page.dart index 09f1364..3f37c60 100644 --- a/lib/view/customer_welcome_page.dart +++ b/lib/view/customer_welcome_page.dart @@ -1,6 +1,10 @@ -import 'package:aitrainer_app/util/app_localization.dart'; +import 'package:aitrainer_app/library/button_animations.dart'; +import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar_min.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_fadein/flutter_fadein.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:percent_indicator/circular_percent_indicator.dart'; // ignore: must_be_immutable class CustomerWelcomePage extends StatefulWidget { @@ -12,11 +16,14 @@ class CustomerWelcomePage extends StatefulWidget { } } -class _CustomerWelcomePageState extends State { +class _CustomerWelcomePageState extends State with Trans { @override Widget build(BuildContext context) { + setContext(context); return Scaffold( - appBar: AppBarMin(), + appBar: AppBarMin( + back: true, + ), body: Container( decoration: BoxDecoration( image: DecorationImage( @@ -30,17 +37,64 @@ class _CustomerWelcomePageState extends State { child: Center( child: Column( children: [ - Divider( - color: Colors.transparent, + SizedBox( + height: 200, ), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: Colors.orange, - onSurface: Colors.white, + CircularPercentIndicator( + radius: 250.0, + animation: true, + animationDuration: 4800, + lineWidth: 20.0, + percent: 0.98, + curve: Curves.bounceInOut, + backgroundWidth: 4, + center: Text( + t("Training Plan Generation"), + textAlign: TextAlign.center, + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + fontSize: 28.0, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), ), - child: InkWell(child: Text(AppLocalizations.of(context)!.translate("Next"))), - onPressed: () => {Navigator.of(context).pop(), Navigator.of(context).pushNamed("home")}, - ) + circularStrokeCap: CircularStrokeCap.round, + progressColor: Color(0xffb4f500), + backgroundColor: Colors.black, + ), + SizedBox( + height: 90, + ), + FadeIn( + child: Container( + width: 160, + height: 80, + child: GestureDetector( + onTap: () => Navigator.of(context).popAndPushNamed("home"), + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset('asset/icon/gomb_orange_a.png', width: 140, height: 80), + Text( + t("Next"), + style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.white), + ), + ], + ), + )), + duration: Duration(seconds: 6), + ), ], ), ))), diff --git a/lib/view/evaluation_page.dart b/lib/view/evaluation_page.dart index c2b6345..e6f4fba 100644 --- a/lib/view/evaluation_page.dart +++ b/lib/view/evaluation_page.dart @@ -502,7 +502,7 @@ class EvaluationPage extends StatelessWidget with Trans { Divider(color: Colors.transparent), getSuggestionWidget(resultBloc, "Gain Strength", "asset/image/pict_weight_volumen_tonna.png", "3x4-8", 0.95, "3-5"), Divider(color: Colors.transparent), - getSuggestionWidget(resultBloc, "Endurance", "asset/image/pict_reps_volumen_db.png", "4x25-35", 0.50, "3"), + getSuggestionWidget(resultBloc, "Muscle Endurance", "asset/image/pict_reps_volumen_db.png", "4x25-35", 0.50, "3"), ], ), ); diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 3c5be48..1f99566 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/bloc/login/login_bloc.dart'; +import 'package:aitrainer_app/repository/training_plan_repository.dart'; import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar_min.dart'; @@ -33,17 +34,18 @@ class RegistrationPage extends StatelessWidget with Trans { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(t(state.message), style: TextStyle(color: Colors.white)))); } else if (state is LoginSuccess) { + TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository(); showDialog( context: context, builder: (BuildContext context) { return DialogCommon( title: t("Successful Registration"), - descriptions: t("Now we would like to know you better to lift the experience of the app."), - description2: t("Please go through the pages, it will take couple of minutes!"), + descriptions: t("Based on your initial data, we will generate the personalized training plan for you."), text: "OK", - onTap: () => {Navigator.of(context).pushNamed('customerModifyPage')}, + onTap: () => {Navigator.of(context).pushNamed('customerWelcomePage')}, onCancel: () => { - Navigator.of(context).pushNamed("home"), + trainingPlanRepository.generateTrainingPlan(), + Navigator.of(context).pushNamed("customerWelcomePage"), }, ); }); @@ -83,7 +85,7 @@ class RegistrationPage extends StatelessWidget with Trans { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: AssetImage('asset/image/WT_login.jpg'), + image: AssetImage('asset/image/WT_menu_dark.jpg'), fit: BoxFit.cover, alignment: Alignment.center, ), @@ -99,17 +101,32 @@ class RegistrationPage extends StatelessWidget with Trans { child: Container( padding: const EdgeInsets.only(left: 20, right: 20), child: ListView(shrinkWrap: false, padding: EdgeInsets.only(top: 10.0), children: [ - GestureDetector( - onTap: () => loginBloc.add(LoginSkip()), - child: Text( - t("I Execute My First Test Now"), - textAlign: TextAlign.right, - style: GoogleFonts.inter(color: loginBloc.testColor, decoration: TextDecoration.underline, fontWeight: FontWeight.bold), - )), SizedBox( - height: 120, + height: 100, + ), + ListTile( + title: Text( + t("SignUp"), + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.w500, + shadows: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + ], + ), + )), + SizedBox( + height: 20, ), - ListTile(title: Text(t("SignUp"), style: GoogleFonts.inter())), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -140,7 +157,6 @@ class RegistrationPage extends StatelessWidget with Trans { : Offstage(), ], ), - //ListTile(title: Text(t("OR"), style: GoogleFonts.inter())), Divider( color: Colors.transparent, ), @@ -149,6 +165,10 @@ class RegistrationPage extends StatelessWidget with Trans { decoration: InputDecoration( contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15), labelText: t('Email'), + labelStyle: TextStyle( + fontSize: 14, + color: Color(0xffb4f500), + ), fillColor: Colors.white24, filled: true, border: OutlineInputBorder( @@ -165,7 +185,7 @@ class RegistrationPage extends StatelessWidget with Trans { }, onChanged: (value) => loginBloc.add(LoginEmailChange(email: value)), keyboardType: TextInputType.emailAddress, - style: new TextStyle(fontSize: 16, color: Colors.indigo), + style: new TextStyle(fontSize: 16, color: Colors.white), ), Divider( color: Colors.transparent, @@ -174,7 +194,10 @@ class RegistrationPage extends StatelessWidget with Trans { key: LibraryKeys.loginPasswordField, obscureText: loginBloc.obscure, decoration: InputDecoration( - labelStyle: TextStyle(fontSize: 14), + labelStyle: TextStyle( + fontSize: 14, + color: Color(0xffb4f500), + ), contentPadding: EdgeInsets.only(left: 15, top: 15, bottom: 15), suffixIcon: IconButton( onPressed: () => {loginBloc.add(LoginPasswordChangeObscure())}, @@ -197,7 +220,7 @@ class RegistrationPage extends StatelessWidget with Trans { }, onChanged: (value) => loginBloc.add(LoginPasswordChange(password: value)), keyboardType: TextInputType.visiblePassword, - style: new TextStyle(fontSize: 16, color: Colors.indigo), + style: new TextStyle(fontSize: 16, color: Colors.white), ), Divider( color: Colors.transparent, @@ -214,7 +237,22 @@ class RegistrationPage extends StatelessWidget with Trans { ), title: Text( t("With the registration I accept the data policy and the terms of use."), - style: GoogleFonts.inter(color: Colors.indigo), + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.w500, + shadows: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + ], + ), ), ), Row(mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -242,7 +280,23 @@ class RegistrationPage extends StatelessWidget with Trans { InkWell( child: Text( t('Login'), - style: GoogleFonts.inter(decoration: TextDecoration.underline), + style: GoogleFonts.inter( + decoration: TextDecoration.underline, + color: Colors.white, + fontWeight: FontWeight.w500, + shadows: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + ], + ), ), onTap: () => Navigator.of(context).pushNamed('login'), ), @@ -250,7 +304,23 @@ class RegistrationPage extends StatelessWidget with Trans { InkWell( child: Text( t('Terms Of Use'), - style: GoogleFonts.inter(decoration: TextDecoration.underline), + style: GoogleFonts.inter( + decoration: TextDecoration.underline, + color: Colors.white, + fontWeight: FontWeight.w500, + shadows: [ + Shadow( + offset: Offset(3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 6.0, + color: Colors.black54, + ), + ], + ), ), onTap: () => { showDialog( @@ -263,7 +333,18 @@ class RegistrationPage extends StatelessWidget with Trans { InkWell( child: Text( t('Privacy'), - style: GoogleFonts.inter(decoration: TextDecoration.underline), + style: GoogleFonts.inter( + decoration: TextDecoration.underline, + color: Colors.white, + fontWeight: FontWeight.w500, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 10.0, + color: Colors.black87, + ), + ], + ), ), onTap: () => { showDialog( diff --git a/lib/view/settings.dart b/lib/view/settings.dart index ed807d5..9ccd6a0 100644 --- a/lib/view/settings.dart +++ b/lib/view/settings.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart'; @@ -17,7 +19,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:toggle_switch/toggle_switch.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:flutter/services.dart'; +import 'dart:async'; // ignore: must_be_immutable class SettingsPage extends StatelessWidget with Trans { @@ -81,7 +84,7 @@ class SettingsPage extends StatelessWidget with Trans { Track().track(TrackingEvent.settings_lang, eventValue: lang) })), getServer(settingsBloc), - getTuturialBasic(settingsBloc), + //getTuturialBasic(settingsBloc), getTermsOfUse(), getPrivacy(), getFaq(), @@ -96,7 +99,7 @@ class SettingsPage extends StatelessWidget with Trans { title: Container(), ); } - print("Live: ${Cache().liveServer}"); + return ListTile( leading: Icon(Icons.data_usage_sharp), subtitle: Text("For Test purpuses select Test-Server. After that please restart the the App"), @@ -260,4 +263,26 @@ class SettingsPage extends StatelessWidget with Trans { ), ); } + + Future _printAndCopy(String cmd) async { + print(cmd); + + await Clipboard.setData(ClipboardData(text: cmd)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Copied to Clipboard')), + ); + } + + void link1() { + String? cmd; + String cmdSuffix; + if (Platform.isIOS) { + cmd = '/usr/bin/xcrun simctl openurl booted'; + } else if (Platform.isAndroid) { + cmd = '\$ANDROID_HOME/platform-tools/adb shell \'am start' + ' -a android.intent.action.VIEW' + ' -c android.intent.category.BROWSABLE -d'; + cmdSuffix = "'"; + } + } } diff --git a/lib/view/training_plan_activate_page.dart b/lib/view/training_plan_activate_page.dart index be8ee66..5f146b1 100644 --- a/lib/view/training_plan_activate_page.dart +++ b/lib/view/training_plan_activate_page.dart @@ -128,7 +128,10 @@ class TrainingPlanActivatePage extends StatelessWidget with Trans { child: Text( parentTitle, maxLines: 2, - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), )), ], ), diff --git a/lib/view/training_plan_execute.dart b/lib/view/training_plan_execute.dart new file mode 100644 index 0000000..7ad2875 --- /dev/null +++ b/lib/view/training_plan_execute.dart @@ -0,0 +1,649 @@ +import 'dart:collection'; + +import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart'; +import 'package:aitrainer_app/library/custom_icon_icons.dart'; +import 'package:aitrainer_app/model/customer_training_plan_details.dart'; +import 'package:aitrainer_app/model/exercise_plan_detail.dart'; +import 'package:aitrainer_app/util/app_localization.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; +import 'package:aitrainer_app/widgets/dialog_common.dart'; +import 'package:aitrainer_app/widgets/dialog_html.dart'; +import 'package:aitrainer_app/widgets/menu_image.dart'; +import 'package:badges/badges.dart'; +import 'package:extended_tabs/extended_tabs.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; + +class TrainingPlanExecute extends StatefulWidget { + const TrainingPlanExecute({Key? key}) : super(key: key); + + @override + _TrainingPlanExecuteState createState() => _TrainingPlanExecuteState(); +} + +class _TrainingPlanExecuteState extends State with Trans { + @override + Widget build(BuildContext context) { + final TrainingPlanBloc bloc = BlocProvider.of(context); + bloc.activateDays(); + + setContext(context); + return Scaffold( + appBar: AppBarNav(depth: 0), + body: Container( + padding: EdgeInsets.all(0), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_black_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + child: BlocConsumer(listener: (context, state) { + if (state is TrainingPlanError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(backgroundColor: Colors.orange, content: Text(state.message, style: TextStyle(color: Colors.white)))); + } else if (state is TrainingPlanDayFinished) { + bloc.celebrating = false; + final HashMap args = HashMap(); + args["bloc"] = bloc; + args["day"] = bloc.dayNames[bloc.activeDayIndex]; + Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args); + } else if (state is TrainingPlanDayReadyToRestart) { + if (!bloc.celebrating) { + showCupertinoDialog( + useRootNavigator: true, + context: context, + builder: (_) => CupertinoAlertDialog( + title: Text(t("The training is finished")), + content: Column(children: [Divider(), Text(t("Do you want to restart, or select a new Training Plan?"))]), + actions: [ + TextButton( + child: Text(t("New Training Plan"), textAlign: TextAlign.center), + onPressed: () => { + Navigator.pop(context), + Navigator.of(context).popAndPushNamed('myTrainingPlans'), + bloc.restarting = false, + }), + TextButton( + child: Text(t("Restart")), + onPressed: () { + bloc.restart(); + Navigator.pop(context); + Navigator.of(context).popAndPushNamed('home'); + }, + ) + ], + )); + } + } + }, builder: (context, state) { + return ModalProgressHUD( + child: ExerciseTabs(bloc: bloc), + inAsyncCall: state is TrainingPlanLoading, + opacity: 0.5, + color: Colors.black54, + progressIndicator: CircularProgressIndicator(), + ); + }), + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + final HashMap args = HashMap(); + args["bloc"] = bloc; + args["day"] = bloc.dayNames[bloc.activeDayIndex]; + bloc.getNext() != null + ? _ExerciseListState.executeExercise(bloc, bloc.getNext()!, context) + : Navigator.of(context).pushNamed('myTrainingEvaluation', arguments: args); + }, + backgroundColor: Colors.orange[600], //Color(0xffb4f500), + icon: Icon( + CustomIcon.weight_hanging, + color: Colors.black, + ), + label: Text( + t("Next Exercise"), + style: GoogleFonts.inter(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + ); + } +} + +class ExerciseTabs extends StatefulWidget { + final TrainingPlanBloc bloc; + ExerciseTabs({required this.bloc}); + @override + _ExerciseTabs createState() => _ExerciseTabs(); +} + +class _ExerciseTabs extends State with TickerProviderStateMixin { + late TabController tabController; + + @override + void initState() { + super.initState(); + tabController = TabController(length: widget.bloc.dayNames.length, vsync: this); + tabController.animateTo(widget.bloc.activeDayIndex, duration: Duration(milliseconds: 300)); + } + + @override + void dispose() { + tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return getTabs(widget.bloc); + } + + Widget getTabs(TrainingPlanBloc bloc) { + return Column(children: [ + Text( + bloc.getMyPlan()!.name!, + style: GoogleFonts.archivoBlack( + fontSize: 14, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black87, + ), + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black87, + ), + ], + ), + ), + ExtendedTabBar( + indicator: BoxDecoration( + color: Colors.black87, + border: Border( + bottom: BorderSide(width: 4.0, color: Color(0xffb4f500)), + // top: BorderSide(width: 4.0, color: Colors.blue), + )), + labelPadding: EdgeInsets.only(left: 0, right: 0), + tabs: getTabNames(), + controller: tabController, + onTap: (index) => bloc.activeDayIndex = index, + ), + Expanded( + child: ExtendedTabBarView( + children: getExerciseLists(), + controller: tabController, + + /// if link is true and current tabbarview over scroll, + /// it will check and scroll ancestor or child tabbarView. + link: true, + + /// cache page count + /// default is 0. + /// if cacheExtent is 1, it has two pages in cache + /// null is infinity, it will cache all pages + cacheExtent: 0, + )), + ]); + } + + List getTabNames() { + List tabs = []; + final int tabCount = widget.bloc.dayNames.length; + double cWidth = MediaQuery.of(context).size.width; + widget.bloc.dayNames.forEach((element) { + final Widget widget = Container( + //height: 40, + padding: EdgeInsets.only(top: 3, left: 10, right: 10, bottom: 3), + width: (cWidth / tabCount), + color: Colors.white10, + child: RichText( + textScaleFactor: 0.8, + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + children: [ + TextSpan( + text: AppLocalizations.of(context)!.translate("Training Day") + ": \n", + style: GoogleFonts.inter( + fontSize: 14, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + TextSpan( + text: element, + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Color(0xffb4f500), + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + )), + ]))); + + tabs.add(Tab(child: widget)); + }); + return tabs; + } + + List getExerciseLists() { + List list = []; + widget.bloc.dayNames.forEach((element) { + list.add(ExerciseList(bloc: widget.bloc, dayName: element)); + }); + return list; + } +} + +class ExerciseList extends StatefulWidget { + final TrainingPlanBloc bloc; + final String dayName; + ExerciseList({required this.bloc, required this.dayName}); + + @override + _ExerciseListState createState() => _ExerciseListState(); +} + +class _ExerciseListState extends State with Trans { + final scrollController = ScrollController(); + double offset = 5; + + @override + void initState() { + WidgetsBinding.instance!.addPostFrameCallback((_) { + animate(); + }); + super.initState(); + } + + @override + void didUpdateWidget(ExerciseList page) { + super.didUpdateWidget(page); + WidgetsBinding.instance!.addPostFrameCallback((_) { + animate(); + }); + } + + void animate() { + offset = widget.bloc.getOffset(); + if (scrollController.hasClients) { + scrollController.animateTo(offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn); + } + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + static void executeExercise(TrainingPlanBloc bloc, CustomerTrainingPlanDetails detail, BuildContext context) { + CustomerTrainingPlanDetails? next = bloc.getNext(); + + if (next != null) { + String title = ""; + String description = ""; + String description2 = ""; + if (next.exerciseTypeId != detail.exerciseTypeId) { + title = AppLocalizations.of(context)!.translate("Stop!"); + description = AppLocalizations.of(context)!.translate("Please continue with the next exercise in the queue:") + + next.exerciseType!.nameTranslation; + } else { + final HashMap args = HashMap(); + args['exerciseType'] = next.exerciseType; + args['customerTrainingPlanDetails'] = detail; + Navigator.of(context).pushNamed('myTrainingPlanExercise', arguments: args); + return; + } + + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return DialogCommon( + title: title, + descriptions: description, + description2: description2, + text: "OK", + onTap: () => {Navigator.of(context).pop()}, + onCancel: () => {Navigator.of(context).pop()}, + ); + }); + } else { + Navigator.of(context).pushNamed('home'); + } + } + + @override + Widget build(BuildContext context) { + setContext(context); + return CustomScrollView(controller: scrollController, slivers: [ + SliverList(delegate: SliverChildListDelegate(getTiles(widget.bloc))), + ]); + } + + List getTiles(TrainingPlanBloc bloc) { + List tiles = []; + + tiles.addAll(getExerciseTiles(bloc, context)); + + return tiles; + } + + List getExerciseTiles(TrainingPlanBloc bloc, BuildContext context) { + List tiles = []; + CustomerTrainingPlanDetails? prev; + if (bloc.getMyPlan() != null && + bloc.getMyPlan()!.details.isNotEmpty && + bloc.getMyPlan()!.days[widget.dayName] != null && + bloc.getMyPlan()!.days[widget.dayName]!.isNotEmpty) { + bloc.getMyPlan()!.days[widget.dayName]!.forEach((element) { + if (prev != null && prev!.exerciseTypeId != element.exerciseTypeId) { + tiles.add(GestureDetector( + onTap: () => + bloc.getNext() != null ? executeExercise(bloc, bloc.getNext()!, context) : Navigator.of(context).pushNamed('home'), + child: ExerciseTile(bloc: bloc, detail: element))); + } + prev = element; + }); + } + + return tiles; + } +} + +// ignore: must_be_immutable +class ExerciseTile extends StatelessWidget with Trans { + final TrainingPlanBloc bloc; + final CustomerTrainingPlanDetails detail; + + ExerciseTile({required this.bloc, required this.detail}); + + Widget getExerciseQuantities(CustomerTrainingPlanDetails detail, int step, bool noFilter) { + bool skipped = detail.state == ExercisePlanDetailState.skipped; + String quantities = ""; + String set = ""; + List spans = []; + if (detail.exerciseType!.name == "Warming Up") { + quantities = t("Min. 10 minutes"); + spans.add( + TextSpan(text: quantities), + ); + } else if (detail.exerciseType!.name == "Stretching") { + quantities = t("Recommended"); + spans.add( + TextSpan(text: quantities), + ); + } else { + set = detail.set! > 1 ? "${detail.set} " + t("set") : ""; + List details = bloc.getAllDetailsSameExercise(detail); + int index = 0; + bool isWeight = true; + + if (set.length > 0) { + spans.add( + TextSpan( + text: step.toString() + " ", + style: GoogleFonts.archivoBlack( + color: Colors.orange[600], + )), + ); + spans.add( + TextSpan(text: "/ " + set), + ); + } + + details.forEach((element) { + quantities = ""; + String delimiter = ","; + if (index == 0) { + delimiter = ""; + } + if (element.repeats == -1) { + quantities += delimiter + " MAX "; + } else { + quantities += delimiter + " ${element.repeats}"; + } + if (element.exerciseType!.unitQuantityUnit != null) { + quantities += "x"; + if (element.weight == -1 || element.weight == -2 || element.weight == -3) { + quantities += "? kg"; + } else { + num weight = element.weight! % element.weight!.round() == 0 ? element.weight!.round() : element.weight!; + quantities += "$weight kg"; + } + } else { + isWeight = false; + } + if (step == index && noFilter) { + spans.add( + TextSpan( + text: quantities, + style: GoogleFonts.archivoBlack( + color: Colors.orange[600], + ), + ), + ); + } else { + spans.add( + TextSpan(text: quantities), + ); + } + index++; + }); + if (isWeight) { + quantities += " kg"; + } + } + + return RichText( + text: TextSpan( + style: GoogleFonts.archivoBlack( + fontSize: 20, + color: skipped ? Colors.grey : Colors.white, + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black87, + ), + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 6.0, + color: Colors.black87, + ), + ], + ), + children: spans), + ); + } + + @override + Widget build(BuildContext context) { + setContext(context); + + final CustomerTrainingPlanDetails? next = bloc.getNext(); + final bool noFilter = next != null && next.exerciseTypeId == detail.exerciseTypeId; + final bool done = detail.state == ExercisePlanDetailState.finished; + final int step = bloc.getStep(detail); //detail.exercises.length; + final bool buddyWarning = detail.exerciseType == null ? false : detail.exerciseType!.buddyWarning; + + return Container( + child: Stack(alignment: Alignment.bottomLeft, children: [ + Badge( + elevation: 0, + padding: EdgeInsets.all(0), + position: BadgePosition.topEnd(top: 5, end: 5), + animationDuration: Duration(milliseconds: 1500), + animationType: BadgeAnimationType.fade, + badgeColor: Colors.transparent, + showBadge: noFilter || done, + badgeContent: IconButton( + iconSize: 40, + onPressed: () => !done ? skip() : {}, + icon: Icon( + done ? CustomIcon.ok_circled : Icons.cancel, + color: done ? Colors.green[600] : Colors.red[600], + )), + child: Badge( + elevation: 0, + padding: EdgeInsets.all(0), + position: BadgePosition.topStart(top: 5, start: 5), + animationDuration: Duration(milliseconds: 500), + animationType: BadgeAnimationType.slide, + badgeColor: Colors.transparent, + showBadge: true, + badgeContent: IconButton( + iconSize: 36, + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return DialogHTML( + title: detail.exerciseType!.nameTranslation, + htmlData: '

' + detail.exerciseType!.descriptionTranslation + '

'); + }), + icon: Icon( + Icons.info_outline, + color: Colors.yellow[200], + )), + child: Badge( + elevation: 0, + padding: EdgeInsets.all(0), + position: BadgePosition.topEnd(top: 65, end: 1), + animationDuration: Duration(milliseconds: 500), + animationType: BadgeAnimationType.fade, + badgeColor: Colors.transparent, + showBadge: buddyWarning, + badgeContent: IconButton( + iconSize: 50, + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return DialogCommon( + warning: true, + text: "Warning", + descriptions: t("Attention!"), + description2: t("The safe and exact execution of this exercise you need a training buddy or a trainer"), + description3: t("Execution at your own risk!"), + onTap: () => Navigator.of(context).pop(), + onCancel: () => Navigator.of(context).pop(), + title: t('Training Buddy'), + ); + }), + icon: Icon( + CustomIcon.exclamation_circle, + color: Colors.red[800], + )), + child: Column(children: [ + MenuImage( + imageName: bloc.getActualImageName(detail.exerciseType!.exerciseTypeId), + workoutTreeId: bloc.getActualWorkoutTreeId(detail.exerciseType!.exerciseTypeId)!, + radius: 0, + filter: !noFilter, + ), + Container( + padding: EdgeInsets.only(left: 20, top: 10, bottom: 0), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('asset/image/WT_zold.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + foregroundDecoration: !noFilter + ? BoxDecoration( + color: Colors.black38, + backgroundBlendMode: BlendMode.darken, + ) + : null, + height: 80, + width: double.infinity, + child: getExerciseQuantities(detail, step, noFilter), + ) + ])))), + Container( + padding: EdgeInsets.only(left: 15, bottom: 80, right: 15), + width: double.infinity, + color: Colors.transparent, + child: Text( + detail.exerciseType!.nameTranslation, + maxLines: 3, + style: GoogleFonts.archivoBlack( + color: noFilter ? Colors.white : Colors.grey, + fontSize: 36, + height: 1.1, + shadows: [ + Shadow( + offset: Offset(16.0, 16.0), + blurRadius: 16.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(16.0, 16.0), + blurRadius: 16.0, + color: Colors.black54, + ), + ], + ), + ), + ), + ]), + ); + } + + void skip() { + showCupertinoDialog( + useRootNavigator: true, + context: context, + builder: (_) => CupertinoAlertDialog( + title: Text(t("You want to skip really this exercise?")), + content: Column(children: [ + Divider(), + ]), + actions: [ + TextButton( + child: Text(t("No")), + onPressed: () => { + Navigator.pop(context), + }), + TextButton( + child: Text(t("Yes")), + onPressed: () { + Navigator.pop(context); + bloc.add(TrainingPlanSkipExercise(detail: detail)); + }, + ) + ], + )); + } +} diff --git a/lib/widgets/dialog_trial.dart b/lib/widgets/dialog_trial.dart new file mode 100644 index 0000000..e3a3f8b --- /dev/null +++ b/lib/widgets/dialog_trial.dart @@ -0,0 +1,170 @@ +import 'package:aitrainer_app/util/trans.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +// ignore: must_be_immutable +class DialogTrialWidget extends StatefulWidget { + final String title, description; + final Widget widget; + final VoidCallback onTap; + final VoidCallback? onCancel; + + DialogTrialWidget({Key? key, required this.title, required this.description, required this.widget, required this.onTap, this.onCancel}) + : super(key: key); + + @override + _DialogTrialWidgetState createState() { + return _DialogTrialWidgetState(); + } +} + +class _DialogTrialWidgetState extends State with Trans { + @override + Widget build(BuildContext context) { + setContext(context); + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(31), + ), + elevation: 0, + backgroundColor: Colors.transparent, + child: contentBox(context), + ); + } + + contentBox(context) { + return Stack(alignment: AlignmentDirectional.topStart, children: [ + Stack( + children: [ + Container( + padding: EdgeInsets.only(left: 20, top: 24, right: 20, bottom: 30), + margin: EdgeInsets.only(top: 30), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24), + boxShadow: [BoxShadow(color: Colors.black, offset: Offset(0, 10), blurRadius: 10)], + image: DecorationImage( + image: AssetImage('asset/image/WT_black_G_background.jpg'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 5, + ), + Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Text( + widget.title, + textAlign: TextAlign.center, + style: GoogleFonts.archivoBlack( + fontSize: 20, + color: Colors.yellow[400], + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + ), + ], + ), + SizedBox( + height: 35, + ), + Text( + widget.description, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + textAlign: TextAlign.center, + ), + SizedBox( + height: 15, + ), + widget.widget, + SizedBox( + height: 52, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: widget.onCancel, + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset('asset/icon/gomb_lila_b.png', width: 100, height: 45), + Text( + t("Nem"), + style: TextStyle(fontSize: 16, color: Colors.white), + ), + ], + )), + GestureDetector( + onTap: widget.onTap, + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset('asset/icon/gomb_orange_c.png', width: 100, height: 45), + Text( + t("Igen"), + style: TextStyle(fontSize: 16, color: Colors.white), + ), + ], + )) + ], + ), + ], + ), + ), + ], + ), + GestureDetector( + onTap: () { + if (widget.onCancel == null) { + Navigator.of(context).pop(); + } else { + widget.onCancel!(); + } + }, + child: CircleAvatar( + backgroundColor: Colors.transparent, + radius: 28, + child: Text( + "X", + style: GoogleFonts.archivoBlack(fontSize: 32, color: Colors.white54), + ), + )), + ]); + } + + @override + void dispose() { + super.dispose(); + } +} diff --git a/lib/widgets/exercise_save.dart b/lib/widgets/exercise_save.dart index d0d0be3..f715382 100644 --- a/lib/widgets/exercise_save.dart +++ b/lib/widgets/exercise_save.dart @@ -98,7 +98,7 @@ class _ExerciseSaveState extends State with Trans { } SchedulerBinding.instance!.addPostFrameCallback((_) { - final TutorialBloc bloc = BlocProvider.of(context); + /* //final TutorialBloc bloc = BlocProvider.of(context); if (bloc.actualCheck == "directTest") { Timer( Duration(milliseconds: 2000), @@ -116,7 +116,7 @@ class _ExerciseSaveState extends State with Trans { ); }) }); - } + } */ }); } diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index d8ed6ec..3a0e4c1 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -4,10 +4,11 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/view/customer_goal_page.dart'; import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/menu_page.dart'; -import 'package:aitrainer_app/view/registration.dart'; import 'package:aitrainer_app/widgets/dialog_common.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -37,6 +38,15 @@ class _HomePageState extends State with Logging, Trans { SchedulerBinding.instance!.addPostFrameCallback((_) { runDelayedEvent(); }); + + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + print('-- FirebaseMessaging: Got a message whilst in the foreground!'); + print('-- FirebaseMessaging: Message data: ${message.data}'); + + if (message.notification != null) { + print('-- FirebaseMessaging: Message also contained a notification: ${message.notification}'); + } + }); } Future runDelayedEvent() async { @@ -58,6 +68,7 @@ class _HomePageState extends State with Logging, Trans { final appcastURL = "https://raw.githubusercontent.com/bossanyit/appcast/main/android_rss.xml"; final cfg = AppcastConfiguration(url: appcastURL, supportedOS: ['android']); print("Packageinfo ${Cache().packageInfo}"); + return Scaffold( key: _scaffoldKey, body: UpgradeAlert( @@ -101,7 +112,7 @@ class _HomePageState extends State with Logging, Trans { if (Cache().startPage == 'login') { return LoginPage(); } else if (Cache().startPage == 'registration') { - return RegistrationPage(); + return CustomerGoalPage(); } else { return MenuPage(parent: 0); } diff --git a/lib/widgets/menu_image.dart b/lib/widgets/menu_image.dart index 3fc511f..da4d1bd 100644 --- a/lib/widgets/menu_image.dart +++ b/lib/widgets/menu_image.dart @@ -3,16 +3,19 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:aitrainer_app/library/image_cache.dart' as wt; import 'package:aitrainer_app/library/transparent_image.dart'; +import 'package:flutter_html/shims/dart_ui_real.dart'; // ignore: must_be_immutable class MenuImage extends StatelessWidget { final int? workoutTreeId; final String imageName; + bool filter; double radius; MenuImage({ required this.workoutTreeId, required this.imageName, this.radius = 24, + this.filter = false, }); @override @@ -23,10 +26,16 @@ class MenuImage extends StatelessWidget { String? imageString = this.getImage(workoutTreeId!, imageName); Widget? widget; if (imageString != null) { + print("MemoryImage $workoutTreeId - $imageName"); widget = ClipRRect( borderRadius: BorderRadius.circular(24.0), child: Container( - color: Colors.transparent, + padding: EdgeInsets.zero, + color: Colors.black38, + foregroundDecoration: BoxDecoration( + color: Colors.black38, + backgroundBlendMode: BlendMode.darken, + ), child: FadeInImage( fadeInDuration: Duration(milliseconds: 100), image: MemoryImage(base64Decode(imageString)), @@ -35,10 +44,18 @@ class MenuImage extends StatelessWidget { )); } else { if (imageName.contains("https")) { + print("https image $workoutTreeId - $imageName"); if (!wt.ImageCache().existsImageInMap(workoutTreeId!, imageName)) { widget = ClipRRect( borderRadius: BorderRadius.circular(radius), child: Container( + padding: EdgeInsets.zero, + foregroundDecoration: filter + ? BoxDecoration( + color: Colors.black38, + backgroundBlendMode: BlendMode.darken, + ) + : null, color: Colors.transparent, child: FadeInImage( fadeInDuration: Duration(milliseconds: 500), @@ -51,7 +68,14 @@ class MenuImage extends StatelessWidget { widget = ClipRRect( borderRadius: BorderRadius.circular(radius), child: Container( + padding: EdgeInsets.zero, color: Colors.transparent, + foregroundDecoration: filter + ? BoxDecoration( + color: Colors.black38, + backgroundBlendMode: BlendMode.darken, + ) + : null, child: Image.asset(imageName), )); } diff --git a/lib/widgets/menu_page_widget.dart b/lib/widgets/menu_page_widget.dart index 6401e2b..b69cf1b 100644 --- a/lib/widgets/menu_page_widget.dart +++ b/lib/widgets/menu_page_widget.dart @@ -1,11 +1,12 @@ import 'dart:collection'; import 'dart:ui'; import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart'; -import 'package:aitrainer_app/bloc/tutorial/tutorial_bloc.dart'; import 'package:aitrainer_app/model/exercise_ability.dart'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; +import 'package:aitrainer_app/repository/training_plan_repository.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/track.dart'; +import 'package:aitrainer_app/widgets/dialog_trial.dart'; import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:aitrainer_app/widgets/menu_search_bar.dart'; import 'package:aitrainer_app/util/app_language.dart'; @@ -15,9 +16,9 @@ import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/dialog_common.dart'; -import 'package:aitrainer_app/widgets/tutorial_widget.dart'; import 'package:badges/badges.dart'; import 'package:ezanimation/ezanimation.dart'; +import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; @@ -39,7 +40,7 @@ class _MenuPageWidgetState extends State with Trans, Logging { final double baseWidth = 312; final double baseHeight = 675.2; late MenuBloc menuBloc; - late TutorialBloc tutorialBloc; + //late TutorialBloc tutorialBloc; final scrollController = ScrollController(); final bool activeExercisePlan = Cache().activeExercisePlan != null; final EzAnimation animation = EzAnimation(35.0, 10.0, Duration(seconds: 2), reverseCurve: Curves.linear); @@ -59,41 +60,11 @@ class _MenuPageWidgetState extends State with Trans, Logging { /// We require the initializers to run after the loading screen is rendered SchedulerBinding.instance!.addPostFrameCallback((_) { menuBloc.add(MenuCreate()); - //runDelayedEvent(); }); super.initState(); } - Future runDelayedEvent() async { - bool isFirst = false; - await Future.delayed(Duration(milliseconds: 600), () async { - 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) { - tutorialBloc.canActivate = true; - tutorialBloc.isActive = true; - tutorialBloc.menuBloc = menuBloc; - tutorialBloc.add(TutorialLoad()); - tutorialBloc.init(); - isFirst = true; - } - } - }); - final bool canActivate = tutorialBloc.activateTutorial(); - if (canActivate) { - if (!isFirst) { - TutorialWidget().tip(context); - } - } - } - @override bool didUpdateWidget(MenuPageWidget oldWidget) { super.didUpdateWidget(oldWidget); @@ -102,10 +73,68 @@ class _MenuPageWidgetState extends State with Trans, Logging { return true; } + Future runDelayedEvent() async { + await Future.delayed(Duration(milliseconds: 3000), () async { + if (Cache().userLoggedIn != null) { + await initDynamicLinks(); + } + if (Cache().canTrial()) { + showDialog( + context: context, + builder: (BuildContext context) { + return DialogTrialWidget( + title: "10 days Premium for free", + description: "Would you like to try all premium functions for 10 days, without any subscription or bank card data?", + widget: Column(children: [ + Text( + "If you click to 'Yes', all premium functions will be available right now.", + style: GoogleFonts.inter(color: Colors.white), + ), + Divider(), + Text( + "If you click to 'No', you can use all basic functions, and you will loose the oppurtunity to try the premium functions for free.", + style: GoogleFonts.inter(color: Colors.white), + ), + ]), + onCancel: () => { + Navigator.of(context).pop(), + menuBloc.add(MenuStartTrial(start: DateTime.parse("1900-01-01 00:00:00"))), + }, + onTap: () => {Navigator.of(context).pop(), menuBloc.add(MenuStartTrial(start: DateTime.now()))}, + ); + }); + } + }); + } + + Future initDynamicLinks() async { + FirebaseDynamicLinks.instance.onLink(onSuccess: (PendingDynamicLinkData? dynamicLink) async { + final Uri? deepLink = dynamicLink?.link; + print("DeepLink: $deepLink"); + if (deepLink != null) { + // ignore: unawaited_futures + final String deepLinkPath = deepLink.path.replaceFirst("/", ""); + Navigator.pushNamed(context, deepLinkPath); + } + }, onError: (OnLinkErrorException e) async { + print('onLinkError'); + print(e.message); + }); + + final PendingDynamicLinkData? data = await FirebaseDynamicLinks.instance.getInitialLink(); + final Uri? deepLink = data?.link; + print("Pending DeepLink: $deepLink"); + if (deepLink != null) { + // ignore: unawaited_futures + final String deepLinkPath = deepLink.path.replaceFirst("/", ""); + Navigator.pushNamed(context, deepLinkPath); + } + } + @override Widget build(BuildContext context) { menuBloc = BlocProvider.of(context); - tutorialBloc = BlocProvider.of(context); + //tutorialBloc = BlocProvider.of(context); setContext(context); double cWidth = MediaQuery.of(context).size.width; double cHeight = MediaQuery.of(context).size.height; @@ -181,6 +210,10 @@ class _MenuPageWidgetState extends State with Trans, Logging { }); } + _columnChildren.add(SizedBox( + height: 50, + )); + SliverList sliverList = SliverList( delegate: SliverChildListDelegate(_columnChildren), ); @@ -292,7 +325,7 @@ class _MenuPageWidgetState extends State with Trans, Logging { } }, ), - Cache().myTrainingPlan != null + /* Cache().myTrainingPlan != null ? GestureDetector( onTap: () => showDialog( context: context, @@ -326,7 +359,7 @@ class _MenuPageWidgetState extends State with Trans, Logging { ), ); })) - : Offstage(), + : Offstage(), */ /* activeExercisePlan ? SizedBox( width: 10, @@ -376,24 +409,59 @@ class _MenuPageWidgetState extends State with Trans, Logging { } void menuClick(WorkoutMenuTree workoutTree, MenuBloc menuBloc) { - if (tutorialBloc.isActive) { + /* if (tutorialBloc.isActive) { final String checkText = workoutTree.nameEnglish; if (!tutorialBloc.checkAction(checkText)) { return; } - } + } */ + print("ability: ${menuBloc.ability} tree: $workoutTree parent: ${workoutTree.parent}"); + if (workoutTree.child == false) { if (menuBloc.ability != null && ExerciseAbility.mini_test_set.equalsTo(menuBloc.ability!) && workoutTree.parent != 0) { HashMap args = HashMap(); args['templateName'] = workoutTree.nameEnglish; args['templateNameTranslation'] = workoutTree.name; Navigator.of(context).pushNamed('testSetEdit', arguments: args); + menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id)); } else if (menuBloc.ability != null && ExerciseAbility.training.equalsTo(menuBloc.ability!) && workoutTree.parent != 0) { HashMap args = HashMap(); args['parentName'] = workoutTree.internalName; Navigator.of(context).pushNamed("myTrainingPlanActivate", arguments: args); + menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id)); + } else if (workoutTree.internalName == "training_execute") { + /* Cache().myTrainingPlan = null; + Cache().deleteMyTrainingPlan(); */ + if (Cache().myTrainingPlan != null) { + final TrainingPlanBloc bloc = BlocProvider.of(context); + bloc.setMyPlan(Cache().myTrainingPlan); + Navigator.of(context).pushNamed("myTrainingPlanExecute"); + } else { + showDialog( + context: context, + builder: (BuildContext context) { + return DialogCommon( + title: t("No selected Training Plan"), + descriptions: t("Based on your initial data, we will generate the personalized training plan for you."), + text: "OK", + onTap: () { + TrainingPlanRepository trainingPlanRepository = TrainingPlanRepository(); + trainingPlanRepository.generateTrainingPlan(); + final TrainingPlanBloc bloc = BlocProvider.of(context); + bloc.setMyPlan(Cache().myTrainingPlan); + Future.delayed(Duration(milliseconds: 1000), () async { + Navigator.of(context).pushNamed("myTrainingPlanExecute"); + }); + }, + onCancel: () => { + Navigator.of(context).pop(), + }, + ); + }); + } + } else { + menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id)); } - menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id)); } else { menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id)); diff --git a/lib/widgets/number_picker.dart b/lib/widgets/number_picker.dart index ce0ec8a..3a2b26f 100644 --- a/lib/widgets/number_picker.dart +++ b/lib/widgets/number_picker.dart @@ -11,6 +11,9 @@ class NumberPickerWidget extends StatefulWidget { final String unit; final Color color; double? fontSize; + double? diameterRatio; + double? itemExtent; + FontWeight? fontWeight; NumberPickerWidget( {Key? key, @@ -19,10 +22,16 @@ class NumberPickerWidget extends StatefulWidget { required this.initalValue, required this.unit, this.fontSize, + this.diameterRatio, + this.itemExtent, + this.fontWeight, required this.color, required this.onChange}) : super(key: key) { fontSize = fontSize ?? 20; + diameterRatio = diameterRatio ?? 0.85; + itemExtent = itemExtent ?? 30; + fontWeight = fontWeight ?? FontWeight.normal; } @override _NumberPickerWidgetState createState() => _NumberPickerWidgetState(); @@ -59,7 +68,7 @@ class _NumberPickerWidgetState extends State with Trans { scrollController: _scrollController, useMagnifier: true, magnification: 1.2, - diameterRatio: 0.85, + diameterRatio: widget.diameterRatio!, backgroundColor: Colors.transparent, selectionOverlay: Container(), onSelectedItemChanged: (x) { @@ -72,8 +81,10 @@ class _NumberPickerWidgetState extends State with Trans { widget.onChange(value); }, children: List.generate( - widget.maxValue, (index) => Text('$index ' + widget.unit, style: TextStyle(color: widget.color, fontSize: widget.fontSize))), - itemExtent: 30, + widget.maxValue, + (index) => Text('$index ' + widget.unit, + style: TextStyle(color: widget.color, fontSize: widget.fontSize, fontWeight: widget.fontWeight))), + itemExtent: widget.itemExtent!, ); } diff --git a/pubspec.lock b/pubspec.lock index 401fb23..84233de 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -43,13 +43,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.6.1" - awesome_notifications: - dependency: "direct main" - description: - name: awesome_notifications - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.6+9" badges: dependency: "direct main" description: @@ -70,7 +63,7 @@ packages: name: bloc_test url: "https://pub.dartlang.org" source: hosted - version: "8.0.0" + version: "8.1.0" boolean_selector: dependency: transitive description: @@ -294,7 +287,7 @@ packages: name: equatable url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" extended_tabs: dependency: "direct main" description: @@ -343,7 +336,7 @@ packages: name: firebase_analytics url: "https://pub.dartlang.org" source: hosted - version: "8.1.0" + version: "8.3.0" firebase_analytics_platform_interface: dependency: transitive description: @@ -364,28 +357,28 @@ packages: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "3.0.2" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.2.3" + version: "6.0.1" firebase_auth_web: dependency: transitive description: name: firebase_auth_web url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "3.0.1" firebase_core: dependency: "direct main" description: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.5.0" firebase_core_platform_interface: dependency: transitive description: @@ -400,41 +393,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + firebase_dynamic_links: + dependency: "direct main" + description: + name: firebase_dynamic_links + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + firebase_in_app_messaging: + dependency: "direct main" + description: + name: firebase_in_app_messaging + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0+8" firebase_messaging: dependency: "direct main" description: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "10.0.0" + version: "10.0.5" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.4" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.4" firebase_remote_config: dependency: "direct main" description: name: firebase_remote_config url: "https://pub.dartlang.org" source: hosted - version: "0.10.0" + version: "0.10.0+4" 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" + version: "0.3.0+4" fixnum: dependency: transitive description: @@ -474,28 +481,28 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "7.1.0" flutter_facebook_auth: dependency: "direct main" description: name: flutter_facebook_auth url: "https://pub.dartlang.org" source: hosted - version: "3.4.0" + version: "3.5.1" flutter_facebook_auth_platform_interface: dependency: transitive description: name: flutter_facebook_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.6.0" + version: "2.7.0" flutter_facebook_auth_web: dependency: transitive description: name: flutter_facebook_auth_web url: "https://pub.dartlang.org" source: hosted - version: "2.6.0" + version: "2.6.0+2" flutter_fadein: dependency: "direct main" description: @@ -561,7 +568,7 @@ packages: name: flutter_uxcam url: "https://pub.dartlang.org" source: hosted - version: "2.0.0-beta.1" + version: "2.0.0" flutter_web_plugins: dependency: transitive description: flutter @@ -1285,7 +1292,7 @@ packages: name: upgrader url: "https://pub.dartlang.org" source: hosted - version: "3.3.0" + version: "3.5.1" url_launcher: dependency: transitive description: @@ -1477,4 +1484,4 @@ packages: version: "3.1.0" sdks: dart: ">=2.12.0 <3.0.0" - flutter: ">=2.0.4" + flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 207f768..0594500 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.21+92 +version: 1.1.22+93 environment: sdk: ">=2.12.0 <3.0.0" @@ -28,8 +28,8 @@ dependencies: google_fonts: ^2.1.0 devicelocale: ^0.4.1 sentry_flutter: ^5.1.0-beta.1 - flutter_bloc: ^7.0.0 - equatable: ^2.0.2 + flutter_bloc: ^7.1.0 + equatable: ^2.0.3 spider_chart: ^0.1.5 rainbow_color: ^2.0.1 @@ -49,33 +49,34 @@ dependencies: purchases_flutter: ^3.2.2 package_info: ^2.0.0 ezanimation: ^0.5.0 - flutter_fadein: ^2.0.0 confetti: ^0.6.0-nullsafety crypto: ^3.0.0 carousel_slider: ^4.0.0-nullsafety.0 convex_bottom_bar: ^3.0.0 flutter_app_badger: ^1.2.0 extended_tabs: ^2.2.0 - upgrader: ^3.3.0 + upgrader: ^3.5.1 web_browser: ^0.5.0 - - firebase_core: ^1.2.0 + flutter_fadein: ^2.0.0 + + firebase_core: ^1.5.0 firebase_analytics: ^8.1.0 firebase_messaging: ^10.0.0 - firebase_auth: ^1.2.0 - firebase_remote_config: ^0.10.0 - awesome_notifications: ^0.0.6+9 + firebase_auth: ^3.0.2 + firebase_remote_config: ^0.10.0+4 + firebase_dynamic_links: ^2.0.8 + firebase_in_app_messaging: ^0.5.0+8 syncfusion_flutter_gauges: ^19.1.63 syncfusion_flutter_datagrid: ^19.1.63 - flutter_facebook_auth: ^3.4.0 + flutter_facebook_auth: ^3.5.1 google_sign_in: ^5.0.3 sign_in_with_apple: ^3.0.0 #smartlook: ^1.0.7 flurry_data: ^0.0.1 - flutter_uxcam: ^2.0.0-beta.1 + flutter_uxcam: ^2.0.0 animated_widgets: ^1.0.6 @@ -94,7 +95,7 @@ dev_dependencies: test: '>=1.0.0 <2.0.0' flutter_test: sdk: flutter - bloc_test: ^8.0.0 + bloc_test: ^8.1.0 build_runner: @@ -154,6 +155,7 @@ flutter: - asset/image/WT_Results_for_men.jpg - asset/image/WT_results_background.jpg - asset/image/WT_cup_victory400.png + - asset/image/WT_zold.jpg - asset/image/button_fb.png - asset/image/button_apple.png @@ -179,6 +181,7 @@ flutter: - asset/image/gain_strength.jpg - asset/image/muscle_endurance.jpg - asset/image/shape_forming.jpg + - asset/image/woman_sizes.png - asset/image/weight_loss.jpg - asset/image/merleg.png @@ -368,6 +371,7 @@ flutter: - asset/menu/situps.jpg - asset/menu/sizes.jpg - asset/menu/smith_machine_chest_press.jpg + - asset/menu/smith_machine_squats.jpg - asset/menu/squats_with_kettlebell.jpg - asset/menu/squat_jump_weight.jpg - asset/menu/squat_jump.jpg @@ -403,6 +407,7 @@ flutter: - asset/menu/training_plans_q_beginner.jpg - asset/menu/training_plans_q_advanced.jpg - asset/menu/training_plans_q_gain_strength.jpg + - asset/menu/training_start.jpg - asset/menu/triceps_extension_on_cable_with_rope.jpg - asset/menu/triceps_kickback.jpg - asset/menu/triceps_pushdown.jpg