forked from eatvio/eatvio_app
Merge remote-tracking branch 'upstream/main' into issue-710
This commit is contained in:
13
lib/app_keys.dart
Normal file
13
lib/app_keys.dart
Normal file
@@ -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');
|
||||
}
|
||||
55
lib/core/app_config.dart
Normal file
55
lib/core/app_config.dart
Normal file
@@ -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 String _baseUrl;
|
||||
String get baseUrl => _baseUrl;
|
||||
|
||||
void setBaseUrl(String baseUrls) {
|
||||
_baseUrl = baseUrls;
|
||||
}
|
||||
|
||||
/// Список глобальных BlocProvider'ов
|
||||
List<BlocProvider> get globalBlocProviders => [
|
||||
BlocProvider<NewMessagesCubit>(create: (context) => sl<NewMessagesCubit>()),
|
||||
BlocProvider<NotificationsCubit>(create: (context) => sl<NotificationsCubit>()..getNotifications()),
|
||||
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
|
||||
BlocProvider<ProfileAvatarCubit>(create: (context) => sl<ProfileAvatarCubit>()..getMyProfile()),
|
||||
BlocProvider<ChatsCubit>(create: (context) => sl<ChatsCubit>()..getChatRoomsWithLoading()),
|
||||
BlocProvider<ViewsCubit>(create: (context) => sl<ViewsCubit>()),
|
||||
BlocProvider<InternetConnectionCubit>(lazy: false, create: (context) => sl<InternetConnectionCubit>()),
|
||||
];
|
||||
|
||||
/// Делегаты локализации
|
||||
List<LocalizationsDelegate> get localizationDelegates => [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
];
|
||||
|
||||
/// Поддерживаемые локали
|
||||
List<Locale> get supportedLocales => [const Locale.fromSubtags(languageCode: 'ru')];
|
||||
}
|
||||
74
lib/core/app_initializer.dart
Normal file
74
lib/core/app_initializer.dart
Normal file
@@ -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<void> initialize({required AppBuildType buildType}) async {
|
||||
await _initializeEnvironment();
|
||||
await _initializeDependencyInjection(buildType: buildType);
|
||||
await _initializeSentry();
|
||||
await _initializeFirebase();
|
||||
await _initializeDebugMode();
|
||||
await _initializeAnalytics();
|
||||
}
|
||||
|
||||
/// Инициализация Sentry
|
||||
static Future<void> _initializeSentry() async {
|
||||
final widgetsBinding = SentryWidgetsFlutterBinding.ensureInitialized();
|
||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
}
|
||||
|
||||
/// Загрузка переменных окружения
|
||||
static Future<void> _initializeEnvironment() async {
|
||||
await dotenv.load(fileName: ".env");
|
||||
}
|
||||
|
||||
/// Инициализация Firebase
|
||||
static Future<void> _initializeFirebase() async {
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
if (sl<AppConfig>().buildType == AppBuildType.prod) {
|
||||
await FirebaseMessaging.instance.requestPermission();
|
||||
}
|
||||
}
|
||||
|
||||
/// Инициализация системы внедрения зависимостей
|
||||
static Future<void> _initializeDependencyInjection({required AppBuildType buildType}) async {
|
||||
await di.init(buildType: buildType);
|
||||
}
|
||||
|
||||
/// Настройка режима отладки
|
||||
static Future<void> _initializeDebugMode() async {
|
||||
if (kDebugMode) {
|
||||
io.HttpClient.enableTimelineLogging = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Инициализация аналитики
|
||||
static Future<void> _initializeAnalytics() async {
|
||||
AppMetrica.activate(AppMetricaConfig(EnvConfig.appMetricaApiKey));
|
||||
AppMetrica.reportEvent('Application started');
|
||||
}
|
||||
|
||||
/// Запуск приложения с Sentry
|
||||
static Future<void> 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 }
|
||||
@@ -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<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
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<ProfileAvatarCubit, ProfileAvatarState>(
|
||||
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<WsManager>().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<ProfileAvatarCubit, ProfileAvatarState>(
|
||||
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<FirebaseApi>().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<Dio>().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<String, dynamic>;
|
||||
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) => GlossaryScreen(passKey: state.extra as String,)),
|
||||
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<String, dynamic>;
|
||||
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<String, dynamic>;
|
||||
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<String, dynamic>;
|
||||
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<String>, 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<String, dynamic>;
|
||||
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<String, dynamic>;
|
||||
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<String, int>;
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: '');
|
||||
|
||||
|
||||
72
lib/core/constants/app_constants.dart
Normal file
72
lib/core/constants/app_constants.dart
Normal file
@@ -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<String> supportedImageFormats = ['jpg', 'jpeg', 'png', 'webp'];
|
||||
static const List<String> 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';
|
||||
}
|
||||
42
lib/core/cubit/base_cubit.dart
Normal file
42
lib/core/cubit/base_cubit.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'dart:developer';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
/// Базовый класс для всех Cubit'ов с унифицированной обработкой ошибок
|
||||
abstract class BaseCubit<State> extends Cubit<State> {
|
||||
BaseCubit(super.initialState);
|
||||
|
||||
/// Унифицированная обработка ошибок
|
||||
void handleError(Object error, [StackTrace? stackTrace]) {
|
||||
log('Error in $runtimeType: $error', stackTrace: stackTrace);
|
||||
// Здесь можно добавить отправку ошибок в Sentry или другую систему мониторинга
|
||||
}
|
||||
|
||||
/// Безопасное выполнение асинхронных операций
|
||||
Future<T?> safeExecute<T>(Future<T> Function() operation) async {
|
||||
try {
|
||||
return await operation();
|
||||
} catch (error, stackTrace) {
|
||||
handleError(error, stackTrace);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Безопасное выполнение асинхронных операций с обработкой состояния
|
||||
Future<T?> safeExecuteWithState<T>(
|
||||
Future<T> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
lib/core/cubit/state_utils.dart
Normal file
28
lib/core/cubit/state_utils.dart
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
12
lib/core/di/app_config_registration.dart
Normal file
12
lib/core/di/app_config_registration.dart
Normal file
@@ -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>(() => AppConfig(buildType: buildType));
|
||||
}
|
||||
}
|
||||
147
lib/core/di/cubit_registrations.dart
Normal file
147
lib/core/di/cubit_registrations.dart
Normal file
@@ -0,0 +1,147 @@
|
||||
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/bloc/menu_cubit/menu_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>(() => NewMessagesCubit());
|
||||
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl()));
|
||||
sl.registerLazySingleton<ProfileAvatarCubit>(() => ProfileAvatarCubit(profileRepository: sl()));
|
||||
sl.registerLazySingleton<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
|
||||
sl.registerLazySingleton<InternetConnectionCubit>(() => InternetConnectionCubit());
|
||||
}
|
||||
|
||||
/// Регистрация Cubit'ов как фабрик
|
||||
static void _registerFactoryCubits() {
|
||||
// Auth related cubits
|
||||
sl.registerFactory<AuthCubit>(() => AuthCubit(authRepository: sl()));
|
||||
sl.registerFactory<RegistrationCubit>(() => RegistrationCubit(registrationRepository: sl()));
|
||||
sl.registerFactory<ResetPasswordCubit>(() => ResetPasswordCubit(resetPasswordRepository: sl()));
|
||||
sl.registerFactory<VerifyEmailCubit>(() => VerifyEmailCubit(profileRepository: sl()));
|
||||
sl.registerFactory<OAuthCubit>(() => OAuthCubit(authRepository: sl()));
|
||||
// Feed related cubits
|
||||
sl.registerFactory<FeedCubit>(() => FeedCubit(feedRepository: sl()));
|
||||
sl.registerFactory<RecipesCubit>(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
|
||||
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
||||
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
|
||||
sl.registerFactory<ConsultantsCubit>(() => ConsultantsCubit(feedRepository: sl()));
|
||||
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl(), commentsRepository: sl()));
|
||||
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
|
||||
|
||||
// Chat related cubits
|
||||
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
|
||||
|
||||
// Profile related cubits
|
||||
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
|
||||
sl.registerFactory<profile_plans.PlansCubit>(() => profile_plans.PlansCubit(plansRepository: sl()));
|
||||
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));
|
||||
|
||||
// Content creation cubits
|
||||
sl.registerFactory<CreateDietCubit>(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit(createPlanRepository: sl()));
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit(recipeRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
|
||||
|
||||
// Content viewing cubits
|
||||
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
|
||||
sl.registerFactory<AboutPlanCubit>(() => AboutPlanCubit(planRepository: sl()));
|
||||
sl.registerFactory<AboutRecipeCubit>(() => AboutRecipeCubit(recipeRepository: sl(), profileRepository: sl()));
|
||||
sl.registerFactory<BlogPostCubit>(() => BlogPostCubit(sl()));
|
||||
|
||||
// User content cubits
|
||||
sl.registerFactory<MyDietsCubit>(() => MyDietsCubit(myDietsRepository: sl(), createDietRepository: sl()));
|
||||
sl.registerFactory<MyPlansCubit>(() => MyPlansCubit(myPlansRepository: sl()));
|
||||
sl.registerFactory<MyRecipesCubit>(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
|
||||
|
||||
// Other feature cubits
|
||||
sl.registerFactory<CalendarCubit>(() => CalendarCubit(calendarRepository: sl()));
|
||||
sl.registerFactory<DiaryCubit>(
|
||||
() => DiaryCubit(
|
||||
chatRepository: sl(),
|
||||
diaryRepository: sl(),
|
||||
glossaryRepository: sl(),
|
||||
profileRepository: sl(),
|
||||
dietRepository: sl(),
|
||||
settingsRepository: sl(),
|
||||
),
|
||||
);
|
||||
sl.registerFactory<SettingsCubit>(
|
||||
() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl(), consultationsRepository: sl()),
|
||||
);
|
||||
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
||||
sl.registerFactory<ComplainingCubit>(() => ComplainingCubit(repository: sl()));
|
||||
sl.registerFactory<UserCardsCubit>(() => UserCardsCubit(cardsRepository: sl()));
|
||||
sl.registerFactory<OrderInfoCubit>(() => OrderInfoCubit(chatRepository: sl()));
|
||||
sl.registerFactory<ConsultationsCubit>(() => ConsultationsCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<ConsultantCommentsCubit>(() => ConsultantCommentsCubit(diaryRepository: sl()));
|
||||
sl.registerFactory<CancelPlanCubit>(() => CancelPlanCubit(cancelPlanRepository: sl()));
|
||||
sl.registerFactory<ConsultationsArchiveCubit>(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<ConsultantFinancesCubit>(() => ConsultantFinancesCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<UserPurchasesCubit>(() => UserPurchasesCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<BlockedUsersCubit>(() => BlockedUsersCubit(blockedUsersRepository: sl()));
|
||||
sl.registerFactory<NotificationSettingsCubit>(() => NotificationSettingsCubit(notificationSettingsRepository: sl()));
|
||||
sl.registerFactory<ConsultantRatingCubit>(() => ConsultantRatingCubit(feedRepository: sl(), repository: sl(), profileRepository: sl()));
|
||||
sl.registerFactory<ViewsCubit>(() => ViewsCubit(viewsRepository: sl()));
|
||||
sl.registerFactory<UserDetailsCubit>(() => UserDetailsCubit(profileRepository: sl()));
|
||||
sl.registerFactory<MenuCubit>(() => MenuCubit(sl()));
|
||||
}
|
||||
}
|
||||
33
lib/core/di/external_registrations.dart
Normal file
33
lib/core/di/external_registrations.dart
Normal file
@@ -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<AppConfig>().baseUrl,
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
),
|
||||
)..interceptors.addAll([
|
||||
TokenInterceptor(),
|
||||
PhoneVerifyErrorInterceptor(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
89
lib/core/di/repository_registrations.dart
Normal file
89
lib/core/di/repository_registrations.dart
Normal file
@@ -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>(() => ChatRepository(chatService: sl()));
|
||||
sl.registerLazySingleton<FeedRepository>(() => FeedRepository(feedService: sl()));
|
||||
sl.registerLazySingleton<ProfileRepository>(() => ProfileRepository(profileService: sl()));
|
||||
sl.registerLazySingleton<CalendarRepository>(() => CalendarRepository(calendarService: sl()));
|
||||
sl.registerLazySingleton<SettingsRepository>(() => SettingsRepository(settingsService: sl()));
|
||||
sl.registerLazySingleton<ConsultantRepository>(() => ConsultantRepository(consultantService: sl()));
|
||||
sl.registerLazySingleton<DiaryRepository>(() => DiaryRepository(diaryService: sl()));
|
||||
sl.registerLazySingleton<GlossaryRepository>(() => GlossaryRepository(glossaryService: sl()));
|
||||
sl.registerLazySingleton<DietRepository>(() => DietRepository(dietsService: sl()));
|
||||
sl.registerLazySingleton<PlansRepository>(() => PlansRepository(plansService: sl()));
|
||||
sl.registerLazySingleton<PlanRepository>(() => PlanRepository(planService: sl()));
|
||||
sl.registerLazySingleton<AboutRecipeRepository>(() => AboutRecipeRepository(recipeService: sl()));
|
||||
sl.registerLazySingleton<RecipeRepository>(() => RecipeRepository(recipeService: sl()));
|
||||
sl.registerLazySingleton<ComplainingRepository>(() => ComplainingRepository(service: sl()));
|
||||
sl.registerLazySingleton<AuthRepository>(() => AuthRepository(authService: sl()));
|
||||
sl.registerLazySingleton<CardsRepository>(() => CardsRepository(cardsService: sl()));
|
||||
sl.registerLazySingleton<ConsultationsRepository>(() => ConsultationsRepository(consultationService: sl()));
|
||||
sl.registerLazySingleton<CancelPlanRepository>(() => CancelPlanRepository(cancelService: sl()));
|
||||
sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl()));
|
||||
sl.registerLazySingleton<NotificationsRepository>(() => NotificationsRepository(notificationsService: sl()));
|
||||
sl.registerLazySingleton<BlockedUsersRepository>(() => BlockedUsersRepository(blockedUserService: sl()));
|
||||
sl.registerLazySingleton<CommentsRepository>(() => CommentsRepository(service: sl()));
|
||||
sl.registerLazySingleton<NotificationSettingsRepository>(() => NotificationSettingsRepository(sl()));
|
||||
|
||||
// Content repositories
|
||||
sl.registerLazySingleton<BlogPostRepository>(() => BlogPostRepository(blogPostService: sl()));
|
||||
sl.registerLazySingleton<CreateDietRepository>(() => CreateDietRepository(createDietService: sl()));
|
||||
sl.registerLazySingleton<MyDietsRepository>(() => MyDietsRepository(myDietsService: sl()));
|
||||
sl.registerLazySingleton<MyPlansRepository>(() => MyPlansRepository(myPlansService: sl()));
|
||||
|
||||
// Auth repositories
|
||||
sl.registerLazySingleton<RegistrationRepository>(() => RegistrationRepository(registrationService: sl()));
|
||||
}
|
||||
|
||||
/// Регистрация Repository как фабрик
|
||||
static void _registerFactoryRepositories() {
|
||||
sl.registerFactory<MyRecipesRepository>(() => MyRecipesRepository(myRecipesService: sl()));
|
||||
sl.registerFactory<ResetPasswordRepository>(() => ResetPasswordRepository(resetPasswordService: sl()));
|
||||
sl.registerFactory<ConsultantRatingRepository>(() => ConsultantRatingRepository(sl()));
|
||||
sl.registerFactory<ViewsRepository>(() => ViewsRepository(viewsService: sl()));
|
||||
}
|
||||
}
|
||||
88
lib/core/di/service_registrations.dart
Normal file
88
lib/core/di/service_registrations.dart
Normal file
@@ -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>(() => ChatService(sl()));
|
||||
sl.registerLazySingleton<FeedService>(() => FeedService(sl()));
|
||||
sl.registerLazySingleton<ProfileService>(() => ProfileService(sl()));
|
||||
sl.registerLazySingleton<CalendarService>(() => CalendarService(sl()));
|
||||
sl.registerLazySingleton<SettingsService>(() => SettingsService(sl()));
|
||||
sl.registerLazySingleton<ConsultantService>(() => ConsultantService(sl()));
|
||||
sl.registerLazySingleton<DiaryService>(() => DiaryService(sl()));
|
||||
sl.registerLazySingleton<GlossaryService>(() => GlossaryService(sl()));
|
||||
sl.registerLazySingleton<DietService>(() => DietService(sl()));
|
||||
sl.registerLazySingleton<PlansService>(() => PlansService(sl()));
|
||||
sl.registerLazySingleton<PlanService>(() => PlanService(sl()));
|
||||
sl.registerLazySingleton<MyDietsService>(() => MyDietsService(sl()));
|
||||
sl.registerLazySingleton<MyPlansService>(() => MyPlansService(sl()));
|
||||
sl.registerLazySingleton<WsService>(() => WsService(sl()));
|
||||
sl.registerLazySingleton<AboutRecipeService>(() => AboutRecipeService(sl()));
|
||||
sl.registerLazySingleton<ComplainingService>(() => ComplainingService(sl()));
|
||||
sl.registerLazySingleton<AuthService>(() => AuthService(sl()));
|
||||
sl.registerLazySingleton<CardsService>(() => CardsService(sl()));
|
||||
sl.registerLazySingleton<ConsultationService>(() => ConsultationService(sl()));
|
||||
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
|
||||
sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl()));
|
||||
sl.registerLazySingleton<NotificationsService>(() => NotificationsService(sl()));
|
||||
sl.registerLazySingleton<FirebaseApi>(() => FirebaseApi());
|
||||
sl.registerLazySingleton<ResetPasswordService>(() => ResetPasswordService(sl()));
|
||||
sl.registerLazySingleton<BlockedUserService>(() => BlockedUserService(sl()));
|
||||
sl.registerLazySingleton<CommentsService>(() => CommentsService(sl()));
|
||||
sl.registerLazySingleton<MyRecipesService>(() => MyRecipesService(sl()));
|
||||
sl.registerLazySingleton<ConsultantRatingService>(() => ConsultantRatingService(sl()));
|
||||
sl.registerLazySingleton<NotificationSettingsService>(() => NotificationSettingsService(sl()));
|
||||
sl.registerLazySingleton<ViewsService>(() => ViewsService(sl()));
|
||||
|
||||
// Content services
|
||||
sl.registerLazySingleton<CreateRecipeService>(() => CreateRecipeService(sl()));
|
||||
sl.registerLazySingleton<BlogPostService>(() => BlogPostService(sl()));
|
||||
sl.registerLazySingleton<CreateDietService>(() => CreateDietService(sl()));
|
||||
|
||||
// Auth services
|
||||
sl.registerLazySingleton<RegistrationService>(() => RegistrationService(sl()));
|
||||
}
|
||||
}
|
||||
@@ -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'] ?? '';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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';
|
||||
|
||||
@@ -19,7 +21,9 @@ class FirebaseApi {
|
||||
if (await _isHuaweiDevice()) {
|
||||
token = await _getHuaweiPushTokenNative();
|
||||
} else {
|
||||
await _firebaseMessaging.requestPermission();
|
||||
if ((sl<AppConfig>().buildType == AppBuildType.prod)) {
|
||||
await _firebaseMessaging.requestPermission();
|
||||
}
|
||||
token = await _firebaseMessaging.getToken();
|
||||
}
|
||||
return token;
|
||||
|
||||
@@ -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<InternetConnectionState> {
|
||||
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<List<ConnectivityResult>> _connectivitySubscription;
|
||||
late final AppLifecycleListener _listener;
|
||||
|
||||
Future<void> checkConnection() async {
|
||||
late List<ConnectivityResult> result;
|
||||
|
||||
try {
|
||||
result = await _connectivity.checkConnectivity();
|
||||
} on PlatformException catch (e) {
|
||||
log('Couldn\'t check connectivity status', error: e);
|
||||
return;
|
||||
}
|
||||
|
||||
return _updateAccessStatus(result);
|
||||
}
|
||||
|
||||
Future<void> _updateAccessStatus(List<ConnectivityResult> result) async {
|
||||
final hasInternet = await _checkInternetAccess(result);
|
||||
if (!hasInternet) showInternetConnectionToast();
|
||||
emit(state.copyWith(isInternetAvailable: hasInternet));
|
||||
}
|
||||
|
||||
Future<bool> _checkInternetAccess(List<ConnectivityResult> 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<void> close() {
|
||||
_connectivitySubscription.cancel();
|
||||
_listener.dispose();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -1,265 +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/views/cubit/views_cubit.dart';
|
||||
import 'package:eatvio_app/core/views/repository/views_repository.dart';
|
||||
import 'package:eatvio_app/core/views/service/views_service.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/bloc/menu_cubit/menu_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<void> init() async {
|
||||
// Cubit singletons
|
||||
/// Инициализация системы внедрения зависимостей
|
||||
Future<void> 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>(() => WsManager(wsService: sl()));
|
||||
sl.registerLazySingleton<NewMessagesCubit>(() => NewMessagesCubit());
|
||||
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl()));
|
||||
sl.registerLazySingleton<ProfileAvatarCubit>(() => ProfileAvatarCubit(profileRepository: sl()));
|
||||
sl.registerLazySingleton<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
|
||||
|
||||
// Cubit factories
|
||||
sl.registerFactory<FeedCubit>(() => FeedCubit(feedRepository: sl()));
|
||||
sl.registerFactory<RegistrationCubit>(() => RegistrationCubit(registrationRepository: sl()));
|
||||
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
|
||||
sl.registerFactory<RecipesCubit>(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
|
||||
sl.registerFactory<CalendarCubit>(() => CalendarCubit(calendarRepository: sl()));
|
||||
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
|
||||
sl.registerFactory<profile_plans.PlansCubit>(() => profile_plans.PlansCubit(plansRepository: sl()));
|
||||
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
||||
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl(), commentsRepository: sl()));
|
||||
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
|
||||
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl(), consultationsRepository: sl()));
|
||||
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
||||
sl.registerFactory<DiaryCubit>(
|
||||
() => DiaryCubit(
|
||||
chatRepository: sl(),
|
||||
diaryRepository: sl(),
|
||||
glossaryRepository: sl(),
|
||||
profileRepository: sl(),
|
||||
dietRepository: sl(),
|
||||
settingsRepository: sl(),
|
||||
),
|
||||
);
|
||||
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
|
||||
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
|
||||
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));
|
||||
sl.registerFactory<CreateDietCubit>(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit(createPlanRepository: sl()));
|
||||
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
|
||||
sl.registerFactory<AboutPlanCubit>(() => AboutPlanCubit(planRepository: sl()));
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit(recipeRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<MyDietsCubit>(() => MyDietsCubit(myDietsRepository: sl(), createDietRepository: sl()));
|
||||
sl.registerFactory<MyPlansCubit>(() => MyPlansCubit(myPlansRepository: sl()));
|
||||
sl.registerFactory<UserDetailsCubit>(() => UserDetailsCubit(profileRepository: sl()));
|
||||
sl.registerFactory<ConsultantsCubit>(() => ConsultantsCubit(feedRepository: sl()));
|
||||
sl.registerFactory<AboutRecipeCubit>(() => AboutRecipeCubit(recipeRepository: sl(), profileRepository: sl()));
|
||||
sl.registerFactory<AuthCubit>(() => AuthCubit(authRepository: sl()));
|
||||
sl.registerFactory<ComplainingCubit>(() => ComplainingCubit(repository: sl()));
|
||||
sl.registerFactory<UserCardsCubit>(() => UserCardsCubit(cardsRepository: sl()));
|
||||
sl.registerFactory<OrderInfoCubit>(() => OrderInfoCubit(chatRepository: sl()));
|
||||
sl.registerFactory<ConsultationsCubit>(() => ConsultationsCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<ConsultantCommentsCubit>(() => ConsultantCommentsCubit(diaryRepository: sl()));
|
||||
sl.registerFactory<CancelPlanCubit>(() => CancelPlanCubit(cancelPlanRepository: sl()));
|
||||
sl.registerFactory<VerifyEmailCubit>(() => VerifyEmailCubit(profileRepository: sl()));
|
||||
sl.registerFactory<MyRecipesCubit>(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
|
||||
sl.registerFactory<ConsultationsArchiveCubit>(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<ConsultantFinancesCubit>(() => ConsultantFinancesCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<UserPurchasesCubit>(() => UserPurchasesCubit(consultationsRepository: sl()));
|
||||
sl.registerFactory<ResetPasswordCubit>(() => ResetPasswordCubit(resetPasswordRepository: sl()));
|
||||
sl.registerFactory<BlogPostCubit>(() => BlogPostCubit(sl()));
|
||||
sl.registerFactory<BlockedUsersCubit>(() => BlockedUsersCubit(blockedUsersRepository: sl()));
|
||||
sl.registerFactory<NotificationSettingsCubit>(() => NotificationSettingsCubit(notificationSettingsRepository: sl()));
|
||||
sl.registerFactory<ConsultantRatingCubit>(() => ConsultantRatingCubit(sl()));
|
||||
sl.registerFactory<ViewsCubit>(() => ViewsCubit(viewsRepository: sl()));
|
||||
sl.registerFactory<MenuCubit>(() => MenuCubit(sl()));
|
||||
|
||||
// Repository
|
||||
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));
|
||||
sl.registerLazySingleton<FeedRepository>(() => FeedRepository(feedService: sl()));
|
||||
sl.registerLazySingleton<RegistrationRepository>(() => RegistrationRepository(registrationService: sl()));
|
||||
sl.registerLazySingleton<CalendarRepository>(() => CalendarRepository(calendarService: sl()));
|
||||
sl.registerLazySingleton<ProfileRepository>(() => ProfileRepository(profileService: sl()));
|
||||
sl.registerLazySingleton<RecipeRepository>(() => RecipeRepository(recipeService: sl()));
|
||||
sl.registerLazySingleton<SettingsRepository>(() => SettingsRepository(settingsService: sl()));
|
||||
sl.registerLazySingleton<ConsultantRepository>(() => ConsultantRepository(consultantService: sl()));
|
||||
sl.registerLazySingleton<DiaryRepository>(() => DiaryRepository(diaryService: sl()));
|
||||
sl.registerLazySingleton<GlossaryRepository>(() => GlossaryRepository(glossaryService: sl()));
|
||||
sl.registerLazySingleton<DietRepository>(() => DietRepository(dietsService: sl()));
|
||||
sl.registerLazySingleton<BlogPostRepository>(() => BlogPostRepository(blogPostService: sl()));
|
||||
sl.registerLazySingleton<CreateDietRepository>(() => CreateDietRepository(createDietService: sl()));
|
||||
sl.registerLazySingleton<PlansRepository>(() => PlansRepository(plansService: sl()));
|
||||
sl.registerLazySingleton<PlanRepository>(() => PlanRepository(planService: sl()));
|
||||
sl.registerLazySingleton<MyDietsRepository>(() => MyDietsRepository(myDietsService: sl()));
|
||||
sl.registerLazySingleton<MyPlansRepository>(() => MyPlansRepository(myPlansService: sl()));
|
||||
sl.registerLazySingleton<AboutRecipeRepository>(() => AboutRecipeRepository(recipeService: sl()));
|
||||
sl.registerLazySingleton<ComplainingRepository>(() => ComplainingRepository(service: sl()));
|
||||
sl.registerLazySingleton<AuthRepository>(() => AuthRepository(authService: sl()));
|
||||
sl.registerLazySingleton<CardsRepository>(() => CardsRepository(cardsService: sl()));
|
||||
sl.registerLazySingleton<ConsultationsRepository>(() => ConsultationsRepository(consultationService: sl()));
|
||||
sl.registerLazySingleton<CancelPlanRepository>(() => CancelPlanRepository(cancelService: sl()));
|
||||
sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl()));
|
||||
sl.registerLazySingleton<NotificationsRepository>(() => NotificationsRepository(notificationsService: sl()));
|
||||
sl.registerFactory<MyRecipesRepository>(() => MyRecipesRepository(myRecipesService: sl()));
|
||||
sl.registerFactory<ResetPasswordRepository>(() => ResetPasswordRepository(resetPasswordService: sl()));
|
||||
sl.registerLazySingleton<BlockedUsersRepository>(() => BlockedUsersRepository(blockedUserService: sl()));
|
||||
sl.registerLazySingleton<CommentsRepository>(() => CommentsRepository(service: sl()));
|
||||
sl.registerLazySingleton<NotificationSettingsRepository>(() => NotificationSettingsRepository(sl()));
|
||||
sl.registerFactory<ConsultantRatingRepository>(() => ConsultantRatingRepository(sl()));
|
||||
sl.registerFactory<ViewsRepository>(() => ViewsRepository(viewsService: sl()));
|
||||
|
||||
// Services
|
||||
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
|
||||
sl.registerLazySingleton<FeedService>(() => FeedService(sl()));
|
||||
sl.registerLazySingleton<RegistrationService>(() => RegistrationService(sl()));
|
||||
sl.registerLazySingleton<CalendarService>(() => CalendarService(sl()));
|
||||
sl.registerLazySingleton<ProfileService>(() => ProfileService(sl()));
|
||||
sl.registerLazySingleton<CreateRecipeService>(() => CreateRecipeService(sl()));
|
||||
sl.registerLazySingleton<SettingsService>(() => SettingsService(sl()));
|
||||
sl.registerLazySingleton<ConsultantService>(() => ConsultantService(sl()));
|
||||
sl.registerLazySingleton<DiaryService>(() => DiaryService(sl()));
|
||||
sl.registerLazySingleton<GlossaryService>(() => GlossaryService(sl()));
|
||||
sl.registerLazySingleton<DietService>(() => DietService(sl()));
|
||||
sl.registerLazySingleton<BlogPostService>(() => BlogPostService(sl()));
|
||||
sl.registerLazySingleton<CreateDietService>(() => CreateDietService(sl()));
|
||||
sl.registerLazySingleton<PlansService>(() => PlansService(sl()));
|
||||
sl.registerLazySingleton<PlanService>(() => PlanService(sl()));
|
||||
sl.registerLazySingleton<MyDietsService>(() => MyDietsService(sl()));
|
||||
sl.registerLazySingleton<MyPlansService>(() => MyPlansService(sl()));
|
||||
sl.registerLazySingleton<WsService>(() => WsService(sl()));
|
||||
sl.registerLazySingleton<AboutRecipeService>(() => AboutRecipeService(sl()));
|
||||
sl.registerLazySingleton<ComplainingService>(() => ComplainingService(sl()));
|
||||
sl.registerLazySingleton<AuthService>(() => AuthService(sl()));
|
||||
sl.registerLazySingleton<CardsService>(() => CardsService(sl()));
|
||||
sl.registerLazySingleton<ConsultationService>(() => ConsultationService(sl()));
|
||||
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
|
||||
sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl()));
|
||||
sl.registerLazySingleton<NotificationsService>(() => NotificationsService(sl()));
|
||||
sl.registerLazySingleton<FirebaseApi>(() => FirebaseApi());
|
||||
sl.registerLazySingleton<ResetPasswordService>(() => ResetPasswordService(sl()));
|
||||
sl.registerLazySingleton<BlockedUserService>(() => BlockedUserService(sl()));
|
||||
sl.registerLazySingleton<CommentsService>(() => CommentsService(sl()));
|
||||
sl.registerLazySingleton<MyRecipesService>(() => MyRecipesService(sl()));
|
||||
sl.registerLazySingleton<ConsultantRatingService>(() => ConsultantRatingService(sl()));
|
||||
sl.registerLazySingleton<NotificationSettingsService>(() => NotificationSettingsService(sl()));
|
||||
sl.registerLazySingleton<ViewsService>(() => ViewsService(sl()));
|
||||
|
||||
// External
|
||||
sl.registerLazySingleton(
|
||||
() =>
|
||||
Dio(BaseOptions(baseUrl: baseUrl, headers: {'accept': 'application/json', 'content-type': 'application/json'}))
|
||||
..interceptors.addAll([TokenInterceptor(), PhoneVerifyErrorInterceptor()]),
|
||||
);
|
||||
}
|
||||
|
||||
47
lib/core/routing/auth_routes.dart
Normal file
47
lib/core/routing/auth_routes.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:eatvio_app/core/routing/route_constants.dart';
|
||||
import 'package:eatvio_app/features/auth/entities/auth_result_entity.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/register_via_social_screen.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/pages/scan_qr_code_screen.dart';
|
||||
import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart';
|
||||
import 'package:eatvio_app/features/registration/presentation/pages/verify_screen.dart';
|
||||
import 'package:eatvio_app/features/registration/presentation/pages/verify_email_screen.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
/// Маршруты для аутентификации
|
||||
class AuthRoutes {
|
||||
/// Создание маршрутов аутентификации
|
||||
static List<RouteBase> createRoutes() {
|
||||
return [
|
||||
GoRoute(
|
||||
path: RouteConstants.authScreen,
|
||||
name: 'authScreen',
|
||||
builder: (context, state) => const AuthScreen(),
|
||||
routes: [
|
||||
GoRoute(path: RouteConstants.registrationScreen, name: 'registrationScreen', builder: (context, state) => const RegistrationScreen()),
|
||||
GoRoute(
|
||||
path: RouteConstants.forgetPasswordScreen,
|
||||
name: 'forgetPasswordScreen',
|
||||
builder: (context, state) => ForgetPasswordScreen(isAuth: state.extra as bool),
|
||||
),
|
||||
GoRoute(path: RouteConstants.scanQRCodeScreen, name: 'scanQRCodeScreen', builder: (context, state) => const ScanQRCodeScreen()),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.verifyScreen,
|
||||
name: 'verifyScreen',
|
||||
builder: (context, state) => VerifyScreen(passKey: state.extra as String),
|
||||
),
|
||||
GoRoute(path: RouteConstants.verifyEmailScreen, name: 'verifyEmailScreen', builder: (context, state) => const VerifyEmailScreen()),
|
||||
GoRoute(
|
||||
path: RouteConstants.registerViaSocialScreen,
|
||||
name: 'registerViaSocialScreen',
|
||||
builder: (context, state) {
|
||||
final extra = state.extra as Map<String, dynamic>?;
|
||||
return RegisterViaSocialScreen(authResult: extra?['authResult'] as AuthResultEntity?, provider: extra?['provider'] as String?);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
78
lib/core/routing/content_routes.dart
Normal file
78
lib/core/routing/content_routes.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'package:eatvio_app/core/routing/route_constants.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/diets/presentation/pages/about_diet_screen.dart';
|
||||
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart';
|
||||
import 'package:eatvio_app/features/recipe/presentation/pages/about_recipe_screen.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
/// Маршруты для контента
|
||||
class ContentRoutes {
|
||||
/// Создание маршрутов контента
|
||||
static List<RouteBase> createRoutes() {
|
||||
return [
|
||||
GoRoute(
|
||||
path: RouteConstants.aboutPlanScreen,
|
||||
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<String, dynamic>;
|
||||
return AboutPlanScreen(slug: data['slug'] as String, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.aboutRecipesScreen,
|
||||
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<String, dynamic>;
|
||||
return AboutRecipeScreen(recipeSlug: data['recipe'] as String, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.aboutDietScreen,
|
||||
name: 'aboutDietScreen',
|
||||
builder: (context, state) {
|
||||
final slug = state.uri.queryParameters['slug'];
|
||||
if (slug != null) {
|
||||
return AboutDietScreen(id: null, slug: slug, index: null, navigateToComments: false);
|
||||
}
|
||||
final data = state.extra as Map<String, dynamic>;
|
||||
return AboutDietScreen(
|
||||
id: data['id'] as int?,
|
||||
slug: data['slug'] as String?,
|
||||
index: data['index'] as int?,
|
||||
navigateToComments: data['navigateToComments'] as bool,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.blogPostCreateScreen,
|
||||
name: 'blogPostCreateScreen',
|
||||
builder: (context, state) => BlogPostCreateScreen(feedBlogPostModel: state.extra != null ? state.extra as FeedBlogPostModel : null),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.blogPostScreen,
|
||||
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);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
94
lib/core/routing/main_navigation_routes.dart
Normal file
94
lib/core/routing/main_navigation_routes.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:eatvio_app/core/routing/route_constants.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/chat/presentation/pages/chats_screen.dart';
|
||||
import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/pages/feed_screen.dart';
|
||||
import 'package:eatvio_app/features/menu/presentation/pages/menu_screen.dart';
|
||||
import 'package:eatvio_app/features/notifications/presentation/notifications_screen.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_manager.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
/// Маршруты для основной навигации
|
||||
class MainNavigationRoutes {
|
||||
/// Создание маршрутов основной навигации
|
||||
static List<RouteBase> createRoutes() {
|
||||
return [
|
||||
ShellRoute(
|
||||
builder: (context, state, child) {
|
||||
return BlocBuilder<ProfileAvatarCubit, ProfileAvatarState>(
|
||||
builder: (BuildContext context, ProfileAvatarState state) {
|
||||
if (state is ProfileAvatarStateLoaded) {
|
||||
return BottomNavigationBarWidget(profileModel: state.profile, child: child);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: RouteConstants.myProfileScreen,
|
||||
name: 'myProfileScreen',
|
||||
pageBuilder: (context, state) {
|
||||
return const NoTransitionPage(child: ProfileScreen());
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.feedScreen,
|
||||
name: 'feedScreen',
|
||||
pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen()),
|
||||
redirect: (context, state) async {
|
||||
try {
|
||||
await sl<WsManager>().initialize();
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.diaryScreen,
|
||||
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: RouteConstants.chatSelectScreen,
|
||||
name: 'chatSelectScreen',
|
||||
pageBuilder: (context, state) => const NoTransitionPage(child: ChatsScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.notificationsScreen,
|
||||
name: 'notificationsScreen',
|
||||
pageBuilder: (context, state) => const NoTransitionPage(child: NotificationsScreen()),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.menuScreen,
|
||||
name: 'menuScreen',
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: BlocBuilder<ProfileAvatarCubit, ProfileAvatarState>(
|
||||
builder: (BuildContext context, ProfileAvatarState state) {
|
||||
if (state is ProfileAvatarStateLoaded) {
|
||||
return MenuScreen(profileModel: state.profile);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
229
lib/core/routing/other_routes.dart
Normal file
229
lib/core/routing/other_routes.dart
Normal file
@@ -0,0 +1,229 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/routing/route_constants.dart';
|
||||
import 'package:eatvio_app/core/token_storage.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/diary/presentation/pages/diary_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/consultant_rating/presentation/consultant_rating_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/create_recipe/presentation/pages/recipe_create_screen.dart';
|
||||
import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart';
|
||||
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
|
||||
import 'package:eatvio_app/features/glossary/presentation/pages/glossary_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/my_diets/presentation/my_diets_screen.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.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/order_info/presentation/info_by_order_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/my_recipes_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/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
|
||||
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart';
|
||||
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.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';
|
||||
|
||||
/// Остальные маршруты приложения
|
||||
class OtherRoutes {
|
||||
/// Создание остальных маршрутов
|
||||
static List<RouteBase> createRoutes() {
|
||||
return [
|
||||
// Welcome and update screens
|
||||
GoRoute(
|
||||
path: RouteConstants.welcomeScreen,
|
||||
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<FirebaseApi>().registerPushToken();
|
||||
if (welcomeScreenWasShowed == false) {
|
||||
return null;
|
||||
} else if (await TokenStorage.haveTokenInStorage()) {
|
||||
return RouteConstants.feedScreen;
|
||||
} else {
|
||||
return RouteConstants.authScreen;
|
||||
}
|
||||
} catch (e) {
|
||||
return RouteConstants.feedScreen;
|
||||
}
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.updateScreen,
|
||||
name: 'updateScreen',
|
||||
builder: (context, state) => const UpdateScreen(),
|
||||
redirect: (context, state) async {
|
||||
try {
|
||||
final versions = (await sl<Dio>().get('${sl<AppConfig>().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 RouteConstants.welcomeScreen;
|
||||
}
|
||||
} catch (e) {
|
||||
return RouteConstants.welcomeScreen;
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
// Archive routes
|
||||
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()),
|
||||
|
||||
// Finance routes
|
||||
GoRoute(path: RouteConstants.userFinanceScreen, 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);
|
||||
},
|
||||
),
|
||||
|
||||
// Content creation routes
|
||||
GoRoute(
|
||||
path: RecipeCreateScreen.path,
|
||||
name: RecipeCreateScreen.name,
|
||||
builder: (context, state) => RecipeCreateScreen(recipe: state.extra != null ? state.extra as Recipe : null),
|
||||
),
|
||||
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: RouteConstants.createPlanScreen, name: 'createPlanScreen', 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: MyRecipesScreen.path, name: MyRecipesScreen.name, builder: (context, state) => const MyRecipesScreen()),
|
||||
|
||||
// Profile routes
|
||||
GoRoute(
|
||||
path: RouteConstants.profileScreen,
|
||||
name: 'profileScreen',
|
||||
builder: (context, state) => ProfileScreen(author: state.extra as Author),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.subsScreen,
|
||||
name: 'subsScreen',
|
||||
builder: (context, state) => SubsScreen(slug: state.pathParameters['slug']!, subType: state.extra as SubType),
|
||||
),
|
||||
|
||||
// Chat routes
|
||||
GoRoute(
|
||||
path: RouteConstants.chatScreen,
|
||||
name: 'chatScreen',
|
||||
builder: (context, state) {
|
||||
final data = state.extra as Map<String, dynamic>;
|
||||
final uuid = data['uuid'] as String;
|
||||
final sendInitialMessage = data['sendInitialMessage'] as bool?;
|
||||
return ChatScreen(uuid: uuid, sendInitialMessage: sendInitialMessage);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.emptyChatScreen,
|
||||
name: 'emptyChatScreen',
|
||||
builder: (context, state) => EmptyChatScreen(author: state.extra as Author),
|
||||
),
|
||||
|
||||
// Consultant routes
|
||||
GoRoute(path: RouteConstants.becomeConsultantScreen, name: 'becomeConsultantScreen', builder: (context, state) => const BecomeConsultantScreen()),
|
||||
GoRoute(
|
||||
path: RouteConstants.stepperBecomeConsultantScreen,
|
||||
name: 'stepperBecomeConsultantScreen',
|
||||
builder: (context, state) => StepperBecomeConsultantScreen(slug: state.extra == null ? null : state.extra as String),
|
||||
),
|
||||
GoRoute(path: RouteConstants.calendarScreen, name: 'calendarScreen', builder: (context, state) => const CalendarScreen()),
|
||||
GoRoute(path: RouteConstants.supportScreen, name: 'supportScreen', builder: (context, state) => const SupportScreen()),
|
||||
GoRoute(
|
||||
path: RouteConstants.photoViewScreen,
|
||||
name: 'photoViewScreen',
|
||||
builder: (context, state) => PhotoViewScreen(imageUrls: state.extra as List<String>, index: int.parse(state.pathParameters['index']!)),
|
||||
),
|
||||
|
||||
// Other routes
|
||||
GoRoute(path: RouteConstants.glossaryScreen, name: 'glossaryScreen', builder: (context, state) => GlossaryScreen(passKey: state.extra as String)),
|
||||
GoRoute(path: RouteConstants.blockedUsersScreen, name: 'blockedUsersScreen', builder: (context, state) => const BlockedUsersScreen()),
|
||||
GoRoute(
|
||||
path: RouteConstants.notificationsSettingsScreen,
|
||||
name: 'notificationsSettingsScreen',
|
||||
builder: (context, state) => const NotificationsSettingsScreen(),
|
||||
),
|
||||
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<String, dynamic>;
|
||||
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<String, dynamic>;
|
||||
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<String, int>;
|
||||
final diaryId = data['diaryId'] as int;
|
||||
final currentPlanDay = data['currentDiaryDay'] as int;
|
||||
return UserDiaryScreen(diaryId: diaryId, currentPlanDay: currentPlanDay);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
85
lib/core/routing/route_constants.dart
Normal file
85
lib/core/routing/route_constants.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
/// Константы маршрутов приложения
|
||||
class RouteConstants {
|
||||
// Auth routes
|
||||
static const String authScreen = '/auth-screen';
|
||||
static const String registrationScreen = 'registration-screen';
|
||||
static const String forgetPasswordScreen = 'forget-password-screen';
|
||||
static const String scanQRCodeScreen = 'scan-qr-code-screen';
|
||||
static const String verifyScreen = '/verify-screen';
|
||||
static const String verifyEmailScreen = '/verify-email-screen';
|
||||
static const String registerViaSocialScreen = '/register-via-social-screen';
|
||||
|
||||
// Main navigation routes
|
||||
static const String myProfileScreen = '/my-profile-screen';
|
||||
static const String feedScreen = '/feed-screen';
|
||||
static const String diaryScreen = '/diary-screen';
|
||||
static const String consultationsScreen = '/consultations-screen';
|
||||
static const String chatSelectScreen = '/chat-select-screen';
|
||||
static const String notificationsScreen = '/notifications-screen';
|
||||
static const String menuScreen = '/menu-screen';
|
||||
|
||||
// Content routes
|
||||
static const String aboutPlanScreen = '/about-plan-screen';
|
||||
static const String aboutRecipesScreen = '/about-recipes-screen';
|
||||
static const String aboutDietScreen = '/about-diet-screen';
|
||||
static const String blogPostScreen = '/blog-post-screen';
|
||||
static const String blogPostCreateScreen = '/blog-post-create-screen';
|
||||
|
||||
// Profile routes
|
||||
static const String profileScreen = '/profile-screen';
|
||||
static const String subsScreen = '/subs-screen';
|
||||
|
||||
// Settings routes
|
||||
static const String settingsScreen = '/settings-screen';
|
||||
static const String generalInfoScreen = 'general-info-screen';
|
||||
static const String otherInfoScreen = 'other-info-screen';
|
||||
static const String physicalActivityScreen = 'physical-activity-screen';
|
||||
static const String physicalFormScreen = 'physical-form-screen';
|
||||
static const String pressureScreen = 'pressure-screen';
|
||||
static const String changePasswordScreen = 'change-password-screen';
|
||||
static const String changeEmailScreen = 'change-email-screen';
|
||||
|
||||
// Consultant routes
|
||||
static const String becomeConsultantScreen = '/become-consultant-screen';
|
||||
static const String stepperBecomeConsultantScreen = '/stepper-become-consultant-screen';
|
||||
static const String consultantRatingScreen = '/consultant-rating-screen';
|
||||
|
||||
// Other routes
|
||||
static const String welcomeScreen = '/welcome-screen';
|
||||
static const String updateScreen = '/update-screen';
|
||||
static const String calendarScreen = '/calendar-screen';
|
||||
static const String supportScreen = '/support-screen';
|
||||
static const String photoViewScreen = '/photo-view-screen';
|
||||
static const String chatScreen = '/chat-screen';
|
||||
static const String emptyChatScreen = '/empty-chat-screen';
|
||||
static const String glossaryScreen = '/glossary-screen';
|
||||
static const String blockedUsersScreen = '/blocked-users-screen';
|
||||
static const String notificationsSettingsScreen = '/notifications-settings-screen';
|
||||
|
||||
// Finance routes
|
||||
static const String userFinanceScreen = '/user-finance-screen';
|
||||
static const String cardSettingsScreen = '/card-settings-screen';
|
||||
static const String orderInfoScreen = '/order-info-screen';
|
||||
static const String paymentScreen = '/payment-screen';
|
||||
|
||||
// Content creation routes
|
||||
static const String recipeCreateScreen = '/recipe-create-screen';
|
||||
static const String createDietScreen = '/create-diet-screen';
|
||||
static const String dietPreviewScreen = '/diet-preview-screen';
|
||||
static const String createPlanScreen = '/create-plan-screen';
|
||||
static const String myDietsScreen = '/my-diets-screen';
|
||||
static const String myPlansScreen = '/my-plans-screen';
|
||||
static const String myRecipesScreen = '/my-recipes-screen';
|
||||
|
||||
// User details
|
||||
static const String userDetailsScreen = '/user-details-screen';
|
||||
static const String userDiaryScreen = '/user-diary-screen';
|
||||
|
||||
// Dev tools
|
||||
static const String devToolsScreen = '/dev-tools-screen';
|
||||
|
||||
// Archive routes
|
||||
static const String consultationsArchiveScreen = '/consultations-archive-screen';
|
||||
static const String consultantFinancesScreen = '/consultant-finances-screen';
|
||||
static const String userPurchasesScreen = '/user-purchases-screen';
|
||||
}
|
||||
62
lib/core/routing/settings_routes.dart
Normal file
62
lib/core/routing/settings_routes.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
import 'package:eatvio_app/core/routing/route_constants.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:go_router/go_router.dart';
|
||||
|
||||
/// Маршруты для настроек
|
||||
class SettingsRoutes {
|
||||
/// Создание маршрутов настроек
|
||||
static List<RouteBase> createRoutes() {
|
||||
return [
|
||||
GoRoute(
|
||||
path: RouteConstants.settingsScreen,
|
||||
name: 'settingsScreen',
|
||||
builder: (context, state) => const SettingsScreen(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: RouteConstants.generalInfoScreen,
|
||||
name: 'generalInfoScreen',
|
||||
builder: (context, state) => GeneralInfoScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.otherInfoScreen,
|
||||
name: 'otherInfoScreen',
|
||||
builder: (context, state) => OtherInfoScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.physicalActivityScreen,
|
||||
name: 'physicalActivityScreen',
|
||||
builder: (context, state) => PhysicalActivityScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.physicalFormScreen,
|
||||
name: 'physicalFormScreen',
|
||||
builder: (context, state) => PhysicalFormScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.pressureScreen,
|
||||
name: 'pressureScreen',
|
||||
builder: (context, state) => PressureScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.changePasswordScreen,
|
||||
name: 'changePasswordScreen',
|
||||
builder: (context, state) => ChangePasswordScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
GoRoute(
|
||||
path: RouteConstants.changeEmailScreen,
|
||||
name: 'changeEmailScreen',
|
||||
builder: (context, state) => ChangeEmailScreen(settingsEntity: state.extra as SettingsEntity),
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -38,4 +38,16 @@ void showSuccessToast(String message) {
|
||||
textColor: appTheme.whiteA700,
|
||||
fontSize: 16.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showInternetConnectionToast() {
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Проверьте подключение к интернету',
|
||||
toastLength: Toast.LENGTH_LONG,
|
||||
gravity: ToastGravity.TOP,
|
||||
timeInSecForIosWeb: 3,
|
||||
backgroundColor: appTheme.red,
|
||||
textColor: Colors.white,
|
||||
fontSize: 16.0,
|
||||
);
|
||||
}
|
||||
|
||||
87
lib/core/utils/validation_utils.dart
Normal file
87
lib/core/utils/validation_utils.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
import 'package:eatvio_app/core/constants/app_constants.dart';
|
||||
|
||||
/// Утилиты для валидации
|
||||
class ValidationUtils {
|
||||
/// Валидация email
|
||||
static bool isValidEmail(String email) {
|
||||
if (email.isEmpty) return false;
|
||||
final regex = RegExp(AppConstants.emailRegex);
|
||||
return regex.hasMatch(email);
|
||||
}
|
||||
|
||||
/// Валидация телефона
|
||||
static bool isValidPhone(String phone) {
|
||||
if (phone.isEmpty) return false;
|
||||
final regex = RegExp(AppConstants.phoneRegex);
|
||||
return regex.hasMatch(phone);
|
||||
}
|
||||
|
||||
/// Валидация URL
|
||||
static bool isValidUrl(String url) {
|
||||
if (url.isEmpty) return false;
|
||||
final regex = RegExp(AppConstants.urlRegex);
|
||||
return regex.hasMatch(url);
|
||||
}
|
||||
|
||||
/// Валидация длины текста
|
||||
static bool isValidTextLength(String text, {int? maxLength}) {
|
||||
final length = maxLength ?? AppConstants.maxTextLength;
|
||||
return text.length <= length;
|
||||
}
|
||||
|
||||
/// Валидация длины заголовка
|
||||
static bool isValidTitleLength(String title) {
|
||||
return title.isNotEmpty && title.length <= AppConstants.maxTitleLength;
|
||||
}
|
||||
|
||||
/// Валидация длины описания
|
||||
static bool isValidDescriptionLength(String description) {
|
||||
return description.length <= AppConstants.maxDescriptionLength;
|
||||
}
|
||||
|
||||
/// Валидация пароля
|
||||
static bool isValidPassword(String password) {
|
||||
// Минимум 8 символов, хотя бы одна буква и одна цифра
|
||||
if (password.length < 8) return false;
|
||||
final hasLetter = RegExp(r'[a-zA-Z]').hasMatch(password);
|
||||
final hasDigit = RegExp(r'[0-9]').hasMatch(password);
|
||||
return hasLetter && hasDigit;
|
||||
}
|
||||
|
||||
/// Валидация имени пользователя
|
||||
static bool isValidUsername(String username) {
|
||||
// От 3 до 20 символов, только буквы, цифры и подчеркивания
|
||||
if (username.length < 3 || username.length > 20) return false;
|
||||
final regex = RegExp(r'^[a-zA-Z0-9_]+$');
|
||||
return regex.hasMatch(username);
|
||||
}
|
||||
|
||||
/// Получение сообщения об ошибке валидации
|
||||
static String? getValidationErrorMessage(String? value, ValidationType type) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Поле не может быть пустым';
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case ValidationType.email:
|
||||
return isValidEmail(value) ? null : 'Введите корректный email';
|
||||
case ValidationType.phone:
|
||||
return isValidPhone(value) ? null : 'Введите корректный номер телефона';
|
||||
case ValidationType.url:
|
||||
return isValidUrl(value) ? null : 'Введите корректный URL';
|
||||
case ValidationType.password:
|
||||
return isValidPassword(value) ? null : 'Пароль должен содержать минимум 8 символов, включая буквы и цифры';
|
||||
case ValidationType.username:
|
||||
return isValidUsername(value) ? null : 'Имя пользователя должно содержать от 3 до 20 символов (буквы, цифры, подчеркивания)';
|
||||
case ValidationType.title:
|
||||
return isValidTitleLength(value) ? null : 'Заголовок не может быть пустым и должен содержать не более ${AppConstants.maxTitleLength} символов';
|
||||
case ValidationType.description:
|
||||
return isValidDescriptionLength(value) ? null : 'Описание не может содержать более ${AppConstants.maxDescriptionLength} символов';
|
||||
case ValidationType.text:
|
||||
return isValidTextLength(value) ? null : 'Текст не может содержать более ${AppConstants.maxTextLength} символов';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Типы валидации
|
||||
enum ValidationType { email, phone, url, password, username, title, description, text }
|
||||
@@ -3,6 +3,7 @@ import 'dart:developer';
|
||||
|
||||
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_service.dart';
|
||||
@@ -111,7 +112,7 @@ class WsManager {
|
||||
_privateNotificationChannel = client?.privateChannel(
|
||||
'$newNotificationsChannelName.$userId',
|
||||
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
|
||||
authorizationEndpoint: Uri.parse('$baseUrl/broadcasting/auth'),
|
||||
authorizationEndpoint: Uri.parse('${sl<AppConfig>().baseUrl}/broadcasting/auth'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
@@ -124,7 +125,7 @@ class WsManager {
|
||||
_privateMessagesChannel = client?.privateChannel(
|
||||
'$newMessagesChannelName.$userId',
|
||||
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
|
||||
authorizationEndpoint: Uri.parse('$baseUrl/broadcasting/auth'),
|
||||
authorizationEndpoint: Uri.parse('${sl<AppConfig>().baseUrl}/broadcasting/auth'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
@@ -137,7 +138,7 @@ class WsManager {
|
||||
_privateDirectChatChannel = client?.privateChannel(
|
||||
'$directChatChannelName.$chatId',
|
||||
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
|
||||
authorizationEndpoint: Uri.parse('$baseUrl/broadcasting/auth'),
|
||||
authorizationEndpoint: Uri.parse('${sl<AppConfig>().baseUrl}/broadcasting/auth'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
@@ -86,7 +86,7 @@ class CustomPopup extends StatelessWidget {
|
||||
|
||||
if (isAuthor) {
|
||||
addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () {
|
||||
Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
|
||||
Clipboard.setData(ClipboardData(text: '${sl<AppConfig>().baseUrl}/ru/$path'));
|
||||
showInfoToast(s.link_was_copied);
|
||||
});
|
||||
addItem('assets/icons/share2.svg', 'Поделиться', () {
|
||||
@@ -94,7 +94,7 @@ class CustomPopup extends StatelessWidget {
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
'${sl<AppConfig>().baseUrl}/ru/$path',
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
@@ -140,7 +140,7 @@ class CustomPopup extends StatelessWidget {
|
||||
});
|
||||
} else {
|
||||
addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () {
|
||||
Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
|
||||
Clipboard.setData(ClipboardData(text: '${sl<AppConfig>().baseUrl}/ru/$path'));
|
||||
showInfoToast(s.link_was_copied);
|
||||
});
|
||||
addItem('assets/icons/share2.svg', 'Поделиться', () {
|
||||
@@ -148,7 +148,7 @@ class CustomPopup extends StatelessWidget {
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
'${sl<AppConfig>().baseUrl}/ru/$path',
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
|
||||
52
lib/features/auth/entities/auth_result_entity.dart
Normal file
52
lib/features/auth/entities/auth_result_entity.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:vk_id/vk_id.dart';
|
||||
|
||||
part 'auth_result_entity.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class AuthResultEntity with _$AuthResultEntity {
|
||||
const factory AuthResultEntity({
|
||||
required String accessToken,
|
||||
required String userId,
|
||||
required String clientId,
|
||||
String? refreshToken,
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
String? email,
|
||||
String? phone,
|
||||
GenderEnum? gender,
|
||||
String? birthDate,
|
||||
}) = _AuthResultEntity;
|
||||
}
|
||||
|
||||
enum GenderEnum {
|
||||
male,
|
||||
female,
|
||||
notSet;
|
||||
|
||||
static GenderEnum? fromVkGender(VkSex? vkGender) {
|
||||
switch (vkGender) {
|
||||
case VkSex.male:
|
||||
return GenderEnum.male;
|
||||
case VkSex.female:
|
||||
return GenderEnum.female;
|
||||
case VkSex.notStated:
|
||||
return GenderEnum.notSet;
|
||||
case null:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static GenderEnum? fromString(String? gender) {
|
||||
switch (gender) {
|
||||
case 'male':
|
||||
return GenderEnum.male;
|
||||
case 'female':
|
||||
return GenderEnum.female;
|
||||
case 'notSet':
|
||||
return GenderEnum.notSet;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
lib/features/auth/models/oauth_login_request.dart
Normal file
16
lib/features/auth/models/oauth_login_request.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'oauth_login_request.freezed.dart';
|
||||
part 'oauth_login_request.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class OAuthLoginRequest with _$OAuthLoginRequest {
|
||||
const factory OAuthLoginRequest({
|
||||
@JsonKey(name: 'client_id') required String clientId,
|
||||
@JsonKey(name: 'access_token') required String accessToken,
|
||||
@JsonKey(name: 'refresh_token') required String refreshToken,
|
||||
required String provider,
|
||||
}) = _OAuthLoginRequest;
|
||||
|
||||
factory OAuthLoginRequest.fromJson(Map<String, dynamic> json) => _$OAuthLoginRequestFromJson(json);
|
||||
}
|
||||
25
lib/features/auth/models/oauth_model.dart
Normal file
25
lib/features/auth/models/oauth_model.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'oauth_model.freezed.dart';
|
||||
part 'oauth_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class OAuthModel with _$OAuthModel {
|
||||
const factory OAuthModel({
|
||||
@JsonKey(name: 'access_token') required String accessToken,
|
||||
@JsonKey(name: 'refresh_token') required String refreshToken,
|
||||
@JsonKey(name: 'first_name') required String firstName,
|
||||
@JsonKey(name: 'last_name') required String lastName,
|
||||
@JsonKey(name: 'date_of_birth') required String dateOfBirth,
|
||||
@JsonKey(name: 'client_id') required String clientId,
|
||||
required String provider,
|
||||
required String id,
|
||||
required String email,
|
||||
required String phone,
|
||||
required String gender,
|
||||
required int weight,
|
||||
required int height,
|
||||
}) = _OAuthModel;
|
||||
|
||||
factory OAuthModel.fromJson(Map<String, dynamic> json) => _$OAuthModelFromJson(json);
|
||||
}
|
||||
30
lib/features/auth/models/yandex_profile_model.dart
Normal file
30
lib/features/auth/models/yandex_profile_model.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'yandex_profile_model.freezed.dart';
|
||||
part 'yandex_profile_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class YandexProfileModel with _$YandexProfileModel {
|
||||
factory YandexProfileModel({
|
||||
required String id,
|
||||
required String login,
|
||||
required String birthday,
|
||||
@JsonKey(name: 'client_id') required String clientId,
|
||||
@JsonKey(name: 'display_name') required String displayName,
|
||||
@JsonKey(name: 'real_name') required String realName,
|
||||
@JsonKey(name: 'first_name') required String firstName,
|
||||
@JsonKey(name: 'last_name') required String lastName,
|
||||
@JsonKey(name: 'sex') required String sex,
|
||||
@JsonKey(name: 'default_email') required String defaultEmail,
|
||||
@JsonKey(name: 'default_phone') required YandexPhoneData defaultPhone,
|
||||
}) = _YandexProfileModel;
|
||||
|
||||
factory YandexProfileModel.fromJson(Map<String, dynamic> json) => _$YandexProfileModelFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class YandexPhoneData with _$YandexPhoneData {
|
||||
factory YandexPhoneData({required int id, required String number}) = _YandexPhoneData;
|
||||
|
||||
factory YandexPhoneData.fromJson(Map<String, dynamic> json) => _$YandexPhoneDataFromJson(json);
|
||||
}
|
||||
@@ -25,19 +25,4 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> getTokenBySocial({required String provider, required String accessToken}) async {
|
||||
emit(const AuthState.loading());
|
||||
try {
|
||||
final token = (await authRepository.getTokenBySocial(provider: provider, accessToken: accessToken)).token;
|
||||
emit(AuthState.loaded(token: token));
|
||||
await TokenStorage.saveTokenInStorage(token);
|
||||
return true;
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Не удалось войти через $provider');
|
||||
emit(AuthState.error(errorMessage: '$e'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
101
lib/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart
Normal file
101
lib/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
import 'dart:developer';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/token_storage.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_state.dart';
|
||||
import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class OAuthCubit extends Cubit<OAuthState> {
|
||||
final AuthRepository authRepository;
|
||||
|
||||
OAuthCubit({required this.authRepository}) : super(const OAuthState());
|
||||
|
||||
Future<void> signInViaSocialMedia({required String provider, required String accessToken, required String refreshToken, required String clientId}) async {
|
||||
try {
|
||||
emit(const OAuthState.loading());
|
||||
|
||||
final tokenModel = await authRepository.signInViaSocialMedia(
|
||||
provider: provider,
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
clientId: clientId,
|
||||
);
|
||||
|
||||
log('OAuthCubit: Got token model: ${tokenModel.token}');
|
||||
TokenStorage.saveTokenInStorage(tokenModel.token);
|
||||
emit(OAuthState.loaded(token: tokenModel.token));
|
||||
} catch (e) {
|
||||
log('OAuthCubit: Error occurred: $e');
|
||||
emit(OAuthState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<RegistrationValidationErrors>?> signUpViaSocialMedia({
|
||||
required String provider,
|
||||
required String accessToken,
|
||||
required String id,
|
||||
required String refreshToken,
|
||||
required String firstName,
|
||||
required String lastName,
|
||||
required String email,
|
||||
required String phone,
|
||||
required String dateOfBirth,
|
||||
required String gender,
|
||||
required int weight,
|
||||
required int height,
|
||||
required String clientId,
|
||||
}) async {
|
||||
try {
|
||||
emit(const OAuthState.loading());
|
||||
final token = await authRepository.signUpViaSocialMedia(
|
||||
provider: provider,
|
||||
accessToken: accessToken,
|
||||
id: id,
|
||||
refreshToken: refreshToken,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
email: email,
|
||||
phone: phone,
|
||||
dateOfBirth: dateOfBirth,
|
||||
gender: gender,
|
||||
weight: weight,
|
||||
height: height,
|
||||
clientId: clientId,
|
||||
);
|
||||
TokenStorage.saveTokenInStorage(token);
|
||||
emit(OAuthState.loaded(token: token));
|
||||
} on DioException catch (e) {
|
||||
emit(OAuthState.error(errorMessage: e.toString()));
|
||||
final errors = e.response?.data['errors'] as Map<String, dynamic>?;
|
||||
final emailErrors = errors?['email'] as List<dynamic>?;
|
||||
final phoneErrors = errors?['phone'] as List<dynamic>?;
|
||||
final dateOfBirth = errors?['date_of_birth'] as List<dynamic>?;
|
||||
final errorsList = <RegistrationValidationErrors>[];
|
||||
if (emailErrors != null && emailErrors.isNotEmpty) {
|
||||
if (emailErrors.contains('Некорректный формат email')) {
|
||||
errorsList.add(RegistrationValidationErrors.incorrectEmailFormat);
|
||||
}
|
||||
if (emailErrors.contains('Такой Эл. почта уже существует.')) {
|
||||
errorsList.add(RegistrationValidationErrors.emailExist);
|
||||
}
|
||||
}
|
||||
if (phoneErrors != null && phoneErrors.isNotEmpty) {
|
||||
if (phoneErrors.contains('Такой phone уже существует.')) {
|
||||
errorsList.add(RegistrationValidationErrors.phoneExist);
|
||||
}
|
||||
}
|
||||
if (dateOfBirth != null && dateOfBirth.isNotEmpty) {
|
||||
if (dateOfBirth.contains('Некорректный формат даты рождения')) {
|
||||
errorsList.add(RegistrationValidationErrors.incorrectDateOfBirthFormat);
|
||||
}
|
||||
}
|
||||
if (errorsList.isNotEmpty) {
|
||||
return errorsList;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
enum RegistrationValidationErrors { incorrectEmailFormat, emailExist, phoneExist, incorrectDateOfBirthFormat }
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'oauth_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class OAuthState with _$OAuthState {
|
||||
const factory OAuthState() = _OAuthState;
|
||||
|
||||
const factory OAuthState.loading() = OAuthStateLoading;
|
||||
|
||||
const factory OAuthState.loaded({required String token}) = OAuthStateLoaded;
|
||||
|
||||
const factory OAuthState.error({required String errorMessage}) = OAuthStateError;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// ignore_for_file: unused_element, duplicate_ignore
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'package:eatvio_app/app_keys.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
@@ -9,9 +10,14 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/image_constant.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_manager.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_state.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_state.dart';
|
||||
import 'package:eatvio_app/features/auth/entities/auth_result_entity.dart';
|
||||
import 'package:eatvio_app/core/di/cubit_registrations.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/dev_tools/presentation/dev_tools_screen.dart';
|
||||
@@ -44,7 +50,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
late StreamSubscription<bool> keyboardSubscription;
|
||||
bool isFocused = false;
|
||||
int tapsCount = 0;
|
||||
|
||||
bool _isLoading = false;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -87,9 +93,142 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
||||
}
|
||||
|
||||
void _onVkPressed() async {
|
||||
try {
|
||||
log('VK button pressed');
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) {
|
||||
log('Context is null');
|
||||
return;
|
||||
}
|
||||
|
||||
log('Getting VK auth result');
|
||||
final authResult = await SocialAuthFacade.signInWithVkAndFetchProfile(context: context);
|
||||
if (authResult == null) {
|
||||
log('VK auth result is null');
|
||||
return;
|
||||
}
|
||||
|
||||
log('VK auth result received, trying OAuth login');
|
||||
if (context.mounted) {
|
||||
await _tryOAuthLogin(context, authResult, 'vk');
|
||||
} else {
|
||||
log('Context is not mounted');
|
||||
}
|
||||
} catch (e) {
|
||||
log('VK button error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _onOkPressed() async {
|
||||
try {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return;
|
||||
|
||||
final authResult = await SocialAuthFacade.signInWithOkAndFetchProfile();
|
||||
if (authResult == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
await _tryOAuthLogin(context, authResult, 'vk');
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _onMailRuPressed() async {
|
||||
try {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return;
|
||||
|
||||
final authResult = await SocialAuthFacade.signInWithMailRuAndFetchProfile();
|
||||
if (authResult == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
await _tryOAuthLogin(context, authResult, 'vk');
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _onYandexPressed() async {
|
||||
try {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return;
|
||||
|
||||
final authResult = await SocialAuthFacade.signInWithYandex();
|
||||
if (authResult == null) return;
|
||||
|
||||
if (context.mounted) {
|
||||
await _tryOAuthLogin(context, authResult, 'yandex');
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _tryOAuthLogin(BuildContext context, AuthResultEntity authResult, String provider) async {
|
||||
log('Starting OAuth login for provider: $provider');
|
||||
|
||||
try {
|
||||
final oauthCubit = CubitRegistrations.sl<OAuthCubit>();
|
||||
StreamSubscription? subscription;
|
||||
|
||||
subscription = oauthCubit.stream.listen((state) {
|
||||
log('OAuth state changed: ${state.runtimeType}');
|
||||
|
||||
if (state is OAuthStateLoaded) {
|
||||
log('OAuth login successful, navigating to main screen');
|
||||
subscription?.cancel();
|
||||
_reconnectAfterAuth();
|
||||
} else if (state is OAuthStateError) {
|
||||
log('OAuth login failed: ${state.errorMessage}');
|
||||
subscription?.cancel();
|
||||
AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider);
|
||||
}
|
||||
});
|
||||
|
||||
log('Calling getOAuthLogin with provider: $provider');
|
||||
await oauthCubit.signInViaSocialMedia(
|
||||
provider: provider,
|
||||
accessToken: authResult.accessToken,
|
||||
refreshToken: authResult.refreshToken ?? '',
|
||||
clientId: authResult.clientId,
|
||||
);
|
||||
} catch (e) {
|
||||
log('OAuth login error in catch block: $e');
|
||||
AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reconnectAfterAuth() async {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return;
|
||||
try {
|
||||
await sl<WsManager>().clear();
|
||||
await sl<WsManager>().initialize();
|
||||
await sl<ProfileAvatarCubit>().getMyProfile();
|
||||
await sl<ChatsCubit>().getChatRoomsWithLoading();
|
||||
await sl<ChatsCubit>().connectToWebSocket();
|
||||
await sl<NotificationsCubit>().getNotifications();
|
||||
await sl<NotificationsCubit>().connectToWebSocket();
|
||||
await sl<FirebaseApi>().registerPushToken();
|
||||
showSuccessToast('Успешный вход');
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var shortestSide = MediaQuery.sizeOf(context).shortestSide;
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final bool useMobileLayout = shortestSide < 600;
|
||||
if (!useMobileLayout) isFocused = true;
|
||||
final s = S.of(context);
|
||||
@@ -100,9 +239,10 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
builder: (BuildContext context, AuthState state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
toolbarHeight: screenSize.height * 0.15,
|
||||
centerTitle: true,
|
||||
toolbarHeight: isFocused ? 64 : MediaQuery.of(context).size.width - 64,
|
||||
title: GestureDetector(
|
||||
key: AppKeys.hiddenLogoButtonAuthScreen,
|
||||
onTap: () {
|
||||
tapsCount++;
|
||||
if (tapsCount == 7) {
|
||||
@@ -110,7 +250,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
GoRouter.of(context).pushNamed(DevToolsScreen.name);
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(ImageConstant.imgLogoEatvio, height: isFocused ? 64 : MediaQuery.of(context).size.width - 64, fit: BoxFit.contain),
|
||||
child: SvgPicture.asset(ImageConstant.imgLogoEatvio, height: screenSize.height * 0.20),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
@@ -119,6 +259,41 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Center(child: Text('Авторизация через')),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_SocialButton(
|
||||
onTap: () => _onVkPressed(),
|
||||
backgroundColor: const Color(0xFF2787F5),
|
||||
tooltip: 'VK ID',
|
||||
child: SvgPicture.asset('assets/icons/vk_logo.svg', height: 24, width: 24),
|
||||
),
|
||||
_SocialButton(
|
||||
onTap: _onOkPressed,
|
||||
backgroundColor: const Color(0xFFF58220),
|
||||
tooltip: 'Одноклассники',
|
||||
child: SvgPicture.asset('assets/icons/ok_logo.svg', height: 24, width: 24),
|
||||
),
|
||||
_SocialButton(
|
||||
onTap: _onMailRuPressed,
|
||||
backgroundColor: const Color(0xFF005FF9),
|
||||
tooltip: 'Mail.ru',
|
||||
child: SvgPicture.asset('assets/icons/mail_logo.svg', height: 24, width: 24),
|
||||
),
|
||||
_SocialButton(
|
||||
onTap: _onYandexPressed,
|
||||
backgroundColor: const Color(0xFFFFCC00),
|
||||
tooltip: 'Яндекс ID',
|
||||
child: SvgPicture.asset('assets/icons/yandex_logo.svg', height: 24, width: 24),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const CustomDivider(),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 16),
|
||||
Text(s.email),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
@@ -126,6 +301,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
textInputAction: TextInputAction.next,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
textInputType: TextInputType.emailAddress,
|
||||
key: AppKeys.emailTextFieldAuthScreen,
|
||||
hintText: 'Введите email',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -134,6 +310,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
CustomTextFormField(
|
||||
obscureText: hidePassword,
|
||||
controller: passwordController,
|
||||
key: AppKeys.passwordTextFieldAuthScreen,
|
||||
hintText: 'Введите пароль',
|
||||
suffix: IconButton(
|
||||
onPressed: () {
|
||||
@@ -168,52 +345,6 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
// const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_SocialButton(
|
||||
onTap: () => _onVkPressed(context),
|
||||
backgroundColor: const Color(0xFF2787F5),
|
||||
tooltip: 'VK ID',
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/vk.svg',
|
||||
height: 24,
|
||||
width: 24,
|
||||
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
_SocialButton(
|
||||
onTap: _onOkPressed,
|
||||
backgroundColor: const Color(0xFFF58220),
|
||||
tooltip: 'Одноклассники',
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/ok.svg',
|
||||
height: 24,
|
||||
width: 24,
|
||||
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
const _SocialButton(
|
||||
onTap: _onMailRuPressed,
|
||||
backgroundColor: Color(0xFF005FF9),
|
||||
tooltip: 'Mail.ru',
|
||||
child: Text(
|
||||
'Mail',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w700),
|
||||
),
|
||||
),
|
||||
const _SocialButton(
|
||||
onTap: _onYandexPressed,
|
||||
backgroundColor: Color(0xFFFFCC00),
|
||||
tooltip: 'Яндекс ID',
|
||||
child: Text(
|
||||
'Yandex',
|
||||
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w700),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Center(
|
||||
child: RichText(
|
||||
@@ -232,7 +363,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
decorationColor: appTheme.blueA400,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/oferta_bs.pdf')),
|
||||
),
|
||||
const TextSpan(text: ' и '),
|
||||
],
|
||||
@@ -244,7 +375,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
decorationColor: appTheme.blueA400,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')),
|
||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/docs/privacy.pdf')),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -257,21 +388,28 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
height: 48,
|
||||
width: MediaQuery.of(context).size.width - 88,
|
||||
child: FloatingActionButton.extended(
|
||||
key: AppKeys.signInButtonAuthScreen,
|
||||
heroTag: 'authButton',
|
||||
highlightElevation: 0,
|
||||
elevation: 0,
|
||||
focusElevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400,
|
||||
onPressed: state is AuthStateLoading
|
||||
backgroundColor: (state is AuthStateLoading || _isLoading) ? Colors.black.withAlpha(50) : appTheme.blueA400,
|
||||
onPressed: (state is AuthStateLoading || _isLoading)
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
final isAuthenticated = await authCubit.getTokenByCredentials(emailController.text, passwordController.text);
|
||||
if (isAuthenticated) {
|
||||
if (context.mounted) {
|
||||
_reconnectWebSocket(context);
|
||||
await _reconnectWebSocket(context);
|
||||
}
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
},
|
||||
label: state is AuthStateLoading
|
||||
? const CupertinoActivityIndicator()
|
||||
@@ -304,7 +442,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
children: [
|
||||
const Text('Где взять QR-код?\n', style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
'1. Войдите на сайте $baseUrl в свой аккаунт\n\n2. Перейдите в настройки профиля\n\n3. В разделе "Доп информация" перейдите на "QR Login"',
|
||||
'1. Войдите на сайте ${sl<AppConfig>().baseUrl} в свой аккаунт\n\n2. Перейдите в настройки профиля\n\n3. В разделе "Доп информация" перейдите на "QR Login"',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
@@ -367,7 +505,6 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
class _SocialButton extends StatelessWidget {
|
||||
const _SocialButton({required this.onTap, required this.child, required this.backgroundColor, required this.tooltip});
|
||||
|
||||
@@ -381,73 +518,19 @@ class _SocialButton extends StatelessWidget {
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
onTap: onTap,
|
||||
child: Ink(
|
||||
height: 48,
|
||||
width: (MediaQuery.of(context).size.width - 16 * 2 - 12 * 3) / 4,
|
||||
decoration: BoxDecoration(color: backgroundColor, borderRadius: BorderRadius.circular(12)),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: const Color(0xFFDBDBDB), width: 1),
|
||||
),
|
||||
child: Center(child: child),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
void _onVkPressed(BuildContext context) async {
|
||||
final accessToken = await SocialAuthFacade.signInWithVk(context: context);
|
||||
if (accessToken == null) return;
|
||||
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'vk', accessToken: accessToken);
|
||||
if (isAuthenticated) {
|
||||
if (AppRouter.navigatorKey.currentContext != null) {
|
||||
// Reuse success flow
|
||||
// ignore: use_build_context_synchronously
|
||||
await _reconnectAfterAuth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onOkPressed() async {
|
||||
final accessToken = await SocialAuthFacade.signInWithOk();
|
||||
if (accessToken == null) return;
|
||||
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'ok', accessToken: accessToken);
|
||||
if (isAuthenticated) {
|
||||
await _reconnectAfterAuth();
|
||||
}
|
||||
}
|
||||
|
||||
void _onMailRuPressed() async {
|
||||
final accessToken = await SocialAuthFacade.signInWithMailRu();
|
||||
if (accessToken == null) return;
|
||||
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'mailru', accessToken: accessToken);
|
||||
if (isAuthenticated) {
|
||||
await _reconnectAfterAuth();
|
||||
}
|
||||
}
|
||||
|
||||
void _onYandexPressed() async {
|
||||
final accessToken = await SocialAuthFacade.signInWithYandex();
|
||||
if (accessToken == null) return;
|
||||
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'yandex', accessToken: accessToken);
|
||||
if (isAuthenticated) {
|
||||
await _reconnectAfterAuth();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reconnectAfterAuth() async {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return;
|
||||
try {
|
||||
await sl<WsManager>().clear();
|
||||
await sl<WsManager>().initialize();
|
||||
await sl<ProfileAvatarCubit>().getMyProfile();
|
||||
await sl<ChatsCubit>().getChatRoomsWithLoading();
|
||||
await sl<ChatsCubit>().connectToWebSocket();
|
||||
await sl<NotificationsCubit>().getNotifications();
|
||||
await sl<NotificationsCubit>().connectToWebSocket();
|
||||
await sl<FirebaseApi>().registerPushToken();
|
||||
showSuccessToast('Успешный вход');
|
||||
} catch (_) {}
|
||||
AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,532 @@
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/core/di/cubit_registrations.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_state.dart';
|
||||
import 'package:eatvio_app/features/auth/entities/auth_result_entity.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_manager.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/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class RegisterViaSocialScreen extends StatefulWidget {
|
||||
final AuthResultEntity? authResult;
|
||||
final String? provider;
|
||||
|
||||
const RegisterViaSocialScreen({super.key, this.authResult, this.provider});
|
||||
|
||||
@override
|
||||
State<RegisterViaSocialScreen> createState() => _RegisterViaSocialScreenState();
|
||||
}
|
||||
|
||||
class _RegisterViaSocialScreenState extends State<RegisterViaSocialScreen> {
|
||||
final maskFormatter = MaskTextInputFormatter(mask: '+7 (###) ### ## ##', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy);
|
||||
final maskFormatterForBirthday = MaskTextInputFormatter(mask: '##.##.####', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy);
|
||||
|
||||
// Контроллеры для полей ввода
|
||||
final firstNameController = TextEditingController();
|
||||
final lastNameController = TextEditingController();
|
||||
final emailController = TextEditingController();
|
||||
final phoneController = TextEditingController();
|
||||
final dateOfBirthController = TextEditingController();
|
||||
final weightController = TextEditingController();
|
||||
final heightController = TextEditingController();
|
||||
bool emailExistError = false;
|
||||
bool phoneExistError = false;
|
||||
bool dateOfBirthError = false;
|
||||
bool weightError = false;
|
||||
bool heightError = false;
|
||||
bool incorrectEmailFormatError = false;
|
||||
|
||||
// Состояние для выбора пола
|
||||
bool isMaleSelected = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeFieldsFromAuthResult();
|
||||
}
|
||||
|
||||
Future<void> _reconnectWebSocket(BuildContext context) async {
|
||||
try {
|
||||
await sl<WsManager>().clear();
|
||||
await sl<WsManager>().initialize();
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
if (context.mounted) {
|
||||
try {
|
||||
await sl<ProfileAvatarCubit>().getMyProfile();
|
||||
await sl<ChatsCubit>().getChatRoomsWithLoading();
|
||||
await sl<ChatsCubit>().connectToWebSocket();
|
||||
await sl<NotificationsCubit>().getNotifications();
|
||||
await sl<NotificationsCubit>().connectToWebSocket();
|
||||
await sl<FirebaseApi>().registerPushToken();
|
||||
showSuccessToast('Успешная регистрация');
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
AppRouter.appRouter.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
||||
}
|
||||
|
||||
void _initializeFieldsFromAuthResult() {
|
||||
if (widget.authResult != null) {
|
||||
final authResult = widget.authResult!;
|
||||
|
||||
if (authResult.firstName != null) {
|
||||
firstNameController.text = authResult.firstName!;
|
||||
}
|
||||
if (authResult.lastName != null) {
|
||||
lastNameController.text = authResult.lastName!;
|
||||
}
|
||||
if (authResult.email != null) {
|
||||
emailController.text = authResult.email!;
|
||||
}
|
||||
if (authResult.phone != null) {
|
||||
final cleanPhone = authResult.phone!.replaceAll(RegExp(r'[^\d]'), '');
|
||||
final normalizedPhone = cleanPhone.startsWith('8') ? '7${cleanPhone.substring(1)}' : cleanPhone;
|
||||
final phoneWithCountryCode = normalizedPhone.startsWith('7') ? normalizedPhone : '7$normalizedPhone';
|
||||
final formattedPhone = maskFormatter.maskText('+$phoneWithCountryCode');
|
||||
phoneController.text = formattedPhone;
|
||||
}
|
||||
if (authResult.birthDate != null) {
|
||||
final formattedDate = _formatBirthDate(authResult.birthDate!);
|
||||
final maskedDate = maskFormatterForBirthday.maskText(formattedDate);
|
||||
dateOfBirthController.text = maskedDate;
|
||||
}
|
||||
if (authResult.gender != null) {
|
||||
isMaleSelected = authResult.gender == GenderEnum.male;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _formatBirthDate(String birthDate) {
|
||||
try {
|
||||
// Пытаемся распарсить различные форматы даты
|
||||
DateTime? date;
|
||||
|
||||
// Если дата в формате ISO (YYYY-MM-DD)
|
||||
if (birthDate.contains('-')) {
|
||||
date = DateTime.tryParse(birthDate);
|
||||
}
|
||||
// Если дата в формате DD.MM.YYYY
|
||||
else if (birthDate.contains('.')) {
|
||||
final parts = birthDate.split('.');
|
||||
if (parts.length == 3) {
|
||||
final day = int.tryParse(parts[0]);
|
||||
final month = int.tryParse(parts[1]);
|
||||
final year = int.tryParse(parts[2]);
|
||||
if (day != null && month != null && year != null) {
|
||||
date = DateTime(year, month, day);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (date != null) {
|
||||
// Форматируем в DD.MM.YYYY
|
||||
final day = date.day.toString().padLeft(2, '0');
|
||||
final month = date.month.toString().padLeft(2, '0');
|
||||
final year = date.year.toString();
|
||||
return '$day.$month.$year';
|
||||
}
|
||||
} catch (e) {
|
||||
// Если не удалось распарсить, возвращаем исходную строку
|
||||
}
|
||||
|
||||
return birthDate;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
firstNameController.dispose();
|
||||
lastNameController.dispose();
|
||||
emailController.dispose();
|
||||
phoneController.dispose();
|
||||
dateOfBirthController.dispose();
|
||||
weightController.dispose();
|
||||
heightController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<OAuthCubit>(
|
||||
create: (context) => CubitRegistrations.sl<OAuthCubit>(),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: BlocBuilder<OAuthCubit, OAuthState>(
|
||||
builder: (context, state) {
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Center(
|
||||
child: Text(
|
||||
'Регистрация',
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700, height: 1.1, color: Color(0xFF323F4B)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 22,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Имя'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(controller: firstNameController, hintText: 'Введите имя', onChanged: (newValue) {}),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 22,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Фамилия'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(controller: lastNameController, hintText: 'Введите фамилию', onChanged: (newValue) {}),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text('Почта'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
controller: emailController,
|
||||
textInputType: TextInputType.emailAddress,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
hintText: 'Введите email',
|
||||
textInputAction: TextInputAction.next,
|
||||
hasError: emailExistError || incorrectEmailFormatError,
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
if (emailExistError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Такая почта уже существует.')],
|
||||
if (incorrectEmailFormatError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Некорректный формат email')],
|
||||
const SizedBox(height: 12),
|
||||
const Text('Номер телефона'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
textInputAction: TextInputAction.done,
|
||||
hintText: '+7 (999) 999 99 99',
|
||||
controller: phoneController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [maskFormatter],
|
||||
hasError: phoneExistError,
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
if (phoneExistError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Такой номер телефона уже существует.')],
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 22,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Дата рождения'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
textInputAction: TextInputAction.done,
|
||||
hintText: '01.01.2000',
|
||||
controller: dateOfBirthController,
|
||||
keyboardType: TextInputType.number,
|
||||
hasError: dateOfBirthError,
|
||||
inputFormatters: [maskFormatterForBirthday],
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
if (dateOfBirthError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Некорректный формат даты рождения')],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 22,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Пол'),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _genderButton('М', isMaleSelected)),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(child: _genderButton('Ж', !isMaleSelected)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 22,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Вес'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(controller: weightController, hintText: 'Введите вес', onChanged: (newValue) {}, hasError: weightError),
|
||||
if (weightError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Вес должен быть от 30 до 300 кг')],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 22,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Рост'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(controller: heightController, hintText: 'Введите рост', onChanged: (newValue) {}, hasError: heightError),
|
||||
if (heightError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Рост должен быть от 100 до 250 см')],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
const Center(
|
||||
child: Text(
|
||||
'Проверьте правильность заполненных\nданных. Пол, рост и вес требуются для\nрасчета вашей дневной нормы калорий',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF323F4B)),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
onTap: () => _handleRegistration(context),
|
||||
child: Ink(
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(color: state is OAuthStateLoading ? Colors.grey : appTheme.blueA400, borderRadius: BorderRadius.circular(20)),
|
||||
child: Center(
|
||||
child: state is OAuthStateLoading
|
||||
? const CircularProgressIndicator(color: Colors.white)
|
||||
: const Text(
|
||||
'Зарегистрироваться',
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 18, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
text: 'Нажимая "Зарегистрироваться", Вы соглашаетесь\n',
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black),
|
||||
children: [
|
||||
const TextSpan(
|
||||
text: 'c ',
|
||||
style: TextStyle(fontSize: 16, color: Colors.black),
|
||||
),
|
||||
if (!hidePlans) ...[
|
||||
TextSpan(
|
||||
text: ' Публичной офертой',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: appTheme.blueA400,
|
||||
decorationColor: appTheme.blueA400,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}oferta_bs.pdf')),
|
||||
),
|
||||
const TextSpan(
|
||||
text: ' и ',
|
||||
style: TextStyle(fontSize: 16, color: Colors.black),
|
||||
),
|
||||
],
|
||||
TextSpan(
|
||||
text: 'Политикой конфиденциальности и защитой персональных данных',
|
||||
style: TextStyle(fontSize: 16, color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
|
||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/docs/privacy.pdf')),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Обработка успешной регистрации
|
||||
if (state is OAuthStateLoaded) ...[
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.shade50,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.green.shade200),
|
||||
),
|
||||
child: const Text(
|
||||
'Регистрация успешно завершена!',
|
||||
style: TextStyle(color: Colors.green),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
// Автоматический переход на главный экран после успешной регистрации
|
||||
Builder(
|
||||
builder: (context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_reconnectWebSocket(context);
|
||||
});
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _genderButton(String gender, bool isActive) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isMaleSelected = gender == 'М';
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF6F6F6),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: isActive ? Border.all(width: 2, color: appTheme.blueA400) : null,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
gender,
|
||||
style: TextStyle(color: isActive ? appTheme.blueA400 : const Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleRegistration(BuildContext context) async {
|
||||
final clientID = widget.authResult?.clientId;
|
||||
// Валидация полей
|
||||
if (firstNameController.text.isEmpty ||
|
||||
lastNameController.text.isEmpty ||
|
||||
emailController.text.isEmpty ||
|
||||
phoneController.text.isEmpty ||
|
||||
dateOfBirthController.text.isEmpty ||
|
||||
weightController.text.isEmpty ||
|
||||
heightController.text.isEmpty) {
|
||||
showErrorToast('Пожалуйста, заполните все поля');
|
||||
return;
|
||||
}
|
||||
|
||||
// Парсим числовые значения
|
||||
int? weight;
|
||||
int? height;
|
||||
|
||||
weight = int.tryParse(weightController.text);
|
||||
height = int.tryParse(heightController.text);
|
||||
bool hasErrorWeightOrHeightError = false;
|
||||
if (weight == null || height == null) {
|
||||
setState(() {
|
||||
weightError = true;
|
||||
heightError = true;
|
||||
});
|
||||
hasErrorWeightOrHeightError = true;
|
||||
}
|
||||
if (weight! < 30 || weight > 300) {
|
||||
setState(() {
|
||||
weightError = true;
|
||||
});
|
||||
hasErrorWeightOrHeightError = true;
|
||||
}
|
||||
if (height! < 100 || height > 250) {
|
||||
setState(() {
|
||||
heightError = true;
|
||||
});
|
||||
hasErrorWeightOrHeightError = true;
|
||||
}
|
||||
|
||||
if (hasErrorWeightOrHeightError) return;
|
||||
|
||||
// Очищаем номер телефона от маски для отправки
|
||||
final cleanPhone = phoneController.text.replaceAll(RegExp(r'[^\d+]'), '');
|
||||
|
||||
if (clientID == null) return;
|
||||
// Вызываем OAuth регистрацию
|
||||
final errors = await context.read<OAuthCubit>().signUpViaSocialMedia(
|
||||
provider: widget.provider ?? 'social',
|
||||
accessToken: widget.authResult?.accessToken ?? 'dummy_token',
|
||||
id: widget.authResult?.userId ?? 'dummy_id',
|
||||
refreshToken: widget.authResult?.refreshToken ?? 'dummy_refresh_token',
|
||||
firstName: firstNameController.text,
|
||||
lastName: lastNameController.text,
|
||||
email: emailController.text,
|
||||
phone: cleanPhone,
|
||||
dateOfBirth: dateOfBirthController.text,
|
||||
gender: isMaleSelected ? 'М' : 'Ж',
|
||||
weight: weight,
|
||||
height: height,
|
||||
clientId: clientID,
|
||||
);
|
||||
if (errors != null) {
|
||||
for (final error in errors) {
|
||||
switch (error) {
|
||||
case RegistrationValidationErrors.incorrectEmailFormat:
|
||||
setState(() {
|
||||
incorrectEmailFormatError = true;
|
||||
});
|
||||
break;
|
||||
case RegistrationValidationErrors.emailExist:
|
||||
setState(() {
|
||||
emailExistError = true;
|
||||
});
|
||||
break;
|
||||
case RegistrationValidationErrors.phoneExist:
|
||||
setState(() {
|
||||
phoneExistError = true;
|
||||
});
|
||||
break;
|
||||
case RegistrationValidationErrors.incorrectDateOfBirthFormat:
|
||||
setState(() {
|
||||
dateOfBirthError = true;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
showSuccessToast('Регистрация успешна');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/token_storage.dart';
|
||||
@@ -40,17 +40,11 @@ class _ScanQRCodeScreenState extends State<ScanQRCodeScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Сканирование QR-кода'),
|
||||
centerTitle: true,
|
||||
),
|
||||
appBar: AppBar(title: const Text('Сканирование QR-кода'), centerTitle: true),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: QRView(
|
||||
key: qrKey,
|
||||
onQRViewCreated: _onQRViewCreated,
|
||||
),
|
||||
child: QRView(key: qrKey, onQRViewCreated: _onQRViewCreated),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -68,17 +62,9 @@ class _ScanQRCodeScreenState extends State<ScanQRCodeScreen> {
|
||||
final scannedUrl = scanData.code;
|
||||
final uri = Uri.parse(scannedUrl ?? '');
|
||||
final scannedBaseUrl = (uri.hasAuthority) ? '${uri.scheme}://${uri.host}' : '';
|
||||
baseUrl = scannedBaseUrl;
|
||||
sl<Dio>().options.baseUrl = baseUrl;
|
||||
final response = await Dio().getUri(
|
||||
uri,
|
||||
options: Options(
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
),
|
||||
);
|
||||
sl<AppConfig>().setBaseUrl(scannedBaseUrl);
|
||||
sl<Dio>().options.baseUrl = scannedBaseUrl;
|
||||
final response = await Dio().getUri(uri, options: Options(headers: {'accept': 'application/json', 'content-type': 'application/json'}));
|
||||
final token = response.data['token'];
|
||||
await TokenStorage.saveTokenInStorage(token);
|
||||
try {
|
||||
@@ -97,10 +83,7 @@ class _ScanQRCodeScreenState extends State<ScanQRCodeScreen> {
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
AppRouter.navigatorKey.currentContext?.go(
|
||||
'/feed-screen',
|
||||
extra: {'needToInitializeWebSocket': false},
|
||||
);
|
||||
AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
||||
showSuccessToast('Добро пожаловать!');
|
||||
} catch (e) {
|
||||
await controller.resumeCamera();
|
||||
|
||||
@@ -1,39 +1,76 @@
|
||||
import 'package:eatvio_app/features/auth/models/credentials_model.dart';
|
||||
import 'package:eatvio_app/features/auth/models/oauth_model.dart';
|
||||
import 'package:eatvio_app/features/auth/models/oauth_login_request.dart';
|
||||
import 'package:eatvio_app/features/auth/models/token_model.dart';
|
||||
import 'package:eatvio_app/features/auth/services/auth_service.dart';
|
||||
import 'package:eatvio_app/features/auth/models/social_exchange_request.dart';
|
||||
|
||||
class AuthRepository {
|
||||
final AuthService authService;
|
||||
|
||||
AuthRepository({required this.authService});
|
||||
|
||||
Future<TokenModel> getTokenByCredentials(
|
||||
String email,
|
||||
String password,
|
||||
) async {
|
||||
Future<TokenModel> getTokenByCredentials(String email, String password) async {
|
||||
try {
|
||||
return await authService.getTokenByCredentials(
|
||||
credentials: CredentialsModel(
|
||||
email: email,
|
||||
password: password,
|
||||
),
|
||||
credentials: CredentialsModel(email: email, password: password),
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<TokenModel> getTokenBySocial({
|
||||
|
||||
Future<TokenModel> signInViaSocialMedia({
|
||||
required String provider,
|
||||
required String accessToken,
|
||||
required String refreshToken,
|
||||
required String clientId,
|
||||
}) async {
|
||||
try {
|
||||
return await authService.getTokenBySocial(
|
||||
body: SocialExchangeRequest(provider: provider, accessToken: accessToken),
|
||||
return await authService.signInViaSocialMedia(
|
||||
body: OAuthLoginRequest(provider: provider, accessToken: accessToken, refreshToken: refreshToken, clientId: clientId),
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> signUpViaSocialMedia({
|
||||
required String provider,
|
||||
required String accessToken,
|
||||
required String id,
|
||||
required String refreshToken,
|
||||
required String firstName,
|
||||
required String lastName,
|
||||
required String email,
|
||||
required String phone,
|
||||
required String dateOfBirth,
|
||||
required String gender,
|
||||
required int weight,
|
||||
required int height,
|
||||
required String clientId,
|
||||
}) async {
|
||||
try {
|
||||
return (await authService.signUpViaSocialMedia(
|
||||
body: OAuthModel(
|
||||
provider: provider,
|
||||
accessToken: accessToken,
|
||||
id: id,
|
||||
refreshToken: refreshToken,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
email: email,
|
||||
phone: phone,
|
||||
dateOfBirth: dateOfBirth,
|
||||
gender: gender == 'М' ? 'male' : 'female',
|
||||
weight: weight,
|
||||
height: height,
|
||||
clientId: clientId,
|
||||
),
|
||||
))['token']!
|
||||
as String;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,125 +1,214 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:eatvio_app/features/auth/entities/auth_result_entity.dart';
|
||||
import 'package:eatvio_app/features/auth/models/yandex_profile_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_login_yandex/flutter_login_yandex.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/env_config.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:vk_id/vk_id.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class VkAuthResult {
|
||||
VkAuthResult({required this.accessToken, this.refreshToken, this.userId, this.firstName, this.lastName, this.email, this.phone, this.gender, this.birthDate});
|
||||
|
||||
final String accessToken;
|
||||
final String? refreshToken;
|
||||
final String? userId;
|
||||
final String? firstName;
|
||||
final String? lastName;
|
||||
final String? email;
|
||||
final String? phone;
|
||||
final String? gender;
|
||||
final String? birthDate;
|
||||
}
|
||||
|
||||
class SocialAuthFacade {
|
||||
static Future<String?> signInWithVk({required BuildContext context}) async {
|
||||
static Future<String?> signInWithVk() async {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return null;
|
||||
final result = await signInWithVkAndFetchProfile(context: context);
|
||||
return result?.accessToken;
|
||||
}
|
||||
|
||||
static Future<VkAuthResult?> signInWithVkAndFetchProfile({required BuildContext context}) async {
|
||||
final clientIdStr = dotenv.maybeGet('VK_CLIENT_ID')?.trim() ?? Platform.environment['VK_CLIENT_ID'];
|
||||
if (clientIdStr == null || clientIdStr.isEmpty) {
|
||||
static Future<AuthResultEntity?> signInWithVkAndFetchProfile({required BuildContext context}) async {
|
||||
final clientIdStr = Platform.isIOS ? EnvConfig.vkClientIdIos.trim() : EnvConfig.vkClientIdAndroid.trim();
|
||||
if (clientIdStr.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
dynamic controller = VkIDController(clID: int.parse(clientIdStr));
|
||||
final authUri = controller.generateAuthorizeLinkWithCodeVerifier();
|
||||
final redirectScheme = 'vk$clientIdStr://';
|
||||
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUri.toString(), redirectSchemePrefix: redirectScheme);
|
||||
if (redirectParams == null) return null;
|
||||
final code = redirectParams['code'];
|
||||
final deviceId = redirectParams['device_id'];
|
||||
if (code == null || deviceId == null) return null;
|
||||
final controller = VkIDController(clID: int.parse(clientIdStr));
|
||||
final authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: VkProvider.vkID, scopes: [VkScope.email, VkScope.phone]);
|
||||
|
||||
final tokenResponse = await controller.exchangeAuthorizationCodeForToken(code: code, deviceId: deviceId);
|
||||
final accessToken = _extractAccessToken(tokenResponse);
|
||||
if (accessToken == null) return null;
|
||||
return VkAuthResult(accessToken: accessToken);
|
||||
} catch (_) {
|
||||
final authUrl = authUri.value.toString();
|
||||
final codeVerifier = authUri.key;
|
||||
final redirectScheme = 'vk$clientIdStr://';
|
||||
|
||||
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUrl, redirectSchemePrefix: redirectScheme);
|
||||
if (redirectParams == null) {
|
||||
return null;
|
||||
}
|
||||
return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier, clientId: clientIdStr);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String?> signInWithOk() async {
|
||||
static Future<AuthResultEntity?> _parseVKAuthResult({
|
||||
required Map<String, String> redirectParams,
|
||||
required VkIDController controller,
|
||||
required String codeVerifier,
|
||||
required String clientId,
|
||||
}) async {
|
||||
final code = redirectParams['code'];
|
||||
final deviceId = redirectParams['device_id'];
|
||||
final state = redirectParams['state'];
|
||||
if (code == null || deviceId == null || state == null) {
|
||||
return null;
|
||||
}
|
||||
final authRes = await controller.retrieveOAuthToken(authorizationCode: code, deviceId: deviceId, codeVerifier: codeVerifier, state: state);
|
||||
final auth = authRes.result;
|
||||
if (auth == null) {
|
||||
var msg = "Retrieve OAuth token error";
|
||||
final err = authRes.error?.vkErr;
|
||||
if (err != null) {
|
||||
msg += ": ${err.error} - ${err.description}";
|
||||
}
|
||||
log(msg);
|
||||
return null;
|
||||
}
|
||||
final profileRes = await controller.getProfileInfo();
|
||||
final profile = profileRes.result;
|
||||
if (profile == null) {
|
||||
var msg = "Profile info load error";
|
||||
final err = profileRes.error?.vkErr;
|
||||
if (err != null) {
|
||||
msg += ": ${err.error} - ${err.description}";
|
||||
}
|
||||
log(msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
return AuthResultEntity(
|
||||
accessToken: auth.accessToken,
|
||||
refreshToken: auth.refreshToken,
|
||||
userId: profile.userId.toString(),
|
||||
firstName: profile.firstName,
|
||||
lastName: profile.lastName,
|
||||
email: profile.email,
|
||||
phone: profile.phone,
|
||||
gender: GenderEnum.fromVkGender(profile.parsedSex),
|
||||
birthDate: profile.bDayDt?.toString(),
|
||||
clientId: clientId,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<AuthResultEntity?> signInWithOkAndFetchProfile() async {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return null;
|
||||
return _signInWithVkIdProvider(context: context, provider: 'ok');
|
||||
return await _signInWithVkIdProvider(context: context, provider: VkProvider.okRu);
|
||||
}
|
||||
|
||||
static Future<String?> signInWithMailRu() async {
|
||||
static Future<AuthResultEntity?> signInWithMailRuAndFetchProfile() async {
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return null;
|
||||
return _signInWithVkIdProvider(context: context, provider: 'mail');
|
||||
return await _signInWithVkIdProvider(context: context, provider: VkProvider.mailRu);
|
||||
}
|
||||
|
||||
static Future<String?> signInWithYandex() async {
|
||||
final clientId = dotenv.maybeGet('YANDEX_CLIENT_ID')?.trim() ?? Platform.environment['YANDEX_CLIENT_ID'];
|
||||
final redirectUri = dotenv.maybeGet('YANDEX_REDIRECT_URI')?.trim() ?? Platform.environment['YANDEX_REDIRECT_URI'];
|
||||
static Future<AuthResultEntity?> signInWithYandex() async {
|
||||
final clientId = EnvConfig.yandexClientId.trim();
|
||||
final redirectUri = EnvConfig.yandexRedirectUri.trim();
|
||||
|
||||
if (clientId == null || clientId.isEmpty || redirectUri == null || redirectUri.isEmpty) {
|
||||
if (clientId.isEmpty || redirectUri.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final plugin = FlutterLoginYandex();
|
||||
final response = await plugin.signIn();
|
||||
final token = (response is Map) ? response['token'] as String? : null;
|
||||
return (token != null && token.isNotEmpty) ? token : null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String?> _signInWithVkIdProvider({required BuildContext context, required String provider}) async {
|
||||
final clientIdStr = dotenv.maybeGet('VK_CLIENT_ID')?.trim() ?? Platform.environment['VK_CLIENT_ID'];
|
||||
if (clientIdStr == null || clientIdStr.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
dynamic controller = VkIDController(clID: int.parse(clientIdStr));
|
||||
final authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: provider);
|
||||
final redirectScheme = 'vk$clientIdStr://';
|
||||
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUri.toString(), redirectSchemePrefix: redirectScheme);
|
||||
if (redirectParams == null) return null;
|
||||
final code = redirectParams['code'];
|
||||
final deviceId = redirectParams['device_id'];
|
||||
if (code == null || deviceId == null) return null;
|
||||
final tokenResponse = await controller.exchangeAuthorizationCodeForToken(code: code, deviceId: deviceId, provider: provider);
|
||||
final accessToken = _extractAccessToken(tokenResponse);
|
||||
return accessToken;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
final context = AppRouter.navigatorKey.currentContext;
|
||||
if (context == null) return null;
|
||||
|
||||
static String? _extractAccessToken(dynamic tokenResponse) {
|
||||
if (tokenResponse == null) return null;
|
||||
if (tokenResponse is Map) {
|
||||
return (tokenResponse['access_token'] as String?) ?? (tokenResponse['token'] as String?);
|
||||
final authorizeUrl = Uri.https('oauth.yandex.ru', '/authorize', {
|
||||
'response_type': 'code',
|
||||
'client_id': clientId,
|
||||
'redirect_uri': redirectUri,
|
||||
'force_confirm': 'true',
|
||||
}).toString();
|
||||
|
||||
final redirectSchemePrefix = redirectUri;
|
||||
|
||||
final params = await _presentOAuthWebView(context: context, initialUrl: authorizeUrl, redirectSchemePrefix: redirectSchemePrefix);
|
||||
|
||||
if (params == null) return null;
|
||||
|
||||
final code = params['code'];
|
||||
if (code == null || code.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final tokens = await _getTokensFomYandex(code: code, clientId: clientId, clientSecret: EnvConfig.yandexClientSecret);
|
||||
if (tokens == null) {
|
||||
return null;
|
||||
}
|
||||
final response = await http.get(Uri.parse('https://login.yandex.ru/info?format=json'), headers: {'Authorization': 'OAuth ${tokens.accessToken}'});
|
||||
|
||||
final Map<String, dynamic> json = jsonDecode(response.body);
|
||||
final yandexProfileData = YandexProfileModel.fromJson(json);
|
||||
return AuthResultEntity(
|
||||
accessToken: tokens.accessToken,
|
||||
refreshToken: tokens.refreshToken,
|
||||
userId: yandexProfileData.id,
|
||||
firstName: yandexProfileData.firstName,
|
||||
lastName: yandexProfileData.lastName,
|
||||
email: yandexProfileData.defaultEmail,
|
||||
phone: yandexProfileData.defaultPhone.number,
|
||||
gender: GenderEnum.fromString(yandexProfileData.sex),
|
||||
birthDate: yandexProfileData.birthday,
|
||||
clientId: clientId,
|
||||
);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
try {
|
||||
final token = (tokenResponse as dynamic).accessToken as String?;
|
||||
if (token != null && token.isNotEmpty) return token;
|
||||
} catch (_) {}
|
||||
try {
|
||||
final token = (tokenResponse as dynamic).token as String?;
|
||||
if (token != null && token.isNotEmpty) return token;
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<({String accessToken, String refreshToken})?> _getTokensFomYandex({
|
||||
required String code,
|
||||
required String clientId,
|
||||
required String clientSecret,
|
||||
}) async {
|
||||
final response = await http.post(
|
||||
Uri.parse('https://oauth.yandex.ru/token'),
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: {'grant_type': 'authorization_code', 'code': code, 'client_id': clientId, 'client_secret': clientSecret},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
log('Yandex token exchange failed: ${response.body}');
|
||||
return null;
|
||||
}
|
||||
|
||||
final tokens = jsonDecode(response.body);
|
||||
final accessToken = tokens['access_token'] as String;
|
||||
final refreshToken = tokens['refresh_token'] as String;
|
||||
return (accessToken: accessToken, refreshToken: refreshToken);
|
||||
}
|
||||
|
||||
static Future<AuthResultEntity?> _signInWithVkIdProvider({required BuildContext context, required VkProvider provider}) async {
|
||||
final clientIdStr = Platform.isIOS ? EnvConfig.vkClientIdIos.trim() : EnvConfig.vkClientIdAndroid.trim();
|
||||
if (clientIdStr.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final controller = VkIDController(clID: int.parse(clientIdStr));
|
||||
|
||||
final authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: provider, scopes: [VkScope.phone, VkScope.email]);
|
||||
final codeVerifier = authUri.key;
|
||||
final authUrl = authUri.value.toString();
|
||||
final redirectScheme = 'vk$clientIdStr://';
|
||||
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUrl, redirectSchemePrefix: redirectScheme);
|
||||
if (redirectParams == null) return null;
|
||||
|
||||
final code = redirectParams['code'];
|
||||
final deviceId = redirectParams['device_id'];
|
||||
if (code == null || deviceId == null) return null;
|
||||
|
||||
return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier, clientId: clientIdStr);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, String>?> _presentOAuthWebView({
|
||||
required BuildContext context,
|
||||
required String initialUrl,
|
||||
@@ -134,22 +223,49 @@ class SocialAuthFacade {
|
||||
if (url.startsWith(redirectSchemePrefix)) {
|
||||
final uri = Uri.parse(url);
|
||||
final params = <String, String>{}..addAll(uri.queryParameters);
|
||||
Navigator.of(context, rootNavigator: true).pop(params);
|
||||
if (uri.fragment.isNotEmpty) {
|
||||
final fragPairs = uri.fragment.split('&');
|
||||
for (final pair in fragPairs) {
|
||||
final eq = pair.indexOf('=');
|
||||
if (eq > 0 && eq < pair.length - 1) {
|
||||
final key = Uri.decodeComponent(pair.substring(0, eq));
|
||||
final value = Uri.decodeComponent(pair.substring(eq + 1));
|
||||
params[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GoRouter.of(context).canPop()) {
|
||||
GoRouter.of(context).pop(params);
|
||||
}
|
||||
return NavigationDecision.prevent;
|
||||
}
|
||||
return NavigationDecision.navigate;
|
||||
},
|
||||
onPageStarted: (url) {},
|
||||
onPageFinished: (url) {},
|
||||
onWebResourceError: (error) {},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(initialUrl));
|
||||
);
|
||||
|
||||
try {
|
||||
await controller.loadRequest(Uri.parse(initialUrl));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
if (!context.mounted) return null;
|
||||
return showDialog<Map<String, String>>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) {
|
||||
const borderRadius = BorderRadius.all(Radius.circular(16));
|
||||
return Dialog(
|
||||
insetPadding: const EdgeInsets.all(16),
|
||||
child: SizedBox(height: 560, child: WebViewWidget(controller: controller)),
|
||||
shape: const RoundedRectangleBorder(borderRadius: borderRadius),
|
||||
elevation: 0,
|
||||
child: ClipRRect(
|
||||
borderRadius: borderRadius,
|
||||
child: SizedBox(height: 560, child: WebViewWidget(controller: controller)),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/auth/models/credentials_model.dart';
|
||||
import 'package:eatvio_app/features/auth/models/oauth_model.dart';
|
||||
import 'package:eatvio_app/features/auth/models/oauth_login_request.dart';
|
||||
import 'package:eatvio_app/features/auth/models/token_model.dart';
|
||||
import 'package:eatvio_app/features/auth/models/social_exchange_request.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
|
||||
part 'auth_service.g.dart';
|
||||
@@ -11,12 +12,11 @@ abstract class AuthService {
|
||||
factory AuthService(Dio dio, {String? baseUrl}) = _AuthService;
|
||||
|
||||
@POST('/api/login')
|
||||
Future<TokenModel> getTokenByCredentials({
|
||||
@Body() required CredentialsModel credentials,
|
||||
});
|
||||
Future<TokenModel> getTokenByCredentials({@Body() required CredentialsModel credentials});
|
||||
|
||||
@POST('/api/social/login')
|
||||
Future<TokenModel> getTokenBySocial({
|
||||
@Body() required SocialExchangeRequest body,
|
||||
});
|
||||
@POST('/api/oauth/callback')
|
||||
Future<Map<String, Object?>> signUpViaSocialMedia({@Body() required OAuthModel body});
|
||||
|
||||
@POST('/api/oauth/login')
|
||||
Future<TokenModel> signInViaSocialMedia({@Body() required OAuthLoginRequest body});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:io';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/utils/date_time_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_popup.dart';
|
||||
@@ -207,7 +207,7 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 16,
|
||||
foregroundImage: NetworkImage(baseUrl + (blogPostState.blogPostModel?.author.authorProfileIcon ?? '')),
|
||||
foregroundImage: NetworkImage(sl<AppConfig>().baseUrl + (blogPostState.blogPostModel?.author.authorProfileIcon ?? '')),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -239,7 +239,7 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (blogPostState.blogPostModel?.image.isNotEmpty ?? false)
|
||||
CachedNetworkImage(imageUrl: '$baseUrl/storage/${blogPostState.blogPostModel?.image ?? ''}'),
|
||||
CachedNetworkImage(imageUrl: '${sl<AppConfig>().baseUrl}/storage/${blogPostState.blogPostModel?.image ?? ''}'),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
@@ -267,7 +267,7 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/blog_post/${widget.id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/blog_post/${widget.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,61 +1,34 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/core/cubit/base_cubit.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_state.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
|
||||
class BlogPostCreateCubit extends Cubit<BlogPostCreateState> {
|
||||
class BlogPostCreateCubit extends BaseCubit<BlogPostCreateState> {
|
||||
final BlogPostRepository blogPostRepository;
|
||||
|
||||
BlogPostCreateCubit({
|
||||
required this.blogPostRepository,
|
||||
}) : super(const BlogPostCreateState());
|
||||
BlogPostCreateCubit({required this.blogPostRepository}) : super(const BlogPostCreateState());
|
||||
|
||||
Future<bool> createBlogPost({
|
||||
String? title,
|
||||
String? videoUrl,
|
||||
String? description,
|
||||
XFile? image,
|
||||
}) async {
|
||||
Future<bool> createBlogPost({String? title, String? videoUrl, String? description, XFile? image}) async {
|
||||
try {
|
||||
emit(const BlogPostCreateState.loading());
|
||||
await blogPostRepository.createBlogPost(
|
||||
title,
|
||||
videoUrl,
|
||||
description,
|
||||
image,
|
||||
);
|
||||
await blogPostRepository.createBlogPost(title, videoUrl, description, image);
|
||||
emit(const BlogPostCreateState.loaded());
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
} catch (error, stackTrace) {
|
||||
handleError(error, stackTrace);
|
||||
emit(const BlogPostCreateState.loaded());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateBlogPost({
|
||||
required int id,
|
||||
String? title,
|
||||
String? videoUrl,
|
||||
String? description,
|
||||
XFile? image,
|
||||
String? oldImageUrl,
|
||||
}) async {
|
||||
Future<bool> updateBlogPost({required int id, String? title, String? videoUrl, String? description, XFile? image, String? oldImageUrl}) async {
|
||||
try {
|
||||
emit(const BlogPostCreateState.loading());
|
||||
await blogPostRepository.updateBlogPost(
|
||||
id,
|
||||
title,
|
||||
videoUrl,
|
||||
description,
|
||||
image,
|
||||
oldImageUrl,
|
||||
);
|
||||
await blogPostRepository.updateBlogPost(id, title, videoUrl, description, image, oldImageUrl);
|
||||
emit(const BlogPostCreateState.loaded());
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
} catch (error, stackTrace) {
|
||||
handleError(error, stackTrace);
|
||||
emit(const BlogPostCreateState.loaded());
|
||||
return false;
|
||||
}
|
||||
@@ -65,8 +38,8 @@ class BlogPostCreateCubit extends Cubit<BlogPostCreateState> {
|
||||
try {
|
||||
await blogPostRepository.deleteBlogPost(id);
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
} catch (error, stackTrace) {
|
||||
handleError(error, stackTrace);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
|
||||
import 'package:eatvio_app/core/theme/app_decoration.dart';
|
||||
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
||||
@@ -304,7 +304,7 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Image(
|
||||
image: NetworkImage('$baseUrl/storage/${widget.feedBlogPostModel!.image}'),
|
||||
image: NetworkImage('${sl<AppConfig>().baseUrl}/storage/${widget.feedBlogPostModel!.image}'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
|
||||
@@ -36,7 +37,7 @@ class BlogPostRepository {
|
||||
Future<XFile?> downloadImage(String imageUrl) async {
|
||||
try {
|
||||
// Загрузка изображения
|
||||
final response = await http.get(Uri.parse('$baseUrl/storage/$imageUrl'));
|
||||
final response = await http.get(Uri.parse('${sl<AppConfig>().baseUrl}/storage/$imageUrl'));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Получение временной директории
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/image_constant.dart';
|
||||
import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart';
|
||||
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
|
||||
@@ -102,7 +103,7 @@ class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
|
||||
label: '',
|
||||
icon: CircleAvatar(
|
||||
foregroundImage: NetworkImage(
|
||||
widget.profileModel.profilePhotoUrl.contains('https') ? widget.profileModel.profilePhotoUrl : baseUrl + widget.profileModel.profilePhotoUrl,
|
||||
widget.profileModel.profilePhotoUrl.contains('https') ? widget.profileModel.profilePhotoUrl : sl<AppConfig>().baseUrl + widget.profileModel.profilePhotoUrl,
|
||||
),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
radius: 16,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dismissible_page/dismissible_page.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/app_decoration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@@ -88,7 +89,7 @@ class _PhotoViewScreenState extends State<PhotoViewScreen> {
|
||||
color: Colors.white,
|
||||
),
|
||||
title: Text(
|
||||
widget.imageUrls[index].replaceAll('$baseUrl/storage/chat_files/', ''),
|
||||
widget.imageUrls[index].replaceAll('${sl<AppConfig>().baseUrl}/storage/chat_files/', ''),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
@@ -47,7 +48,7 @@ class _OrderCardState extends State<OrderCard> {
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.network(
|
||||
'$baseUrl/storage/${widget.plan.image}',
|
||||
'${sl<AppConfig>().baseUrl}/storage/${widget.plan.image}',
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -49,7 +51,7 @@ class _LastStepScreenState extends State<LastStepScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: TextButton(
|
||||
onPressed: () async => await launchUrl(Uri.parse(hidePlans ? '$baseUrl/docs/privacy.pdf' : '$baseUrl/oferta_bs.pdf')),
|
||||
onPressed: () async => await launchUrl(Uri.parse(hidePlans ? '${sl<AppConfig>().baseUrl}/docs/privacy.pdf' : '${sl<AppConfig>().baseUrl}/oferta_bs.pdf')),
|
||||
child: Text(
|
||||
hidePlans ? 'Политика конфиденциальности и пользовательское соглашение' : 'Договор офёрты',
|
||||
style: const TextStyle(height: 1, fontSize: 16, color: Color(0xFF323F4B), decoration: TextDecoration.underline),
|
||||
|
||||
@@ -13,6 +13,7 @@ abstract class StatisticsModel with _$StatisticsModel {
|
||||
@JsonKey(name: 'count_diets') int? dietsCount,
|
||||
@JsonKey(name: 'count_consultants') int? consultationsCount,
|
||||
@JsonKey(name: 'count_products_nutritional_values') int? qwe,
|
||||
@JsonKey(name: 'referral_link') String? refLink,
|
||||
}) = _StatisticsModel;
|
||||
|
||||
factory StatisticsModel.fromJson(Map<String, dynamic> json) => _$StatisticsModelFromJson(json);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
|
||||
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart';
|
||||
@@ -10,6 +11,7 @@ import 'package:eatvio_app/features/consultant_rating/presentation/widgets/ratin
|
||||
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class ConsultantRatingScreen extends StatelessWidget {
|
||||
@@ -53,8 +55,8 @@ class ConsultantRatingPageContent extends StatelessWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
const RatingWidget(
|
||||
rating: 1058,
|
||||
RatingWidget(
|
||||
rating: state.absoluteRating!,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
@@ -67,7 +69,10 @@ class ConsultantRatingPageContent extends StatelessWidget {
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: state.statistics?.refLink ?? ''));
|
||||
showInfoToast(s.link_was_copied);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -2,17 +2,23 @@ import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_state.dart';
|
||||
import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class ConsultantRatingCubit extends Cubit<ConsultantRatingState> {
|
||||
final ConsultantRatingRepository repository;
|
||||
ConsultantRatingCubit(this.repository) : super(ConsultantRatingState());
|
||||
final FeedRepository feedRepository;
|
||||
final ProfileRepository profileRepository;
|
||||
|
||||
ConsultantRatingCubit({required this.repository, required this.feedRepository, required this.profileRepository}) : super(ConsultantRatingState());
|
||||
|
||||
Future<void> getConsultantStatistics({String? date}) async {
|
||||
try {
|
||||
emit(state.copyWith(isStatisticsLoading: true));
|
||||
final statistics = await repository.getConsultantStatistics(date: date);
|
||||
emit(state.copyWith(isStatisticsLoading: false, statistics: statistics, error: null));
|
||||
final absoluteRating = await feedRepository.getConsultants((await profileRepository.getMyProfile()).name);
|
||||
emit(state.copyWith(isStatisticsLoading: false, statistics: statistics, error: null, absoluteRating: absoluteRating.first.totalScores));
|
||||
} catch (e, st) {
|
||||
emit(state.copyWith(isStatisticsLoading: true, error: e.toString()));
|
||||
log(e.toString(), stackTrace: st);
|
||||
|
||||
@@ -12,5 +12,6 @@ abstract class ConsultantRatingState with _$ConsultantRatingState {
|
||||
@Default(null) StatisticsModel? statistics,
|
||||
@Default(null) String? error,
|
||||
@Default(<RatingModel>[]) List<RatingModel> ratings,
|
||||
@Default(0) int? absoluteRating,
|
||||
}) = _ConsultantRatingState;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class StatisticWidget extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(child: StatisticBouble(number: statistics?.consultationsCount ?? 0, title: s.consultations_count)),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: StatisticBouble(number: statistics?.qwe ?? 0, title: s.active_consultations_count)),
|
||||
Expanded(child: StatisticBouble(number: statistics?.qwe ?? 0, title: s.added_products_in_glossary)),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
|
||||
@@ -2,8 +2,10 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:developer';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
@@ -53,7 +55,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
name: diet.name,
|
||||
duration: diet.duration,
|
||||
type: type,
|
||||
imageUrl: '$baseUrl/storage/${diet.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${diet.image}',
|
||||
),
|
||||
);
|
||||
emit(state.copyWith(dietState: diet.state, durationIndex: durationValues.indexOf(diet.duration)));
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/entities/recipe_create_entity.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/models/ingredient.dart' as i;
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
|
||||
@@ -187,13 +188,13 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
}
|
||||
|
||||
Future<void> recipeToEntity(Recipe recipe) async {
|
||||
final file = await downloadImageAsFile('$baseUrl/storage/${recipe.image}');
|
||||
final file = await downloadImageAsFile('${sl<AppConfig>().baseUrl}/storage/${recipe.image}');
|
||||
|
||||
final List<StepEntity> steps = await Future.wait(
|
||||
recipe.steps!.map(
|
||||
(e) async => StepEntity(
|
||||
description: e.description,
|
||||
image: e.image.isEmpty ? null : await downloadImageAsFile('$baseUrl/storage/${e.image}'),
|
||||
image: e.image.isEmpty ? null : await downloadImageAsFile('${sl<AppConfig>().baseUrl}/storage/${e.image}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
@@ -84,7 +84,7 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
setState(() {
|
||||
baseUrlForTest = baseUrl;
|
||||
baseUrlForTest = sl<AppConfig>().baseUrl;
|
||||
});
|
||||
testsStates = List.generate(testCases.length, (index) => TestState.notRun);
|
||||
}
|
||||
@@ -111,8 +111,8 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
baseUrlForTest = controller.text;
|
||||
baseUrl = baseUrlForTest.toLowerCase();
|
||||
sl<Dio>().options.baseUrl = baseUrl;
|
||||
sl<AppConfig>().setBaseUrl(baseUrlForTest.toLowerCase());
|
||||
sl<Dio>().options.baseUrl = baseUrlForTest.toLowerCase();
|
||||
controller.clear();
|
||||
});
|
||||
},
|
||||
@@ -122,23 +122,23 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CustomOutlinedButton(
|
||||
text: baseUrl == EnvConfig.devBaseUrl ? 'Set production base url' : 'Set dev2 base url',
|
||||
text: sl<AppConfig>().baseUrl == EnvConfig.devBaseUrl ? 'Set production base url' : 'Set dev2 base url',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
if (baseUrl == EnvConfig.baseUrl) {
|
||||
if (sl<AppConfig>().baseUrl == EnvConfig.baseUrl) {
|
||||
baseUrlForTest = EnvConfig.devBaseUrl;
|
||||
} else {
|
||||
baseUrlForTest = EnvConfig.baseUrl;
|
||||
}
|
||||
baseUrl = baseUrlForTest.toLowerCase();
|
||||
sl<Dio>().options.baseUrl = baseUrl;
|
||||
sl<AppConfig>().setBaseUrl(baseUrlForTest.toLowerCase());
|
||||
sl<Dio>().options.baseUrl = baseUrlForTest.toLowerCase();
|
||||
controller.clear();
|
||||
});
|
||||
},
|
||||
buttonStyle: const ButtonStyle(),
|
||||
),
|
||||
SizedBox(height: baseUrlForTest != baseUrl ? 40 : 10),
|
||||
if (baseUrl.toLowerCase() != EnvConfig.baseUrl.toLowerCase())
|
||||
SizedBox(height: baseUrlForTest != sl<AppConfig>().baseUrl ? 40 : 10),
|
||||
if (sl<AppConfig>().baseUrl.toLowerCase() != EnvConfig.baseUrl.toLowerCase())
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: testCases.length,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:eatvio_app/core/routing/route_constants.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
@@ -6,77 +7,84 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class ChosenDietWidget extends StatelessWidget {
|
||||
const ChosenDietWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DiaryCubit, DiaryState>(builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
if (state.diary.typeOfDiary.hasChosenDiet) {
|
||||
return ChosenDietWidgetContent(
|
||||
currentDietDay: state.diary.dayOfChosenDiet ?? 0,
|
||||
dietName: state.diary.diet?.name ?? '',
|
||||
dietDuration: state.diary.diet?.duration ?? 0,
|
||||
);
|
||||
return BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
if (state.diary.typeOfDiary.hasChosenDiet) {
|
||||
return ChosenDietWidgetContent(
|
||||
currentDietDay: state.diary.dayOfChosenDiet ?? 0,
|
||||
dietName: state.diary.diet?.name ?? '',
|
||||
dietDuration: state.diary.diet?.duration ?? 0,
|
||||
dietId: state.diary.diet?.id ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state is DiaryStateLoadedTomorrowWithDiet) {
|
||||
if (state.diary.typeOfDiary.hasChosenDiet) {
|
||||
return Column(
|
||||
children: [
|
||||
const CustomDivider(),
|
||||
const SizedBox(height: 16),
|
||||
ChosenDietWidgetContent(
|
||||
currentDietDay: state.diary.dayOfChosenDiet ?? 0,
|
||||
dietName: state.diary.diet?.name ?? '',
|
||||
dietDuration: state.diary.diet?.duration ?? 0,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (state is DiaryStateLoadedTomorrowWithDiet) {
|
||||
if (state.diary.typeOfDiary.hasChosenDiet) {
|
||||
return Column(
|
||||
children: [
|
||||
const CustomDivider(),
|
||||
const SizedBox(height: 16),
|
||||
ChosenDietWidgetContent(
|
||||
currentDietDay: state.diary.dayOfChosenDiet ?? 0,
|
||||
dietName: state.diary.diet?.name ?? '',
|
||||
dietDuration: state.diary.diet?.duration ?? 0,
|
||||
dietId: state.diary.diet?.id ?? 0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return const SizedBox.shrink();
|
||||
});
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChosenDietWidgetContent extends StatelessWidget {
|
||||
const ChosenDietWidgetContent({
|
||||
super.key,
|
||||
required this.currentDietDay,
|
||||
required this.dietName,
|
||||
required this.dietDuration,
|
||||
});
|
||||
const ChosenDietWidgetContent({super.key, required this.currentDietDay, required this.dietName, required this.dietDuration, required this.dietId});
|
||||
final int currentDietDay;
|
||||
final String dietName;
|
||||
final int dietDuration;
|
||||
final int dietId;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
final s = S.of(context);
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: size.width * 0.4,
|
||||
child: Text(dietName, style: TextStyles.textLMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
decoration: BoxDecoration(color: appTheme.dividerColor, borderRadius: BorderRadius.circular(12)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
child: Text('${s.day} $currentDietDay из $dietDuration', style: TextStyles.textMSemibold.copyWith(color: appTheme.gray)),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
)
|
||||
],
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
GoRouter.of(context).push(RouteConstants.aboutDietScreen, extra: {'id': dietId, 'navigateToComments': false});
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: size.width * 0.4,
|
||||
child: Text(
|
||||
dietName,
|
||||
style: TextStyles.textLMedium.copyWith(color: appTheme.black, decoration: TextDecoration.underline),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
decoration: BoxDecoration(color: appTheme.dividerColor, borderRadius: BorderRadius.circular(12)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
child: Text('${s.day} $currentDietDay из $dietDuration', style: TextStyles.textMSemibold.copyWith(color: appTheme.gray)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
|
||||
@@ -139,7 +140,7 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex].path}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex].path}',
|
||||
),
|
||||
),
|
||||
if (!state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
|
||||
@@ -211,7 +212,7 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
||||
height: 52,
|
||||
width: 52,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${state.diary.meals[mealIndex].photos[i].path}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.diary.meals[mealIndex].photos[i].path}',
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -19,9 +19,14 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
String? userStartedDietName;
|
||||
List<int> _selectedIndexes = [];
|
||||
|
||||
Future<void> getDiet(String slug, {int? index}) async {
|
||||
Future<void> getDiet({String? slug, int? id, int? index}) async {
|
||||
try {
|
||||
final diet = await dietsRepository.getDietBySlug(slug);
|
||||
final Diet diet;
|
||||
if (slug != null) {
|
||||
diet = await dietsRepository.getDietBySlug(slug);
|
||||
} else {
|
||||
diet = await dietsRepository.getDiet(id!);
|
||||
}
|
||||
if (index != null) {
|
||||
dietsRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: diet.totalComments));
|
||||
}
|
||||
@@ -32,10 +37,16 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> initDietPage(String slug) async {
|
||||
Future<void> initDietPage({String? slug, int? id}) async {
|
||||
try {
|
||||
emit(const AboutDietState.loading());
|
||||
final diet = await dietsRepository.getDietBySlug(slug);
|
||||
final Diet diet;
|
||||
if (slug != null) {
|
||||
diet = await dietsRepository.getDietBySlug(slug);
|
||||
} else {
|
||||
diet = await dietsRepository.getDiet(id!);
|
||||
}
|
||||
|
||||
final chosenDiets = await profileRepository.getChosenDiets();
|
||||
final lastChosenDiet = chosenDiets.firstOrNull;
|
||||
if (lastChosenDiet != null && ['inited', 'started'].contains(lastChosenDiet.state)) {
|
||||
@@ -50,12 +61,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
}
|
||||
}
|
||||
_selectedIndexes = List.generate(diet.dailySchedule.length, (index) => 0);
|
||||
emit(AboutDietState.loaded(
|
||||
diet: diet,
|
||||
isStarted: isCurrentDietStarted,
|
||||
hasStartedDiet: hasStartedDiet,
|
||||
selectedIndexes: _selectedIndexes,
|
||||
));
|
||||
emit(AboutDietState.loaded(diet: diet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet, selectedIndexes: _selectedIndexes));
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
emit(AboutDietState.error(message: e.toString()));
|
||||
@@ -68,41 +74,30 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
final currentDiet = (state as DietStateLoaded).diet;
|
||||
|
||||
if (currentDiet.wasLikedByMe) {
|
||||
final updatedDiet = currentDiet.copyWith(
|
||||
totalLikes: currentDiet.totalLikes - 1,
|
||||
wasLikedByMe: false,
|
||||
);
|
||||
final updatedDiet = currentDiet.copyWith(totalLikes: currentDiet.totalLikes - 1, wasLikedByMe: false);
|
||||
if (dietIndexInFeed != null) {
|
||||
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
|
||||
}
|
||||
emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet, selectedIndexes: _selectedIndexes));
|
||||
} else {
|
||||
final updatedDiet = currentDiet.copyWith(
|
||||
totalLikes: currentDiet.totalLikes + 1,
|
||||
wasLikedByMe: true,
|
||||
);
|
||||
final updatedDiet = currentDiet.copyWith(totalLikes: currentDiet.totalLikes + 1, wasLikedByMe: true);
|
||||
if (dietIndexInFeed != null) {
|
||||
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
|
||||
}
|
||||
emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet, selectedIndexes: _selectedIndexes));
|
||||
}
|
||||
|
||||
EasyDebounce.debounce(
|
||||
'like_diet',
|
||||
const Duration(seconds: 1),
|
||||
() async {
|
||||
try {
|
||||
await dietsRepository.likeDiet(id);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
if (dietIndexInFeed != null) {
|
||||
dietsRepository
|
||||
.emitLikesStateToFeed(LikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed));
|
||||
}
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
EasyDebounce.debounce('like_diet', const Duration(seconds: 1), () async {
|
||||
try {
|
||||
await dietsRepository.likeDiet(id);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
if (dietIndexInFeed != null) {
|
||||
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed));
|
||||
}
|
||||
},
|
||||
);
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addToFavorite(int id, int? dietIndex) async {
|
||||
@@ -110,30 +105,24 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
|
||||
final currentDiet = (state as DietStateLoaded).diet;
|
||||
|
||||
final updatedDiet = currentDiet.copyWith(
|
||||
isFavorite: true,
|
||||
);
|
||||
final updatedDiet = currentDiet.copyWith(isFavorite: true);
|
||||
if (dietIndex != null) {
|
||||
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
|
||||
}
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
|
||||
EasyDebounce.debounce(
|
||||
'add_or_remove_diet_to_favorites',
|
||||
const Duration(seconds: 1),
|
||||
() async {
|
||||
try {
|
||||
await dietsRepository.addToFavorite(id);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (dietIndex != null) {
|
||||
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
|
||||
}
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
EasyDebounce.debounce('add_or_remove_diet_to_favorites', const Duration(seconds: 1), () async {
|
||||
try {
|
||||
await dietsRepository.addToFavorite(id);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (dietIndex != null) {
|
||||
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
|
||||
}
|
||||
},
|
||||
);
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> removeFromFavorite(int id, int? dietIndex) async {
|
||||
@@ -141,30 +130,24 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
|
||||
final currentDiet = (state as DietStateLoaded).diet;
|
||||
|
||||
final updatedDiet = currentDiet.copyWith(
|
||||
isFavorite: false,
|
||||
);
|
||||
final updatedDiet = currentDiet.copyWith(isFavorite: false);
|
||||
if (dietIndex != null) {
|
||||
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
|
||||
}
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
|
||||
EasyDebounce.debounce(
|
||||
'add_or_remove_diet_to_favorites',
|
||||
const Duration(seconds: 1),
|
||||
() async {
|
||||
try {
|
||||
await dietsRepository.removeFromFavorite(id);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (dietIndex != null) {
|
||||
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
|
||||
}
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
EasyDebounce.debounce('add_or_remove_diet_to_favorites', const Duration(seconds: 1), () async {
|
||||
try {
|
||||
await dietsRepository.removeFromFavorite(id);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (dietIndex != null) {
|
||||
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
|
||||
}
|
||||
},
|
||||
);
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> startDiet(DateTime date) async {
|
||||
@@ -199,13 +182,13 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
void clearStartedDietsState() {
|
||||
if (state is! DietStateLoaded) return;
|
||||
final diet = (state as DietStateLoaded).diet;
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: diet));
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: diet));
|
||||
}
|
||||
|
||||
void emitCurrentStartedDietsState() {
|
||||
if (state is! DietStateLoaded) return;
|
||||
final diet = (state as DietStateLoaded).diet;
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes,diet: diet, isStarted: true));
|
||||
emit(AboutDietState.loaded(selectedIndexes: _selectedIndexes, diet: diet, isStarted: true));
|
||||
}
|
||||
|
||||
Future<Diet?> getDietBySlug(String slug) async {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
@@ -35,11 +35,12 @@ import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class AboutDietScreen extends StatefulWidget {
|
||||
final String slug;
|
||||
final String? slug;
|
||||
final int? id;
|
||||
final int? index;
|
||||
final bool navigateToComments;
|
||||
|
||||
const AboutDietScreen({super.key, required this.index, required this.navigateToComments, required this.slug});
|
||||
const AboutDietScreen({super.key, required this.index, required this.navigateToComments, required this.slug, required this.id});
|
||||
|
||||
@override
|
||||
State<AboutDietScreen> createState() => _AboutDietScreenState();
|
||||
@@ -79,7 +80,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
final s = S.of(context);
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => dietsCubit..initDietPage(widget.slug),
|
||||
create: (context) => dietsCubit..initDietPage(slug: widget.slug, id: widget.id),
|
||||
child: BlocProvider<CommentsCubit>(
|
||||
create: (BuildContext context) => commentsCubit,
|
||||
child: BlocConsumer<CommentsCubit, CommentsState>(
|
||||
@@ -283,7 +284,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
replyId = null;
|
||||
});
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
dietsCubit.getDiet(dietState.diet.slug, index: widget.index);
|
||||
dietsCubit.getDiet(slug: dietState.diet.slug, index: widget.index);
|
||||
});
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
}
|
||||
@@ -353,7 +354,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${dietState.diet.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${dietState.diet.image}',
|
||||
height: 300,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
fit: BoxFit.fill,
|
||||
@@ -526,7 +527,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
),
|
||||
),
|
||||
body: DataErrorWidget(
|
||||
onReload: () => context.read<AboutDietCubit>().getDiet(widget.slug, index: widget.index),
|
||||
onReload: () => context.read<AboutDietCubit>().getDiet(slug: widget.slug, id: widget.id, index: widget.index),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
@@ -37,45 +38,25 @@ class AuthorCard extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(baseUrl + author.authorProfileIcon),
|
||||
image: NetworkImage(
|
||||
author.authorProfileIcon.contains('https') ? author.authorProfileIcon : sl<AppConfig>().baseUrl + author.authorProfileIcon,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
author.authorName,
|
||||
style: TextStyles.textLSemibold.copyWith(
|
||||
color: appTheme.black,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: titleStyle ?? TextStyles.textSSemibold.copyWith(
|
||||
color: appTheme.gray,
|
||||
),
|
||||
),
|
||||
subtitle: Text(author.authorName, style: TextStyles.textLSemibold.copyWith(color: appTheme.black)),
|
||||
title: Text(title, style: titleStyle ?? TextStyles.textSSemibold.copyWith(color: appTheme.gray)),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Консультант',
|
||||
style: TextStyles.textMMedium.copyWith(
|
||||
color: appTheme.black,
|
||||
),
|
||||
),
|
||||
Text('Консультант', style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
const SizedBox(width: 10),
|
||||
if (author.authorPlaceInRating != null)
|
||||
InfoBouble(
|
||||
radius: 12,
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
children: [
|
||||
Text(
|
||||
'${author.authorPlaceInRating}-й в рейтинге',
|
||||
style: TextStyles.textMMediumAuto.copyWith(
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
),
|
||||
],
|
||||
children: [Text('${author.authorPlaceInRating}-й в рейтинге', style: TextStyles.textMMediumAuto.copyWith(color: appTheme.blueA400))],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -88,18 +69,8 @@ class AuthorCard extends StatelessWidget {
|
||||
child: Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.whiteA700,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/arrow.svg',
|
||||
width: 12,
|
||||
height: 12,
|
||||
fit: BoxFit.none,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(color: appTheme.whiteA700, borderRadius: BorderRadius.circular(8)),
|
||||
child: Center(child: SvgPicture.asset('assets/icons/arrow.svg', width: 12, height: 12, fit: BoxFit.none)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
@@ -35,7 +36,7 @@ class MoreOptions extends StatelessWidget {
|
||||
onTap: disableActions == true
|
||||
? null
|
||||
: () {
|
||||
Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
|
||||
Clipboard.setData(ClipboardData(text: '${sl<AppConfig>().baseUrl}/ru/$path'));
|
||||
showInfoToast(s.link_was_copied);
|
||||
},
|
||||
child: Row(
|
||||
@@ -62,7 +63,7 @@ class MoreOptions extends StatelessWidget {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/$path',),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/$path',),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dismissible_page/dismissible_page.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/date_time_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_popup.dart';
|
||||
@@ -64,7 +65,7 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
authorName: state.feedBlogPostList[widget.index].author.authorName,
|
||||
authorProfileIcon: state.feedBlogPostList[widget.index].author.authorProfileIcon.contains('https')
|
||||
? state.feedBlogPostList[widget.index].author.authorProfileIcon
|
||||
: baseUrl + state.feedBlogPostList[widget.index].author.authorProfileIcon,
|
||||
: sl<AppConfig>().baseUrl + state.feedBlogPostList[widget.index].author.authorProfileIcon,
|
||||
authorIsMe: state.feedBlogPostList[widget.index].author.authorIsMe,
|
||||
),
|
||||
);
|
||||
@@ -75,7 +76,7 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
foregroundImage: NetworkImage(
|
||||
state.feedBlogPostList[widget.index].author.authorProfileIcon.contains('https')
|
||||
? state.feedBlogPostList[widget.index].author.authorProfileIcon
|
||||
: baseUrl + state.feedBlogPostList[widget.index].author.authorProfileIcon,
|
||||
: sl<AppConfig>().baseUrl + state.feedBlogPostList[widget.index].author.authorProfileIcon,
|
||||
),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
),
|
||||
@@ -157,15 +158,15 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
onTap: () {
|
||||
context.pushTransparentRoute(
|
||||
rootNavigator: true,
|
||||
PhotoViewScreen(imageUrls: ['$baseUrl/storage/${state.feedBlogPostList[widget.index].image}'], index: 0),
|
||||
PhotoViewScreen(imageUrls: ['${sl<AppConfig>().baseUrl}/storage/${state.feedBlogPostList[widget.index].image}'], index: 0),
|
||||
);
|
||||
},
|
||||
child: Hero(
|
||||
tag: '$baseUrl/storage/${state.feedBlogPostList[widget.index].image}',
|
||||
tag: '${sl<AppConfig>().baseUrl}/storage/${state.feedBlogPostList[widget.index].image}',
|
||||
child: CachedNetworkImage(
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
imageUrl: '$baseUrl/storage/${state.feedBlogPostList[widget.index].image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.feedBlogPostList[widget.index].image}',
|
||||
errorWidget: (context, str, object) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
@@ -219,7 +220,7 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/blog_post/${state.feedBlogPostList[widget.index].id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/blog_post/${state.feedBlogPostList[widget.index].id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/comments/presentation/comments_actions_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/date_time_utils.dart';
|
||||
@@ -99,13 +99,13 @@ class _CommentsWidgetState extends State<CommentsWidget> {
|
||||
extra: Author(
|
||||
authorSlug: comment.authorSlug,
|
||||
authorName: comment.authorName,
|
||||
authorProfileIcon: comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : baseUrl + comment.authorProfileIcon,
|
||||
authorProfileIcon: comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : sl<AppConfig>().baseUrl + comment.authorProfileIcon,
|
||||
authorIsMe: isMe,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: CircleAvatar(
|
||||
foregroundImage: NetworkImage(comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : baseUrl + comment.authorProfileIcon),
|
||||
foregroundImage: NetworkImage(comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : sl<AppConfig>().baseUrl + comment.authorProfileIcon),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
radius: radius,
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
|
||||
@@ -14,10 +14,7 @@ import 'package:go_router/go_router.dart';
|
||||
class ConsultantWidget extends StatelessWidget {
|
||||
final FeedConsultantModel consultant;
|
||||
|
||||
const ConsultantWidget({
|
||||
super.key,
|
||||
required this.consultant,
|
||||
});
|
||||
const ConsultantWidget({super.key, required this.consultant});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -36,10 +33,7 @@ class ConsultantWidget extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: const Color(0xFFF6F6F6),
|
||||
),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: const Color(0xFFF6F6F6)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -51,46 +45,32 @@ class ConsultantWidget extends StatelessWidget {
|
||||
children: [
|
||||
Ink(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
|
||||
child: Text(
|
||||
'${consultant.ratingPlace}',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: appTheme.blueA400,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
style: TextStyle(fontSize: 20, color: appTheme.blueA400, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
CircleAvatar(
|
||||
foregroundImage: NetworkImage(baseUrl + consultant.photo),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
),
|
||||
if (consultant.photo.contains('https'))
|
||||
CircleAvatar(foregroundImage: NetworkImage(consultant.photo), backgroundImage: const AssetImage('assets/images/no_avatar.png'))
|
||||
else
|
||||
CircleAvatar(
|
||||
foregroundImage: NetworkImage(sl<AppConfig>().baseUrl + consultant.photo),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
consultant.name,
|
||||
style: const TextStyle(
|
||||
height: 1,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 15,
|
||||
color: Color(0xFF323F4B),
|
||||
),
|
||||
style: const TextStyle(height: 1, fontWeight: FontWeight.w500, fontSize: 15, color: Color(0xFF323F4B)),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Рейтинг: ${consultant.totalScores}',
|
||||
style: TextStyle(
|
||||
color: appTheme.blueA400,
|
||||
height: 1,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
),
|
||||
style: TextStyle(color: appTheme.blueA400, height: 1, fontWeight: FontWeight.w500, fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -98,21 +78,13 @@ class ConsultantWidget extends StatelessWidget {
|
||||
),
|
||||
Ink(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/go_to_arrow.svg',
|
||||
),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4), color: Colors.white),
|
||||
child: SvgPicture.asset('assets/icons/go_to_arrow.svg'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
consultant.description,
|
||||
maxLines: 2,
|
||||
),
|
||||
Text(consultant.description, maxLines: 2),
|
||||
if (consultant.badges.isNotEmpty) const SizedBox(height: 8),
|
||||
_badges(consultant.badges, context),
|
||||
],
|
||||
@@ -128,16 +100,8 @@ class ConsultantWidget extends StatelessWidget {
|
||||
children: [
|
||||
for (int i = 0; i < badges.length; i++) ...[
|
||||
badges[i].image.contains('svg')
|
||||
? SvgPicture.network(
|
||||
'$baseUrl/storage/${badges[i].image}',
|
||||
width: 28,
|
||||
height: 28,
|
||||
)
|
||||
: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${badges[i].image}',
|
||||
width: 28,
|
||||
height: 28,
|
||||
),
|
||||
? SvgPicture.network('${sl<AppConfig>().baseUrl}/storage/${badges[i].image}', width: 28, height: 28)
|
||||
: CachedNetworkImage(imageUrl: '${sl<AppConfig>().baseUrl}/storage/${badges[i].image}', width: 28, height: 28),
|
||||
i < badges.length - 1 ? const SizedBox(width: 8) : const SizedBox.shrink(),
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/widgets/bookmark_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_popup.dart';
|
||||
import 'package:eatvio_app/core/widgets/info_row.dart';
|
||||
@@ -56,7 +57,7 @@ class _DietsWidgetState extends State<DietsWidget> {
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${widget.diet.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${widget.diet.image}',
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
),
|
||||
@@ -189,7 +190,7 @@ class _DietsWidgetState extends State<DietsWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
final result = await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/diet/${widget.diet.id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/diet/${widget.diet.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/widgets/bookmark_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_popup.dart';
|
||||
import 'package:eatvio_app/core/widgets/info_row.dart';
|
||||
@@ -61,7 +62,7 @@ class _PlanWidgetState extends State<PlanWidget> {
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${widget.plan.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${widget.plan.image}',
|
||||
fit: BoxFit.cover,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 426,
|
||||
@@ -187,7 +188,7 @@ class _PlanWidgetState extends State<PlanWidget> {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(baseUrl + widget.plan.author.authorProfileIcon),
|
||||
image: NetworkImage(sl<AppConfig>().baseUrl + widget.plan.author.authorProfileIcon),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
@@ -263,7 +264,7 @@ class _PlanWidgetState extends State<PlanWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
final result = await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/diet/${widget.plan.id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/diet/${widget.plan.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// ignore_for_file: dead_code
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/bookmark_button.dart';
|
||||
@@ -75,7 +76,7 @@ class _RecipesWidgetState extends State<RecipesWidget> {
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${widget.recipe.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${widget.recipe.image}',
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
),
|
||||
@@ -231,7 +232,7 @@ class _RecipesWidgetState extends State<RecipesWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/recipe/${widget.recipe.id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/recipe/${widget.recipe.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -133,9 +133,9 @@ class FeedRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<FeedConsultantModel>> getConsultants() async {
|
||||
Future<List<FeedConsultantModel>> getConsultants([String? name]) async {
|
||||
try {
|
||||
return (await feedService.getConsultants()).data;
|
||||
return (await feedService.getConsultants(name: name)).data;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
||||
@@ -16,15 +16,10 @@ abstract class FeedService {
|
||||
factory FeedService(Dio dio, {String baseUrl}) = _FeedService;
|
||||
|
||||
@GET('/api/blogPosts/index')
|
||||
Future<FeedBlogPostSettingsModel> getBlogPostList({
|
||||
@Query('page') required int page,
|
||||
@Query('query_search') String? query,
|
||||
});
|
||||
Future<FeedBlogPostSettingsModel> getBlogPostList({@Query('page') required int page, @Query('query_search') String? query});
|
||||
|
||||
@GET('/api/blogPosts/show/{postId}')
|
||||
Future<FeedBlogPostModel> getBlogPost({
|
||||
@Path('postId') required int postId,
|
||||
});
|
||||
Future<FeedBlogPostModel> getBlogPost({@Path('postId') required int postId});
|
||||
|
||||
@GET('/api/recipes/index')
|
||||
Future<RecipesPagination> getRecipes({
|
||||
@@ -53,88 +48,50 @@ abstract class FeedService {
|
||||
});
|
||||
|
||||
@GET('/api/blogPosts/{id}/comments/show')
|
||||
Future<CommentModel> getBlogPostComments({
|
||||
@Path() required String id,
|
||||
});
|
||||
Future<CommentModel> getBlogPostComments({@Path() required String id});
|
||||
|
||||
@POST('/api/blogPosts/{id}/comment/create')
|
||||
Future<void> sendBlogPostComment({
|
||||
@Path() required String id,
|
||||
@Part() required String body,
|
||||
@Part() int? parentCommentId,
|
||||
});
|
||||
Future<void> sendBlogPostComment({@Path() required String id, @Part() required String body, @Part() int? parentCommentId});
|
||||
|
||||
@GET('/api/diets/{id}/comments/show')
|
||||
Future<CommentModel> getDietsComments({
|
||||
@Path() required String id,
|
||||
});
|
||||
Future<CommentModel> getDietsComments({@Path() required String id});
|
||||
|
||||
@POST('/api/diets/{id}/comment/create')
|
||||
Future<void> sendDietsComment({
|
||||
@Path() required String id,
|
||||
@Part() required String body,
|
||||
@Part() int? parentCommentId,
|
||||
});
|
||||
Future<void> sendDietsComment({@Path() required String id, @Part() required String body, @Part() int? parentCommentId});
|
||||
|
||||
@GET('/api/plans/{slug}/comments/show')
|
||||
Future<CommentModel> getPlansComments({
|
||||
@Path() required String slug,
|
||||
});
|
||||
Future<CommentModel> getPlansComments({@Path() required String slug});
|
||||
|
||||
@POST('/api/plans/{slug}/comment/create')
|
||||
Future<void> sendPlansComment({
|
||||
@Path() required String slug,
|
||||
@Part() required String body,
|
||||
@Part() int? parentCommentId,
|
||||
});
|
||||
Future<void> sendPlansComment({@Path() required String slug, @Part() required String body, @Part() int? parentCommentId});
|
||||
|
||||
@GET('/api/recipes/{id}/comments/show')
|
||||
Future<CommentModel> getRecipesComments({
|
||||
@Path() required String id,
|
||||
});
|
||||
Future<CommentModel> getRecipesComments({@Path() required String id});
|
||||
|
||||
@POST('/api/recipes/{id}/comment/create')
|
||||
Future<void> sendRecipesComment({
|
||||
@Path() required String id,
|
||||
@Part() required String body,
|
||||
@Part() int? parentCommentId,
|
||||
});
|
||||
Future<void> sendRecipesComment({@Path() required String id, @Part() required String body, @Part() int? parentCommentId});
|
||||
|
||||
@POST('/api/blogPosts/{id}/like')
|
||||
Future<void> likeBlogPost({
|
||||
@Path() required int id,
|
||||
});
|
||||
Future<void> likeBlogPost({@Path() required int id});
|
||||
|
||||
@POST('/api/blogPosts/{id}/unliked')
|
||||
Future<void> unlikedBlogPost({
|
||||
@Path() required int id,
|
||||
});
|
||||
Future<void> unlikedBlogPost({@Path() required int id});
|
||||
|
||||
@GET('/api/consultants/index')
|
||||
Future<FeedConsultantPaginationModel> getConsultants();
|
||||
Future<FeedConsultantPaginationModel> getConsultants({@Query('filters[name][\$startsWith]') String? name});
|
||||
|
||||
@POST('/api/blogPosts/{id}/view')
|
||||
Future<void> viewPost({
|
||||
@Path() required int id,
|
||||
});
|
||||
Future<void> viewPost({@Path() required int id});
|
||||
|
||||
@POST('/api/recipes/{slug}/view')
|
||||
Future<void> viewRecipe({
|
||||
@Path() required String slug,
|
||||
});
|
||||
Future<void> viewRecipe({@Path() required String slug});
|
||||
|
||||
@POST('/api/diets/{id}/view')
|
||||
Future<void> viewDiet({
|
||||
@Path() required int id,
|
||||
});
|
||||
Future<void> viewDiet({@Path() required int id});
|
||||
|
||||
@POST('/api/plans/{slug}/view')
|
||||
Future<void> viewPlan({
|
||||
@Path() required String slug,
|
||||
});
|
||||
Future<void> viewPlan({@Path() required String slug});
|
||||
|
||||
@POST('/api/views')
|
||||
Future<void> viewContent({
|
||||
@Body() required ViewModel viewModel,
|
||||
});
|
||||
Future<void> viewContent({@Body() required ViewModel viewModel});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
@@ -18,7 +19,7 @@ class _GlossaryScreenState extends State<GlossaryScreen> {
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..loadRequest(Uri.parse('$baseUrl/login_with_pass_key?id=${widget.passKey}'));
|
||||
..loadRequest(Uri.parse('${sl<AppConfig>().baseUrl}/login_with_pass_key?id=${widget.passKey}'));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/token_storage.dart';
|
||||
@@ -64,7 +65,7 @@ class MenuScreen extends StatelessWidget {
|
||||
if (profileModel.roles.contains('consultant')) _divider(context),
|
||||
if (profileModel.roles.contains('consultant'))
|
||||
_tile(
|
||||
leading: 'assets/icons/finance.svg',
|
||||
leading: 'assets/icons/consultant_rating_icon.svg',
|
||||
title: 'Мой рейтинг',
|
||||
onTap: () {
|
||||
GoRouter.of(context).push(ConsultantRatingScreen.path);
|
||||
@@ -139,7 +140,7 @@ class MenuScreen extends StatelessWidget {
|
||||
CupertinoDialogAction(
|
||||
onPressed: () async {
|
||||
try {
|
||||
await sl<Dio>().post('$baseUrl/api/profile/logout');
|
||||
await sl<Dio>().post('${sl<AppConfig>().baseUrl}/api/profile/logout');
|
||||
} finally {
|
||||
await TokenStorage.removeTokenInStorage();
|
||||
if (context.mounted) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
|
||||
import 'package:eatvio_app/features/profile/models/profile_model.dart';
|
||||
@@ -33,7 +34,7 @@ class _BlockedUserWidgetState extends State<BlockedUserWidget> {
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
foregroundImage: NetworkImage(baseUrl + widget.profile.profilePhotoUrl),
|
||||
foregroundImage: NetworkImage(sl<AppConfig>().baseUrl + widget.profile.profilePhotoUrl),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
radius: 28,
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
@@ -56,7 +57,7 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${widget.diet.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${widget.diet.image}',
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
),
|
||||
@@ -153,7 +154,7 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/diet/${widget.diet.id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/diet/${widget.diet.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
@@ -42,7 +43,7 @@ class _MyPlanWidgetState extends State<MyPlanWidget> {
|
||||
onTap: () => GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'slug': widget.plan.slug, 'navigateToComments': false}),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(imageUrl: '$baseUrl/storage/${widget.plan.image}', fit: BoxFit.fitWidth, width: MediaQuery.of(context).size.width),
|
||||
child: CachedNetworkImage(imageUrl: '${sl<AppConfig>().baseUrl}/storage/${widget.plan.image}', fit: BoxFit.fitWidth, width: MediaQuery.of(context).size.width),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
@@ -157,7 +158,7 @@ class _MyPlanWidgetState extends State<MyPlanWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/plan/${widget.plan.id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/plan/${widget.plan.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
@@ -31,7 +32,7 @@ class PlanInfo extends StatelessWidget {
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/$imagePath',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/$imagePath',
|
||||
height: 220,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
fit: BoxFit.fill,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
@@ -401,7 +401,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
||||
Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${state.planModel?.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.planModel?.image}',
|
||||
height: 300,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
fit: BoxFit.fill,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dismissible_page/dismissible_page.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
@@ -655,7 +656,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
boxShadow: [BoxShadow(color: Colors.black.withAlpha((255 * 0.2).toInt()), blurRadius: 32)],
|
||||
),
|
||||
child: SvgPicture.network('$baseUrl/storage/${state.profile.badges[i].image}', width: 40, height: 40),
|
||||
child: SvgPicture.network('${sl<AppConfig>().baseUrl}/storage/${state.profile.badges[i].image}', width: 40, height: 40),
|
||||
)
|
||||
: Tooltip(
|
||||
message: state.profile.badges[i].description,
|
||||
@@ -667,7 +668,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
boxShadow: [BoxShadow(color: Colors.black.withAlpha((255 * 0.2).toInt()), blurRadius: 32)],
|
||||
),
|
||||
child: CachedNetworkImage(imageUrl: '$baseUrl/storage/${state.profile.badges[i].image}', width: 40, height: 40),
|
||||
child: CachedNetworkImage(imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.profile.badges[i].image}', width: 40, height: 40),
|
||||
),
|
||||
i < state.profile.badges.length - 1 ? const SizedBox(width: 8) : const SizedBox.shrink(),
|
||||
],
|
||||
@@ -803,7 +804,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
icon: 'assets/icons/eye.svg',
|
||||
text: 'Посмотреть',
|
||||
onTap: () async {
|
||||
context.pushTransparentRoute(rootNavigator: true, PhotoViewScreen(imageUrls: [baseUrl + state.profile.profilePhotoUrl], index: 0));
|
||||
context.pushTransparentRoute(rootNavigator: true, PhotoViewScreen(imageUrls: [sl<AppConfig>().baseUrl + state.profile.profilePhotoUrl], index: 0));
|
||||
},
|
||||
),
|
||||
if (widget.author == null || widget.author!.authorIsMe) ...[
|
||||
@@ -885,7 +886,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
child: CircleAvatar(
|
||||
radius: 32,
|
||||
foregroundImage: NetworkImage(
|
||||
state.profile.profilePhotoUrl.contains('https') ? state.profile.profilePhotoUrl : baseUrl + state.profile.profilePhotoUrl,
|
||||
state.profile.profilePhotoUrl.contains('https') ? state.profile.profilePhotoUrl : sl<AppConfig>().baseUrl + state.profile.profilePhotoUrl,
|
||||
),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
),
|
||||
@@ -960,7 +961,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
onTap: () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
SharePlus.instance.share(
|
||||
ShareParams(uri: Uri.parse('$baseUrl/ru/profile/${state.profile.slug}'), sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size),
|
||||
ShareParams(uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/profile/${state.profile.slug}'), sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dismissible_page/dismissible_page.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/date_time_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_popup.dart';
|
||||
import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
|
||||
@@ -69,7 +70,7 @@ class _ProfileBlogPostWidgetState extends State<ProfileBlogPostWidget> {
|
||||
foregroundImage: NetworkImage(
|
||||
state.blogPosts[widget.index].author.authorProfileIcon.contains('https')
|
||||
? state.blogPosts[widget.index].author.authorProfileIcon
|
||||
: baseUrl + state.blogPosts[widget.index].author.authorProfileIcon,
|
||||
: sl<AppConfig>().baseUrl + state.blogPosts[widget.index].author.authorProfileIcon,
|
||||
),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
),
|
||||
@@ -157,17 +158,17 @@ class _ProfileBlogPostWidgetState extends State<ProfileBlogPostWidget> {
|
||||
context.pushTransparentRoute(
|
||||
rootNavigator: true,
|
||||
PhotoViewScreen(
|
||||
imageUrls: ['$baseUrl/storage/${state.blogPosts[widget.index].image}'],
|
||||
imageUrls: ['${sl<AppConfig>().baseUrl}/storage/${state.blogPosts[widget.index].image}'],
|
||||
index: 0,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Hero(
|
||||
tag: '$baseUrl/storage/${state.blogPosts[widget.index].image}',
|
||||
tag: '${sl<AppConfig>().baseUrl}/storage/${state.blogPosts[widget.index].image}',
|
||||
child: CachedNetworkImage(
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
imageUrl: '$baseUrl/storage/${state.blogPosts[widget.index].image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.blogPosts[widget.index].image}',
|
||||
errorWidget: (context, str, object) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
@@ -226,7 +227,7 @@ class _ProfileBlogPostWidgetState extends State<ProfileBlogPostWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/blog_post/${state.blogPosts[widget.index].id}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/blog_post/${state.blogPosts[widget.index].id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/profile/models/sub_model.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/widgets/sub_action_button_widget.dart';
|
||||
@@ -36,7 +37,7 @@ class _SubWidgetState extends State<SubWidget> {
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
foregroundImage: NetworkImage(baseUrl + widget.subModel.profilePhotoUrl),
|
||||
foregroundImage: NetworkImage(sl<AppConfig>().baseUrl + widget.subModel.profilePhotoUrl),
|
||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||
radius: 28,
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
@@ -247,7 +247,7 @@ class _AboutRecipeScreenState extends State<AboutRecipeScreen> {
|
||||
Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${recipeState.recipe.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${recipeState.recipe.image}',
|
||||
height: 300,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
fit: BoxFit.fill,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
@@ -71,7 +71,7 @@ class _MyRecipeWidgetState extends State<MyRecipeWidget> {
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '$baseUrl/storage/${widget.recipe.image}',
|
||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${widget.recipe.image}',
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
),
|
||||
@@ -162,7 +162,7 @@ class _MyRecipeWidgetState extends State<MyRecipeWidget> {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/recipe/${widget.recipe.slug}'),
|
||||
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/recipe/${widget.recipe.slug}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
@@ -44,7 +45,7 @@ class StepsWidget extends StatelessWidget {
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Image.network(
|
||||
'$baseUrl/storage/${recipe.steps![index].image}',
|
||||
'${sl<AppConfig>().baseUrl}/storage/${recipe.steps![index].image}',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/features/registration/services/registration_analytics_service.dart';
|
||||
@@ -89,7 +90,7 @@ class _VerifyScreenState extends State<VerifyScreen> {
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse('$baseUrl/login_with_pass_key?id=${widget.passKey}'));
|
||||
..loadRequest(Uri.parse('${sl<AppConfig>().baseUrl}/login_with_pass_key?id=${widget.passKey}'));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart';
|
||||
import 'package:eatvio_app/features/registration/services/registration_analytics_service.dart';
|
||||
@@ -54,7 +54,7 @@ class _WebViewRegistrationScreenState extends State<WebViewRegistrationScreen> {
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse('$baseUrl/registration/step1'));
|
||||
..loadRequest(Uri.parse('${sl<AppConfig>().baseUrl}/registration/step1'));
|
||||
|
||||
_injectJavaScript();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
@@ -71,7 +73,7 @@ class SupportScreen extends StatelessWidget {
|
||||
const SizedBox(height: 12),
|
||||
if (!hidePlans) ...[
|
||||
InkWell(
|
||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/public_offer.pdf')),
|
||||
onTap: () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/docs/public_offer.pdf')),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
@@ -81,7 +83,7 @@ class SupportScreen extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
InkWell(
|
||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')),
|
||||
onTap: () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/docs/privacy.pdf')),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
@@ -90,7 +92,7 @@ class SupportScreen extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/company-details')),
|
||||
onTap: () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/company-details')),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:eatvio_app/app_keys.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/token_storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -79,6 +80,7 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
InkWell(
|
||||
key: AppKeys.welcomeScreenButton,
|
||||
onTap: () async {
|
||||
final sharedPreferences = await SharedPreferences.getInstance();
|
||||
await sharedPreferences.setBool('welcomeScreenWasShowed', true);
|
||||
|
||||
@@ -66,6 +66,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"add_card": MessageLookupByLibrary.simpleMessage("Привязать карту"),
|
||||
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
|
||||
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"),
|
||||
"added_products_in_glossary": MessageLookupByLibrary.simpleMessage(
|
||||
"Кол-во продуктов добавленных в глоссарий",
|
||||
),
|
||||
"adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"),
|
||||
"additional_information": MessageLookupByLibrary.simpleMessage(
|
||||
"Дополнительная информация",
|
||||
|
||||
@@ -2400,6 +2400,16 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Кол-во продуктов добавленных в глоссарий`
|
||||
String get added_products_in_glossary {
|
||||
return Intl.message(
|
||||
'Кол-во продуктов добавленных в глоссарий',
|
||||
name: 'added_products_in_glossary',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Кол-во активных консультаций`
|
||||
String get active_consultations_count {
|
||||
return Intl.message(
|
||||
|
||||
@@ -300,6 +300,7 @@
|
||||
"published_recipes_count": "Кол-во опубликованных рецептов",
|
||||
"published_diets_count": "Кол-во опубликованных диет",
|
||||
"consultations_count": "Кол-во проведенных консулдьтаций",
|
||||
"added_products_in_glossary": "Кол-во продуктов добавленных в глоссарий",
|
||||
"active_consultations_count": "Кол-во активных консультаций",
|
||||
"rating_history": "Начисление рейтинга",
|
||||
"something_went_wrong": "Упс! Что-то пошло не так",
|
||||
|
||||
@@ -1,53 +1,16 @@
|
||||
import 'dart:io' as io;
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_initializer.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart' as di;
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.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/features/internet_connection_checker/internet_connection_checker_screen.dart';
|
||||
import 'package:eatvio_app/firebase_options.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:eatvio_app/core/env_config.dart';
|
||||
import 'package:appmetrica_plugin/appmetrica_plugin.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsBinding widgetsBinding = SentryWidgetsFlutterBinding.ensureInitialized();
|
||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
|
||||
await dotenv.load(fileName: ".env");
|
||||
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
await FirebaseMessaging.instance.requestPermission();
|
||||
await di.init();
|
||||
|
||||
if (kDebugMode) {
|
||||
io.HttpClient.enableTimelineLogging = true;
|
||||
}
|
||||
|
||||
AppMetrica.activate(AppMetricaConfig(EnvConfig.appMetricaApiKey));
|
||||
AppMetrica.reportEvent('Application started');
|
||||
|
||||
await SentryFlutter.init((options) {
|
||||
options.dsn = EnvConfig.sentryDsn;
|
||||
options.tracesSampleRate = 1.0;
|
||||
options.debug = false;
|
||||
}, appRunner: () => runApp(const MyApp()));
|
||||
await AppInitializer.initialize(buildType: AppBuildType.prod);
|
||||
await AppInitializer.runAppWithSentry(() => runApp(const MyApp()));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@@ -56,30 +19,15 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<NewMessagesCubit>(create: (context) => sl<NewMessagesCubit>()),
|
||||
BlocProvider<NotificationsCubit>(create: (context) => sl<NotificationsCubit>()..getNotifications()),
|
||||
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
|
||||
BlocProvider<ProfileAvatarCubit>(create: (context) => sl<ProfileAvatarCubit>()..getMyProfile()),
|
||||
BlocProvider<ChatsCubit>(create: (context) => sl<ChatsCubit>()..getChatRoomsWithLoading()),
|
||||
],
|
||||
providers: sl<AppConfig>().globalBlocProviders,
|
||||
child: Portal(
|
||||
child: KeyboardDismisser(
|
||||
child: MaterialApp.router(
|
||||
theme: ThemeHelper().themeData(),
|
||||
routerConfig: AppRouter.appRouter,
|
||||
builder: (context, child) => BlocProvider(
|
||||
create: (context) => sl<ViewsCubit>(),
|
||||
child: InternetConnectionCheckerScreen(child: child!),
|
||||
),
|
||||
debugShowCheckedModeBanner: false,
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale.fromSubtags(languageCode: 'ru')],
|
||||
localizationsDelegates: sl<AppConfig>().localizationDelegates,
|
||||
supportedLocales: sl<AppConfig>().supportedLocales,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user