forked from eatvio/eatvio_app
feat(plans): fixed plans price field in CreatePlanScreen, feat(menu): removed dev tools, feat(chats,notifications): added ws reconection
This commit is contained in:
@@ -101,7 +101,6 @@ class AppRouter {
|
||||
path: '/my-profile-screen',
|
||||
name: 'myProfileScreen',
|
||||
redirect: (context, state) async {
|
||||
await sl<WsManager>().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<String, dynamic>?;
|
||||
if (data?['needToInitializeWebSocket'] == false) {
|
||||
return null;
|
||||
}
|
||||
await sl<WsManager>().initialize();
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -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<int> durationValues = [7, 14, 21];
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class WsManager {
|
||||
PrivateChannel? _privateDirectChatChannel;
|
||||
|
||||
Stream<ChannelReadEvent>? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated');
|
||||
|
||||
|
||||
Stream<ChannelReadEvent>? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated');
|
||||
|
||||
Stream<ChannelReadEvent>? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated');
|
||||
@@ -135,4 +135,16 @@ class WsManager {
|
||||
void unsubscribeDirectChatChannel() {
|
||||
_privateDirectChatChannel?.unsubscribe();
|
||||
}
|
||||
|
||||
Future<void> clear() async{
|
||||
await client?.disconnect();
|
||||
client = null;
|
||||
userId = null;
|
||||
_privateDirectChatChannel?.unsubscribe();
|
||||
_privateMessagesChannel?.unsubscribe();
|
||||
_privateNotificationChannel?.unsubscribe();
|
||||
_privateDirectChatChannel = null;
|
||||
_privateMessagesChannel = null;
|
||||
_privateNotificationChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AuthState> {
|
||||
final AuthRepository authRepository;
|
||||
@@ -28,9 +27,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
token: token,
|
||||
),
|
||||
);
|
||||
showSuccessToast('Успешный вход');
|
||||
await TokenStorage.saveTokenInStorage(token);
|
||||
AppRouter.navigatorKey.currentContext?.go('/feed-screen');
|
||||
await TokenStorage.saveTokenInStorage(token);
|
||||
return true;
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
|
||||
@@ -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<AuthScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _reconnectWebSocket(BuildContext context) async {
|
||||
await sl<WsManager>().clear();
|
||||
await sl<WsManager>().initialize();
|
||||
if (context.mounted) {
|
||||
context.read<ProfileAvatarCubit>().getMyProfile();
|
||||
context.read<ChatsCubit>().getChatRoomsWithLoading();
|
||||
context.read<ChatsCubit>().connectToWebSocket();
|
||||
context.read<NotificationsCubit>().getNotifications();
|
||||
context.read<NotificationsCubit>().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<AuthScreen> {
|
||||
);
|
||||
if (isAuthenticated) {
|
||||
if (context.mounted) {
|
||||
context.read<ProfileAvatarCubit>().getMyProfile();
|
||||
context.read<ChatsCubit>().getChatRoomsWithLoading();
|
||||
_reconnectWebSocket(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -14,18 +14,14 @@ class ChatsCubit extends Cubit<ChatsState> {
|
||||
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<ChatsState> {
|
||||
String? _query;
|
||||
int page = 1;
|
||||
late int lastPage;
|
||||
Future<void> connectToWebSocket() async {
|
||||
await newIncomingMessagesSubscription?.cancel();
|
||||
newIncomingMessagesSubscription = wsManager.newMessagesStream?.listen((event) {
|
||||
_handleNewMessageViaSocket(event.data);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getChatRoomsWithLoading({String? query}) async {
|
||||
page = 1;
|
||||
_query = query;
|
||||
|
||||
@@ -21,21 +21,45 @@ class OptionWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _OptionWidgetState extends State<OptionWidget> {
|
||||
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<OptionWidget> {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
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<OptionWidget> {
|
||||
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<CreatePlanCubit>().setPrice(price);
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
@@ -11,14 +12,19 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
||||
final List<BaseNotificationModel> _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<void> connectToWebSocket() async {
|
||||
await _newNotificationsSubscription?.cancel();
|
||||
_newNotificationsSubscription = wsManager.newNotificationsStream?.listen((event) {
|
||||
_handleNotification(event.data);
|
||||
});
|
||||
}
|
||||
|
||||
BaseNotificationModel _parseNotification(Map<String, dynamic> notificationJson, NotificationType type) {
|
||||
try {
|
||||
return switch (type) {
|
||||
|
||||
Reference in New Issue
Block a user