From 77c52a50e1916486142843028762c74df4dfd10d Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 30 May 2025 17:00:01 +0300 Subject: [PATCH 1/8] fix: upd ver --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 9821d31a..02918a68 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: eatvio_app description: A new Flutter project. -version: 1.1.1+1 +version: 1.1.1+30 publish_to: 'none' environment: From 35fc032d31bb520e823489397697d0c4b061500f Mon Sep 17 00:00:00 2001 From: Username1548 Date: Fri, 30 May 2025 21:19:49 +0700 Subject: [PATCH 2/8] fix(notifications): fixed consultantLeftComment notifications issue --- lib/features/notifications/models/notification_model.dart | 4 +++- .../presentation/widgets/notification_widget.dart | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/features/notifications/models/notification_model.dart b/lib/features/notifications/models/notification_model.dart index a8813247..766f6ddb 100644 --- a/lib/features/notifications/models/notification_model.dart +++ b/lib/features/notifications/models/notification_model.dart @@ -1,3 +1,5 @@ +import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; + sealed class BaseNotificationModel { abstract final NotificationType type; abstract final bool isRead; @@ -127,7 +129,7 @@ class ConsultantLeftCommentNotification implements BaseNotificationModel { factory ConsultantLeftCommentNotification.fromJson(Map json, NotificationType type, {bool? isRead}) { return ConsultantLeftCommentNotification( type: type, - date: json['diary_date'], + date: parseDate(json['diary_date']), isRead: isRead ?? false, ); } diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index 848fecc2..4ff0faff 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -1,5 +1,6 @@ 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/date_time_utils.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; import 'package:eatvio_app/features/notifications/models/notification_model.dart'; @@ -135,7 +136,7 @@ class _ConsultantLeftCommentWidget extends StatelessWidget { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: s.consultantLeftComment(notification.date), + notificationsText: s.consultantLeftComment(notification.date.format('dd.MM.yyyy')), ); } } From 74ecbbdbffa45ac21a1b55e17b2461b3bc22f5d6 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 2 Jun 2025 13:31:00 +0300 Subject: [PATCH 3/8] fix: plan widget in profile image size overflow --- lib/features/feed/presentation/widgets/plan_widget.dart | 3 ++- lib/features/profile/presentation/pages/profile_screen.dart | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/features/feed/presentation/widgets/plan_widget.dart b/lib/features/feed/presentation/widgets/plan_widget.dart index e47ca1db..c1ac6b90 100644 --- a/lib/features/feed/presentation/widgets/plan_widget.dart +++ b/lib/features/feed/presentation/widgets/plan_widget.dart @@ -62,8 +62,9 @@ class _PlanWidgetState extends State { borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), child: CachedNetworkImage( imageUrl: '$baseUrl/storage/${widget.plan.image}', - fit: BoxFit.fitWidth, + fit: BoxFit.cover, width: MediaQuery.of(context).size.width, + height: 426, ), ), Positioned( diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index d5d360d8..174500c1 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -329,7 +329,7 @@ class _ProfileScreenState extends State { ), const SizedBox(height: 8), SizedBox( - height: 378, + height: 759, child: PageView.builder( itemBuilder: (BuildContext context, int index) { return PlanWidget( From 8a9a913420dbb0aee50e088e6f816ec4d13bf56e Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 2 Jun 2025 13:38:19 +0300 Subject: [PATCH 4/8] fix(recipe): infinity loading recipe edit --- .../bloc/create_recipe_cubit/create_recipe_cubit.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart index 43273ae6..f13d3d02 100644 --- a/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart +++ b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart @@ -16,6 +16,7 @@ import 'package:path_provider/path_provider.dart'; class CreateRecipeCubit extends Cubit { final RecipeRepository recipeRepository; final GlossaryRepository glossaryRepository; + CreateRecipeCubit({ required this.recipeRepository, required this.glossaryRepository, @@ -192,7 +193,7 @@ class CreateRecipeCubit extends Cubit { recipe.steps!.map( (e) async => StepEntity( description: e.description, - image: await downloadImageAsFile('$baseUrl/storage/${e.image}'), + image: e.image.isEmpty ? null : await downloadImageAsFile('$baseUrl/storage/${e.image}'), ), ), ); From f0a184b800642e98a3e4d7cc9bd9296291e3a2ed Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 2 Jun 2025 16:05:47 +0300 Subject: [PATCH 5/8] feat(consultant): update consultant info --- lib/core/app_router.dart | 7 +- lib/core/const.dart | 2 +- .../models/consultation_request_model.dart | 2 +- .../register_consultant_cubit.dart | 69 ++- .../register_consultant_state.dart | 1 + .../certificates_and_diplomas_screen.dart | 25 +- .../stepper_become_consultant_screen.dart | 482 +++++++++--------- .../repositories/consultant_repository.dart | 21 +- .../services/consultant_service.dart | 10 + .../presentation/pages/profile_screen.dart | 34 +- 10 files changed, 392 insertions(+), 261 deletions(-) diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index fec9b100..d29fea2d 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -293,7 +293,12 @@ class AppRouter { ), GoRoute(path: '/become-consultant-screen', name: 'becomeConsultantScreen', builder: (context, state) => const BecomeConsultantScreen()), GoRoute( - path: '/stepper-become-consultant-screen', name: 'stepperBecomeConsultantScreen', builder: (context, state) => const StepperBecomeConsultantScreen()), + 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( diff --git a/lib/core/const.dart b/lib/core/const.dart index 31c47e9c..73e7ecfd 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,4 +1,4 @@ -String baseUrl = 'https://eatvio.ru'; + String baseUrl = 'https://eatvio.ru'; // String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/features/consultant/become_consultant/models/consultation_request_model.dart b/lib/features/consultant/become_consultant/models/consultation_request_model.dart index 1579fab5..547ff856 100644 --- a/lib/features/consultant/become_consultant/models/consultation_request_model.dart +++ b/lib/features/consultant/become_consultant/models/consultation_request_model.dart @@ -8,7 +8,7 @@ part 'consultation_request_model.g.dart'; @freezed abstract class ConsultationRequestModel with _$ConsultationRequestModel { const factory ConsultationRequestModel({ - @JsonKey(name: 'rating_place') required int ratingPlace, + @JsonKey(name: 'rating_place') int? ratingPlace, @JsonKey(name: 'education_resource') required EducationModel educationModel, @JsonKey(name: 'cert_photos') required List photos, }) = _ConsultationRequestModel; diff --git a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart index a7171f89..7ca3ac48 100644 --- a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart +++ b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart @@ -1,13 +1,15 @@ import 'dart:io'; - import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart'; import 'package:eatvio_app/features/consultant/become_consultant/models/consultant_profile_model.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart'; import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; class RegisterConsultantCubit extends Cubit { final ConsultantRepository consultantRepository; + bool _update = false; RegisterConsultantCubit({ required this.consultantRepository, @@ -17,12 +19,73 @@ class RegisterConsultantCubit extends Cubit { socialLinks: SocialLinksModel(), hasMedicalEducation: true, ), + loading: true, acceptOffer: false, ), ); + Future downloadImageAsFile(String imageUrl) async { + final response = await http.get(Uri.parse(imageUrl)); + + if (response.statusCode == 200) { + final tempDir = await getTemporaryDirectory(); + final filePath = '${tempDir.path}/${imageUrl.split('/').last}'; + final file = File(filePath); + return await file.writeAsBytes(response.bodyBytes); + } else { + throw Exception('Не удалось загрузить изображение'); + } + } + + getMyRequestStatus(String slug) async { + _update = true; + final myRequestStatus = await consultantRepository.getMyRequestStatus(slug); + + final List photos = await Future.wait( + myRequestStatus.photos.map( + (e) async => (await downloadImageAsFile(e.url)).path, + ), + ); + + List? certificates = myRequestStatus.educationModel.completedCourses?.map((courseMap) { + final course = courseMap.values.first; + + return CertificatesModel( + title: course.title, + date: course.date, + ); + }).toList(); + + String? sanitizeLink(String? link) { + return (link != null && link.isNotEmpty) ? link : null; + } + + emit( + RegisterConsultantState( + consultant: ConsultantProfileModel( + photos: photos, + certificates: certificates, + socialLinks: SocialLinksModel( + vkLink: sanitizeLink(myRequestStatus.educationModel.vkLink), + tgLink: sanitizeLink(myRequestStatus.educationModel.tgLink), + fbLink: sanitizeLink(myRequestStatus.educationModel.fbLink), + instaLink: sanitizeLink(myRequestStatus.educationModel.instaLink), + personalSiteLink: sanitizeLink(myRequestStatus.educationModel.personalSiteLink), + ), + hasMedicalEducation: myRequestStatus.educationModel.hasMedicalEducation == 1, + ), + acceptOffer: false, + loading: false, + ), + ); + } + + disableLoading() { + emit(state.copyWith(loading: false)); + } + updateConsultantProfileModel(ConsultantProfileModel consultant, [bool acceptOffer = false]) { - emit(RegisterConsultantState(consultant: consultant, acceptOffer: acceptOffer)); + emit(RegisterConsultantState(consultant: consultant, acceptOffer: acceptOffer, loading: false)); } registerConsultant() async { @@ -35,7 +98,7 @@ class RegisterConsultantCubit extends Cubit { ), ); } - await consultantRepository.registerConsultant(consultant: state.consultant); + await consultantRepository.registerConsultant(consultant: state.consultant, update: _update); } Future> uploadPhotos(List files) async { diff --git a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart index 5f318efe..32f495d9 100644 --- a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart +++ b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart @@ -8,5 +8,6 @@ abstract class RegisterConsultantState with _$RegisterConsultantState { const factory RegisterConsultantState({ required ConsultantProfileModel consultant, required bool acceptOffer, + required bool loading, }) = _RegisterConsultantState; } diff --git a/lib/features/consultant/become_consultant/presentation/pages/certificates_and_diplomas_screen.dart b/lib/features/consultant/become_consultant/presentation/pages/certificates_and_diplomas_screen.dart index 6e49d3b3..3b877c4d 100644 --- a/lib/features/consultant/become_consultant/presentation/pages/certificates_and_diplomas_screen.dart +++ b/lib/features/consultant/become_consultant/presentation/pages/certificates_and_diplomas_screen.dart @@ -30,19 +30,20 @@ class _CertificatesAndDiplomasScreenState extends State().state.consultant.certificates != null) { - diplomasList = context - .read() - .state - .consultant - .certificates! - .map( - (e) => DiplomaEntity( - date: DateTime.tryParse(e.date), - textEditingController: TextEditingController(text: e.title), - enableDeleteButton: true, + diplomasList = [ + ...context.read().state.consultant.certificates!.map( + (e) => DiplomaEntity( + date: DateTime.tryParse(e.date), + textEditingController: TextEditingController(text: e.title), + enableDeleteButton: true, + ), ), - ) - .toList(); + DiplomaEntity( + textEditingController: TextEditingController(), + enableDeleteButton: false, + date: null, + ), + ]; diplomasList.last.enableDeleteButton = false; } } diff --git a/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart b/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart index 28692a4c..b49610fc 100644 --- a/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart +++ b/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart @@ -1,5 +1,7 @@ import 'package:eatvio_app/core/utils/image_constant.dart'; +import 'package:eatvio_app/core/widgets/loader.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/presentation/bloc/register_consultant_cubit/register_consultant_state.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/additional_settings_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/certificates_and_diplomas_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/last_step_screen.dart'; @@ -15,7 +17,9 @@ import 'package:go_router/go_router.dart'; import 'package:lottie/lottie.dart'; class StepperBecomeConsultantScreen extends StatefulWidget { - const StepperBecomeConsultantScreen({super.key}); + final String? slug; + + const StepperBecomeConsultantScreen({super.key, this.slug}); @override State createState() => _StepperBecomeConsultantScreenState(); @@ -25,249 +29,271 @@ class _StepperBecomeConsultantScreenState extends State(); + late final RegisterConsultantCubit registrationConsultantCubit; bool isLoading = false; + @override + void initState() { + super.initState(); + if (widget.slug == null) { + registrationConsultantCubit = sl()..disableLoading(); + } else { + registrationConsultantCubit = sl()..getMyRequestStatus(widget.slug!); + } + } + @override Widget build(BuildContext context) { return BlocProvider( create: (context) => registrationConsultantCubit, - child: Scaffold( - floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - floatingActionButton: Row( - children: [ - Container( - height: 48, - width: MediaQuery.of(context).size.width * 0.2, - padding: const EdgeInsets.only(left: 16, right: 4), - child: FloatingActionButton.extended( - heroTag: 'backButtonRegistration', - highlightElevation: 0, - focusElevation: 0, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - backgroundColor: const Color(0xFFEAF2FD), - onPressed: () async { - setState(() { - pageController.previousPage( - duration: const Duration(milliseconds: 350), - curve: Curves.easeInOut, - ); - }); - }, - label: Icon( - Icons.arrow_back_ios_new_rounded, - color: appTheme.blueA400, - ), - ), - ), - Container( - height: 48, - width: MediaQuery.of(context).size.width * 0.8, - padding: const EdgeInsets.only(left: 4, right: 16), - child: FloatingActionButton.extended( - heroTag: 'nextButtonRegistration', - highlightElevation: 0, - elevation: 0, - focusElevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - backgroundColor: isLoading ? Colors.grey : appTheme.blueA400, - onPressed: isLoading - ? null - : () async { - if (step == 6) { - if (registrationConsultantCubit.state.acceptOffer) { - setState(() { - isLoading = true; - }); - try { - await registrationConsultantCubit.registerConsultant(); - if (!context.mounted) return; - await showModalBottomSheet( - useRootNavigator: true, - showDragHandle: true, - context: context, - builder: (context) => Container( - padding: const EdgeInsets.all(16), - width: MediaQuery.of(context).size.width, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'Спасибо!\nВаши данные отправлены на модерацию', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - height: 1, - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: 16), - Lottie.asset( - 'assets/lottie_animations/success.json', - width: 160, - height: 160, - ), - const SizedBox(height: 16), - const Text( - 'В течении трех рабочих дней Вам придет уведомление о смене статуса', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, - height: 1, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 16), - InkWell( - onTap: () { - Navigator.of(context).pop(); - GoRouter.of(context).pop(); - }, - child: Ink( - width: MediaQuery.of(context).size.width - 32, - height: 52, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: appTheme.blueA400, - ), - child: const Center( - child: Text( - 'Закрыть', + child: BlocBuilder( + builder: (context, state) { + if (state.loading == false) { + return Scaffold( + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + floatingActionButton: Row( + children: [ + Container( + height: 48, + width: MediaQuery.of(context).size.width * 0.2, + padding: const EdgeInsets.only(left: 16, right: 4), + child: FloatingActionButton.extended( + heroTag: 'backButtonRegistration', + highlightElevation: 0, + focusElevation: 0, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + backgroundColor: const Color(0xFFEAF2FD), + onPressed: () async { + setState(() { + pageController.previousPage( + duration: const Duration(milliseconds: 350), + curve: Curves.easeInOut, + ); + }); + }, + label: Icon( + Icons.arrow_back_ios_new_rounded, + color: appTheme.blueA400, + ), + ), + ), + Container( + height: 48, + width: MediaQuery.of(context).size.width * 0.8, + padding: const EdgeInsets.only(left: 4, right: 16), + child: FloatingActionButton.extended( + heroTag: 'nextButtonRegistration', + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + backgroundColor: isLoading ? Colors.grey : appTheme.blueA400, + onPressed: isLoading + ? null + : () async { + if (step == 6) { + if (registrationConsultantCubit.state.acceptOffer) { + setState(() { + isLoading = true; + }); + try { + await registrationConsultantCubit.registerConsultant(); + if (!context.mounted) return; + await showModalBottomSheet( + useRootNavigator: true, + showDragHandle: true, + context: context, + builder: (context) => Container( + padding: const EdgeInsets.all(16), + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Спасибо!\nВаши данные отправлены на модерацию', + textAlign: TextAlign.center, style: TextStyle( - color: Colors.white, - fontSize: 18, + fontSize: 20, + height: 1, fontWeight: FontWeight.w600, ), ), - ), + const SizedBox(height: 16), + Lottie.asset( + 'assets/lottie_animations/success.json', + width: 160, + height: 160, + ), + const SizedBox(height: 16), + const Text( + 'В течении трех рабочих дней Вам придет уведомление о смене статуса', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + height: 1, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 16), + InkWell( + onTap: () { + Navigator.of(context).pop(); + GoRouter.of(context).pop(); + }, + child: Ink( + width: MediaQuery.of(context).size.width - 32, + height: 52, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: appTheme.blueA400, + ), + child: const Center( + child: Text( + 'Закрыть', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + ], ), ), - ], - ), - ), - ); - } catch (e) { - if (!context.mounted) return; - showDialog( - context: context, - builder: (context) => AlertDialog( - actionsPadding: const EdgeInsets.symmetric(horizontal: 4), - content: Text(e.toString()), - actions: [ - TextButton( - child: const Text('Ок'), - onPressed: () { - Navigator.of(context).pop(); - }, + ); + } catch (e) { + if (!context.mounted) return; + showDialog( + context: context, + builder: (context) => AlertDialog( + actionsPadding: const EdgeInsets.symmetric(horizontal: 4), + content: Text(e.toString()), + actions: [ + TextButton( + child: const Text('Ок'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } finally { + setState(() { + isLoading = false; + }); + } + } else { + showDialog( + context: context, + builder: (context) => AlertDialog( + actionsPadding: const EdgeInsets.symmetric(horizontal: 4), + content: const Text('Примите договор публичной офёрты'), + actions: [ + TextButton( + child: const Text('Ок'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], ), - ], - ), - ); - } finally { - setState(() { - isLoading = false; - }); - } - } else { - showDialog( - context: context, - builder: (context) => AlertDialog( - actionsPadding: const EdgeInsets.symmetric(horizontal: 4), - content: const Text('Примите договор публичной офёрты'), - actions: [ - TextButton( - child: const Text('Ок'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ), - ); - } - } else { - FocusManager.instance.primaryFocus?.unfocus(); - setState(() { - pageController.nextPage( - duration: const Duration(milliseconds: 350), - curve: Curves.easeInOut, - ); - }); - } - }, - label: isLoading ? const CircularProgressIndicator() : Text( - 'Продолжить', - style: theme.textTheme.titleSmall!.copyWith(color: Colors.white), + ); + } + } else { + FocusManager.instance.primaryFocus?.unfocus(); + setState(() { + pageController.nextPage( + duration: const Duration(milliseconds: 350), + curve: Curves.easeInOut, + ); + }); + } + }, + label: isLoading + ? const CircularProgressIndicator() + : Text( + 'Продолжить', + style: theme.textTheme.titleSmall!.copyWith(color: Colors.white), + ), + ), + ), + ], + ), + appBar: AppBar( + automaticallyImplyLeading: false, + leading: Builder( + builder: (context) => IconButton( + icon: const Icon(Icons.close_rounded), + onPressed: () => GoRouter.of(context).pop(), + ), + ), + centerTitle: true, + title: SvgPicture.asset( + ImageConstant.imgLogoEatvio, + height: 48, + fit: BoxFit.contain, + ), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 16), + child: Text('шаг $step из 6'), + ), + ], + bottom: PreferredSize( + preferredSize: Size(MediaQuery.of(context).size.width, 32), + child: Padding( + padding: const EdgeInsets.all(16), + child: TweenAnimationBuilder( + duration: const Duration(milliseconds: 350), + curve: Curves.easeInOut, + tween: Tween( + begin: 1 / 6, + end: progressValue, + ), + builder: (context, value, _) => LinearProgressIndicator( + value: value, + borderRadius: BorderRadius.circular(16), + minHeight: 8, + backgroundColor: Colors.grey.shade100, + valueColor: AlwaysStoppedAnimation(appTheme.blueA400), + ), + ), + ), ), ), - ), - ], - ), - appBar: AppBar( - automaticallyImplyLeading: false, - leading: Builder( - builder: (context) => IconButton( - icon: const Icon(Icons.close_rounded), - onPressed: () => GoRouter.of(context).pop(), - ), - ), - centerTitle: true, - title: SvgPicture.asset( - ImageConstant.imgLogoEatvio, - height: 48, - fit: BoxFit.contain, - ), - actions: [ - Padding( - padding: const EdgeInsets.only(right: 16), - child: Text('шаг $step из 6'), - ), - ], - bottom: PreferredSize( - preferredSize: Size(MediaQuery.of(context).size.width, 32), - child: Padding( - padding: const EdgeInsets.all(16), - child: TweenAnimationBuilder( - duration: const Duration(milliseconds: 350), - curve: Curves.easeInOut, - tween: Tween( - begin: 1 / 6, - end: progressValue, - ), - builder: (context, value, _) => LinearProgressIndicator( - value: value, - borderRadius: BorderRadius.circular(16), - minHeight: 8, - backgroundColor: Colors.grey.shade100, - valueColor: AlwaysStoppedAnimation(appTheme.blueA400), - ), + body: PageView( + physics: const NeverScrollableScrollPhysics(), + controller: pageController, + onPageChanged: (index) { + setState(() { + step = index + 1; + progressValue = step / 6; + }); + }, + children: const [ + CertificatesAndDiplomasScreen(), + PhotoOrScansScreen(), + AdditionalSettingsScreen(), + SocialNetworksScreen(), + RegistrationAsScreen(), + LastStepScreen(), + ], ), - ), - ), - ), - body: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: pageController, - onPageChanged: (index) { - setState(() { - step = index + 1; - progressValue = step / 6; - }); - }, - children: const [ - CertificatesAndDiplomasScreen(), - PhotoOrScansScreen(), - AdditionalSettingsScreen(), - SocialNetworksScreen(), - RegistrationAsScreen(), - LastStepScreen(), - ], - ), + ); + } else { + return const Scaffold( + body: Loader(), + ); + } + }, ), ); } diff --git a/lib/features/consultant/become_consultant/repositories/consultant_repository.dart b/lib/features/consultant/become_consultant/repositories/consultant_repository.dart index 6af88cb5..89eef1c8 100644 --- a/lib/features/consultant/become_consultant/repositories/consultant_repository.dart +++ b/lib/features/consultant/become_consultant/repositories/consultant_repository.dart @@ -14,11 +14,18 @@ class ConsultantRepository { Future registerConsultant({ required ConsultantProfileModel consultant, + bool update = false, }) async { try { - return await consultantService.consultantRegistration( - consultantModel: consultant, - ); + if (update) { + return await consultantService.consultantUpdate( + consultantModel: consultant, + ); + } else { + return await consultantService.consultantRegistration( + consultantModel: consultant, + ); + } } catch (e) { rethrow; } @@ -57,4 +64,12 @@ class ConsultantRepository { rethrow; } } + + Future getMyRequestStatus(String slug) async { + try { + return await consultantService.getMyRequestStatus(slug: slug); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/consultant/become_consultant/services/consultant_service.dart b/lib/features/consultant/become_consultant/services/consultant_service.dart index fbf7541d..bfde5a92 100644 --- a/lib/features/consultant/become_consultant/services/consultant_service.dart +++ b/lib/features/consultant/become_consultant/services/consultant_service.dart @@ -24,4 +24,14 @@ abstract class ConsultantService { Future getConsultantRequestStatus({ @Path() required String slug, }); + + @GET('/api/consultant_request/{slug}/status') + Future getMyRequestStatus({ + @Path() required String slug, + }); + + @PUT('/api/consultant_request/update') + Future consultantUpdate({ + @Body() required ConsultantProfileModel consultantModel, + }); } diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 174500c1..6c91bbff 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -449,12 +449,17 @@ class _ProfileScreenState extends State { fontWeight: FontWeight.w600, ), ), - Text( - widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', - style: const TextStyle( - color: Color(0xFF2F80ED), - fontSize: 16, - fontWeight: FontWeight.w500, + GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); + }, + child: Text( + widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', + style: const TextStyle( + color: Color(0xFF2F80ED), + fontSize: 16, + fontWeight: FontWeight.w500, + ), ), ), ], @@ -544,12 +549,17 @@ class _ProfileScreenState extends State { fontWeight: FontWeight.w600, ), ), - Text( - widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', - style: const TextStyle( - color: Color(0xFF2F80ED), - fontSize: 16, - fontWeight: FontWeight.w500, + GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); + }, + child: Text( + widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', + style: const TextStyle( + color: Color(0xFF2F80ED), + fontSize: 16, + fontWeight: FontWeight.w500, + ), ), ), ], From 7c354cc0a321a5ce6d80e3d3485bb21c684d2c9b Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 2 Jun 2025 17:51:27 +0300 Subject: [PATCH 6/8] feat(consultant): ***WIP: consultant request status in settings --- ios/Runner.xcodeproj/project.pbxproj | 6 +-- ios/Runner/Info.plist | 2 +- lib/core/const.dart | 2 +- lib/core/locator_service.dart | 2 +- .../presentation/pages/profile_screen.dart | 9 +++- .../bloc/settings_cubit/settings_cubit.dart | 24 +++++++-- .../bloc/settings_cubit/settings_state.dart | 2 + .../presentation/pages/settings_screen.dart | 50 +++++++++---------- 8 files changed, 59 insertions(+), 38 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index d535af69..b77d4412 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -388,7 +388,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 21; + CURRENT_PROJECT_VERSION = 22; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -538,7 +538,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 21; + CURRENT_PROJECT_VERSION = 22; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -576,7 +576,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 21; + CURRENT_PROJECT_VERSION = 22; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 498d4b70..2b0b59b5 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 21 + 22 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/lib/core/const.dart b/lib/core/const.dart index 73e7ecfd..31c47e9c 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,4 +1,4 @@ - String baseUrl = 'https://eatvio.ru'; +String baseUrl = 'https://eatvio.ru'; // String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 9ee53c7c..cd8731a3 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -120,7 +120,7 @@ Future init() async { sl.registerFactory(() => DietsCubit(feedRepository: sl(), dietsRepository: sl())); sl.registerFactory(() => CommentsCubit(feedRepository: sl())); sl.registerFactory(() => PlansCubit(feedRepository: sl(), planRepository: sl())); - sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl())); + sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl())); sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl())); sl.registerFactory( () => DiaryCubit(chatRepository: sl(), diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl(), dietRepository: sl())); diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 6c91bbff..b9ecfff6 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -33,6 +33,7 @@ import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:intl/intl.dart'; import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -594,6 +595,11 @@ class _ProfileScreenState extends State { } _educationBlock(String title, String date) { + final parsedDate = DateTime.tryParse(date); + final formattedDate = parsedDate != null + ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate) + : date; + return SizedBox( width: double.infinity, child: Material( @@ -615,7 +621,7 @@ class _ProfileScreenState extends State { ), const SizedBox(height: 4), Text( - date, + formattedDate, style: const TextStyle( height: 1, color: Color(0xFF6D6F71), @@ -630,6 +636,7 @@ class _ProfileScreenState extends State { ); } + _socialLinks(ProfileStateLoaded state) { if (!state.profile.roles.contains('consultant')) { return const SizedBox.shrink(); diff --git a/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart b/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart index f0a388a4..1bc68637 100644 --- a/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart +++ b/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart @@ -1,3 +1,5 @@ +import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart'; +import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:eatvio_app/features/settings/models/profile_settings_model.dart'; @@ -8,19 +10,31 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class SettingsCubit extends Cubit { final ProfileRepository profileRepository; final SettingsRepository settingsRepository; + final ConsultantRepository consultantRepository; SettingsCubit({ required this.profileRepository, required this.settingsRepository, + required this.consultantRepository, }) : super(const SettingsState.loading()); Future init() async { + final profile = await profileRepository.getMyProfile(); + late final ConsultationRequestModel? consultantInfo; try { - emit(SettingsState.loaded( - profileSettings: await settingsRepository.getProfileSettings(), - profileDetails: await profileRepository.getMyProfileDetails(), - profile: await profileRepository.getMyProfile(), - ),); + consultantInfo = await consultantRepository.getMyRequestStatus(profile.slug); + } catch (e) { + consultantInfo = null; + } + try { + emit( + SettingsState.loaded( + profileSettings: await settingsRepository.getProfileSettings(), + profileDetails: await profileRepository.getMyProfileDetails(), + profile: profile, + consultationRequestModel: consultantInfo, + ), + ); } catch (e) { emit(SettingsState.error(errorMessage: e.toString())); } diff --git a/lib/features/settings/presentation/bloc/settings_cubit/settings_state.dart b/lib/features/settings/presentation/bloc/settings_cubit/settings_state.dart index 34922051..f220fe5e 100644 --- a/lib/features/settings/presentation/bloc/settings_cubit/settings_state.dart +++ b/lib/features/settings/presentation/bloc/settings_cubit/settings_state.dart @@ -1,3 +1,4 @@ +import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:eatvio_app/features/settings/models/profile_settings_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -14,6 +15,7 @@ abstract class SettingsState with _$SettingsState { required ProfileSettingsModel profileSettings, required ProfileDetailsModel profileDetails, required ProfileModel profile, + ConsultationRequestModel? consultationRequestModel, }) = SettingsStateLoaded; const factory SettingsState.error({ diff --git a/lib/features/settings/presentation/pages/settings_screen.dart b/lib/features/settings/presentation/pages/settings_screen.dart index 18d6a261..7c4ab26e 100644 --- a/lib/features/settings/presentation/pages/settings_screen.dart +++ b/lib/features/settings/presentation/pages/settings_screen.dart @@ -58,11 +58,7 @@ class _SettingsScreenState extends State { const SizedBox(height: 12), _aboutEatvio(state), const SizedBox(height: 12), - if (!state.profile.roles.contains('consultant')) - Padding( - padding: const EdgeInsets.only(bottom: 8), - child: _becomeConsultantButton(state), - ), + _becomeConsultantButton(state), _deleteAccountButton(state), ], ), @@ -355,31 +351,33 @@ class _SettingsScreenState extends State { } _becomeConsultantButton(SettingsStateLoaded state) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: InkWell( - onTap: () => GoRouter.of(context).pushNamed('becomeConsultantScreen'), - borderRadius: BorderRadius.circular(20), - child: Ink( - height: 52, - width: MediaQuery.of(context).size.width - 16, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: appTheme.blueA400, - ), - child: const Center( - child: Text( - 'Стать консультантом', - style: TextStyle( - color: Colors.white, - height: 1, - fontSize: 18, + if (state.consultationRequestModel == null) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16) + const EdgeInsets.only(bottom: 8), + child: InkWell( + onTap: () => GoRouter.of(context).pushNamed('becomeConsultantScreen'), + borderRadius: BorderRadius.circular(20), + child: Ink( + height: 52, + width: MediaQuery.of(context).size.width - 16, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: appTheme.blueA400, + ), + child: const Center( + child: Text( + 'Стать консультантом', + style: TextStyle( + color: Colors.white, + height: 1, + fontSize: 18, + ), ), ), ), ), - ), - ); + ); + } } _deleteAccountButton(SettingsStateLoaded state) { From c361e12d354f914b0a123937de31e309829b4342 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Tue, 3 Jun 2025 18:17:03 +0300 Subject: [PATCH 7/8] Issue-483: WIP* moderation consultant request status and message --- lib/core/widgets/custom_popup.dart | 5 +- .../models/consultation_request_model.dart | 18 +++++ .../register_consultant_cubit.dart | 1 + .../register_consultant_state.dart | 2 + .../stepper_become_consultant_screen.dart | 70 +++++++++++++++++-- .../presentation/cubit/create_diet_cubit.dart | 1 + .../presentation/diet_preview_screen.dart | 2 +- .../presentation/pages/about_diet_screen.dart | 6 +- .../feed/models/diets_pagination_model.dart | 1 + .../presentation/widgets/diets_widget.dart | 2 +- .../presentation/widgets/plan_widget.dart | 2 +- .../presentation/pages/about_plan_screen.dart | 6 +- .../presentation/pages/profile_screen.dart | 3 +- .../widgets/my_recipe_widget.dart | 24 ------- .../presentation/pages/settings_screen.dart | 52 ++++++++++++++ 15 files changed, 151 insertions(+), 44 deletions(-) diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart index 47081135..dbe04ec4 100644 --- a/lib/core/widgets/custom_popup.dart +++ b/lib/core/widgets/custom_popup.dart @@ -6,7 +6,6 @@ import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart'; -import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_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'; @@ -98,12 +97,10 @@ class CustomPopup extends StatelessWidget { ), ); }); - if (complainedContentType == ComplainedContent.posts || complainedContentType == ComplainedContent.recipe) { + if (complainedContentType == ComplainedContent.posts) { addItem('assets/icons/edit_paint.svg', 'Редактировать', () async { if (complainedContentType == ComplainedContent.posts) { GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedBlogPostModel); - } else if (complainedContentType == ComplainedContent.recipe) { - GoRouter.of(context).pushNamed(RecipeCreateScreen.name, extra: recipe); } }); } diff --git a/lib/features/consultant/become_consultant/models/consultation_request_model.dart b/lib/features/consultant/become_consultant/models/consultation_request_model.dart index 547ff856..68549759 100644 --- a/lib/features/consultant/become_consultant/models/consultation_request_model.dart +++ b/lib/features/consultant/become_consultant/models/consultation_request_model.dart @@ -11,6 +11,7 @@ abstract class ConsultationRequestModel with _$ConsultationRequestModel { @JsonKey(name: 'rating_place') int? ratingPlace, @JsonKey(name: 'education_resource') required EducationModel educationModel, @JsonKey(name: 'cert_photos') required List photos, + @JsonKey(name: 'canceled_moderation') required CanceledModerationModel canceledModeration, }) = _ConsultationRequestModel; factory ConsultationRequestModel.fromJson(Map json) => _$ConsultationRequestModelFromJson(json); @@ -24,3 +25,20 @@ abstract class CertPhotosModel with _$CertPhotosModel { factory CertPhotosModel.fromJson(Map json) => _$CertPhotosModelFromJson(json); } + +enum ModerationStatus { + approved, + created, + canceled +} + +@freezed +abstract class CanceledModerationModel with _$CanceledModerationModel { + const factory CanceledModerationModel({ + required String title, + required ModerationStatus status, + required List? additional, + }) = _CanceledModerationModel; + + factory CanceledModerationModel.fromJson(Map json) => _$CanceledModerationModelFromJson(json); +} diff --git a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart index 7ca3ac48..2eb9f373 100644 --- a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart +++ b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_cubit.dart @@ -76,6 +76,7 @@ class RegisterConsultantCubit extends Cubit { ), acceptOffer: false, loading: false, + requestModel: myRequestStatus, ), ); } diff --git a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart index 32f495d9..5f613821 100644 --- a/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart +++ b/lib/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/features/consultant/become_consultant/models/consultant_profile_model.dart'; +import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'register_consultant_state.freezed.dart'; @@ -9,5 +10,6 @@ abstract class RegisterConsultantState with _$RegisterConsultantState { required ConsultantProfileModel consultant, required bool acceptOffer, required bool loading, + ConsultationRequestModel? requestModel, }) = _RegisterConsultantState; } diff --git a/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart b/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart index b49610fc..a365c1fc 100644 --- a/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart +++ b/lib/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; +import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.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/presentation/bloc/register_consultant_cubit/register_consultant_state.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/additional_settings_screen.dart'; @@ -80,9 +81,57 @@ class _StepperBecomeConsultantScreenState extends State AlertDialog( + title: Text(state.requestModel!.canceledModeration.title), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + for (int i = 0; i < state.requestModel!.canceledModeration.additional!.length; i++) ...[ + Text('• ${state.requestModel!.canceledModeration.additional![i]}'), + ] + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('OK'), + ), + ], + ), + ); + }, + borderRadius: BorderRadius.circular(20), + child: Ink( + height: 48, + width: MediaQuery.of(context).size.width * 0.6, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: const Color(0x33FFCC00), + ), + child: const Center( + child: Text( + 'Комментарий', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF323F4B), + height: 1, + ), + ), + ), + ), + ), Container( height: 48, - width: MediaQuery.of(context).size.width * 0.8, + width: state.requestModel?.canceledModeration.status == ModerationStatus.canceled + ? MediaQuery.of(context).size.width * 0.2 + : MediaQuery.of(context).size.width * 0.8, padding: const EdgeInsets.only(left: 4, right: 16), child: FloatingActionButton.extended( heroTag: 'nextButtonRegistration', @@ -92,7 +141,11 @@ class _StepperBecomeConsultantScreenState extends State { final diet = Diet( id: 0, name: state.dietData.name ?? '', + slug: '', image: imageInBase64, duration: state.dietData.duration ?? 0, state: state.dietData.state ?? DietState.active, diff --git a/lib/features/create_diet/presentation/diet_preview_screen.dart b/lib/features/create_diet/presentation/diet_preview_screen.dart index 884c283d..3901841f 100644 --- a/lib/features/create_diet/presentation/diet_preview_screen.dart +++ b/lib/features/create_diet/presentation/diet_preview_screen.dart @@ -192,7 +192,7 @@ class _DietPreviewScreenState extends State { actions: [ CustomPopup( isAuthor: widget.diet.author.authorIsMe, - path: 'diet/${widget.diet.id}', + path: 'diet/${widget.diet.slug}', complainedContentType: ComplainedContent.diet, ), const SizedBox(width: 16), diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index 4dd3878d..2252324e 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -318,7 +318,7 @@ class _AboutDietScreenState extends State { actions: [ CustomPopup( isAuthor: widget.diet.author.authorIsMe, - path: 'diet/${widget.diet.id}', + path: 'diet/${widget.diet.slug}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, diet: widget.diet, @@ -499,7 +499,7 @@ class _AboutDietScreenState extends State { actions: [ CustomPopup( isAuthor: widget.diet.author.authorIsMe, - path: 'diet/${widget.diet.id}', + path: 'diet/${widget.diet.slug}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, diet: widget.diet, @@ -526,7 +526,7 @@ class _AboutDietScreenState extends State { actions: [ CustomPopup( isAuthor: widget.diet.author.authorIsMe, - path: 'diet/${widget.diet.id}', + path: 'diet/${widget.diet.slug}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, diet: widget.diet, diff --git a/lib/features/feed/models/diets_pagination_model.dart b/lib/features/feed/models/diets_pagination_model.dart index 868f6556..5da0bf7e 100644 --- a/lib/features/feed/models/diets_pagination_model.dart +++ b/lib/features/feed/models/diets_pagination_model.dart @@ -20,6 +20,7 @@ abstract class DietsPagination with _$DietsPagination { abstract class Diet with _$Diet { const factory Diet({ @JsonKey(name: "id") required int id, + @JsonKey(name: "slug") required String slug, @JsonKey(name: "name") required String name, @JsonKey(name: "image") required String image, @JsonKey(name: "duration") required int duration, diff --git a/lib/features/feed/presentation/widgets/diets_widget.dart b/lib/features/feed/presentation/widgets/diets_widget.dart index e857e979..3b0a7662 100644 --- a/lib/features/feed/presentation/widgets/diets_widget.dart +++ b/lib/features/feed/presentation/widgets/diets_widget.dart @@ -90,7 +90,7 @@ class _DietsWidgetState extends State { borderRadius: BorderRadius.circular(4), child: CustomPopup( isAuthor: widget.diet.author.authorIsMe, - path: 'diet/${widget.diet.id}', + path: 'diet/${widget.diet.slug}', id: widget.diet.id, complainedContentType: ComplainedContent.diet, diet: widget.diet, diff --git a/lib/features/feed/presentation/widgets/plan_widget.dart b/lib/features/feed/presentation/widgets/plan_widget.dart index c1ac6b90..8a8229b8 100644 --- a/lib/features/feed/presentation/widgets/plan_widget.dart +++ b/lib/features/feed/presentation/widgets/plan_widget.dart @@ -96,7 +96,7 @@ class _PlanWidgetState extends State { borderRadius: BorderRadius.circular(4), child: CustomPopup( isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.id}', + path: 'plan/${widget.plan.slug}', id: widget.plan.id, slug: widget.plan.slug, complainedContentType: ComplainedContent.plan, diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index 72cb71bd..6b8980cf 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -113,7 +113,7 @@ class _AboutPlanScreenState extends State { actions: [ CustomPopup( isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.id}', + path: 'plan/${widget.plan.slug}', complainedContentType: ComplainedContent.plan, slug: widget.plan.slug, ), @@ -143,7 +143,7 @@ class _AboutPlanScreenState extends State { actions: [ CustomPopup( isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.id}', + path: 'plan/${widget.plan.slug}', complainedContentType: ComplainedContent.plan, slug: widget.plan.slug, ), @@ -362,7 +362,7 @@ class _AboutPlanScreenState extends State { actions: [ CustomPopup( isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.id}', + path: 'plan/${widget.plan.slug}', complainedContentType: ComplainedContent.plan, slug: widget.plan.slug, ), diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index b9ecfff6..a09ff174 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -330,7 +330,7 @@ class _ProfileScreenState extends State { ), const SizedBox(height: 8), SizedBox( - height: 759, + height: 781, child: PageView.builder( itemBuilder: (BuildContext context, int index) { return PlanWidget( @@ -348,6 +348,7 @@ class _ProfileScreenState extends State { ), const SizedBox(height: 8), _bullets(_currentBullet, state.plansList.length), + const SizedBox(height: 8), ], ); } diff --git a/lib/features/recipe/presentation/widgets/my_recipe_widget.dart b/lib/features/recipe/presentation/widgets/my_recipe_widget.dart index 1deb983f..370b4756 100644 --- a/lib/features/recipe/presentation/widgets/my_recipe_widget.dart +++ b/lib/features/recipe/presentation/widgets/my_recipe_widget.dart @@ -6,7 +6,6 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; -import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; import 'package:eatvio_app/features/recipe/models/my_recipe_model.dart'; import 'package:eatvio_app/features/recipe/presentation/cubit/my_recipes_cubit/my_recieps_cubit.dart'; @@ -131,29 +130,6 @@ class _MyRecipeWidgetState extends State { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( children: [ - Row( - children: [ - Expanded( - child: CustomOutlinedButton( - onPressed: () async { - GoRouter.of(context).pushNamed( - RecipeCreateScreen.name, - extra: await context.read().getRecipeById(widget.recipe.slug), - ); - }, - text: s.edit, - height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), - buttonStyle: ButtonStyle( - shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))), - backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), - side: const WidgetStatePropertyAll(BorderSide.none), - ), - ), - ), - ], - ), - const SizedBox(height: 8), Row( children: [ Expanded( diff --git a/lib/features/settings/presentation/pages/settings_screen.dart b/lib/features/settings/presentation/pages/settings_screen.dart index 7c4ab26e..90c516ca 100644 --- a/lib/features/settings/presentation/pages/settings_screen.dart +++ b/lib/features/settings/presentation/pages/settings_screen.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; +import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart'; import 'package:eatvio_app/features/settings/entities/settings_entity.dart'; import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart'; import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_state.dart'; @@ -377,7 +378,58 @@ class _SettingsScreenState extends State { ), ), ); + } else { + if (state.consultationRequestModel!.canceledModeration.status == ModerationStatus.created) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16) + const EdgeInsets.only(bottom: 8), + child: Ink( + height: 52, + width: MediaQuery.of(context).size.width - 16, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: const Color(0xFFEAEAEA), + ), + child: const Center( + child: Text( + 'Данные на проверке...', + style: TextStyle( + color: Colors.black, + height: 1, + fontSize: 18, + ), + ), + ), + ), + ); + } else if (state.consultationRequestModel!.canceledModeration.status == ModerationStatus.canceled) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16) + const EdgeInsets.only(bottom: 8), + child: InkWell( + onTap: () => GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug), + borderRadius: BorderRadius.circular(20), + child: Ink( + height: 52, + width: MediaQuery.of(context).size.width - 16, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: const Color(0x33FFCC00), + ), + child: const Center( + child: Text( + 'Заявка обработана', + style: TextStyle( + color: Colors.black, + height: 1, + fontSize: 18, + ), + ), + ), + ), + ), + ); + } } + return const SizedBox.shrink(); } _deleteAccountButton(SettingsStateLoaded state) { From da8d21bb08b4651ae0367a1c4327e0e04b362971 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 3 Jun 2025 23:19:15 +0700 Subject: [PATCH 8/8] feat(plans): fixed plans price field in CreatePlanScreen, feat(menu): removed dev tools, feat(chats,notifications): added ws reconection --- lib/core/app_router.dart | 5 +- lib/core/const.dart | 4 +- lib/core/websockets/ws_manager.dart | 14 +++- .../auth/presentation/bloc/auth_cubit.dart | 7 +- .../auth/presentation/pages/auth_screen.dart | 20 +++++- .../bloc/chats_cubit/chats_cubit.dart | 17 +++-- .../presentation/widgets/option_widget.dart | 68 +++++++++++++++---- .../menu/presentation/pages/menu_screen.dart | 17 +++-- .../notifications_cubit.dart | 10 ++- 9 files changed, 119 insertions(+), 43 deletions(-) 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) {