diff --git a/.gitea/workflows/test-flight-cd.yaml b/.gitea/workflows/test-flight-cd.yaml
index c9649f0e..4736a47f 100644
--- a/.gitea/workflows/test-flight-cd.yaml
+++ b/.gitea/workflows/test-flight-cd.yaml
@@ -1,4 +1,4 @@
-name: "Build Test Flight"
+name: 'Build Test Flight'
on:
pull_request:
branches:
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
-
+
- name: Checkout env repository
uses: actions/checkout@v2
with:
@@ -39,8 +39,8 @@ jobs:
run: |
security unlock-keychain -p "$KEYCHAIN_PASSWORD" /Users/gitea_runner_ci/Library/Keychains/login.keychain-db
env:
- KEYCHAIN_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
-
+ KEYCHAIN_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
+
- name: Setup API key
run: |
echo "${API_APPSTORE_KEY}" | tr -d '\n' | base64 --decode > ios/AuthKey.p8
@@ -58,17 +58,19 @@ jobs:
flutter pub get
flutter pub upgrade
flutter pub run build_runner build --delete-conflicting-outputs
+ cd ios
+ pod repo update
+ pod update
# Configure Flutter for release mode
flutter build ios --config-only --release
- cd ios
- pod update
+
fastlane beta
env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
- MATCH_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
- FASTLANE_PASSWORD: ${{ vars.FASTLANE_PASSWORD }}
+ MATCH_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
+ FASTLANE_PASSWORD: ${{ vars.FASTLANE_PASSWORD }}
MATCH_GIT_URL: http://gitea.office.local:3000/mso7nueh/fastlane
FASTLANE_USER: eatvio2023@icloud.com
- FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ vars.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
+ FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ vars.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
API_APPSTORE_KEY: ${{ vars.API_APPSTORE_KEY }}
diff --git a/.gitignore b/.gitignore
index 4f05e3a1..afeb23f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,7 @@
*.mocks.dart
android/build/
ios/XCBuildData/
-env/.env
\ No newline at end of file
+env/.env
+.env
+android/key.properties
+app/upload-keystore.jks
\ No newline at end of file
diff --git a/ENV_SETUP.md b/ENV_SETUP.md
index 0873fb1c..092a3bd9 100644
--- a/ENV_SETUP.md
+++ b/ENV_SETUP.md
@@ -54,6 +54,15 @@ cp env.example .env
- `GOOGLE_CHECK_URL` - URL для проверки соединения (Google)
- `YANDEX_CHECK_URL` - URL для проверки соединения (Yandex)
+### VK ID Configuration
+- `VK_CLIENT_ID` - ID приложения VK (числовой)
+- **Примечание**: VK_CLIENT_ID - это числовой "ID приложения" из консоли VK ID, НЕ "Защищённый ключ" или "Сервисный ключ доступа"
+
+### Yandex ID Configuration
+- `YANDEX_CLIENT_ID` - ID приложения Yandex OAuth
+- `YANDEX_CLIENT_SECRET` - секретный ключ приложения Yandex OAuth
+- `YANDEX_REDIRECT_URI` - URI для перенаправления (должен совпадать с настройками в Yandex OAuth приложении)
+
## Безопасность
⚠️ **ВАЖНО**: Файл `.env` добавлен в `.gitignore` и не должен попадать в репозиторий. Все секретные данные должны храниться только в этом файле.
diff --git a/android/app/agconnect-services.json b/android/app/agconnect-services.json
new file mode 100644
index 00000000..8ca899db
--- /dev/null
+++ b/android/app/agconnect-services.json
@@ -0,0 +1 @@
+{"agcgw":{"url":"connect-drru.hispace.dbankcloud.ru","backurl":"connect-drru.hispace.dbankcloud.cn","websocketurl":"connect-ws-drru.hispace.dbankcloud.ru","websocketbackurl":"connect-ws-drru.hispace.dbankcloud.cn"},"agcgw_all":{"SG":"connect-dra.dbankcloud.cn","SG_back":"connect-dra.hispace.hicloud.com","CN":"connect-drcn.dbankcloud.cn","CN_back":"connect-drcn.hispace.hicloud.com","RU":"connect-drru.hispace.dbankcloud.ru","RU_back":"connect-drru.hispace.dbankcloud.cn","DE":"connect-dre.dbankcloud.cn","DE_back":"connect-dre.hispace.hicloud.com"},"websocketgw_all":{"SG":"connect-ws-dra.hispace.dbankcloud.cn","SG_back":"connect-ws-dra.hispace.dbankcloud.com","CN":"connect-ws-drcn.hispace.dbankcloud.cn","CN_back":"connect-ws-drcn.hispace.dbankcloud.com","RU":"connect-ws-drru.hispace.dbankcloud.ru","RU_back":"connect-ws-drru.hispace.dbankcloud.cn","DE":"connect-ws-dre.hispace.dbankcloud.cn","DE_back":"connect-ws-dre.hispace.dbankcloud.com"},"client":{"cp_id":"20007000036693451","product_id":"461323198429446894","client_id":"1595824543740483264","client_secret":"2A1727B0D31AA9BE1F5D9E8A5CDA1A67B9EF084AC9FB68D6C633A3CCB08ECDEE","project_id":"461323198429446894","app_id":"113238569","api_key":"DQEDAJNRITp9M3uc1rPHyT8PzLc7bnVcKJAeavZZk0TQNMx9ifvBebr+JOmZe9tEvjnLwv7c1hAHXmPGR/gWG86iABDrCVotq+NGnw==","package_name":"com.eatvioapp.app"},"oauth_client":{"client_id":"113238569","client_type":1},"app_info":{"app_id":"113238569","package_name":"com.eatvioapp.app"},"service":{"analytics":{"collector_url":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn","collector_url_ru":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn","resource_id":"p1","channel_id":""},"ml":{"mlservice_url":"ml-api-drru.ai.dbankcloud.ru,ml-api-drru.ai.dbankcloud.cn"},"cloudstorage":{"storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg":"https://ops-dra.agcstorage.link","storage_url_de":"https://ops-dre.agcstorage.link","storage_url_cn":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru_back":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg_back":"https://agc-storage-dra.cloud.huawei.asia","storage_url_de_back":"https://agc-storage-dre.cloud.huawei.eu","storage_url_cn_back":"https://agc-storage-drcn.cloud.huawei.com.cn"},"search":{"url":"https://search-drru.cloud.huawei.ru"},"edukit":{"edu_url":"edukit.cloud.huawei.com.cn","dh_url":"edukit.cloud.huawei.com.cn"}},"region":"RU","configuration_version":"3.0","appInfos":[{"package_name":"com.eatvioapp.app","client":{"app_id":"113238569"},"app_info":{"package_name":"com.eatvioapp.app","app_id":"113238569"},"oauth_client":{"client_type":1,"client_id":"113238569"}}]}
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 9ffa87c4..8fc22d6d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -5,6 +5,7 @@ plugins {
// END: FlutterFire Configuration
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
+ id 'com.huawei.agconnect'
}
def keystoreProperties = new Properties()
@@ -13,6 +14,8 @@ if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
+def yandexClientId = project.hasProperty('YANDEX_CLIENT_ID') ? project.property('YANDEX_CLIENT_ID') : (System.getenv('YANDEX_CLIENT_ID') ?: "")
+
android {
namespace = "com.eatvioapp.app"
compileSdk = flutter.compileSdkVersion
@@ -33,6 +36,10 @@ android {
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
+ manifestPlaceholders = [
+ YANDEX_CLIENT_ID: yandexClientId,
+ applicationName: "io.flutter.app.FlutterApplication"
+ ]
}
signingConfigs {
@@ -47,6 +54,9 @@ android {
buildTypes {
release {
signingConfig signingConfigs.release
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.debug
@@ -57,6 +67,15 @@ android {
}
}
+dependencies {
+ implementation 'com.huawei.agconnect:agconnect-core:1.9.1.301'
+ implementation 'com.huawei.hms:push:6.12.0.300'
+ // Huawei Analytics dependency to satisfy com.huawei.hianalytics.* references during R8
+ implementation 'com.huawei.hms:hianalytics:6.12.0.300'
+ // BouncyCastle provider required by Huawei secure utils used transitively by HMS
+ implementation 'org.bouncycastle:bcprov-jdk15to18:1.78.1'
+}
+
flutter {
source = "../.."
}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
new file mode 100644
index 00000000..b657fa16
--- /dev/null
+++ b/android/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+-keep class com.huawei.** { *; }
+-dontwarn com.huawei.**
+
+-keep class org.bouncycastle.** { *; }
+-dontwarn org.bouncycastle.**
+
+-keep class io.flutter.** { *; }
+-dontwarn io.flutter.**
+
+-keep class kotlinx.coroutines.** { *; }
+-dontwarn kotlinx.coroutines.**
+
+-keep class **.R$* { *; }
+-keepclassmembers class ** { @androidx.annotation.Keep *; }
+-keep @androidx.annotation.Keep class * { *; }
+
+-keep public class * extends android.app.Application
+-keep public class * extends android.content.ContentProvider
+
+-keep class * implements android.os.Parcelable { *; }
+-keepclassmembers enum * { *; }
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 0d239126..a99f3deb 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -11,6 +11,16 @@
android:usesCleartextTraffic="true"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
+
+
+
+
+
+
+ when (call.method) {
+ "getHuaweiToken" -> {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ // Try to read appId from agconnect-services.json
+ var appId: String? = try {
+ AGConnectServicesConfig.fromContext(applicationContext)
+ .getString("client/app_id")
+ } catch (_: Exception) {
+ null
+ }
+
+ // Fallback to manifest meta-data if needed
+ if (appId.isNullOrBlank()) {
+ try {
+ val pm: PackageManager = applicationContext.packageManager
+ val ai: ApplicationInfo = pm.getApplicationInfo(applicationContext.packageName, PackageManager.GET_META_DATA)
+ val raw = ai.metaData?.getString("com.huawei.hms.client.appid")
+ if (!raw.isNullOrBlank()) {
+ appId = raw.removePrefix("appid=")
+ }
+ } catch (_: Exception) {
+ // ignore and handle below
+ }
+ }
+
+ if (appId.isNullOrBlank()) {
+ runOnUiThread {
+ result.error("HMS_TOKEN_ERROR", "Missing Huawei appId. Ensure agconnect-services.json or manifest meta-data is configured.", null)
+ }
+ return@launch
+ }
+ val token = HmsInstanceId.getInstance(applicationContext)
+ .getToken(appId, "HCM")
+ runOnUiThread {
+ result.success(token)
+ }
+ } catch (e: Exception) {
+ runOnUiThread {
+ result.error("HMS_TOKEN_ERROR", e.message, null)
+ }
+ }
+ }
+ }
+ else -> result.notImplemented()
+ }
+ }
+ }
}
diff --git a/android/build.gradle b/android/build.gradle
index d2ffbffa..af06c34c 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,7 +1,21 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven {url 'https://developer.huawei.com/repo/'}
+ gradlePluginPortal()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.12.0'
+ classpath 'com.huawei.agconnect:agcp:1.9.1.301'
+ }
+}
+
allprojects {
repositories {
google()
mavenCentral()
+ maven {url 'https://developer.huawei.com/repo/'}
}
}
@@ -13,6 +27,32 @@ subprojects {
project.evaluationDependsOn(":app")
}
+// Temporary safeguard: set missing namespaces for Android library modules based on their manifest package
+subprojects { Project subproject ->
+ subproject.pluginManager.withPlugin('com.android.library') {
+ def androidExt = subproject.extensions.findByName('android')
+ if (androidExt != null) {
+ def hasNamespaceProp = androidExt.hasProperty('namespace')
+ def currentNamespace = hasNamespaceProp ? androidExt.namespace : null
+ if (hasNamespaceProp && !currentNamespace) {
+ def manifestFile = file("${subproject.projectDir}/src/main/AndroidManifest.xml")
+ if (manifestFile.exists()) {
+ try {
+ def manifest = new groovy.xml.XmlSlurper().parse(manifestFile)
+ def manifestPackage = manifest.@package?.text()
+ if (manifestPackage) {
+ androidExt.namespace = manifestPackage
+ subproject.logger.lifecycle("[namespace-fix] Set namespace for ${subproject.path} to '${manifestPackage}' from AndroidManifest.xml")
+ }
+ } catch (Throwable t) {
+ subproject.logger.warn("[namespace-fix] Failed to read manifest for ${subproject.path}: ${t.message}")
+ }
+ }
+ }
+ }
+ }
+}
+
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
index 4921e6e4..ce07f9e3 100644
--- a/android/build/reports/problems/problems-report.html
+++ b/android/build/reports/problems/problems-report.html
@@ -650,12 +650,12 @@ code + .copy-button {
diff --git a/android/settings.gradle b/android/settings.gradle
index e173b2c9..76846d0d 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -12,6 +12,7 @@ pluginManagement {
repositories {
google()
mavenCentral()
+ maven { url 'https://developer.huawei.com/repo/' }
gradlePluginPortal()
}
}
diff --git a/assets/icons/consultant_rating_icon.svg b/assets/icons/consultant_rating_icon.svg
new file mode 100644
index 00000000..493b4d95
--- /dev/null
+++ b/assets/icons/consultant_rating_icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/mail_logo.svg b/assets/icons/mail_logo.svg
new file mode 100644
index 00000000..5891b116
--- /dev/null
+++ b/assets/icons/mail_logo.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/icons/ok_logo.svg b/assets/icons/ok_logo.svg
new file mode 100644
index 00000000..07a62fbd
--- /dev/null
+++ b/assets/icons/ok_logo.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/vk_logo.svg b/assets/icons/vk_logo.svg
new file mode 100644
index 00000000..274b3486
--- /dev/null
+++ b/assets/icons/vk_logo.svg
@@ -0,0 +1,16 @@
+
diff --git a/assets/icons/yandex_logo.svg b/assets/icons/yandex_logo.svg
new file mode 100644
index 00000000..51b51eee
--- /dev/null
+++ b/assets/icons/yandex_logo.svg
@@ -0,0 +1,16 @@
+
diff --git a/integration_test/auth_screen/auth_login_test.dart b/integration_test/auth_screen/auth_login_test.dart
new file mode 100644
index 00000000..ce538770
--- /dev/null
+++ b/integration_test/auth_screen/auth_login_test.dart
@@ -0,0 +1,37 @@
+import 'package:eatvio_app/app_keys.dart';
+import 'package:eatvio_app/core/app_initializer.dart';
+import 'package:eatvio_app/main.dart' as app;
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import '../common_cases/skip_welcome_screen.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ setUpAll(() async {
+ await AppInitializer.initialize(buildType: AppBuildType.tests);
+ });
+
+ testWidgets('Signs in successfully with valid credentials', (WidgetTester tester) async {
+ await tester.pumpWidget(const app.MyApp());
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+ await skipWelcomeScreen(tester);
+ final emailFieldFinder = find.byKey(AppKeys.emailTextFieldAuthScreen);
+ final passwordFieldFinder = find.byKey(AppKeys.passwordTextFieldAuthScreen);
+ final signInButtonFinder = find.byKey(AppKeys.signInButtonAuthScreen);
+
+ expect(emailFieldFinder, findsOneWidget, reason: 'Email text field should be found.');
+ expect(passwordFieldFinder, findsOneWidget, reason: 'Password text field should be found.');
+ expect(signInButtonFinder, findsOneWidget, reason: 'Sign In button should be found.');
+
+ await tester.enterText(emailFieldFinder, 'reshetnick.mike+user4@gmail.com');
+ await tester.enterText(passwordFieldFinder, '1234567890');
+ await tester.pumpAndSettle();
+
+ await tester.tap(signInButtonFinder);
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ expect(signInButtonFinder, findsNothing, reason: 'Sign-in button should not be visible after successful login and navigation.');
+ });
+}
diff --git a/integration_test/auth_screen/auth_logo_button_test.dart b/integration_test/auth_screen/auth_logo_button_test.dart
new file mode 100644
index 00000000..c7ac598d
--- /dev/null
+++ b/integration_test/auth_screen/auth_logo_button_test.dart
@@ -0,0 +1,31 @@
+import 'package:eatvio_app/app_keys.dart';
+import 'package:eatvio_app/core/app_initializer.dart';
+import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:eatvio_app/main.dart' as app;
+import '../common_cases/skip_welcome_screen.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ setUpAll(() async {
+ await AppInitializer.initialize(buildType: AppBuildType.tests);
+ });
+
+ testWidgets('tapping logo 7 times opens dev tools', (WidgetTester tester) async {
+ await tester.pumpWidget(const app.MyApp());
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+ await skipWelcomeScreen(tester);
+ final logoButtonFinder = find.byKey(AppKeys.hiddenLogoButtonAuthScreen);
+ expect(logoButtonFinder, findsOneWidget, reason: 'The logo GestureDetector should be found in auth screen');
+
+ for (int i = 0; i < 7; i++) {
+ await tester.tap(logoButtonFinder);
+ await tester.pump(const Duration(milliseconds: 50));
+ }
+ await tester.pumpAndSettle();
+
+ expect(find.byType(DevToolsScreen), findsOneWidget, reason: 'DevToolsScreen should be visible after tapping the logo 7 times.');
+ });
+}
diff --git a/integration_test/common_cases/skip_welcome_screen.dart b/integration_test/common_cases/skip_welcome_screen.dart
new file mode 100644
index 00000000..20f8899b
--- /dev/null
+++ b/integration_test/common_cases/skip_welcome_screen.dart
@@ -0,0 +1,8 @@
+import 'package:eatvio_app/app_keys.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+Future skipWelcomeScreen(WidgetTester tester) async {
+ final welcomeScreenButtonFinder = find.byKey(AppKeys.welcomeScreenButton);
+ await tester.tap(welcomeScreenButtonFinder);
+ await tester.pumpAndSettle();
+}
\ No newline at end of file
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 198e6f7b..2fa79910 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -388,7 +388,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 72;
+ CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -410,6 +410,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -538,7 +539,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 72;
+ CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -560,6 +561,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -576,7 +578,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 72;
+ CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -598,6 +600,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index b6363034..fc70b0b0 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -1,5 +1,6 @@
-import UIKit
import Flutter
+import UIKit
+import YandexLoginSDK
@main
@objc class AppDelegate: FlutterAppDelegate {
@@ -10,4 +11,20 @@ import Flutter
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+
+ @available(iOS 8.0, *)
+ func application(
+ _ application: UIApplication, continue userActivity: NSUserActivity,
+ restorationHandler: @escaping ([Any]?) -> Void
+ ) -> Bool {
+ YXLSdk.shared.processUserActivity(userActivity)
+ return true
+ }
+
+ override public func application(
+ _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]
+ ) -> Bool {
+ return YXLSdk.shared.handleOpen(
+ url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String)
+ }
}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index b05e5fac..2b41fbe1 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -21,7 +21,7 @@
CFBundleSignature
????
CFBundleVersion
- 72
+ 79
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
@@ -67,5 +67,24 @@
UIViewControllerBasedStatusBarAppearance
+ LSApplicationQueriesSchemes
+
+ yandexauth
+ yandexauth2
+ yandexauth4
+
+ YAClientId
+ 5399b03636104c6d920b87c1ad329254
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ YandexLoginSDK
+ CFBundleURLSchemes
+
+ yx5399b03636104c6d920b87c1ad329254
+
+
+
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
index 7b8ca2a8..4ed4ad00 100644
--- a/ios/Runner/Runner.entitlements
+++ b/ios/Runner/Runner.entitlements
@@ -8,6 +8,7 @@
applinks:eatvio.ru
applinks:www.eatvio.ru
+ applinks:5399b03636104c6d920b87c1ad329254.oauth.yandex.ru
diff --git a/lib/app_keys.dart b/lib/app_keys.dart
new file mode 100644
index 00000000..3e732506
--- /dev/null
+++ b/lib/app_keys.dart
@@ -0,0 +1,13 @@
+import 'package:flutter/material.dart';
+
+class AppKeys {
+
+ // Auth Screen
+ static const hiddenLogoButtonAuthScreen = ValueKey('hiddenLogoButtonAuthScreen');
+ static const emailTextFieldAuthScreen = ValueKey('emailTextFieldAuthScreen');
+ static const passwordTextFieldAuthScreen = ValueKey('passwordTextFieldAuthScreen');
+ static const signInButtonAuthScreen = ValueKey('signInButtonAuthScreen');
+
+ // Welcome Screen
+ static const welcomeScreenButton = ValueKey('welcomeScreenButton');
+}
diff --git a/lib/core/app_config.dart b/lib/core/app_config.dart
new file mode 100644
index 00000000..45ad250b
--- /dev/null
+++ b/lib/core/app_config.dart
@@ -0,0 +1,55 @@
+import 'package:eatvio_app/core/app_initializer.dart';
+import 'package:eatvio_app/core/env_config.dart';
+import 'package:eatvio_app/core/internet_connection_cubit/internet_connection_cubit.dart';
+import 'package:eatvio_app/core/locator_service.dart';
+import 'package:eatvio_app/core/views/cubit/views_cubit.dart';
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
+import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
+import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
+import 'package:eatvio_app/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+
+/// Конфигурация приложения
+class AppConfig {
+ AppConfig({required this.buildType}) {
+ _baseUrl = switch (buildType) {
+ AppBuildType.prod => EnvConfig.baseUrl,
+ AppBuildType.tests => EnvConfig.devBaseUrl,
+ AppBuildType.dev => EnvConfig.devBaseUrl,
+ };
+ }
+
+ final AppBuildType buildType;
+ late final String _baseUrl;
+ String get baseUrl => _baseUrl;
+
+ void setBaseUrl(String baseUrls) {
+ _baseUrl = baseUrls;
+ }
+
+ /// Список глобальных BlocProvider'ов
+ List get globalBlocProviders => [
+ BlocProvider(create: (context) => sl()),
+ BlocProvider(create: (context) => sl()..getNotifications()),
+ BlocProvider(create: (context) => sl()..loadFeedBlogPostList()),
+ BlocProvider(create: (context) => sl()..getMyProfile()),
+ BlocProvider(create: (context) => sl()..getChatRoomsWithLoading()),
+ BlocProvider(create: (context) => sl()),
+ BlocProvider(lazy: false, create: (context) => sl()),
+ ];
+
+ /// Делегаты локализации
+ List get localizationDelegates => [
+ S.delegate,
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ ];
+
+ /// Поддерживаемые локали
+ List get supportedLocales => [const Locale.fromSubtags(languageCode: 'ru')];
+}
diff --git a/lib/core/app_initializer.dart b/lib/core/app_initializer.dart
new file mode 100644
index 00000000..ea0b8f5d
--- /dev/null
+++ b/lib/core/app_initializer.dart
@@ -0,0 +1,74 @@
+import 'dart:io' as io;
+import 'package:eatvio_app/core/app_config.dart';
+import 'package:eatvio_app/core/env_config.dart';
+import 'package:eatvio_app/core/locator_service.dart' as di;
+import 'package:eatvio_app/core/locator_service.dart';
+import 'package:eatvio_app/firebase_options.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_native_splash/flutter_native_splash.dart';
+import 'package:flutter_dotenv/flutter_dotenv.dart';
+import 'package:appmetrica_plugin/appmetrica_plugin.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
+
+/// Класс для инициализации приложения
+class AppInitializer {
+ /// Инициализация всех сервисов приложения
+ static Future initialize({required AppBuildType buildType}) async {
+ await _initializeEnvironment();
+ await _initializeDependencyInjection(buildType: buildType);
+ await _initializeSentry();
+ await _initializeFirebase();
+ await _initializeDebugMode();
+ await _initializeAnalytics();
+ }
+
+ /// Инициализация Sentry
+ static Future _initializeSentry() async {
+ final widgetsBinding = SentryWidgetsFlutterBinding.ensureInitialized();
+ FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
+ }
+
+ /// Загрузка переменных окружения
+ static Future _initializeEnvironment() async {
+ await dotenv.load(fileName: ".env");
+ }
+
+ /// Инициализация Firebase
+ static Future _initializeFirebase() async {
+ await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
+ if (sl().buildType == AppBuildType.prod) {
+ await FirebaseMessaging.instance.requestPermission();
+ }
+ }
+
+ /// Инициализация системы внедрения зависимостей
+ static Future _initializeDependencyInjection({required AppBuildType buildType}) async {
+ await di.init(buildType: buildType);
+ }
+
+ /// Настройка режима отладки
+ static Future _initializeDebugMode() async {
+ if (kDebugMode) {
+ io.HttpClient.enableTimelineLogging = true;
+ }
+ }
+
+ /// Инициализация аналитики
+ static Future _initializeAnalytics() async {
+ AppMetrica.activate(AppMetricaConfig(EnvConfig.appMetricaApiKey));
+ AppMetrica.reportEvent('Application started');
+ }
+
+ /// Запуск приложения с Sentry
+ static Future runAppWithSentry(VoidCallback appRunner) async {
+ await SentryFlutter.init((options) {
+ options.dsn = EnvConfig.sentryDsn;
+ options.tracesSampleRate = 1.0;
+ options.debug = false;
+ }, appRunner: appRunner);
+ }
+}
+
+enum AppBuildType { prod, tests, dev }
diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart
index cc028255..d17019d2 100644
--- a/lib/core/app_router.dart
+++ b/lib/core/app_router.dart
@@ -1,455 +1,58 @@
-import 'package:dio/dio.dart';
-import 'package:eatvio_app/core/const.dart';
-import 'package:eatvio_app/core/firebase_api.dart';
-import 'package:eatvio_app/core/token_storage.dart';
-import 'package:eatvio_app/core/websockets/ws_manager.dart';
-import 'package:eatvio_app/core/locator_service.dart';
-import 'package:eatvio_app/features/auth/presentation/pages/auth_screen.dart';
-import 'package:eatvio_app/features/auth/presentation/pages/forget_password_screen.dart';
-import 'package:eatvio_app/features/auth/presentation/pages/scan_qr_code_screen.dart';
-import 'package:eatvio_app/features/blog_post/presentation/pages/blog_post_screen.dart';
-import 'package:eatvio_app/features/blog_post_create/presentation/pages/blog_post_create_screen.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart';
-import 'package:eatvio_app/features/calendar/presentation/pages/calendar_screen.dart';
-import 'package:eatvio_app/features/chat/presentation/pages/chat_screen.dart';
-import 'package:eatvio_app/features/chat/presentation/pages/chats_screen.dart';
-import 'package:eatvio_app/features/chat/presentation/pages/empty_chat_screen.dart';
-import 'package:eatvio_app/features/chat/presentation/pages/payment_screen.dart';
-import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
-import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart';
-import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart';
-import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart';
-import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
-import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
-import 'package:eatvio_app/features/menu/presentation/pages/blocked_users_screen.dart';
-import 'package:eatvio_app/features/menu/presentation/pages/notifications_settings_screen.dart';
-import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart';
-import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
-import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
-import 'package:eatvio_app/features/create_plan/presentation/create_plan_screen.dart';
-import 'package:eatvio_app/features/consultant_rating/presentation/consultant_rating_screen.dart';
-import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart';
-import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart';
-import 'package:eatvio_app/features/diets/presentation/pages/about_diet_screen.dart';
-import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
-import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
-import 'package:eatvio_app/features/feed/models/recipes_model.dart';
-import 'package:eatvio_app/features/feed/presentation/pages/feed_screen.dart';
-import 'package:eatvio_app/features/glossary/presentation/pages/glossary_screen.dart';
-import 'package:eatvio_app/features/menu/presentation/pages/menu_screen.dart';
-import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart';
-import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
-import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart';
-import 'package:eatvio_app/features/my_finance/presentation/pages/user_finance_screen.dart';
-import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart';
-import 'package:eatvio_app/features/notifications/presentation/notifications_screen.dart';
-import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart';
-import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart';
-import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart';
-import 'package:eatvio_app/features/recipe/presentation/pages/about_recipe_screen.dart';
-import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart';
-import 'package:eatvio_app/features/recipe/presentation/pages/my_recipes_screen.dart';
-import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart';
-import 'package:eatvio_app/features/registration/presentation/pages/verify_email_screen.dart';
-import 'package:eatvio_app/features/registration/presentation/pages/verify_screen.dart';
-import 'package:eatvio_app/features/settings/entities/settings_entity.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/change_email_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/change_password_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/general_info_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/other_info_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/physical_activity_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/physical_form_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/pressure_screen.dart';
-import 'package:eatvio_app/features/settings/presentation/pages/settings_screen.dart';
-import 'package:eatvio_app/features/support/presentation/pages/support_screen.dart';
-import 'package:eatvio_app/features/update_screen/presentation/update_screen.dart';
-import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart';
-import 'package:eatvio_app/features/welcome/presentation/pages/welcome_screen.dart';
+import 'package:eatvio_app/core/routing/auth_routes.dart';
+import 'package:eatvio_app/core/routing/content_routes.dart';
+import 'package:eatvio_app/core/routing/main_navigation_routes.dart';
+import 'package:eatvio_app/core/routing/other_routes.dart';
+import 'package:eatvio_app/core/routing/route_constants.dart';
+import 'package:eatvio_app/core/routing/settings_routes.dart';
+import 'package:eatvio_app/features/auth/entities/auth_result_entity.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:go_router/go_router.dart';
-import 'package:package_info_plus/package_info_plus.dart';
-import 'package:shared_preferences/shared_preferences.dart';
-import 'package:version/version.dart';
-import 'dart:developer';
+/// Главный роутер приложения
class AppRouter {
static GlobalKey navigatorKey = GlobalKey();
static GoRouter appRouter = GoRouter(
- initialLocation: '/update-screen',
+ initialLocation: RouteConstants.updateScreen,
navigatorKey: navigatorKey,
- redirect: (context, state) {
- final segments = state.uri.pathSegments;
- if (segments.length >= 3) {
- final type = segments[1];
- final contentIdentifier = segments[2];
-
- switch (type) {
- case 'recipe':
- FlutterNativeSplash.remove();
- return '/about-recipes-screen?slug=$contentIdentifier';
- case 'blog_post':
- FlutterNativeSplash.remove();
- return '/blog-post-screen?id=$contentIdentifier';
- case 'plan':
- FlutterNativeSplash.remove();
- return '/about-plan-screen?slug=$contentIdentifier';
- case 'diet':
- FlutterNativeSplash.remove();
- return '/about-diet-screen?slug=$contentIdentifier';
- }
- }
-
- return null;
- },
+ redirect: _handleRedirect,
routes: [
- GoRoute(
- path: '/auth-screen',
- name: 'authScreen',
- builder: (context, state) => const AuthScreen(),
- routes: [
- GoRoute(path: 'registration-screen', name: 'registrationScreen', builder: (context, state) => const RegistrationScreen()),
- GoRoute(
- path: 'forget-password-screen',
- name: 'forgetPasswordScreen',
- builder: (context, state) => ForgetPasswordScreen(isAuth: state.extra as bool),
- ),
- GoRoute(path: 'scan-qr-code-screen', name: 'scanQRCodeScreen', builder: (context, state) => const ScanQRCodeScreen()),
- ],
- ),
- ShellRoute(
- builder: (context, state, child) {
- return BlocBuilder(
- builder: (BuildContext context, ProfileAvatarState state) {
- if (state is ProfileAvatarStateLoaded) {
- return BottomNavigationBarWidget(profileModel: state.profile, child: child);
- }
- return const SizedBox.shrink();
- },
- );
- },
- routes: [
- GoRoute(
- path: '/my-profile-screen',
- name: 'myProfileScreen',
- pageBuilder: (context, state) {
- return const NoTransitionPage(child: ProfileScreen());
- },
- ),
- GoRoute(
- path: '/feed-screen',
- name: 'feedScreen',
- pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen()),
- redirect: (context, state) async {
- try {
- await sl().initialize();
- } catch (e, st) {
- log(e.toString(), stackTrace: st);
- }
- return null;
- },
- ),
- GoRoute(
- path: '/diary-screen',
- name: 'diaryScreen',
- pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen()),
- ),
- GoRoute(
- path: ConsultationsScreen.path,
- name: ConsultationsScreen.name,
- pageBuilder: (context, state) => const NoTransitionPage(child: ConsultationsScreen()),
- ),
- GoRoute(
- path: '/chat-select-screen',
- name: 'chatSelectScreen',
- pageBuilder: (context, state) => const NoTransitionPage(child: ChatsScreen()),
- ),
- GoRoute(
- path: '/notifications-screen',
- name: 'notificationsScreen',
- pageBuilder: (context, state) => const NoTransitionPage(child: NotificationsScreen()),
- ),
- GoRoute(
- path: '/menu-screen',
- name: 'menuScreen',
- pageBuilder: (context, state) => NoTransitionPage(
- child: BlocBuilder(
- builder: (BuildContext context, ProfileAvatarState state) {
- if (state is ProfileAvatarStateLoaded) {
- return MenuScreen(profileModel: state.profile);
- }
- return const SizedBox.shrink();
- },
- ),
- ),
- ),
- ],
- ),
- GoRoute(name: 'blockedUsersScreen', path: '/blocked-users-screen', builder: (context, state) => const BlockedUsersScreen()),
- GoRoute(
- path: '/welcome-screen',
- name: 'welcomeScreen',
- builder: (context, state) => const WelcomeScreen(),
- redirect: (context, state) async {
- try {
- final sharedPreferences = await SharedPreferences.getInstance();
- final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed') ?? false;
- await sl().registerPushToken();
- if (welcomeScreenWasShowed == false) {
- return null;
- } else if (await TokenStorage.haveTokenInStorage()) {
- return '/feed-screen';
- } else {
- return '/auth-screen';
- }
- } catch (e) {
- return '/feed-screen';
- }
- },
- ),
- GoRoute(
- path: '/update-screen',
- name: 'updateScreen',
- builder: (context, state) => const UpdateScreen(),
- redirect: (context, state) async {
- try {
- final versions = (await sl().get('$baseUrl/api/app_versions')).data;
- PackageInfo packageInfo = await PackageInfo.fromPlatform();
- String version = packageInfo.version;
- Version currentVersion = Version.parse(version);
- Version latestVersion = Version.parse(versions['min_version']);
- if (latestVersion > currentVersion) {
- return null;
- } else {
- return '/welcome-screen';
- }
- } catch (e) {
- return '/welcome-screen';
- }
- },
- ),
- GoRoute(path: ConsultationsArchiveScreen.path, name: ConsultationsArchiveScreen.name, builder: (context, state) => const ConsultationsArchiveScreen()),
- GoRoute(path: ConsultantFinancesScreen.path, name: ConsultantFinancesScreen.name, builder: (context, state) => const ConsultantFinancesScreen()),
- GoRoute(path: UserPurchasesScreen.path, name: UserPurchasesScreen.name, builder: (context, state) => const UserPurchasesScreen()),
- GoRoute(path: '/user-finance-screen', name: 'userFinanceScreen', builder: (context, state) => const UserFinanceScreen()),
- GoRoute(
- path: CardSettingsScreen.path,
- name: CardSettingsScreen.name,
- builder: (context, state) {
- final chosenPlanId = state.extra as int?;
- return CardSettingsScreen(chosenPlanId: chosenPlanId);
- },
- ),
- GoRoute(
- path: '/about-plan-screen',
- name: 'aboutPlanScreen',
- builder: (context, state) {
- final slug = state.uri.queryParameters['slug'];
- if (slug != null) {
- return AboutPlanScreen(slug: slug, navigateToComments: false, index: null);
- }
-
- final data = state.extra as Map;
- return AboutPlanScreen(slug: data['slug'] as String, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool);
- },
- ),
- GoRoute(path: '/glossary-screen', name: 'glossaryScreen', builder: (context, state) => const GlossaryScreen()),
- GoRoute(
- path: '/about-recipes-screen',
- name: 'aboutRecipesScreen',
- builder: (context, state) {
- final slug = state.uri.queryParameters['slug'];
- if (slug != null) {
- return AboutRecipeScreen(recipeSlug: slug, navigateToComments: false, index: null);
- }
-
- final data = state.extra as Map;
- return AboutRecipeScreen(recipeSlug: data['recipe'] as String, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool);
- },
- ),
- GoRoute(
- path: RecipeCreateScreen.path,
- name: RecipeCreateScreen.name,
- builder: (context, state) => RecipeCreateScreen(recipe: state.extra != null ? state.extra as Recipe : null),
- ),
- GoRoute(
- path: '/profile-screen',
- name: 'profileScreen',
- builder: (context, state) => ProfileScreen(author: state.extra as Author),
- ),
- GoRoute(
- path: '/subs-screen/:slug',
- name: 'subsScreen',
- builder: (context, state) => SubsScreen(slug: state.pathParameters['slug']!, subType: state.extra as SubType),
- ),
- GoRoute(
- path: '/about-diet-screen',
- name: 'aboutDietScreen',
- builder: (context, state) {
- final slug = state.uri.queryParameters['slug'];
- if (slug != null) {
- return AboutDietScreen(slug: slug, index: null, navigateToComments: false);
- }
- final data = state.extra as Map;
- return AboutDietScreen(slug: data['slug'] as String, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool);
- },
- ),
- GoRoute(
- path: '/blog-post-create-screen',
- name: 'blogPostCreateScreen',
- builder: (context, state) => BlogPostCreateScreen(feedBlogPostModel: state.extra != null ? state.extra as FeedBlogPostModel : null),
- ),
- GoRoute(
- path: '/blog-post-screen',
- name: 'blogPostScreen',
- builder: (context, state) {
- final id = state.uri.queryParameters['id'];
- final parsedId = int.tryParse(id ?? '');
- if (parsedId != null) {
- return BlogPostScreen(id: parsedId);
- }
-
- return BlogPostScreen(id: state.extra as int);
- },
- ),
- GoRoute(
- path: '/chat-screen',
- name: 'chatScreen',
- builder: (context, state) {
- final data = state.extra as Map;
- final uuid = data['uuid'] as String;
- final sendInitialMessage = data['sendInitialMessage'] as bool?;
- return ChatScreen(uuid: uuid, sendInitialMessage: sendInitialMessage);
- },
- ),
- GoRoute(
- path: '/empty-chat-screen',
- name: 'emptyChatScreen',
- builder: (context, state) => EmptyChatScreen(author: state.extra as Author),
- ),
- GoRoute(
- path: '/settings-screen',
- name: 'settingsScreen',
- builder: (context, state) => const SettingsScreen(),
- routes: [
- GoRoute(
- path: 'general-info-screen',
- name: 'generalInfoScreen',
- builder: (context, state) => GeneralInfoScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- GoRoute(
- path: 'other-info-screen',
- name: 'otherInfoScreen',
- builder: (context, state) => OtherInfoScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- GoRoute(
- path: 'physical-activity-screen',
- name: 'physicalActivityScreen',
- builder: (context, state) => PhysicalActivityScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- GoRoute(
- path: 'physical-form-screen',
- name: 'physicalFormScreen',
- builder: (context, state) => PhysicalFormScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- GoRoute(
- path: 'pressure-screen',
- name: 'pressureScreen',
- builder: (context, state) => PressureScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- GoRoute(
- path: 'change-password-screen',
- name: 'changePasswordScreen',
- builder: (context, state) => ChangePasswordScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- GoRoute(
- path: 'change-email-screen',
- name: 'changeEmailScreen',
- builder: (context, state) => ChangeEmailScreen(settingsEntity: state.extra as SettingsEntity),
- ),
- ],
- ),
- GoRoute(path: '/become-consultant-screen', name: 'becomeConsultantScreen', builder: (context, state) => const BecomeConsultantScreen()),
- GoRoute(
- path: '/stepper-become-consultant-screen',
- name: 'stepperBecomeConsultantScreen',
- builder: (context, state) => StepperBecomeConsultantScreen(slug: state.extra == null ? null : state.extra as String),
- ),
- GoRoute(path: '/calendar-screen', name: 'calendarScreen', builder: (context, state) => const CalendarScreen()),
- GoRoute(path: '/support-screen', name: 'supportScreen', builder: (context, state) => const SupportScreen()),
- GoRoute(
- path: '/photo-view-screen/:index',
- name: 'photoViewScreen',
- builder: (context, state) => PhotoViewScreen(imageUrls: state.extra as List, index: int.parse(state.pathParameters['index']!)),
- ),
- GoRoute(
- path: CreateDietScreen.path,
- name: CreateDietScreen.name,
- builder: (context, state) {
- final diet = state.extra as Diet?;
- return CreateDietScreen(diet: diet);
- },
- ),
- GoRoute(
- path: DietPreviewScreen.path,
- name: DietPreviewScreen.name,
- builder: (context, state) {
- final data = state.extra as Diet;
- return DietPreviewScreen(diet: data);
- },
- ),
- GoRoute(path: CreatePlanScreen.path, name: CreatePlanScreen.name, builder: (context, state) => const CreatePlanScreen()),
- GoRoute(path: MyDietsScreen.path, name: MyDietsScreen.name, builder: (context, state) => const MyDietsScreen()),
- GoRoute(path: MyPlansScreen.path, name: MyPlansScreen.name, builder: (context, state) => const MyPlansScreen()),
- GoRoute(
- path: UserDetailsScreen.path,
- name: UserDetailsScreen.name,
- builder: (context, state) => UserDetailsScreen(slug: state.extra as String),
- ),
- GoRoute(path: DevToolsScreen.path, name: DevToolsScreen.name, builder: (context, state) => const DevToolsScreen()),
- GoRoute(path: ConsultantRatingScreen.path, name: ConsultantRatingScreen.name, builder: (context, state) => const ConsultantRatingScreen()),
- GoRoute(
- path: InfoByOrderScreen.path,
- name: InfoByOrderScreen.name,
- builder: (context, state) {
- final data = state.extra as Map;
- final planId = data['planId'] as int;
- final successfulPayment = data['successfulPayment'] as bool?;
- return InfoByOrderScreen(planId: planId, successfulPayment: successfulPayment);
- },
- ),
- GoRoute(
- path: PaymentScreen.path,
- name: PaymentScreen.name,
- builder: (context, state) {
- final data = state.extra as Map;
- final planId = data['planId'] as int;
- final url = data['url'] as String;
- return PaymentScreen(url: url, planId: planId);
- },
- ),
- GoRoute(
- path: UserDiaryScreen.path,
- name: UserDiaryScreen.name,
- builder: (context, state) {
- final data = state.extra as Map;
- final diaryId = data['diaryId'] as int;
- final currentPlanDay = data['currentDiaryDay'] as int;
- return UserDiaryScreen(diaryId: diaryId, currentPlanDay: currentPlanDay);
- },
- ),
- GoRoute(
- path: '/verify-screen',
- name: 'verifyScreen',
- builder: (context, state) => VerifyScreen(passKey: state.extra as String),
- ),
- GoRoute(path: '/verify-email-screen', name: 'verifyEmailScreen', builder: (context, state) => const VerifyEmailScreen()),
- GoRoute(path: MyRecipesScreen.path, name: MyRecipesScreen.name, builder: (context, state) => const MyRecipesScreen()),
- GoRoute(path: '/notifications-settings-screen', name: 'notificationsSettingsScreen', builder: (context, state) => const NotificationsSettingsScreen()),
+ ...AuthRoutes.createRoutes(),
+ ...MainNavigationRoutes.createRoutes(),
+ ...ContentRoutes.createRoutes(),
+ ...SettingsRoutes.createRoutes(),
+ ...OtherRoutes.createRoutes(),
],
);
+
+ /// Обработка редиректов для deep links
+ static String? _handleRedirect(BuildContext context, GoRouterState state) {
+ final segments = state.uri.pathSegments;
+ if (segments.length >= 3) {
+ final type = segments[1];
+ final contentIdentifier = segments[2];
+
+ switch (type) {
+ case 'recipe':
+ FlutterNativeSplash.remove();
+ return '${RouteConstants.aboutRecipesScreen}?slug=$contentIdentifier';
+ case 'blog_post':
+ FlutterNativeSplash.remove();
+ return '${RouteConstants.blogPostScreen}?id=$contentIdentifier';
+ case 'plan':
+ FlutterNativeSplash.remove();
+ return '${RouteConstants.aboutPlanScreen}?slug=$contentIdentifier';
+ case 'diet':
+ FlutterNativeSplash.remove();
+ return '${RouteConstants.aboutDietScreen}?slug=$contentIdentifier';
+ }
+ }
+
+ return null;
+ }
+
+ static void navigateToRegisterViaSocial({required AuthResultEntity authResult, required String provider}) {
+ appRouter.pushNamed('registerViaSocialScreen', extra: {'authResult': authResult, 'provider': provider});
+ }
}
diff --git a/lib/core/const.dart b/lib/core/const.dart
index 98f7bc4a..5aab4fd4 100644
--- a/lib/core/const.dart
+++ b/lib/core/const.dart
@@ -1,6 +1,3 @@
-import 'package:eatvio_app/core/env_config.dart';
-
-String baseUrl = EnvConfig.baseUrl;
bool hidePlans = const bool.fromEnvironment('HIDE_PLANS', defaultValue: false);
const buildDate = String.fromEnvironment('BUILD_DATE', defaultValue: '');
diff --git a/lib/core/constants/app_constants.dart b/lib/core/constants/app_constants.dart
new file mode 100644
index 00000000..f9c945c9
--- /dev/null
+++ b/lib/core/constants/app_constants.dart
@@ -0,0 +1,72 @@
+/// Константы приложения
+class AppConstants {
+ // Размеры
+ static const double defaultPadding = 16.0;
+ static const double smallPadding = 8.0;
+ static const double largePadding = 24.0;
+ static const double extraLargePadding = 32.0;
+
+ // Радиусы
+ static const double defaultBorderRadius = 8.0;
+ static const double smallBorderRadius = 4.0;
+ static const double largeBorderRadius = 16.0;
+
+ // Высоты
+ static const double defaultButtonHeight = 48.0;
+ static const double smallButtonHeight = 36.0;
+ static const double largeButtonHeight = 56.0;
+
+ // Ширины
+ static const double maxContentWidth = 400.0;
+ static const double minContentWidth = 280.0;
+
+ // Анимации
+ static const Duration defaultAnimationDuration = Duration(milliseconds: 300);
+ static const Duration fastAnimationDuration = Duration(milliseconds: 150);
+ static const Duration slowAnimationDuration = Duration(milliseconds: 500);
+
+ // Таймауты
+ static const Duration defaultTimeout = Duration(seconds: 30);
+ static const Duration shortTimeout = Duration(seconds: 10);
+ static const Duration longTimeout = Duration(minutes: 2);
+
+ // Лимиты
+ static const int maxImageSize = 5 * 1024 * 1024; // 5MB
+ static const int maxVideoSize = 100 * 1024 * 1024; // 100MB
+ static const int maxTextLength = 1000;
+ static const int maxTitleLength = 100;
+ static const int maxDescriptionLength = 500;
+
+ // Пагинация
+ static const int defaultPageSize = 20;
+ static const int smallPageSize = 10;
+ static const int largePageSize = 50;
+
+ // Форматы
+ static const List supportedImageFormats = ['jpg', 'jpeg', 'png', 'webp'];
+ static const List supportedVideoFormats = ['mp4', 'mov', 'avi'];
+
+ // Регулярные выражения
+ static const String emailRegex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$';
+ static const String phoneRegex = r'^\+?[1-9]\d{1,14}$';
+ static const String urlRegex = r'^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$';
+
+ // Сообщения
+ static const String defaultErrorMessage = 'Произошла ошибка. Попробуйте еще раз.';
+ static const String networkErrorMessage = 'Проблема с подключением к интернету.';
+ static const String timeoutErrorMessage = 'Превышено время ожидания.';
+ static const String unknownErrorMessage = 'Неизвестная ошибка.';
+
+ // Ключи для SharedPreferences
+ static const String welcomeScreenShownKey = 'welcomeScreenWasShowed';
+ static const String userTokenKey = 'userToken';
+ static const String userDataKey = 'userData';
+ static const String settingsKey = 'settings';
+
+ // API endpoints
+ static const String apiVersion = '/api';
+ static const String authEndpoint = '/auth';
+ static const String usersEndpoint = '/users';
+ static const String postsEndpoint = '/posts';
+ static const String commentsEndpoint = '/comments';
+}
diff --git a/lib/core/cubit/base_cubit.dart b/lib/core/cubit/base_cubit.dart
new file mode 100644
index 00000000..03ff3cc2
--- /dev/null
+++ b/lib/core/cubit/base_cubit.dart
@@ -0,0 +1,42 @@
+import 'dart:developer';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+/// Базовый класс для всех Cubit'ов с унифицированной обработкой ошибок
+abstract class BaseCubit extends Cubit {
+ BaseCubit(super.initialState);
+
+ /// Унифицированная обработка ошибок
+ void handleError(Object error, [StackTrace? stackTrace]) {
+ log('Error in $runtimeType: $error', stackTrace: stackTrace);
+ // Здесь можно добавить отправку ошибок в Sentry или другую систему мониторинга
+ }
+
+ /// Безопасное выполнение асинхронных операций
+ Future safeExecute(Future Function() operation) async {
+ try {
+ return await operation();
+ } catch (error, stackTrace) {
+ handleError(error, stackTrace);
+ return null;
+ }
+ }
+
+ /// Безопасное выполнение асинхронных операций с обработкой состояния
+ Future safeExecuteWithState(
+ Future Function() operation, {
+ required State loadingState,
+ required State Function(T result) successState,
+ required State Function(Object error) errorState,
+ }) async {
+ try {
+ emit(loadingState);
+ final result = await operation();
+ emit(successState(result));
+ return result;
+ } catch (error, stackTrace) {
+ handleError(error, stackTrace);
+ emit(errorState(error));
+ return null;
+ }
+ }
+}
diff --git a/lib/core/cubit/state_utils.dart b/lib/core/cubit/state_utils.dart
new file mode 100644
index 00000000..9c6dfb78
--- /dev/null
+++ b/lib/core/cubit/state_utils.dart
@@ -0,0 +1,28 @@
+/// Утилиты для работы с состояниями Cubit'ов
+class StateUtils {
+ /// Проверяет, является ли состояние загрузочным
+ static bool isLoading(dynamic state) {
+ return state.toString().contains('loading') || state.toString().contains('Loading');
+ }
+
+ /// Проверяет, является ли состояние загруженным
+ static bool isLoaded(dynamic state) {
+ return state.toString().contains('loaded') || state.toString().contains('Loaded');
+ }
+
+ /// Проверяет, является ли состояние ошибочным
+ static bool isError(dynamic state) {
+ return state.toString().contains('error') || state.toString().contains('Error');
+ }
+
+ /// Получает сообщение об ошибке из состояния
+ static String? getErrorMessage(dynamic state) {
+ if (isError(state)) {
+ // Попытка извлечь сообщение об ошибке из состояния
+ final stateString = state.toString();
+ final errorMatch = RegExp(r'error[:\s]+([^,}]+)').firstMatch(stateString);
+ return errorMatch?.group(1)?.trim();
+ }
+ return null;
+ }
+}
diff --git a/lib/core/di/app_config_registration.dart b/lib/core/di/app_config_registration.dart
new file mode 100644
index 00000000..de003d20
--- /dev/null
+++ b/lib/core/di/app_config_registration.dart
@@ -0,0 +1,12 @@
+import 'package:eatvio_app/core/app_config.dart';
+import 'package:eatvio_app/core/app_initializer.dart';
+import 'package:get_it/get_it.dart';
+
+class AppConfigRegistration {
+ static final sl = GetIt.instance;
+
+ /// Регистрация AppConfig
+ static void registerAppConfig({required AppBuildType buildType}) {
+ sl.registerLazySingleton(() => AppConfig(buildType: buildType));
+ }
+}
diff --git a/lib/core/di/cubit_registrations.dart b/lib/core/di/cubit_registrations.dart
new file mode 100644
index 00000000..2f4e764d
--- /dev/null
+++ b/lib/core/di/cubit_registrations.dart
@@ -0,0 +1,145 @@
+import 'package:eatvio_app/core/internet_connection_cubit/internet_connection_cubit.dart';
+import 'package:eatvio_app/core/views/cubit/views_cubit.dart';
+import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart';
+import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart';
+import 'package:eatvio_app/features/auth/presentation/bloc/reset_password_cubit/reset_password_cubit.dart';
+import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart';
+import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
+import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart';
+import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
+import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
+import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart';
+import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart';
+import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
+import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
+import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
+import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
+import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/consultants_cubit/consultants_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_cubit.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
+import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
+import 'package:eatvio_app/features/menu/presentation/bloc/notification_settings_cubit/notification_settings_cubit.dart';
+import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_cubit.dart';
+import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart';
+import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart';
+import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
+import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart';
+import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
+import 'package:eatvio_app/features/profile/presentation/bloc/plans_cubit/plans_cubit.dart' as profile_plans;
+import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
+import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart';
+import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_cubit.dart';
+import 'package:eatvio_app/features/recipe/presentation/cubit/my_recipes_cubit/my_recieps_cubit.dart';
+import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart';
+import 'package:eatvio_app/features/registration/presentation/bloc/verify_email_cubit/verify_email_cubit.dart';
+import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart';
+import 'package:eatvio_app/features/user_details/presentation/cubit/user_details_cubit.dart';
+import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
+import 'package:get_it/get_it.dart';
+
+/// Регистрация всех Cubit'ов в системе внедрения зависимостей
+class CubitRegistrations {
+ static final sl = GetIt.instance;
+
+ /// Регистрация всех Cubit'ов
+ static void registerAll() {
+ _registerSingletonCubits();
+ _registerFactoryCubits();
+ }
+
+ /// Регистрация Cubit'ов как синглтонов
+ static void _registerSingletonCubits() {
+ sl.registerLazySingleton(() => NewMessagesCubit());
+ sl.registerLazySingleton(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl()));
+ sl.registerLazySingleton(() => ProfileAvatarCubit(profileRepository: sl()));
+ sl.registerLazySingleton(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
+ sl.registerLazySingleton(() => InternetConnectionCubit());
+ }
+
+ /// Регистрация Cubit'ов как фабрик
+ static void _registerFactoryCubits() {
+ // Auth related cubits
+ sl.registerFactory(() => AuthCubit(authRepository: sl()));
+ sl.registerFactory(() => RegistrationCubit(registrationRepository: sl()));
+ sl.registerFactory(() => ResetPasswordCubit(resetPasswordRepository: sl()));
+ sl.registerFactory(() => VerifyEmailCubit(profileRepository: sl()));
+ sl.registerFactory(() => OAuthCubit(authRepository: sl()));
+ // Feed related cubits
+ sl.registerFactory(() => FeedCubit(feedRepository: sl()));
+ sl.registerFactory(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
+ sl.registerFactory(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
+ sl.registerFactory(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
+ sl.registerFactory(() => ConsultantsCubit(feedRepository: sl()));
+ sl.registerFactory(() => CommentsCubit(feedRepository: sl(), commentsRepository: sl()));
+ sl.registerFactory(() => FeedProfileCubit(profileRepository: sl()));
+
+ // Chat related cubits
+ sl.registerFactory(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
+
+ // Profile related cubits
+ sl.registerFactory(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
+ sl.registerFactory(() => profile_plans.PlansCubit(plansRepository: sl()));
+ sl.registerFactory(() => SubCubit(profileRepository: sl()));
+
+ // Content creation cubits
+ sl.registerFactory(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
+ sl.registerFactory(() => CreatePlanCubit(createPlanRepository: sl()));
+ sl.registerFactory(() => CreateRecipeCubit(recipeRepository: sl(), glossaryRepository: sl()));
+ sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl()));
+
+ // Content viewing cubits
+ sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
+ sl.registerFactory(() => AboutPlanCubit(planRepository: sl()));
+ sl.registerFactory(() => AboutRecipeCubit(recipeRepository: sl(), profileRepository: sl()));
+ sl.registerFactory(() => BlogPostCubit(sl()));
+
+ // User content cubits
+ sl.registerFactory(() => MyDietsCubit(myDietsRepository: sl(), createDietRepository: sl()));
+ sl.registerFactory(() => MyPlansCubit(myPlansRepository: sl()));
+ sl.registerFactory(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
+
+ // Other feature cubits
+ sl.registerFactory(() => CalendarCubit(calendarRepository: sl()));
+ sl.registerFactory(
+ () => DiaryCubit(
+ chatRepository: sl(),
+ diaryRepository: sl(),
+ glossaryRepository: sl(),
+ profileRepository: sl(),
+ dietRepository: sl(),
+ settingsRepository: sl(),
+ ),
+ );
+ sl.registerFactory(
+ () => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl(), consultationsRepository: sl()),
+ );
+ sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl()));
+ sl.registerFactory(() => ComplainingCubit(repository: sl()));
+ sl.registerFactory(() => UserCardsCubit(cardsRepository: sl()));
+ sl.registerFactory(() => OrderInfoCubit(chatRepository: sl()));
+ sl.registerFactory(() => ConsultationsCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => ConsultantCommentsCubit(diaryRepository: sl()));
+ sl.registerFactory(() => CancelPlanCubit(cancelPlanRepository: sl()));
+ sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => ConsultantFinancesCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => UserPurchasesCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => BlockedUsersCubit(blockedUsersRepository: sl()));
+ sl.registerFactory(() => NotificationSettingsCubit(notificationSettingsRepository: sl()));
+ sl.registerFactory(() => ConsultantRatingCubit(feedRepository: sl(), repository: sl(), profileRepository: sl()));
+ sl.registerFactory(() => ViewsCubit(viewsRepository: sl()));
+ sl.registerFactory(() => UserDetailsCubit(profileRepository: sl()));
+ }
+}
diff --git a/lib/core/di/external_registrations.dart b/lib/core/di/external_registrations.dart
new file mode 100644
index 00000000..231eeef3
--- /dev/null
+++ b/lib/core/di/external_registrations.dart
@@ -0,0 +1,33 @@
+import 'package:dio/dio.dart';
+import 'package:eatvio_app/core/app_config.dart';
+import 'package:eatvio_app/core/interceptors/phone_verify_error_interceptor.dart';
+import 'package:eatvio_app/core/interceptors/token_interceptor.dart';
+import 'package:get_it/get_it.dart';
+
+/// Регистрация внешних зависимостей в системе внедрения зависимостей
+class ExternalRegistrations {
+ static final sl = GetIt.instance;
+
+ /// Регистрация всех внешних зависимостей
+ static void registerAll() {
+ _registerDio();
+ }
+
+ /// Регистрация Dio HTTP клиента
+ static void _registerDio() {
+ sl.registerLazySingleton(
+ () => Dio(
+ BaseOptions(
+ baseUrl: sl().baseUrl,
+ headers: {
+ 'accept': 'application/json',
+ 'content-type': 'application/json',
+ },
+ ),
+ )..interceptors.addAll([
+ TokenInterceptor(),
+ PhoneVerifyErrorInterceptor(),
+ ]),
+ );
+ }
+}
diff --git a/lib/core/di/repository_registrations.dart b/lib/core/di/repository_registrations.dart
new file mode 100644
index 00000000..6d467810
--- /dev/null
+++ b/lib/core/di/repository_registrations.dart
@@ -0,0 +1,89 @@
+import 'package:eatvio_app/core/comments/repositories/comments_repository.dart';
+import 'package:eatvio_app/core/views/repository/views_repository.dart';
+import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
+import 'package:eatvio_app/features/auth/repositories/reset_password_repository.dart';
+import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
+import 'package:eatvio_app/features/calendar/repositories/calendar_repository.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart';
+import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
+import 'package:eatvio_app/features/complaining/repositories/complaining_repository.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
+import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart';
+import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
+import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
+import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart';
+import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart';
+import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
+import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
+import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
+import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
+import 'package:eatvio_app/features/menu/presentation/repositories/blocked_users_repository.dart';
+import 'package:eatvio_app/features/menu/repositories/notification_settings_repository.dart';
+import 'package:eatvio_app/features/my_diets/repositories/my_diets_repository.dart';
+import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
+import 'package:eatvio_app/features/my_plans/repositories/my_diets_repository.dart';
+import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart';
+import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
+import 'package:eatvio_app/features/plans/repositories/plans_repository.dart';
+import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
+import 'package:eatvio_app/features/recipe/repositories/about_recipe_repository.dart';
+import 'package:eatvio_app/features/recipe/repositories/my_recipes_repository.dart';
+import 'package:eatvio_app/features/registration/repositories/registration_repository.dart';
+import 'package:eatvio_app/features/settings/repositories/service_repository.dart';
+import 'package:get_it/get_it.dart';
+
+/// Регистрация всех Repository в системе внедрения зависимостей
+class RepositoryRegistrations {
+ static final sl = GetIt.instance;
+
+ /// Регистрация всех Repository
+ static void registerAll() {
+ _registerSingletonRepositories();
+ _registerFactoryRepositories();
+ }
+
+ /// Регистрация Repository как синглтонов
+ static void _registerSingletonRepositories() {
+ // Core repositories
+ sl.registerLazySingleton(() => ChatRepository(chatService: sl()));
+ sl.registerLazySingleton(() => FeedRepository(feedService: sl()));
+ sl.registerLazySingleton(() => ProfileRepository(profileService: sl()));
+ sl.registerLazySingleton(() => CalendarRepository(calendarService: sl()));
+ sl.registerLazySingleton(() => SettingsRepository(settingsService: sl()));
+ sl.registerLazySingleton(() => ConsultantRepository(consultantService: sl()));
+ sl.registerLazySingleton(() => DiaryRepository(diaryService: sl()));
+ sl.registerLazySingleton(() => GlossaryRepository(glossaryService: sl()));
+ sl.registerLazySingleton(() => DietRepository(dietsService: sl()));
+ sl.registerLazySingleton(() => PlansRepository(plansService: sl()));
+ sl.registerLazySingleton(() => PlanRepository(planService: sl()));
+ sl.registerLazySingleton(() => AboutRecipeRepository(recipeService: sl()));
+ sl.registerLazySingleton(() => RecipeRepository(recipeService: sl()));
+ sl.registerLazySingleton(() => ComplainingRepository(service: sl()));
+ sl.registerLazySingleton(() => AuthRepository(authService: sl()));
+ sl.registerLazySingleton(() => CardsRepository(cardsService: sl()));
+ sl.registerLazySingleton(() => ConsultationsRepository(consultationService: sl()));
+ sl.registerLazySingleton(() => CancelPlanRepository(cancelService: sl()));
+ sl.registerLazySingleton(() => CreatePlanRepository(createPlanService: sl()));
+ sl.registerLazySingleton(() => NotificationsRepository(notificationsService: sl()));
+ sl.registerLazySingleton(() => BlockedUsersRepository(blockedUserService: sl()));
+ sl.registerLazySingleton(() => CommentsRepository(service: sl()));
+ sl.registerLazySingleton(() => NotificationSettingsRepository(sl()));
+
+ // Content repositories
+ sl.registerLazySingleton(() => BlogPostRepository(blogPostService: sl()));
+ sl.registerLazySingleton(() => CreateDietRepository(createDietService: sl()));
+ sl.registerLazySingleton(() => MyDietsRepository(myDietsService: sl()));
+ sl.registerLazySingleton(() => MyPlansRepository(myPlansService: sl()));
+
+ // Auth repositories
+ sl.registerLazySingleton(() => RegistrationRepository(registrationService: sl()));
+ }
+
+ /// Регистрация Repository как фабрик
+ static void _registerFactoryRepositories() {
+ sl.registerFactory(() => MyRecipesRepository(myRecipesService: sl()));
+ sl.registerFactory(() => ResetPasswordRepository(resetPasswordService: sl()));
+ sl.registerFactory(() => ConsultantRatingRepository(sl()));
+ sl.registerFactory(() => ViewsRepository(viewsService: sl()));
+ }
+}
diff --git a/lib/core/di/service_registrations.dart b/lib/core/di/service_registrations.dart
new file mode 100644
index 00000000..454d9064
--- /dev/null
+++ b/lib/core/di/service_registrations.dart
@@ -0,0 +1,88 @@
+import 'package:eatvio_app/core/comments/services/comments_service.dart';
+import 'package:eatvio_app/core/firebase_api.dart';
+import 'package:eatvio_app/core/views/service/views_service.dart';
+import 'package:eatvio_app/core/websockets/ws_service.dart';
+import 'package:eatvio_app/features/auth/services/auth_service.dart';
+import 'package:eatvio_app/features/auth/services/reset_password_service.dart';
+import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
+import 'package:eatvio_app/features/calendar/services/calendar_service.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart';
+import 'package:eatvio_app/features/chat/services/chat_service.dart';
+import 'package:eatvio_app/features/complaining/services/complaining_service.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/services/consultant_service.dart';
+import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart';
+import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
+import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
+import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart';
+import 'package:eatvio_app/features/create_recipe/services/recipe_service.dart';
+import 'package:eatvio_app/features/diary/services/diary_service.dart';
+import 'package:eatvio_app/features/diets/services/diet_service.dart';
+import 'package:eatvio_app/features/feed/services/feed_service.dart';
+import 'package:eatvio_app/features/glossary/services/glossary_service.dart';
+import 'package:eatvio_app/features/menu/services/blocked_users_service.dart';
+import 'package:eatvio_app/features/menu/services/notification_settings_service.dart';
+import 'package:eatvio_app/features/my_diets/services/my_diets_service.dart';
+import 'package:eatvio_app/features/my_finance/services/cards_service.dart';
+import 'package:eatvio_app/features/my_plans/services/my_plans_service.dart';
+import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
+import 'package:eatvio_app/features/plans/services/plan_service.dart';
+import 'package:eatvio_app/features/plans/services/plans_service.dart';
+import 'package:eatvio_app/features/profile/services/profile_service.dart';
+import 'package:eatvio_app/features/recipe/services/about_recipe_service.dart';
+import 'package:eatvio_app/features/recipe/services/my_recipes_service.dart';
+import 'package:eatvio_app/features/registration/services/registration_service.dart';
+import 'package:eatvio_app/features/settings/services/settings_service.dart';
+import 'package:get_it/get_it.dart';
+
+/// Регистрация всех Service в системе внедрения зависимостей
+class ServiceRegistrations {
+ static final sl = GetIt.instance;
+
+ /// Регистрация всех Service
+ static void registerAll() {
+ _registerSingletonServices();
+ }
+
+ /// Регистрация Service как синглтонов
+ static void _registerSingletonServices() {
+ // Core services
+ sl.registerLazySingleton(() => ChatService(sl()));
+ sl.registerLazySingleton(() => FeedService(sl()));
+ sl.registerLazySingleton(() => ProfileService(sl()));
+ sl.registerLazySingleton(() => CalendarService(sl()));
+ sl.registerLazySingleton(() => SettingsService(sl()));
+ sl.registerLazySingleton(() => ConsultantService(sl()));
+ sl.registerLazySingleton(() => DiaryService(sl()));
+ sl.registerLazySingleton(() => GlossaryService(sl()));
+ sl.registerLazySingleton(() => DietService(sl()));
+ sl.registerLazySingleton(() => PlansService(sl()));
+ sl.registerLazySingleton(() => PlanService(sl()));
+ sl.registerLazySingleton(() => MyDietsService(sl()));
+ sl.registerLazySingleton(() => MyPlansService(sl()));
+ sl.registerLazySingleton(() => WsService(sl()));
+ sl.registerLazySingleton(() => AboutRecipeService(sl()));
+ sl.registerLazySingleton(() => ComplainingService(sl()));
+ sl.registerLazySingleton(() => AuthService(sl()));
+ sl.registerLazySingleton(() => CardsService(sl()));
+ sl.registerLazySingleton(() => ConsultationService(sl()));
+ sl.registerLazySingleton(() => CancelPlanService(sl()));
+ sl.registerLazySingleton(() => CreatePlanService(sl()));
+ sl.registerLazySingleton(() => NotificationsService(sl()));
+ sl.registerLazySingleton(() => FirebaseApi());
+ sl.registerLazySingleton(() => ResetPasswordService(sl()));
+ sl.registerLazySingleton(() => BlockedUserService(sl()));
+ sl.registerLazySingleton(() => CommentsService(sl()));
+ sl.registerLazySingleton(() => MyRecipesService(sl()));
+ sl.registerLazySingleton(() => ConsultantRatingService(sl()));
+ sl.registerLazySingleton(() => NotificationSettingsService(sl()));
+ sl.registerLazySingleton(() => ViewsService(sl()));
+
+ // Content services
+ sl.registerLazySingleton(() => CreateRecipeService(sl()));
+ sl.registerLazySingleton(() => BlogPostService(sl()));
+ sl.registerLazySingleton(() => CreateDietService(sl()));
+
+ // Auth services
+ sl.registerLazySingleton(() => RegistrationService(sl()));
+ }
+}
diff --git a/lib/core/env_config.dart b/lib/core/env_config.dart
index a3404aeb..3d451c6f 100644
--- a/lib/core/env_config.dart
+++ b/lib/core/env_config.dart
@@ -43,4 +43,13 @@ class EnvConfig {
// Connection Check URLs
static String get googleCheckUrl => dotenv.env['GOOGLE_CHECK_URL'] ?? '';
static String get yandexCheckUrl => dotenv.env['YANDEX_CHECK_URL'] ?? '';
+
+ // VK ID Configuration
+ static String get vkClientIdIos => dotenv.env['VK_CLIENT_ID_IOS'] ?? '';
+ static String get vkClientIdAndroid => dotenv.env['VK_CLIENT_ID_ANDROID'] ?? '';
+
+ // Yandex ID Configuration
+ static String get yandexClientId => dotenv.env['YANDEX_CLIENT_ID'] ?? '';
+ static String get yandexClientSecret => dotenv.env['YANDEX_CLIENT_SECRET'] ?? '';
+ static String get yandexRedirectUri => dotenv.env['YANDEX_REDIRECT_URI'] ?? '';
}
diff --git a/lib/core/firebase_api.dart b/lib/core/firebase_api.dart
index 572b56f6..7f4baad7 100644
--- a/lib/core/firebase_api.dart
+++ b/lib/core/firebase_api.dart
@@ -1,28 +1,43 @@
import 'dart:developer';
import 'dart:io';
+import 'package:eatvio_app/core/app_config.dart';
+import 'package:eatvio_app/core/app_initializer.dart';
+import 'package:eatvio_app/core/utils/toasts.dart';
+import 'package:flutter/services.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/services.dart' show MethodChannel, PlatformException;
class FirebaseApi {
final _firebaseMessaging = FirebaseMessaging.instance;
Future getToken() async {
- await _firebaseMessaging.requestPermission();
- return await _firebaseMessaging.getToken();
+ String? token;
+
+ if (await _isHuaweiDevice()) {
+ token = await _getHuaweiPushTokenNative();
+ } else {
+ if ((sl().buildType == AppBuildType.prod)) {
+ await _firebaseMessaging.requestPermission();
+ }
+ token = await _firebaseMessaging.getToken();
+ }
+ return token;
}
Future registerPushToken() async {
if (await TokenStorage.haveTokenInStorage()) {
try {
final deviceInfo = DeviceInfoPlugin();
+ final isHuawei = Platform.isAndroid ? await _isHuaweiDevice() : false;
await sl().post(
'/api/profile/notification/token/register',
data: {
- 'platform': Platform.isIOS ? 'ios' : 'android',
+ 'platform': Platform.isIOS ? 'ios' : (isHuawei ? 'huawei' : 'android'),
'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id,
'token': await getToken(),
},
@@ -32,4 +47,32 @@ class FirebaseApi {
}
}
}
+
+ Future _isHuaweiDevice() async {
+ if (!Platform.isAndroid) return false;
+ try {
+ final info = await DeviceInfoPlugin().androidInfo;
+ final brand = info.brand.toLowerCase();
+ final manufacturer = info.manufacturer.toLowerCase();
+ return brand.contains('huawei') || brand.contains('honor') || manufacturer.contains('huawei') || manufacturer.contains('honor');
+ } catch (_) {
+ return false;
+ }
+ }
+
+ Future _getHuaweiPushTokenNative() async {
+ try {
+ const channel = MethodChannel('com.eatvioapp/hms_push');
+ final String? token = await channel.invokeMethod('getHuaweiToken');
+ return token;
+ } on PlatformException catch (e, st) {
+ showErrorToast('$e $st');
+ log(e.toString(), stackTrace: st);
+ return null;
+ } catch (e, st) {
+ showErrorToast('$e $st');
+ log(e.toString(), stackTrace: st);
+ return null;
+ }
+ }
}
diff --git a/lib/core/dio/phone_verify_error_interceptor.dart b/lib/core/interceptors/phone_verify_error_interceptor.dart
similarity index 100%
rename from lib/core/dio/phone_verify_error_interceptor.dart
rename to lib/core/interceptors/phone_verify_error_interceptor.dart
diff --git a/lib/core/internet_connection_cubit/internet_connection_cubit.dart b/lib/core/internet_connection_cubit/internet_connection_cubit.dart
new file mode 100644
index 00000000..67427c6c
--- /dev/null
+++ b/lib/core/internet_connection_cubit/internet_connection_cubit.dart
@@ -0,0 +1,73 @@
+import 'dart:async';
+import 'dart:developer';
+
+import 'package:connectivity_plus/connectivity_plus.dart';
+import 'package:eatvio_app/core/env_config.dart';
+import 'package:eatvio_app/core/internet_connection_cubit/internet_connection_state.dart';
+import 'package:eatvio_app/core/utils/toasts.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:http/http.dart' as http;
+
+class InternetConnectionCubit extends Cubit {
+ InternetConnectionCubit() : super(InternetConnectionState()) {
+ checkConnection();
+ _connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateAccessStatus);
+ _listener = AppLifecycleListener(
+ onResume: () async {
+ _connectivitySubscription.resume();
+ await checkConnection();
+ },
+ onHide: _connectivitySubscription.pause,
+ onPause: _connectivitySubscription.pause,
+ );
+ }
+
+ final Connectivity _connectivity = Connectivity();
+ late StreamSubscription> _connectivitySubscription;
+ late final AppLifecycleListener _listener;
+
+ Future checkConnection() async {
+ late List result;
+
+ try {
+ result = await _connectivity.checkConnectivity();
+ } on PlatformException catch (e) {
+ log('Couldn\'t check connectivity status', error: e);
+ return;
+ }
+
+ return _updateAccessStatus(result);
+ }
+
+ Future _updateAccessStatus(List result) async {
+ final hasInternet = await _checkInternetAccess(result);
+ if (!hasInternet) showInternetConnectionToast();
+ emit(state.copyWith(isInternetAvailable: hasInternet));
+ }
+
+ Future _checkInternetAccess(List result) async {
+ if (result.contains(ConnectivityResult.none)) {
+ return false;
+ }
+
+ final uris = [Uri.parse(EnvConfig.googleCheckUrl), Uri.parse(EnvConfig.yandexCheckUrl), Uri.parse(EnvConfig.devBaseUrl), Uri.parse(EnvConfig.baseUrl)];
+ for (final uri in uris) {
+ try {
+ final response = await http.get(uri).timeout(const Duration(seconds: 3));
+ return response.statusCode >= 100 && response.statusCode < 600;
+ } catch (e) {
+ continue;
+ }
+ }
+ return false;
+ }
+
+ @override
+ Future close() {
+ _connectivitySubscription.cancel();
+ _listener.dispose();
+ return super.close();
+ }
+}
diff --git a/lib/core/internet_connection_cubit/internet_connection_state.dart b/lib/core/internet_connection_cubit/internet_connection_state.dart
new file mode 100644
index 00000000..95c831e1
--- /dev/null
+++ b/lib/core/internet_connection_cubit/internet_connection_state.dart
@@ -0,0 +1,13 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'internet_connection_state.freezed.dart';
+
+
+@freezed
+abstract class InternetConnectionState with _$InternetConnectionState {
+
+ factory InternetConnectionState({
+ @Default(true) bool isInternetAvailable,
+ }) = _InternetConnectionState;
+
+}
\ No newline at end of file
diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart
index e4a13866..d3ed9b95 100644
--- a/lib/core/locator_service.dart
+++ b/lib/core/locator_service.dart
@@ -1,257 +1,32 @@
-import 'package:dio/dio.dart';
-import 'package:eatvio_app/core/comments/repositories/comments_repository.dart';
-import 'package:eatvio_app/core/comments/services/comments_service.dart';
-import 'package:eatvio_app/core/const.dart';
-import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart';
-import 'package:eatvio_app/core/firebase_api.dart';
-import 'package:eatvio_app/core/interceptors/token_interceptor.dart';
+import 'package:eatvio_app/core/app_initializer.dart';
+import 'package:eatvio_app/core/di/app_config_registration.dart';
+import 'package:eatvio_app/core/di/cubit_registrations.dart';
+import 'package:eatvio_app/core/di/external_registrations.dart';
+import 'package:eatvio_app/core/di/repository_registrations.dart';
+import 'package:eatvio_app/core/di/service_registrations.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart';
-import 'package:eatvio_app/core/websockets/ws_service.dart';
-import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart';
-import 'package:eatvio_app/features/auth/presentation/bloc/reset_password_cubit/reset_password_cubit.dart';
-import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
-import 'package:eatvio_app/features/auth/repositories/reset_password_repository.dart';
-import 'package:eatvio_app/features/auth/services/auth_service.dart';
-import 'package:eatvio_app/features/auth/services/reset_password_service.dart';
-import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart';
-import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart';
-import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart';
-import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart';
-import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
-import 'package:eatvio_app/features/complaining/repositories/complaining_repository.dart';
-import 'package:eatvio_app/features/complaining/services/complaining_service.dart';
-import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart';
-import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart';
-import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart';
-import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
-import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
-import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
-import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart';
-import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
-import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
-import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
-import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
-import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart';
-import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart';
-import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
-import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
-import 'package:eatvio_app/features/diets/services/diet_service.dart';
-import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
-import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
-import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
-import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
-import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
-import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
-import 'package:eatvio_app/features/menu/presentation/bloc/notification_settings_cubit/notification_settings_cubit.dart';
-import 'package:eatvio_app/features/menu/presentation/repositories/blocked_users_repository.dart';
-import 'package:eatvio_app/features/menu/repositories/notification_settings_repository.dart';
-import 'package:eatvio_app/features/menu/services/blocked_users_service.dart';
-import 'package:eatvio_app/features/menu/services/notification_settings_service.dart';
-import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
-import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart';
-import 'package:eatvio_app/features/calendar/repositories/calendar_repository.dart';
-import 'package:eatvio_app/features/calendar/services/calendar_service.dart';
-import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
-import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
-import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
-import 'package:eatvio_app/features/chat/services/chat_service.dart';
-import 'package:eatvio_app/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart';
-import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
-import 'package:eatvio_app/features/consultant/become_consultant/services/consultant_service.dart';
-import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
-import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
-import 'package:eatvio_app/features/diary/services/diary_service.dart';
-import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
-import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
-import 'package:eatvio_app/features/feed/presentation/bloc/consultants_cubit/consultants_cubit.dart';
-import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
-import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
-import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_cubit.dart';
-import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
-import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
-import 'package:eatvio_app/features/feed/services/feed_service.dart';
-import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
-import 'package:eatvio_app/features/glossary/services/glossary_service.dart';
-import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
-import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_cubit.dart';
-import 'package:eatvio_app/features/my_diets/repositories/my_diets_repository.dart';
-import 'package:eatvio_app/features/my_diets/services/my_diets_service.dart';
-import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart';
-import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
-import 'package:eatvio_app/features/my_finance/services/cards_service.dart';
-import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart';
-import 'package:eatvio_app/features/my_plans/repositories/my_diets_repository.dart';
-import 'package:eatvio_app/features/my_plans/services/my_plans_service.dart';
-import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart';
-import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
-import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart';
-import 'package:eatvio_app/features/plans/repositories/plans_repository.dart';
-import 'package:eatvio_app/features/plans/services/plans_service.dart';
-import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
-import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
-import 'package:eatvio_app/features/plans/services/plan_service.dart';
-import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
-import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart';
-import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
-import 'package:eatvio_app/features/profile/services/profile_service.dart';
-import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
-import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart';
-import 'package:eatvio_app/features/create_recipe/services/recipe_service.dart';
-import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_cubit.dart';
-import 'package:eatvio_app/features/recipe/presentation/cubit/my_recipes_cubit/my_recieps_cubit.dart';
-import 'package:eatvio_app/features/recipe/repositories/about_recipe_repository.dart';
-import 'package:eatvio_app/features/recipe/repositories/my_recipes_repository.dart';
-import 'package:eatvio_app/features/recipe/services/about_recipe_service.dart';
-import 'package:eatvio_app/features/recipe/services/my_recipes_service.dart';
-import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart';
-import 'package:eatvio_app/features/registration/presentation/bloc/verify_email_cubit/verify_email_cubit.dart';
-import 'package:eatvio_app/features/registration/repositories/registration_repository.dart';
-import 'package:eatvio_app/features/registration/services/registration_service.dart';
-import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart';
-import 'package:eatvio_app/features/settings/repositories/service_repository.dart';
-import 'package:eatvio_app/features/settings/services/settings_service.dart';
-import 'package:eatvio_app/features/user_details/presentation/cubit/user_details_cubit.dart';
import 'package:get_it/get_it.dart';
-import 'package:eatvio_app/features/profile/presentation/bloc/plans_cubit/plans_cubit.dart' as profile_plans;
+/// Глобальный экземпляр GetIt для внедрения зависимостей
final sl = GetIt.instance;
-Future init() async {
- // Cubit singletons
+/// Инициализация системы внедрения зависимостей
+Future init({required AppBuildType buildType}) async {
+ // Регистрация AppConfig
+ AppConfigRegistration.registerAppConfig(buildType: buildType);
+
+ // Регистрация внешних зависимостей (Dio, etc.)
+ ExternalRegistrations.registerAll();
+
+ // Регистрация сервисов
+ ServiceRegistrations.registerAll();
+
+ // Регистрация репозиториев
+ RepositoryRegistrations.registerAll();
+
+ // Регистрация Cubit'ов
+ CubitRegistrations.registerAll();
+
+ // Регистрация WebSocket менеджера
sl.registerLazySingleton(() => WsManager(wsService: sl()));
- sl.registerLazySingleton(() => NewMessagesCubit());
- sl.registerLazySingleton(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl()));
- sl.registerLazySingleton(() => ProfileAvatarCubit(profileRepository: sl()));
- sl.registerLazySingleton(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
-
- // Cubit factories
- sl.registerFactory(() => FeedCubit(feedRepository: sl()));
- sl.registerFactory(() => RegistrationCubit(registrationRepository: sl()));
- sl.registerFactory(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
- sl.registerFactory(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
- sl.registerFactory(() => CalendarCubit(calendarRepository: sl()));
- sl.registerFactory(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
- sl.registerFactory(() => profile_plans.PlansCubit(plansRepository: sl()));
- sl.registerFactory(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
- sl.registerFactory(() => CommentsCubit(feedRepository: sl(), commentsRepository: sl()));
- sl.registerFactory(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
- sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl(), consultationsRepository: sl()));
- sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl()));
- sl.registerFactory(
- () => DiaryCubit(
- chatRepository: sl(),
- diaryRepository: sl(),
- glossaryRepository: sl(),
- profileRepository: sl(),
- dietRepository: sl(),
- settingsRepository: sl(),
- ),
- );
- sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
- sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl()));
- sl.registerFactory(() => SubCubit(profileRepository: sl()));
- sl.registerFactory(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
- sl.registerFactory(() => CreatePlanCubit(createPlanRepository: sl()));
- sl.registerFactory(() => FeedProfileCubit(profileRepository: sl()));
- sl.registerFactory(() => AboutPlanCubit(planRepository: sl()));
- sl.registerFactory(() => CreateRecipeCubit(recipeRepository: sl(), glossaryRepository: sl()));
- sl.registerFactory(() => MyDietsCubit(myDietsRepository: sl(), createDietRepository: sl()));
- sl.registerFactory(() => MyPlansCubit(myPlansRepository: sl()));
- sl.registerFactory(() => UserDetailsCubit(profileRepository: sl()));
- sl.registerFactory(() => ConsultantsCubit(feedRepository: sl()));
- sl.registerFactory(() => AboutRecipeCubit(recipeRepository: sl(), profileRepository: sl()));
- sl.registerFactory(() => AuthCubit(authRepository: sl()));
- sl.registerFactory(() => ComplainingCubit(repository: sl()));
- sl.registerFactory(() => UserCardsCubit(cardsRepository: sl()));
- sl.registerFactory(() => OrderInfoCubit(chatRepository: sl()));
- sl.registerFactory(() => ConsultationsCubit(consultationsRepository: sl()));
- sl.registerFactory(() => ConsultantCommentsCubit(diaryRepository: sl()));
- sl.registerFactory(() => CancelPlanCubit(cancelPlanRepository: sl()));
- sl.registerFactory(() => VerifyEmailCubit(profileRepository: sl()));
- sl.registerFactory(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
- sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
- sl.registerFactory(() => ConsultantFinancesCubit(consultationsRepository: sl()));
- sl.registerFactory(() => UserPurchasesCubit(consultationsRepository: sl()));
- sl.registerFactory(() => ResetPasswordCubit(resetPasswordRepository: sl()));
- sl.registerFactory(() => BlogPostCubit(sl()));
- sl.registerFactory(() => BlockedUsersCubit(blockedUsersRepository: sl()));
- sl.registerFactory(() => NotificationSettingsCubit(notificationSettingsRepository: sl()));
- sl.registerFactory(() => ConsultantRatingCubit(sl()));
-
- // Repository
- sl.registerLazySingleton(() => ChatRepository(chatService: sl()));
- sl.registerLazySingleton(() => FeedRepository(feedService: sl()));
- sl.registerLazySingleton(() => RegistrationRepository(registrationService: sl()));
- sl.registerLazySingleton(() => CalendarRepository(calendarService: sl()));
- sl.registerLazySingleton(() => ProfileRepository(profileService: sl()));
- sl.registerLazySingleton(() => RecipeRepository(recipeService: sl()));
- sl.registerLazySingleton(() => SettingsRepository(settingsService: sl()));
- sl.registerLazySingleton(() => ConsultantRepository(consultantService: sl()));
- sl.registerLazySingleton(() => DiaryRepository(diaryService: sl()));
- sl.registerLazySingleton(() => GlossaryRepository(glossaryService: sl()));
- sl.registerLazySingleton(() => DietRepository(dietsService: sl()));
- sl.registerLazySingleton(() => BlogPostRepository(blogPostService: sl()));
- sl.registerLazySingleton(() => CreateDietRepository(createDietService: sl()));
- sl.registerLazySingleton(() => PlansRepository(plansService: sl()));
- sl.registerLazySingleton(() => PlanRepository(planService: sl()));
- sl.registerLazySingleton(() => MyDietsRepository(myDietsService: sl()));
- sl.registerLazySingleton(() => MyPlansRepository(myPlansService: sl()));
- sl.registerLazySingleton