diff --git a/assets/icons/edit.svg b/assets/icons/edit.svg
new file mode 100644
index 00000000..2f5d6efd
--- /dev/null
+++ b/assets/icons/edit.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart
index 26776d2b..58ccde6e 100644
--- a/lib/core/app_router.dart
+++ b/lib/core/app_router.dart
@@ -33,7 +33,7 @@ import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.
import 'package:eatvio_app/features/my_finance/presentation/pages/consultant_finance_screen.dart';
import 'package:eatvio_app/features/my_finance/presentation/pages/user_finance_screen.dart';
import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart';
-import 'package:eatvio_app/features/notifications/presentation/pages/notifications_screen.dart';
+import 'package:eatvio_app/features/notifications/presentation/notifications_screen.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart';
import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart';
@@ -99,8 +99,8 @@ class AppRouter {
GoRoute(path: '/feed-screen', name: 'feedScreen', pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen())),
GoRoute(path: '/diary-screen', name: 'diaryScreen', pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen())),
GoRoute(
- path: '/consultations-screen',
- name: 'consultationsScreen',
+ path: ConsultationsScreen.path,
+ name: ConsultationsScreen.name,
pageBuilder: (context, state) => const NoTransitionPage(child: ConsultationsScreen())),
GoRoute(path: '/chat-select-screen', name: 'chatSelectScreen', pageBuilder: (context, state) => const NoTransitionPage(child: ChatsScreen())),
GoRoute(
@@ -256,29 +256,41 @@ class AppRouter {
GoRoute(path: DevToolsScreen.path, name: DevToolsScreen.name, builder: (context, state) => const DevToolsScreen()),
GoRoute(path: ConsultantRatingScreen.path, name: ConsultantRatingScreen.name, builder: (context, state) => const ConsultantRatingScreen()),
GoRoute(
- path: InfoByOrderScreen.path,
- name: InfoByOrderScreen.name,
- builder: (context, state) {
- final data = state.extra as Map;
- final planId = data['planId'] as int;
- final successfulPayment = data['successfulPayment'] as bool?;
- return InfoByOrderScreen(
- planId: planId,
- successfulPayment: successfulPayment,
- );
- }),
+ path: InfoByOrderScreen.path,
+ name: InfoByOrderScreen.name,
+ builder: (context, state) {
+ final data = state.extra as Map;
+ final planId = data['planId'] as int;
+ final successfulPayment = data['successfulPayment'] as bool?;
+ return InfoByOrderScreen(
+ planId: planId,
+ successfulPayment: successfulPayment,
+ );
+ },
+ ),
GoRoute(
- path: PaymentScreen.path,
- name: PaymentScreen.name,
- builder: (context, state) {
- final data = state.extra as Map;
- final planId = data['planId'] as int;
- final url = data['url'] as String;
- return PaymentScreen(
- url: url,
- planId: planId,
- );
- }),
+ path: PaymentScreen.path,
+ name: PaymentScreen.name,
+ builder: (context, state) {
+ final data = state.extra as Map;
+ final planId = data['planId'] as int;
+ final url = data['url'] as String;
+ return PaymentScreen(
+ url: url,
+ planId: planId,
+ );
+ },
+ ),
+ GoRoute(
+ path: UserDiaryScreen.path,
+ name: UserDiaryScreen.name,
+ builder: (context, state) {
+ final diaryId = state.extra as int;
+ return UserDiaryScreen(
+ diaryId: diaryId,
+ );
+ },
+ ),
],
);
}
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/locator_service.dart b/lib/core/locator_service.dart
index b00a6d29..8794ce95 100644
--- a/lib/core/locator_service.dart
+++ b/lib/core/locator_service.dart
@@ -6,20 +6,27 @@ import 'package:eatvio_app/core/websockets/ws_service.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart';
import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
import 'package:eatvio_app/features/auth/services/auth_service.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart';
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
import 'package:eatvio_app/features/complaining/repositories/complaining_repository.dart';
import 'package:eatvio_app/features/complaining/services/complaining_service.dart';
+import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
+import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
+import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/diets/services/diet_service.dart';
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
+import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart';
import 'package:eatvio_app/features/calendar/repositories/calendar_repository.dart';
@@ -123,6 +130,9 @@ Future init() async {
sl.registerFactory(() => ComplainingCubit(repository: sl()));
sl.registerFactory(() => UserCardsCubit(cardsRepository: sl()));
sl.registerFactory(() => OrderInfoCubit(chatRepository: sl()));
+ sl.registerFactory(() => ConsultationsCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => ConsultantCommentsCubit(diaryRepository: sl()));
+ sl.registerFactory(() => CancelPlanCubit(cancelPlanRepository: sl()));
// Repository
sl.registerLazySingleton(() => ChatRepository(chatService: sl()));
@@ -146,6 +156,8 @@ Future init() async {
sl.registerLazySingleton(() => ComplainingRepository(service: sl()));
sl.registerLazySingleton(() => AuthRepository(authService: sl()));
sl.registerLazySingleton(() => CardsRepository(cardsService: sl()));
+ sl.registerLazySingleton(() => ConsultationsRepository(consultationService: sl()));
+ sl.registerLazySingleton(() => CancelPlanRepository(cancelService: sl()));
// Services
sl.registerLazySingleton(() => ChatService(sl()));
@@ -170,6 +182,8 @@ Future init() async {
sl.registerLazySingleton(() => ComplainingService(sl()));
sl.registerLazySingleton(() => AuthService(sl()));
sl.registerLazySingleton(() => CardsService(sl()));
+ sl.registerLazySingleton(() => ConsultationService(sl()));
+ sl.registerLazySingleton(() => CancelPlanService(sl()));
// External
sl.registerLazySingleton(
diff --git a/lib/core/theme/theme_helper.dart b/lib/core/theme/theme_helper.dart
index a919944d..9bed83fd 100644
--- a/lib/core/theme/theme_helper.dart
+++ b/lib/core/theme/theme_helper.dart
@@ -185,6 +185,7 @@ class PrimaryColors {
Color get red => const Color(0xFFCD4141);
Color get strongRed => const Color(0xFFD01717);
+ Color get lightRed => const Color(0XFFD01717).withValues(alpha: 0.1);
Color get transparentRed => const Color(0xFFCD4141).withValues(alpha: 0.75);
@@ -196,6 +197,10 @@ class PrimaryColors {
Color get blueGray => const Color(0xFF809CB9);
Color get dialogShadow => const Color.fromARGB(125, 0, 0, 0);
+
+ Color get darkGray => const Color(0XFFBBBCC0);
+
+ Color get commentBg => const Color(0XFFFFFDD8);
}
PrimaryColors get appTheme => ThemeHelper().themeColor();
diff --git a/lib/core/websockets/ws_manager.dart b/lib/core/websockets/ws_manager.dart
index b22a2048..3a64ff14 100644
--- a/lib/core/websockets/ws_manager.dart
+++ b/lib/core/websockets/ws_manager.dart
@@ -20,7 +20,7 @@ class WsManager {
Stream? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated');
// TODO: change name of event to a right one
- Stream? get newNotificationsStream => _privateNotificationChannel?.bind('App\\Events\\ChatMessageUpdated');
+ Stream? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated');
Stream? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated');
diff --git a/lib/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart b/lib/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart
deleted file mode 100644
index e193a23c..00000000
--- a/lib/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-import 'package:eatvio_app/core/websockets/ws_manager.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class NotificationsCubit extends Cubit {
- int _notificationsCount = 0;
- List notifications = [];
- final WsManager wsManager;
- NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) {
- wsManager.newNotificationsStream?.listen((event) {
- incrementCounter(event.data);
- });
- }
-
- incrementCounter(String data) {
- _notificationsCount++;
- notifications.add(data);
- emit(
- NotificationsState.loaded(
- notifications: notifications,
- notificationsCount: _notificationsCount,
- ),
- );
- }
-}
diff --git a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart
index 57a20923..5fdf10c0 100644
--- a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart
+++ b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart
@@ -1,7 +1,7 @@
import 'package:badges/badges.dart' as badges;
import 'package:eatvio_app/core/utils/image_constant.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
-import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
+import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
+import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_state.dart';
diff --git a/lib/features/cancel_plan_bottom_sheet/cancel_plan_bottom_sheet.dart b/lib/features/cancel_plan_bottom_sheet/cancel_plan_bottom_sheet.dart
new file mode 100644
index 00000000..ceec1294
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/cancel_plan_bottom_sheet.dart
@@ -0,0 +1,289 @@
+import 'package:eatvio_app/core/app_router.dart';
+import 'package:eatvio_app/core/locator_service.dart';
+import 'package:eatvio_app/core/theme/custom_text_style.dart';
+import 'package:eatvio_app/core/theme/theme_helper.dart';
+import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
+import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
+import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
+import 'package:eatvio_app/core/widgets/loader.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart';
+import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
+import 'package:eatvio_app/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
+
+class CancelPlanBottomSheet extends StatefulWidget {
+ const CancelPlanBottomSheet({super.key, this.enableForm = false, required this.planId});
+ final bool enableForm;
+ final int planId;
+ @override
+ State createState() => _CancelPlanBottomSheetState();
+}
+
+class _CancelPlanBottomSheetState extends State {
+ final amountController = TextEditingController();
+ final reasonController = TextEditingController();
+ @override
+ void initState() {
+ super.initState();
+ amountController.addListener(() {
+ if (!amountController.text.endsWith('₽')) {
+ final text = amountController.text.replaceAll('₽', '');
+ amountController.text = '$text₽';
+ amountController.selection = TextSelection.collapsed(offset: text.length);
+ }
+ });
+ }
+
+ bool isPriceValid = true;
+ bool isReasonValid = true;
+ bool? cancelationResult;
+
+ bool isLoading = false;
+ @override
+ Widget build(BuildContext context) {
+ final s = S.of(context);
+ if (cancelationResult != null) {
+ return _ComplaintWasSendWidget(isSuccess: cancelationResult!);
+ }
+ return BlocProvider(
+ create: (context) => sl(),
+ child: Builder(builder: (context) {
+ if (!widget.enableForm) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const SizedBox(height: 8),
+ const BottomSheetHandlerBar(),
+ const SizedBox(height: 20),
+ Text(
+ s.orederCancelation,
+ style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
+ ),
+ const SizedBox(height: 8),
+ Text(s.youShureYouWantToCancelOrder, style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
+ const SizedBox(height: 20),
+ isLoading
+ ? const Center(child: Loader())
+ : CustomOutlinedButton(
+ text: s.cancel,
+ height: 52,
+ buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
+ buttonStyle:
+ ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.strongRed)),
+ onPressed: () async {
+ setState(() {
+ isLoading = true;
+ });
+ final result = await context.read().cancelChosenPlan(id: widget.planId);
+ setState(() {
+ cancelationResult = result;
+ isLoading = false;
+ });
+ },
+ ),
+ const SizedBox(height: 30),
+ ],
+ ),
+ );
+ }
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const SizedBox(height: 8),
+ const Center(child: BottomSheetHandlerBar()),
+ const SizedBox(height: 20),
+ Center(
+ child: Text(
+ s.orederCancelation,
+ style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(s.explainResonOfCancelation, style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
+ const SizedBox(height: 4),
+ CustomTextFormField(
+ maxLines: 6,
+ minLines: 4,
+ hintText: s.reasonExample,
+ controller: reasonController,
+ hasError: !isReasonValid,
+ ),
+ if (!isReasonValid) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
+ const SizedBox(height: 12),
+ Text(s.typeAmoutOfReturn, style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
+ const SizedBox(height: 4),
+ CustomTextFormField(
+ hintText: '0 ₽',
+ controller: amountController,
+ keyboardType: TextInputType.number,
+ hasError: !isPriceValid,
+ ),
+ if (!isPriceValid) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимальная сумма 500₽')],
+ const SizedBox(height: 8),
+ Row(
+ children: [
+ InkWell(
+ onTap: () {
+ amountController.text = '1000₽';
+ },
+ borderRadius: BorderRadius.circular(200),
+ child: Ink(
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
+ decoration: BoxDecoration(
+ color: appTheme.lightGray0,
+ borderRadius: BorderRadius.circular(200),
+ ),
+ child: Text('1000 ₽', style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
+ ),
+ ),
+ const SizedBox(width: 8),
+ InkWell(
+ onTap: () {
+ amountController.text = '2000₽';
+ },
+ borderRadius: BorderRadius.circular(200),
+ child: Ink(
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
+ decoration: BoxDecoration(
+ color: appTheme.lightGray0,
+ borderRadius: BorderRadius.circular(200),
+ ),
+ child: Text('2000 ₽', style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
+ ),
+ )
+ ],
+ ),
+ const SizedBox(height: 20),
+ isLoading
+ ? const Center(child: Loader())
+ : CustomOutlinedButton(
+ text: s.cancel,
+ height: 52,
+ buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
+ buttonStyle: ButtonStyle(
+ side: const WidgetStatePropertyAll(BorderSide.none),
+ backgroundColor: WidgetStatePropertyAll(
+ appTheme.strongRed,
+ ),
+ ),
+ onPressed: () async {
+ bool isValid = true;
+ if (reasonController.text.length < 3) {
+ setState(() {
+ isReasonValid = false;
+ });
+ isValid = false;
+ } else {
+ setState(() {
+ isReasonValid = true;
+ });
+ }
+ final price = int.tryParse(amountController.text.replaceAll('₽', '').trim());
+ if (price == null || price < 500) {
+ setState(() {
+ isPriceValid = false;
+ });
+ isValid = false;
+ } else {
+ setState(() {
+ isPriceValid = true;
+ });
+ }
+ if (!isValid) {
+ return;
+ }
+ setState(() {
+ isLoading = true;
+ });
+ final result = await context.read().cancelChosenPlan(
+ id: widget.planId,
+ reason: reasonController.text,
+ price: price,
+ );
+ setState(() {
+ cancelationResult = result;
+ isLoading = false;
+ });
+ },
+ ),
+ const SizedBox(height: 30),
+ ],
+ ),
+ );
+ }),
+ );
+ }
+}
+
+class _ComplaintWasSendWidget extends StatelessWidget {
+ const _ComplaintWasSendWidget({required this.isSuccess});
+ final bool isSuccess;
+ @override
+ Widget build(BuildContext context) {
+ final s = S.of(context);
+ if (isSuccess) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const SizedBox(height: 8),
+ const BottomSheetHandlerBar(),
+ const SizedBox(height: 20),
+ Text(
+ s.orderCanceled,
+ style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
+ ),
+ const SizedBox(height: 20),
+ CustomOutlinedButton(
+ text: s.ok,
+ height: 52,
+ buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
+ buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.strongRed)),
+ onPressed: () async {
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ ),
+ const SizedBox(height: 30),
+ ],
+ ),
+ );
+ }
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const SizedBox(height: 8),
+ const BottomSheetHandlerBar(),
+ const SizedBox(height: 20),
+ Text(
+ s.couldNotCancelOrder,
+ style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
+ ),
+ const SizedBox(height: 20),
+ CustomOutlinedButton(
+ text: s.ok,
+ height: 52,
+ buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
+ buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.strongRed)),
+ onPressed: () async {
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ ),
+ const SizedBox(height: 30),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart
new file mode 100644
index 00000000..aa6689a1
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart
@@ -0,0 +1,25 @@
+import 'dart:developer';
+
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class CancelPlanCubit extends Cubit {
+ final CancelPlanRepository cancelPlanRepository;
+ CancelPlanCubit({required this.cancelPlanRepository}) : super(CancelPlanState());
+
+ Future cancelChosenPlan({required int id, String? reason, int? price}) async{
+ try {
+ CancelationModel? body;
+ if (reason != null && price != null) {
+ body = CancelationModel(description: reason, price: price);
+ }
+ await cancelPlanRepository.cancelChosenPlan(id: id, body: body);
+ return true;
+ } catch (e, st) {
+ log(e.toString(), stackTrace: st);
+ }
+ return false;
+ }
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.dart b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.dart
new file mode 100644
index 00000000..3c5b52d7
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.dart
@@ -0,0 +1,9 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'cancel_plan_state.freezed.dart';
+
+@freezed
+class CancelPlanState with _$CancelPlanState {
+
+ factory CancelPlanState() = _CancelPlanState;
+}
\ No newline at end of file
diff --git a/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart
new file mode 100644
index 00000000..90f3350c
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart
@@ -0,0 +1,82 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'cancel_plan_state.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
+
+/// @nodoc
+mixin _$CancelPlanState {}
+
+/// @nodoc
+abstract class $CancelPlanStateCopyWith<$Res> {
+ factory $CancelPlanStateCopyWith(
+ CancelPlanState value, $Res Function(CancelPlanState) then) =
+ _$CancelPlanStateCopyWithImpl<$Res, CancelPlanState>;
+}
+
+/// @nodoc
+class _$CancelPlanStateCopyWithImpl<$Res, $Val extends CancelPlanState>
+ implements $CancelPlanStateCopyWith<$Res> {
+ _$CancelPlanStateCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of CancelPlanState
+ /// with the given fields replaced by the non-null parameter values.
+}
+
+/// @nodoc
+abstract class _$$CancelPlanStateImplCopyWith<$Res> {
+ factory _$$CancelPlanStateImplCopyWith(_$CancelPlanStateImpl value,
+ $Res Function(_$CancelPlanStateImpl) then) =
+ __$$CancelPlanStateImplCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$$CancelPlanStateImplCopyWithImpl<$Res>
+ extends _$CancelPlanStateCopyWithImpl<$Res, _$CancelPlanStateImpl>
+ implements _$$CancelPlanStateImplCopyWith<$Res> {
+ __$$CancelPlanStateImplCopyWithImpl(
+ _$CancelPlanStateImpl _value, $Res Function(_$CancelPlanStateImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of CancelPlanState
+ /// with the given fields replaced by the non-null parameter values.
+}
+
+/// @nodoc
+
+class _$CancelPlanStateImpl implements _CancelPlanState {
+ _$CancelPlanStateImpl();
+
+ @override
+ String toString() {
+ return 'CancelPlanState()';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType && other is _$CancelPlanStateImpl);
+ }
+
+ @override
+ int get hashCode => runtimeType.hashCode;
+}
+
+abstract class _CancelPlanState implements CancelPlanState {
+ factory _CancelPlanState() = _$CancelPlanStateImpl;
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.dart b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.dart
new file mode 100644
index 00000000..0ddfa748
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.dart
@@ -0,0 +1,14 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'cancelation_model.freezed.dart';
+part 'cancelation_model.g.dart';
+
+@freezed
+class CancelationModel with _$CancelationModel {
+ factory CancelationModel({
+ required String description,
+ required int price,
+ }) = _CancelationModel;
+
+ factory CancelationModel.fromJson(Map json) => _$CancelationModelFromJson(json);
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart
new file mode 100644
index 00000000..d68a42ba
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart
@@ -0,0 +1,184 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'cancelation_model.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
+
+CancelationModel _$CancelationModelFromJson(Map json) {
+ return _CancelationModel.fromJson(json);
+}
+
+/// @nodoc
+mixin _$CancelationModel {
+ String get description => throw _privateConstructorUsedError;
+ int get price => throw _privateConstructorUsedError;
+
+ /// Serializes this CancelationModel to a JSON map.
+ Map toJson() => throw _privateConstructorUsedError;
+
+ /// Create a copy of CancelationModel
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $CancelationModelCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $CancelationModelCopyWith<$Res> {
+ factory $CancelationModelCopyWith(
+ CancelationModel value, $Res Function(CancelationModel) then) =
+ _$CancelationModelCopyWithImpl<$Res, CancelationModel>;
+ @useResult
+ $Res call({String description, int price});
+}
+
+/// @nodoc
+class _$CancelationModelCopyWithImpl<$Res, $Val extends CancelationModel>
+ implements $CancelationModelCopyWith<$Res> {
+ _$CancelationModelCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of CancelationModel
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? description = null,
+ Object? price = null,
+ }) {
+ return _then(_value.copyWith(
+ description: null == description
+ ? _value.description
+ : description // ignore: cast_nullable_to_non_nullable
+ as String,
+ price: null == price
+ ? _value.price
+ : price // ignore: cast_nullable_to_non_nullable
+ as int,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$CancelationModelImplCopyWith<$Res>
+ implements $CancelationModelCopyWith<$Res> {
+ factory _$$CancelationModelImplCopyWith(_$CancelationModelImpl value,
+ $Res Function(_$CancelationModelImpl) then) =
+ __$$CancelationModelImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call({String description, int price});
+}
+
+/// @nodoc
+class __$$CancelationModelImplCopyWithImpl<$Res>
+ extends _$CancelationModelCopyWithImpl<$Res, _$CancelationModelImpl>
+ implements _$$CancelationModelImplCopyWith<$Res> {
+ __$$CancelationModelImplCopyWithImpl(_$CancelationModelImpl _value,
+ $Res Function(_$CancelationModelImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of CancelationModel
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? description = null,
+ Object? price = null,
+ }) {
+ return _then(_$CancelationModelImpl(
+ description: null == description
+ ? _value.description
+ : description // ignore: cast_nullable_to_non_nullable
+ as String,
+ price: null == price
+ ? _value.price
+ : price // ignore: cast_nullable_to_non_nullable
+ as int,
+ ));
+ }
+}
+
+/// @nodoc
+@JsonSerializable()
+class _$CancelationModelImpl implements _CancelationModel {
+ _$CancelationModelImpl({required this.description, required this.price});
+
+ factory _$CancelationModelImpl.fromJson(Map json) =>
+ _$$CancelationModelImplFromJson(json);
+
+ @override
+ final String description;
+ @override
+ final int price;
+
+ @override
+ String toString() {
+ return 'CancelationModel(description: $description, price: $price)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$CancelationModelImpl &&
+ (identical(other.description, description) ||
+ other.description == description) &&
+ (identical(other.price, price) || other.price == price));
+ }
+
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ int get hashCode => Object.hash(runtimeType, description, price);
+
+ /// Create a copy of CancelationModel
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$CancelationModelImplCopyWith<_$CancelationModelImpl> get copyWith =>
+ __$$CancelationModelImplCopyWithImpl<_$CancelationModelImpl>(
+ this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$CancelationModelImplToJson(
+ this,
+ );
+ }
+}
+
+abstract class _CancelationModel implements CancelationModel {
+ factory _CancelationModel(
+ {required final String description,
+ required final int price}) = _$CancelationModelImpl;
+
+ factory _CancelationModel.fromJson(Map json) =
+ _$CancelationModelImpl.fromJson;
+
+ @override
+ String get description;
+ @override
+ int get price;
+
+ /// Create a copy of CancelationModel
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$CancelationModelImplCopyWith<_$CancelationModelImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart
new file mode 100644
index 00000000..d7bd21e9
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart
@@ -0,0 +1,21 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'cancelation_model.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+_$CancelationModelImpl _$$CancelationModelImplFromJson(
+ Map json) =>
+ _$CancelationModelImpl(
+ description: json['description'] as String,
+ price: (json['price'] as num).toInt(),
+ );
+
+Map _$$CancelationModelImplToJson(
+ _$CancelationModelImpl instance) =>
+ {
+ 'description': instance.description,
+ 'price': instance.price,
+ };
diff --git a/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart b/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart
new file mode 100644
index 00000000..2e55549a
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart
@@ -0,0 +1,17 @@
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart';
+
+class CancelPlanRepository {
+ final CancelPlanService cancelService;
+ CancelPlanRepository({required this.cancelService});
+ Future cancelChosenPlan({
+ required int id,
+ CancelationModel? body
+ }) async {
+ try {
+ await cancelService.cancelChosenPlan(id: id, body: body);
+ } catch (e) {
+ rethrow;
+ }
+ }
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart b/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart
new file mode 100644
index 00000000..ae677280
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart
@@ -0,0 +1,16 @@
+import 'package:dio/dio.dart';
+import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart';
+import 'package:retrofit/retrofit.dart';
+
+part 'cancel_plan_service.g.dart';
+
+@RestApi()
+abstract class CancelPlanService {
+ factory CancelPlanService(Dio dio, {String baseUrl}) = _CancelPlanService;
+
+ @POST('/api/chosen_plans/{id}/cancel')
+ Future cancelChosenPlan({
+ @Path() required int id,
+ @Body() CancelationModel? body
+ });
+}
diff --git a/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart b/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart
new file mode 100644
index 00000000..943e9d25
--- /dev/null
+++ b/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart
@@ -0,0 +1,69 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'cancel_plan_service.dart';
+
+// **************************************************************************
+// RetrofitGenerator
+// **************************************************************************
+
+// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
+
+class _CancelPlanService implements CancelPlanService {
+ _CancelPlanService(this._dio, {this.baseUrl, this.errorLogger});
+
+ final Dio _dio;
+
+ String? baseUrl;
+
+ final ParseErrorLogger? errorLogger;
+
+ @override
+ Future cancelChosenPlan({
+ required int id,
+ CancelationModel? body,
+ }) async {
+ final _extra = {};
+ final queryParameters = {};
+ queryParameters.removeWhere((k, v) => v == null);
+ final _headers = {};
+ final _data = body;
+ final _options = _setStreamType(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/api/chosen_plans/${id}/cancel',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ await _dio.fetch(_options);
+ }
+
+ RequestOptions _setStreamType(RequestOptions requestOptions) {
+ if (T != dynamic &&
+ !(requestOptions.responseType == ResponseType.bytes ||
+ requestOptions.responseType == ResponseType.stream)) {
+ if (T == String) {
+ requestOptions.responseType = ResponseType.plain;
+ } else {
+ requestOptions.responseType = ResponseType.json;
+ }
+ }
+ return requestOptions;
+ }
+
+ String _combineBaseUrls(String dioBaseUrl, String? baseUrl) {
+ if (baseUrl == null || baseUrl.trim().isEmpty) {
+ return dioBaseUrl;
+ }
+
+ final url = Uri.parse(baseUrl);
+
+ if (url.isAbsolute) {
+ return url.toString();
+ }
+
+ return Uri.parse(dioBaseUrl).resolveUri(url).toString();
+ }
+}
diff --git a/lib/features/chat/models/chat_room_model.dart b/lib/features/chat/models/chat_room_model.dart
index 9d77fdf0..73daf1be 100644
--- a/lib/features/chat/models/chat_room_model.dart
+++ b/lib/features/chat/models/chat_room_model.dart
@@ -12,7 +12,8 @@ class ChatRoomModel with _$ChatRoomModel {
required String id,
@JsonKey(name: 'chat_room_setting') required ChatRoomSettingsModel chatRoomSettings,
required Users users,
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan) required ChosenPlanModel? chosenPlan,
+ @JsonKey(name: 'chosen_plan_id') required int? chosenPlanId,
+ ChosenPlanModel? chosenPlan,
}) = _ChatRoomModel;
factory ChatRoomModel.fromJson(Map json) => _$ChatRoomModelFromJson(json);
diff --git a/lib/features/chat/models/chat_room_model.freezed.dart b/lib/features/chat/models/chat_room_model.freezed.dart
index 829dd69f..04415447 100644
--- a/lib/features/chat/models/chat_room_model.freezed.dart
+++ b/lib/features/chat/models/chat_room_model.freezed.dart
@@ -25,7 +25,8 @@ mixin _$ChatRoomModel {
ChatRoomSettingsModel get chatRoomSettings =>
throw _privateConstructorUsedError;
Users get users => throw _privateConstructorUsedError;
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
+ @JsonKey(name: 'chosen_plan_id')
+ int? get chosenPlanId => throw _privateConstructorUsedError;
ChosenPlanModel? get chosenPlan => throw _privateConstructorUsedError;
/// Serializes this ChatRoomModel to a JSON map.
@@ -49,7 +50,7 @@ abstract class $ChatRoomModelCopyWith<$Res> {
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel chatRoomSettings,
Users users,
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
+ @JsonKey(name: 'chosen_plan_id') int? chosenPlanId,
ChosenPlanModel? chosenPlan});
$ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings;
@@ -75,6 +76,7 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
Object? id = null,
Object? chatRoomSettings = null,
Object? users = null,
+ Object? chosenPlanId = freezed,
Object? chosenPlan = freezed,
}) {
return _then(_value.copyWith(
@@ -90,6 +92,10 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
? _value.users
: users // ignore: cast_nullable_to_non_nullable
as Users,
+ chosenPlanId: freezed == chosenPlanId
+ ? _value.chosenPlanId
+ : chosenPlanId // ignore: cast_nullable_to_non_nullable
+ as int?,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
@@ -146,7 +152,7 @@ abstract class _$$ChatRoomModelImplCopyWith<$Res>
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel chatRoomSettings,
Users users,
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
+ @JsonKey(name: 'chosen_plan_id') int? chosenPlanId,
ChosenPlanModel? chosenPlan});
@override
@@ -173,6 +179,7 @@ class __$$ChatRoomModelImplCopyWithImpl<$Res>
Object? id = null,
Object? chatRoomSettings = null,
Object? users = null,
+ Object? chosenPlanId = freezed,
Object? chosenPlan = freezed,
}) {
return _then(_$ChatRoomModelImpl(
@@ -188,6 +195,10 @@ class __$$ChatRoomModelImplCopyWithImpl<$Res>
? _value.users
: users // ignore: cast_nullable_to_non_nullable
as Users,
+ chosenPlanId: freezed == chosenPlanId
+ ? _value.chosenPlanId
+ : chosenPlanId // ignore: cast_nullable_to_non_nullable
+ as int?,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
@@ -203,8 +214,8 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
{required this.id,
@JsonKey(name: 'chat_room_setting') required this.chatRoomSettings,
required this.users,
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
- required this.chosenPlan});
+ @JsonKey(name: 'chosen_plan_id') required this.chosenPlanId,
+ this.chosenPlan});
factory _$ChatRoomModelImpl.fromJson(Map json) =>
_$$ChatRoomModelImplFromJson(json);
@@ -217,12 +228,14 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
@override
final Users users;
@override
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
+ @JsonKey(name: 'chosen_plan_id')
+ final int? chosenPlanId;
+ @override
final ChosenPlanModel? chosenPlan;
@override
String toString() {
- return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, users: $users, chosenPlan: $chosenPlan)';
+ return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, users: $users, chosenPlanId: $chosenPlanId, chosenPlan: $chosenPlan)';
}
@override
@@ -234,14 +247,16 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
(identical(other.chatRoomSettings, chatRoomSettings) ||
other.chatRoomSettings == chatRoomSettings) &&
(identical(other.users, users) || other.users == users) &&
+ (identical(other.chosenPlanId, chosenPlanId) ||
+ other.chosenPlanId == chosenPlanId) &&
(identical(other.chosenPlan, chosenPlan) ||
other.chosenPlan == chosenPlan));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
- int get hashCode =>
- Object.hash(runtimeType, id, chatRoomSettings, users, chosenPlan);
+ int get hashCode => Object.hash(
+ runtimeType, id, chatRoomSettings, users, chosenPlanId, chosenPlan);
/// Create a copy of ChatRoomModel
/// with the given fields replaced by the non-null parameter values.
@@ -265,8 +280,8 @@ abstract class _ChatRoomModel implements ChatRoomModel {
@JsonKey(name: 'chat_room_setting')
required final ChatRoomSettingsModel chatRoomSettings,
required final Users users,
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
- required final ChosenPlanModel? chosenPlan}) = _$ChatRoomModelImpl;
+ @JsonKey(name: 'chosen_plan_id') required final int? chosenPlanId,
+ final ChosenPlanModel? chosenPlan}) = _$ChatRoomModelImpl;
factory _ChatRoomModel.fromJson(Map json) =
_$ChatRoomModelImpl.fromJson;
@@ -279,7 +294,9 @@ abstract class _ChatRoomModel implements ChatRoomModel {
@override
Users get users;
@override
- @JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
+ @JsonKey(name: 'chosen_plan_id')
+ int? get chosenPlanId;
+ @override
ChosenPlanModel? get chosenPlan;
/// Create a copy of ChatRoomModel
diff --git a/lib/features/chat/models/chat_room_model.g.dart b/lib/features/chat/models/chat_room_model.g.dart
index fb9e6257..a5ce8e0d 100644
--- a/lib/features/chat/models/chat_room_model.g.dart
+++ b/lib/features/chat/models/chat_room_model.g.dart
@@ -12,7 +12,11 @@ _$ChatRoomModelImpl _$$ChatRoomModelImplFromJson(Map json) =>
chatRoomSettings: ChatRoomSettingsModel.fromJson(
json['chat_room_setting'] as Map),
users: Users.fromJson(json['users'] as Map),
- chosenPlan: parseChosenPlan(json['chosen_plan']),
+ chosenPlanId: (json['chosen_plan_id'] as num?)?.toInt(),
+ chosenPlan: json['chosenPlan'] == null
+ ? null
+ : ChosenPlanModel.fromJson(
+ json['chosenPlan'] as Map),
);
Map _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) =>
@@ -20,7 +24,8 @@ Map _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) =>
'id': instance.id,
'chat_room_setting': instance.chatRoomSettings,
'users': instance.users,
- 'chosen_plan': instance.chosenPlan,
+ 'chosen_plan_id': instance.chosenPlanId,
+ 'chosenPlan': instance.chosenPlan,
};
_$UsersImpl _$$UsersImplFromJson(Map json) => _$UsersImpl(
diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart
index e37fd767..bb8e6d9f 100644
--- a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart
+++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart
@@ -14,7 +14,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
-import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
+import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ChatRoomCubit extends Cubit {
@@ -57,13 +57,13 @@ class ChatRoomCubit extends Cubit {
Future checkCards() async {
try {
final cards = await cardsRepository.getCards();
- setCard(cards: cards);
- } catch (e) {
- emit(const ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
+ _setCard(cards: cards);
+ } catch (e, st) {
+ log(e.toString(), stackTrace: st);
}
}
- void setCard({required List cards}) {
+ void _setCard({required List cards}) {
final currentState = state;
if (currentState is ChatRoomStateLoaded) {
if (cards.where((card) => card.isDefault).isNotEmpty) {
@@ -75,15 +75,27 @@ class ChatRoomCubit extends Cubit {
initChat(String chatId) async {
try {
final chatRoom = await chatRepository.getChatRoom(chatId);
-
+ ChosenPlanModel? chosenPlan;
+ if (chatRoom.chosenPlanId != null) {
+ chosenPlan = await chatRepository.getChosenPlan(id: chatRoom.chosenPlanId!);
+ }
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage);
lastPage = chatMessages.meta.lastPage;
await _subscribeToChatChannel(chatId: chatId);
+ emit(
+ ChatRoomState.loaded(
+ chatRoom: chatRoom.copyWith(chosenPlan: chosenPlan),
+ chatMessages: chatMessages.messagesList,
+ isTyping: false,
+ isLoading: isLoading,
+ hasCard: false,
+ ),
+ );
- emit(ChatRoomState.loaded(chatRoom: chatRoom, chatMessages: chatMessages.messagesList, isTyping: false, isLoading: isLoading, hasCard: false));
- // final cards = await cardsRepository.getCards();
- // setCard(cards: cards);
+ if (chatRoom.chosenPlan?.consultantId == chatRoom.users.receiver.id) {
+ await checkCards();
+ }
} catch (e) {
log(e.toString());
typingSubscription?.cancel();
@@ -93,14 +105,26 @@ class ChatRoomCubit extends Cubit {
}
}
+ Future updateChosenPlanState() async {
+ final currentState = state;
+ if (currentState is ChatRoomStateLoaded) {
+ final chosenPlanId = currentState.chatRoom.chosenPlanId;
+ if (chosenPlanId != null) {
+ final chosenPlan = await chatRepository.getChosenPlan(id: chosenPlanId);
+ emit(currentState.copyWith.chatRoom(chosenPlan: chosenPlan));
+ }
+ }
+ }
+
Future choseStartPlanDate(DateTime date) async {
try {
if (state is! ChatRoomStateLoaded) return false;
- final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id;
+ final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlanId;
if (planId == null) return false;
final stringDate = date.format('yyyy-MM-dd');
await chatRepository.choseStartPlanDate(id: planId, date: {'start_date': stringDate});
- emit((state as ChatRoomStateLoaded).copyWith.chatRoom.chosenPlan?.call(chosenPlanState: ChosenPlanStateEnum.stateStartDateHasBeenSet) ?? state);
+ final chosenPlan = await chatRepository.getChosenPlan(id: planId);
+ emit((state as ChatRoomStateLoaded).copyWith.chatRoom(chosenPlan: chosenPlan));
return true;
} catch (e, st) {
log(e.toString(), stackTrace: st);
@@ -111,8 +135,7 @@ class ChatRoomCubit extends Cubit {
Future cancelChosenPlan({required String chatId, required int planId}) async {
try {
await chatRepository.cancelChosenPlan(id: planId);
- final chatRoom = await chatRepository.getChatRoom(chatId);
- emit((state as ChatRoomStateLoaded).copyWith(chatRoom: chatRoom));
+ emit((state as ChatRoomStateLoaded).copyWith.chatRoom(chosenPlan: null));
} catch (e) {
emit(const ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
}
@@ -133,7 +156,8 @@ class ChatRoomCubit extends Cubit {
typingSubscription = wsManager.clientTypingStream?.listen((event) {
emit((state as ChatRoomStateLoaded).copyWith(isTyping: true, isLoading: isLoading));
- EasyDebounce.debounce('client-typing', const Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
+ EasyDebounce.debounce(
+ 'client-typing', const Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
});
}
diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart
index fe5ef6df..5f14e0bf 100644
--- a/lib/features/chat/presentation/pages/chat_screen.dart
+++ b/lib/features/chat/presentation/pages/chat_screen.dart
@@ -14,7 +14,6 @@ import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
-import 'package:eatvio_app/features/chat/presentation/widgets/order_card.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
@@ -85,15 +84,24 @@ class _ChatScreenState extends State {
_buildChat(BuildContext context, ChatRoomStateLoaded state) {
final chosenPlan = state.chatRoom.chosenPlan;
final amIConsultant = chosenPlan?.consultantId == state.chatRoom.users.sender.id;
- final showAboutOrderButton = showableChatBadgeStates.contains(chosenPlan?.chosenPlanState);
+ final showUserDetailsButton = amIConsultant &&
+ [
+ ChosenPlanStateEnum.stateStartDateHasBeenSet,
+ ChosenPlanStateEnum.statePendingPayment,
+ ChosenPlanStateEnum.stateInCoordination,
+ ].contains(chosenPlan?.chosenPlanState);
+
+ final showAboutOrderButton =
+ state.chatRoom.chosenPlan != null && state.chatRoom.chosenPlan!.chosenPlanState != ChosenPlanStateEnum.stateCanceledByTheInitiator;
BannerType? bannerType;
- if (amIConsultant && state.hasCard) {
+ if (chosenPlan == null) {
+ bannerType = null;
+ } else if (amIConsultant && state.hasCard) {
bannerType = BannerType.card;
- } else if (amIConsultant && chosenPlan?.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
+ } else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
bannerType = BannerType.date;
} else if (!amIConsultant &&
- (chosenPlan?.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet ||
- chosenPlan?.chosenPlanState == ChosenPlanStateEnum.statePendingPayment)) {
+ (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet || chosenPlan.chosenPlanState == ChosenPlanStateEnum.statePendingPayment)) {
bannerType = BannerType.payment;
}
return Scaffold(
@@ -254,15 +262,15 @@ class _ChatScreenState extends State {
itemCount: state.chatMessages.length + (state.isLoading ? 1 : 0),
),
const SizedBox(height: 16),
- if (chosenPlan != null) ...[
- OrderCard(
- plan: chosenPlan.plan,
- isCustomerView: !amIConsultant,
- optionType: chosenPlan.optionType,
- onCancel: () async {
- await context.read().cancelChosenPlan(chatId: state.chatRoom.id, planId: chosenPlan.id);
- },
- ),
+ if (showUserDetailsButton) ...[
+ // OrderCard(
+ // plan: chosenPlan.plan,
+ // isCustomerView: !amIConsultant,
+ // optionType: chosenPlan.optionType,
+ // onCancel: () async {
+ // await context.read().cancelChosenPlan(chatId: state.chatRoom.id, planId: chosenPlan.id);
+ // },
+ // ),
if (amIConsultant)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
@@ -394,7 +402,7 @@ class _ChatScreenState extends State {
right: 15,
left: 16,
child: AboutOrderButton(
- onTap: () {
+ onTap: () async {
GoRouter.of(context).push(InfoByOrderScreen.path, extra: {'planId': state.chatRoom.chosenPlan?.id});
},
)),
diff --git a/lib/features/chat/presentation/widgets/notification_banner.dart b/lib/features/chat/presentation/widgets/notification_banner.dart
index cfe81a39..0059f7cc 100644
--- a/lib/features/chat/presentation/widgets/notification_banner.dart
+++ b/lib/features/chat/presentation/widgets/notification_banner.dart
@@ -5,6 +5,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/chat/presentation/pages/payment_screen.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/chose_plan_date_widget.dart';
+import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -76,7 +77,11 @@ class _NotificationBannerState extends State {
),
const SizedBox(height: 12),
GestureDetector(
- onTap: () {},
+ onTap: () {
+ GoRouter.of(context).pushReplacement(
+ CardSettingsScreen.path,
+ );
+ },
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
const SizedBox(height: 16),
diff --git a/lib/features/chat/repositories/chat_repository.dart b/lib/features/chat/repositories/chat_repository.dart
index 5027aeed..30c34586 100644
--- a/lib/features/chat/repositories/chat_repository.dart
+++ b/lib/features/chat/repositories/chat_repository.dart
@@ -7,6 +7,7 @@ import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/models/payment_model.dart';
import 'package:eatvio_app/features/chat/services/chat_service.dart';
+import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:http_parser/http_parser.dart';
class ChatRepository {
@@ -121,4 +122,14 @@ class ChatRepository {
rethrow;
}
}
+
+ Future getChosenPlan({
+ required int id,
+ }) async {
+ try {
+ return await chatService.getChosenPlan(id: id);
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/chat/services/chat_service.dart b/lib/features/chat/services/chat_service.dart
index e8e47e2b..2e2a127e 100644
--- a/lib/features/chat/services/chat_service.dart
+++ b/lib/features/chat/services/chat_service.dart
@@ -3,6 +3,7 @@ import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/models/payment_model.dart';
+import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:retrofit/retrofit.dart';
part 'chat_service.g.dart';
@@ -62,4 +63,9 @@ abstract class ChatService {
Future getPaymentLink({
@Path() required int id,
});
+
+ @GET('/api/chosen_plans/show/{id}')
+ Future getChosenPlan({
+ @Path() required int id,
+ });
}
diff --git a/lib/features/chat/services/chat_service.g.dart b/lib/features/chat/services/chat_service.g.dart
index d25bec4d..9280c2af 100644
--- a/lib/features/chat/services/chat_service.g.dart
+++ b/lib/features/chat/services/chat_service.g.dart
@@ -262,6 +262,33 @@ class _ChatService implements ChatService {
return _value;
}
+ @override
+ Future getChosenPlan({required int id}) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ const Map? _data = null;
+ final _options = _setStreamType(
+ Options(method: 'GET', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/api/chosen_plans/show/${id}',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch