diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index fec9b100..e6a76463 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -101,7 +101,6 @@ class AppRouter { path: '/my-profile-screen', name: 'myProfileScreen', redirect: (context, state) async { - await sl().initialize(); return null; }, pageBuilder: (context, state) { @@ -113,6 +112,10 @@ class AppRouter { name: 'feedScreen', pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen()), redirect: (context, state) async { + final data = state.extra as Map?; + if (data?['needToInitializeWebSocket'] == false) { + return null; + } await sl().initialize(); return null; }, diff --git a/lib/core/const.dart b/lib/core/const.dart index 31c47e9c..4f38a54e 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,5 @@ -String baseUrl = 'https://eatvio.ru'; -// String baseUrl = 'https://dev2.eatvio.com'; +// String baseUrl = 'https://eatvio.ru'; +String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/websockets/ws_manager.dart b/lib/core/websockets/ws_manager.dart index ce47f26a..02857ca0 100644 --- a/lib/core/websockets/ws_manager.dart +++ b/lib/core/websockets/ws_manager.dart @@ -18,7 +18,7 @@ class WsManager { PrivateChannel? _privateDirectChatChannel; Stream? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated'); - + Stream? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated'); Stream? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated'); @@ -135,4 +135,16 @@ class WsManager { void unsubscribeDirectChatChannel() { _privateDirectChatChannel?.unsubscribe(); } + + Future clear() async{ + await client?.disconnect(); + client = null; + userId = null; + _privateDirectChatChannel?.unsubscribe(); + _privateMessagesChannel?.unsubscribe(); + _privateNotificationChannel?.unsubscribe(); + _privateDirectChatChannel = null; + _privateMessagesChannel = null; + _privateNotificationChannel = null; + } } diff --git a/lib/features/auth/presentation/bloc/auth_cubit.dart b/lib/features/auth/presentation/bloc/auth_cubit.dart index 03d3c896..3c5e30af 100644 --- a/lib/features/auth/presentation/bloc/auth_cubit.dart +++ b/lib/features/auth/presentation/bloc/auth_cubit.dart @@ -1,12 +1,11 @@ import 'dart:developer'; -import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/auth_state.dart'; import 'package:eatvio_app/features/auth/repositories/auth_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:go_router/go_router.dart'; + class AuthCubit extends Cubit { final AuthRepository authRepository; @@ -28,9 +27,7 @@ class AuthCubit extends Cubit { token: token, ), ); - showSuccessToast('Успешный вход'); - await TokenStorage.saveTokenInStorage(token); - AppRouter.navigatorKey.currentContext?.go('/feed-screen'); + await TokenStorage.saveTokenInStorage(token); return true; } catch (e) { log(e.toString()); diff --git a/lib/features/auth/presentation/pages/auth_screen.dart b/lib/features/auth/presentation/pages/auth_screen.dart index 8c068755..580b1f47 100644 --- a/lib/features/auth/presentation/pages/auth_screen.dart +++ b/lib/features/auth/presentation/pages/auth_screen.dart @@ -5,10 +5,13 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/locator_service.dart'; 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_text_form_field.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/auth_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/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; @@ -49,6 +52,20 @@ class _AuthScreenState extends State { }); } + Future _reconnectWebSocket(BuildContext context) async { + await sl().clear(); + await sl().initialize(); + if (context.mounted) { + context.read().getMyProfile(); + context.read().getChatRoomsWithLoading(); + context.read().connectToWebSocket(); + context.read().getNotifications(); + context.read().connectToWebSocket(); + showSuccessToast('Успешный вход'); + GoRouter.of(context).go('/feed-screen', extra: {'needToInitializeWebSocket': false}); + } + } + @override Widget build(BuildContext context) { final s = S.of(context); @@ -179,8 +196,7 @@ class _AuthScreenState extends State { ); if (isAuthenticated) { if (context.mounted) { - context.read().getMyProfile(); - context.read().getChatRoomsWithLoading(); + _reconnectWebSocket(context); } } }, diff --git a/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart b/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart index a4f0bbf2..3b50c995 100644 --- a/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart +++ b/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart @@ -14,18 +14,14 @@ class ChatsCubit extends Cubit { final ChatRepository chatRepository; final ProfileRepository profileRepository; final WsManager wsManager; - late StreamSubscription? newIncomingMessagesSubscription; - late StreamSubscription? newLastMessageSubscription; + StreamSubscription? newIncomingMessagesSubscription; + StreamSubscription? newLastMessageSubscription; ChatsCubit({ required this.chatRepository, required this.wsManager, required this.profileRepository, - }) : super(ChatsState()) { - newIncomingMessagesSubscription = wsManager.newMessagesStream?.listen((event) { - _handleNewMessageViaSocket(event.data); - }); - + }) : super(ChatsState()) { newLastMessageSubscription = chatRepository.newUserMessagesStream.listen((message) { _setNewLastMessage(message); }); @@ -33,6 +29,13 @@ class ChatsCubit extends Cubit { String? _query; int page = 1; late int lastPage; + Future connectToWebSocket() async { + await newIncomingMessagesSubscription?.cancel(); + newIncomingMessagesSubscription = wsManager.newMessagesStream?.listen((event) { + _handleNewMessageViaSocket(event.data); + }); + } + Future getChatRoomsWithLoading({String? query}) async { page = 1; _query = query; diff --git a/lib/features/create_plan/presentation/widgets/option_widget.dart b/lib/features/create_plan/presentation/widgets/option_widget.dart index fffa43b7..42362e96 100644 --- a/lib/features/create_plan/presentation/widgets/option_widget.dart +++ b/lib/features/create_plan/presentation/widgets/option_widget.dart @@ -21,21 +21,45 @@ class OptionWidget extends StatefulWidget { } class _OptionWidgetState extends State { - final customerPriceController = TextEditingController()..text = '1200₽'; + final firstOptionCustomerPriceController = TextEditingController()..text = '1200₽'; + final secondOptionCustomerPriceController = TextEditingController()..text = '1200₽'; + final thirdOptionCustomerPriceController = TextEditingController()..text = '1200₽'; + final creatorPriceController = TextEditingController()..text = '1000₽'; - final optionTitleController = TextEditingController(); - final optionDescriptionController = TextEditingController(); + + final firstOptionTitleController = TextEditingController(); + final secondOptionTitleController = TextEditingController(); + final thirdOptionTitleController = TextEditingController(); + + final firstOptionDescriptionController = TextEditingController(); + final secondOptionDescriptionController = TextEditingController(); + final thirdOptionDescriptionController = TextEditingController(); + bool firstExplanationVisible = false; bool isChecked = false; bool secondExplanationVisible = false; @override void initState() { super.initState(); - customerPriceController.addListener(() { - if (!customerPriceController.text.endsWith('₽')) { - final text = customerPriceController.text.replaceAll('₽', ''); - customerPriceController.text = '$text₽'; - customerPriceController.selection = TextSelection.collapsed(offset: text.length); + firstOptionCustomerPriceController.addListener(() { + if (!firstOptionCustomerPriceController.text.endsWith('₽')) { + final text = firstOptionCustomerPriceController.text.replaceAll('₽', ''); + firstOptionCustomerPriceController.text = '$text₽'; + firstOptionCustomerPriceController.selection = TextSelection.collapsed(offset: text.length); + } + }); + secondOptionCustomerPriceController.addListener(() { + if (!secondOptionCustomerPriceController.text.endsWith('₽')) { + final text = secondOptionCustomerPriceController.text.replaceAll('₽', ''); + secondOptionCustomerPriceController.text = '$text₽'; + secondOptionCustomerPriceController.selection = TextSelection.collapsed(offset: text.length); + } + }); + thirdOptionCustomerPriceController.addListener(() { + if (!thirdOptionCustomerPriceController.text.endsWith('₽')) { + final text = thirdOptionCustomerPriceController.text.replaceAll('₽', ''); + thirdOptionCustomerPriceController.text = '$text₽'; + thirdOptionCustomerPriceController.selection = TextSelection.collapsed(offset: text.length); } }); } @@ -45,15 +69,32 @@ class _OptionWidgetState extends State { final s = S.of(context); return BlocBuilder( builder: (context, state) { - final optionNameHintText = switch (state.selectedOptionIndex ?? 1) { + final selectedOptionIndex = state.selectedOptionIndex ?? 0; + final optionNameHintText = switch (selectedOptionIndex) { 0 => s.economy, 2 => s.comfort, _ => s.standard, }; - final selectedOptionIndex = state.selectedOptionIndex ?? 0; - - optionTitleController.text = state.planData.options[selectedOptionIndex].short; - optionDescriptionController.text = state.planData.options[selectedOptionIndex].description; + final optionTitleController = switch (selectedOptionIndex) { + 0 => firstOptionTitleController, + 2 => thirdOptionTitleController, + _ => secondOptionTitleController, + }; + final optionDescriptionController = switch (selectedOptionIndex) { + 0 => firstOptionDescriptionController, + 2 => thirdOptionDescriptionController, + _ => secondOptionDescriptionController, + }; + final customerPriceController = switch (selectedOptionIndex) { + 0 => firstOptionCustomerPriceController, + 2 => thirdOptionCustomerPriceController, + _ => secondOptionCustomerPriceController, + }; + + final price = double.tryParse(customerPriceController.text.replaceAll('₽', '')); + creatorPriceController.text = '${((price ?? 0) * 0.8).toStringAsFixed(2)}₽'; + firstOptionTitleController.text = state.planData.options[selectedOptionIndex].short; + firstOptionDescriptionController.text = state.planData.options[selectedOptionIndex].description; return MainBackgroundBouble( padding: const EdgeInsets.all(16), child: Column( @@ -151,7 +192,6 @@ class _OptionWidgetState extends State { hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].priceHasError, onChanged: (value) { final price = double.tryParse(value.replaceAll('₽', '')); - creatorPriceController.text = '${((price ?? 0) * 0.8).toStringAsFixed(2)}₽'; if (price != null) { context.read().setPrice(price); } diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart index 6ccb0973..cb2ff7ff 100644 --- a/lib/features/menu/presentation/pages/menu_screen.dart +++ b/lib/features/menu/presentation/pages/menu_screen.dart @@ -4,7 +4,6 @@ import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; -import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart'; import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; @@ -48,14 +47,14 @@ class MenuScreen extends StatelessWidget { _tile(leading: ImageConstant.icnGlossary, title: 'Глоссарий', onTap: () => GoRouter.of(context).push('/glossary-screen')), _divider(context), _tile(leading: ImageConstant.icnSupport, title: 'Поддержка', onTap: () => GoRouter.of(context).push('/support-screen')), - _divider(context), - _tile( - leading: ImageConstant.icnDevTools, - title: 'Для разработчиков', - onTap: () { - GoRouter.of(context).pushNamed(DevToolsScreen.name); - }, - ), + // _divider(context), + // _tile( + // leading: ImageConstant.icnDevTools, + // title: 'Для разработчиков', + // onTap: () { + // GoRouter.of(context).pushNamed(DevToolsScreen.name); + // }, + // ), _divider(context), _tile(leading: ImageConstant.icnExit, title: 'Выход', onTap: _onTapLogout(context)), ], diff --git a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart index 5e1f1139..16cf1f40 100644 --- a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart +++ b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:developer'; @@ -11,14 +12,19 @@ class NotificationsCubit extends Cubit { final List _notifications = []; final WsManager wsManager; final NotificationsRepository notificationsRepository; + StreamSubscription? _newNotificationsSubscription; NotificationsCubit({ required this.wsManager, required this.notificationsRepository, - }) : super(const NotificationsState.empty()) { - wsManager.newNotificationsStream?.listen((event) { + }) : super(const NotificationsState.empty()); + + Future connectToWebSocket() async { + await _newNotificationsSubscription?.cancel(); + _newNotificationsSubscription = wsManager.newNotificationsStream?.listen((event) { _handleNotification(event.data); }); } + BaseNotificationModel _parseNotification(Map notificationJson, NotificationType type) { try { return switch (type) {