diff --git a/assets/images/icn_dev_tools.svg b/assets/images/icn_dev_tools.svg
new file mode 100644
index 00000000..1377ef9d
--- /dev/null
+++ b/assets/images/icn_dev_tools.svg
@@ -0,0 +1,6 @@
+
diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart
index ff26e3bb..39daded4 100644
--- a/lib/core/app_router.dart
+++ b/lib/core/app_router.dart
@@ -58,6 +58,7 @@ class AppRouter {
path: '/auth-screen',
name: 'authScreen',
redirect: (context, state) async {
+ // TokenStorage.removeTokenInStorage();
if (await TokenStorage.haveTokenInStorage()) {
return '/my-profile-screen';
}
@@ -133,8 +134,8 @@ class AppRouter {
),
),
GoRoute(
- path: '/recipe-create-screen',
- name: 'recipeCreateScreen',
+ path: RecipeCreateScreen.path,
+ name: RecipeCreateScreen.name,
builder: (context, state) => RecipeCreateScreen(),
),
GoRoute(
diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart
index 6c62f438..e258d702 100644
--- a/lib/core/locator_service.dart
+++ b/lib/core/locator_service.dart
@@ -91,14 +91,14 @@ Future init() async {
sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl()));
sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl()));
- sl.registerFactory(() => AboutDietCubit(dietsRepository: sl()));
+ sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl()));
sl.registerFactory(() => SubCubit(profileRepository: sl()));
sl.registerFactory(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
sl.registerFactory(() => CreatePlanCubit());
sl.registerFactory(() => FeedProfileCubit(profileRepository: sl()));
sl.registerFactory(() => AboutPlanCubit(planRepository: sl()));
- sl.registerFactory(() => CreateRecipeCubit());
+ sl.registerFactory(() => CreateRecipeCubit(recipeRepository: sl()));
sl.registerFactory(() => MyDietsCubit(myDietsRepository: sl()));
sl.registerFactory(() => MyPlansCubit(myPlansRepository: sl()));
sl.registerFactory(() => UserDetailsCubit(profileRepository: sl()));
diff --git a/lib/core/theme/theme_helper.dart b/lib/core/theme/theme_helper.dart
index 5abec056..31fbd60b 100644
--- a/lib/core/theme/theme_helper.dart
+++ b/lib/core/theme/theme_helper.dart
@@ -185,6 +185,8 @@ class PrimaryColors {
Color get red => Color(0xFFCD4141);
+ Color get transparentRed => Color(0xFFCD4141).withValues(alpha: 0.75);
+
Color get transparentBlack => Color(0xFF1313264).withValues(alpha: 0.3);
Color get gray60004 => Color(0xFFD4DAE2).withValues(alpha: 0.2);
diff --git a/lib/core/utils/image_constant.dart b/lib/core/utils/image_constant.dart
index 1353a5cf..73bdf50a 100644
--- a/lib/core/utils/image_constant.dart
+++ b/lib/core/utils/image_constant.dart
@@ -218,5 +218,7 @@ class ImageConstant {
static String icnSupport = '$imagePath/icn_support.svg';
+ static String icnDevTools = '$imagePath/icn_dev_tools.svg';
+
static String icnVeggies = '$imagePath/icn_veggies.svg';
}
diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart
index 3b76cb7c..d446e4b2 100644
--- a/lib/features/chat/presentation/pages/chat_screen.dart
+++ b/lib/features/chat/presentation/pages/chat_screen.dart
@@ -154,7 +154,17 @@ class _ChatScreenState extends State {
if ((index + 1 != state.chatMessages.length &&
DateTime.parse(state.chatMessages[index].updatedAt).day != DateTime.parse(state.chatMessages[index + 1].updatedAt).day) ||
index + 1 == state.chatMessages.length) {
- return _messageDate(state.chatMessages[index].updatedAt);
+ return Column(
+ children: [
+ _messageDate(state.chatMessages[index].updatedAt),
+ ChatBubbleWidget(
+ sendTime: state.chatMessages[index].updatedAt,
+ text: state.chatMessages[index].data.trim(),
+ isCurrentUser: state.chatRoom.users['to_chat_user']!.id != state.chatMessages[index].authorId,
+ mediaData: state.chatMessages[index].mediaData ?? [],
+ ),
+ ],
+ );
}
return ChatBubbleWidget(
sendTime: state.chatMessages[index].updatedAt,
diff --git a/lib/features/dev_tools/presentation/dev_tools_screen.dart b/lib/features/dev_tools/presentation/dev_tools_screen.dart
index 0b641420..d3b25f40 100644
--- a/lib/features/dev_tools/presentation/dev_tools_screen.dart
+++ b/lib/features/dev_tools/presentation/dev_tools_screen.dart
@@ -25,7 +25,7 @@ class DevToolsScreenContent extends StatefulWidget {
class _DevToolsScreenContentState extends State {
String baseUrlForTest = '';
- final List testCases = [Test850task()];
+ final List testCases = [Test850task(), GetChatRoomPreviews(), GetChatRoom(), SendMessage(), GetChatMessages(), GetChatRoomIdByUserSlug()];
final controller = TextEditingController();
late final List testsStates;
diff --git a/lib/features/dev_tools/test_cases.dart b/lib/features/dev_tools/test_cases.dart
index 3f0891b7..cb812174 100644
--- a/lib/features/dev_tools/test_cases.dart
+++ b/lib/features/dev_tools/test_cases.dart
@@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
+import 'package:eatvio_app/features/chat/services/chat_service.dart';
import 'package:eatvio_app/features/diets/services/diet_service.dart';
import 'package:eatvio_app/features/feed/services/feed_service.dart';
import 'package:eatvio_app/features/profile/services/profile_service.dart';
@@ -113,3 +114,106 @@ class Test850task implements TestCases {
}
}
}
+
+class GetChatRoomPreviews implements TestCases {
+ @override
+ String get name => '/api/chat/index';
+
+ @override
+ Future run({required String baseUrl}) async {
+ try {
+ // get all of needed services
+ final dio = sl();
+ final chatService = ChatService(dio, baseUrl: baseUrl);
+
+ await chatService.getChatRoomPreviews();
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
+
+class GetChatRoom implements TestCases {
+ @override
+ String get name => '/api/chat/show/{id}';
+
+ @override
+ Future run({required String baseUrl}) async {
+ try {
+ final dio = sl();
+ final chatService = ChatService(dio, baseUrl: baseUrl);
+
+ final chatRoomsModels = await chatService.getChatRoomPreviews();
+ await chatService.getChatRoom(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '');
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
+
+class SendMessage implements TestCases {
+ @override
+ String get name => '/api/chat/{id}/message/send';
+
+ @override
+ Future run({required String baseUrl}) async {
+ try {
+ final dio = sl();
+ final chatService = ChatService(dio, baseUrl: baseUrl);
+
+ final chatRoomsModels = await chatService.getChatRoomPreviews();
+ await chatService.sendMessage(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', data: 'test message');
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
+
+class GetChatMessages implements TestCases {
+ @override
+ String get name => '/api/chat/show/{id}/messages?page={page}';
+
+ @override
+ Future run({required String baseUrl}) async {
+ try {
+ final dio = sl();
+ final chatService = ChatService(dio, baseUrl: baseUrl);
+
+ final chatRoomsModels = await chatService.getChatRoomPreviews();
+ await chatService.getChatMessages(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', page: 1);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
+
+class GetChatRoomIdByUserSlug implements TestCases {
+ @override
+ String get name => '/api/chat/get_by_user/{user}';
+
+ @override
+ Future run({required String baseUrl}) async {
+ try {
+ final dio = sl();
+ final chatService = ChatService(dio, baseUrl: baseUrl);
+ final consultantService = FeedService(dio, baseUrl: baseUrl);
+ final profileService = ProfileService(dio, baseUrl: baseUrl);
+ final myProfile = await profileService.getMyProfile();
+ final consultantsModel = await consultantService.getConsultants();
+
+ for (var consultant in consultantsModel.data) {
+ if (consultant.slug != myProfile.slug) {
+ await chatService.getChatRoomIdByUserSlug(user: consultant.slug);
+ break;
+ }
+ }
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
diff --git a/lib/features/diets/presentation/cubit/about_diet_cubit.dart b/lib/features/diets/presentation/cubit/about_diet_cubit.dart
index 54215fe9..ea57351d 100644
--- a/lib/features/diets/presentation/cubit/about_diet_cubit.dart
+++ b/lib/features/diets/presentation/cubit/about_diet_cubit.dart
@@ -1,14 +1,21 @@
import 'dart:async';
import 'dart:developer';
+import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_debounce.dart';
+import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_state.dart';
+import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AboutDietCubit extends Cubit {
- AboutDietCubit({required this.dietsRepository}) : super(AboutDietState.initial());
+ AboutDietCubit({required this.dietsRepository, required this.profileRepository}) : super(AboutDietState.initial());
final DietRepository dietsRepository;
+ final ProfileRepository profileRepository;
+ bool isCurrentDietStarted = false;
+ bool hasStartedDiet = false;
+ String? userStartedDietName;
Future getDiet(int id, {int? index}) async {
try {
@@ -16,17 +23,30 @@ class AboutDietCubit extends Cubit {
if (index != null) {
dietsRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: diet.totalComments));
}
- emit(AboutDietState.loaded(diet));
+ emit(AboutDietState.loaded(diet: diet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
} catch (e) {
log(e.toString());
}
}
- Future getDietWithLoading(int id) async {
+ Future initDietPage(int id) async {
try {
emit(AboutDietState.loading());
final diet = await dietsRepository.getDiet(id);
- emit(AboutDietState.loaded(diet));
+ final chosenDiets = await profileRepository.getChosenDiets();
+ final lastChosenDiet = chosenDiets.firstOrNull;
+ if (lastChosenDiet != null && ['inited', 'started'].contains(lastChosenDiet.state)) {
+ hasStartedDiet = true;
+ if (diet.id == lastChosenDiet.dietId) {
+ isCurrentDietStarted = true;
+ } else {
+ if (lastChosenDiet.dietId != null) {
+ final chosenDiet = await dietsRepository.getDiet(lastChosenDiet.dietId!);
+ userStartedDietName = chosenDiet.name;
+ }
+ }
+ }
+ emit(AboutDietState.loaded(diet: diet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
} catch (e) {
log(e.toString());
}
@@ -45,7 +65,7 @@ class AboutDietCubit extends Cubit {
if (dietIndexInFeed != null) {
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
}
- emit(AboutDietState.loaded(updatedDiet));
+ emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
} else {
final updatedDiet = currentDiet.copyWith(
totalLikes: currentDiet.totalLikes + 1,
@@ -54,7 +74,7 @@ class AboutDietCubit extends Cubit {
if (dietIndexInFeed != null) {
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
}
- emit(AboutDietState.loaded(updatedDiet));
+ emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
EasyDebounce.debounce(
@@ -69,7 +89,7 @@ class AboutDietCubit extends Cubit {
dietsRepository
.emitLikesStateToFeed(LikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed));
}
- emit(AboutDietState.loaded(currentDiet));
+ emit(AboutDietState.loaded(diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
},
);
@@ -86,7 +106,7 @@ class AboutDietCubit extends Cubit {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
}
- emit(AboutDietState.loaded(updatedDiet));
+ emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
EasyDebounce.debounce(
'add_or_remove_diet_to_favorites',
@@ -100,7 +120,7 @@ class AboutDietCubit extends Cubit {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
}
- emit(AboutDietState.loaded(currentDiet));
+ emit(AboutDietState.loaded(diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
},
);
@@ -117,7 +137,7 @@ class AboutDietCubit extends Cubit {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
}
- emit(AboutDietState.loaded(updatedDiet));
+ emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
EasyDebounce.debounce(
'add_or_remove_diet_to_favorites',
@@ -131,9 +151,50 @@ class AboutDietCubit extends Cubit {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
}
- emit(AboutDietState.loaded(currentDiet));
+ emit(AboutDietState.loaded(diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
},
);
}
+
+ Future startDiet(DateTime date) async {
+ try {
+ if (state is! DietStateLoaded) return false;
+
+ final id = (state as DietStateLoaded).diet.id;
+ final stringDate = date.format('yyyy-MM-dd');
+ await dietsRepository.startDiet(id, stringDate);
+ return true;
+ } catch (e, st) {
+ log(e.toString(), stackTrace: st);
+ return false;
+ }
+ }
+
+ Future cancelDiet() async {
+ try {
+ if (state is! DietStateLoaded) return false;
+ final id = (state as DietStateLoaded).diet.id;
+ await dietsRepository.cancelDiet(id);
+ return true;
+ } on DioException catch (e) {
+ log(e.toString());
+ return false;
+ } catch (e) {
+ log(e.toString());
+ return false;
+ }
+ }
+
+ void clearStartedDietsState() {
+ if (state is! DietStateLoaded) return;
+ final diet = (state as DietStateLoaded).diet;
+ emit(AboutDietState.loaded(diet: diet));
+ }
+
+ void emitCurrentStartedDietsState() {
+ if (state is! DietStateLoaded) return;
+ final diet = (state as DietStateLoaded).diet;
+ emit(AboutDietState.loaded(diet: diet, isStarted: true));
+ }
}
diff --git a/lib/features/diets/presentation/cubit/about_diet_state.dart b/lib/features/diets/presentation/cubit/about_diet_state.dart
index 36a78a31..ba9234e8 100644
--- a/lib/features/diets/presentation/cubit/about_diet_state.dart
+++ b/lib/features/diets/presentation/cubit/about_diet_state.dart
@@ -9,6 +9,10 @@ class AboutDietState with _$AboutDietState {
const factory AboutDietState.initial() = DietStateInitial;
const factory AboutDietState.loading() = DietStateLoading;
- const factory AboutDietState.loaded(Diet diet) = DietStateLoaded;
+ const factory AboutDietState.loaded({
+ required Diet diet,
+ @Default(false) bool isStarted,
+ @Default(false) bool hasStartedDiet,
+ }) = DietStateLoaded;
const factory AboutDietState.error({required String message}) = DietStateError;
}
diff --git a/lib/features/diets/presentation/cubit/about_diet_state.freezed.dart b/lib/features/diets/presentation/cubit/about_diet_state.freezed.dart
index ce2270dc..493edb32 100644
--- a/lib/features/diets/presentation/cubit/about_diet_state.freezed.dart
+++ b/lib/features/diets/presentation/cubit/about_diet_state.freezed.dart
@@ -21,7 +21,8 @@ mixin _$AboutDietState {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
- required TResult Function(Diet diet) loaded,
+ required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
+ loaded,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@@ -30,7 +31,7 @@ mixin _$AboutDietState {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
- TResult? Function(Diet diet)? loaded,
+ TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@@ -39,7 +40,7 @@ mixin _$AboutDietState {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
- TResult Function(Diet diet)? loaded,
+ TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) =>
@@ -139,7 +140,8 @@ class _$AboutDietStateImpl implements _AboutDietState {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
- required TResult Function(Diet diet) loaded,
+ required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
+ loaded,
required TResult Function(String message) error,
}) {
return $default();
@@ -151,7 +153,7 @@ class _$AboutDietStateImpl implements _AboutDietState {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
- TResult? Function(Diet diet)? loaded,
+ TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return $default?.call();
@@ -163,7 +165,7 @@ class _$AboutDietStateImpl implements _AboutDietState {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
- TResult Function(Diet diet)? loaded,
+ TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
@@ -262,7 +264,8 @@ class _$DietStateInitialImpl implements DietStateInitial {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
- required TResult Function(Diet diet) loaded,
+ required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
+ loaded,
required TResult Function(String message) error,
}) {
return initial();
@@ -274,7 +277,7 @@ class _$DietStateInitialImpl implements DietStateInitial {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
- TResult? Function(Diet diet)? loaded,
+ TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return initial?.call();
@@ -286,7 +289,7 @@ class _$DietStateInitialImpl implements DietStateInitial {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
- TResult Function(Diet diet)? loaded,
+ TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
@@ -385,7 +388,8 @@ class _$DietStateLoadingImpl implements DietStateLoading {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
- required TResult Function(Diet diet) loaded,
+ required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
+ loaded,
required TResult Function(String message) error,
}) {
return loading();
@@ -397,7 +401,7 @@ class _$DietStateLoadingImpl implements DietStateLoading {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
- TResult? Function(Diet diet)? loaded,
+ TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return loading?.call();
@@ -409,7 +413,7 @@ class _$DietStateLoadingImpl implements DietStateLoading {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
- TResult Function(Diet diet)? loaded,
+ TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
@@ -470,7 +474,7 @@ abstract class _$$DietStateLoadedImplCopyWith<$Res> {
$Res Function(_$DietStateLoadedImpl) then) =
__$$DietStateLoadedImplCopyWithImpl<$Res>;
@useResult
- $Res call({Diet diet});
+ $Res call({Diet diet, bool isStarted, bool hasStartedDiet});
$DietCopyWith<$Res> get diet;
}
@@ -489,12 +493,22 @@ class __$$DietStateLoadedImplCopyWithImpl<$Res>
@override
$Res call({
Object? diet = null,
+ Object? isStarted = null,
+ Object? hasStartedDiet = null,
}) {
return _then(_$DietStateLoadedImpl(
- null == diet
+ diet: null == diet
? _value.diet
: diet // ignore: cast_nullable_to_non_nullable
as Diet,
+ isStarted: null == isStarted
+ ? _value.isStarted
+ : isStarted // ignore: cast_nullable_to_non_nullable
+ as bool,
+ hasStartedDiet: null == hasStartedDiet
+ ? _value.hasStartedDiet
+ : hasStartedDiet // ignore: cast_nullable_to_non_nullable
+ as bool,
));
}
@@ -512,14 +526,23 @@ class __$$DietStateLoadedImplCopyWithImpl<$Res>
/// @nodoc
class _$DietStateLoadedImpl implements DietStateLoaded {
- const _$DietStateLoadedImpl(this.diet);
+ const _$DietStateLoadedImpl(
+ {required this.diet,
+ this.isStarted = false,
+ this.hasStartedDiet = false});
@override
final Diet diet;
+ @override
+ @JsonKey()
+ final bool isStarted;
+ @override
+ @JsonKey()
+ final bool hasStartedDiet;
@override
String toString() {
- return 'AboutDietState.loaded(diet: $diet)';
+ return 'AboutDietState.loaded(diet: $diet, isStarted: $isStarted, hasStartedDiet: $hasStartedDiet)';
}
@override
@@ -527,11 +550,15 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietStateLoadedImpl &&
- (identical(other.diet, diet) || other.diet == diet));
+ (identical(other.diet, diet) || other.diet == diet) &&
+ (identical(other.isStarted, isStarted) ||
+ other.isStarted == isStarted) &&
+ (identical(other.hasStartedDiet, hasStartedDiet) ||
+ other.hasStartedDiet == hasStartedDiet));
}
@override
- int get hashCode => Object.hash(runtimeType, diet);
+ int get hashCode => Object.hash(runtimeType, diet, isStarted, hasStartedDiet);
/// Create a copy of AboutDietState
/// with the given fields replaced by the non-null parameter values.
@@ -548,10 +575,11 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
- required TResult Function(Diet diet) loaded,
+ required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
+ loaded,
required TResult Function(String message) error,
}) {
- return loaded(diet);
+ return loaded(diet, isStarted, hasStartedDiet);
}
@override
@@ -560,10 +588,10 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
- TResult? Function(Diet diet)? loaded,
+ TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
- return loaded?.call(diet);
+ return loaded?.call(diet, isStarted, hasStartedDiet);
}
@override
@@ -572,12 +600,12 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
- TResult Function(Diet diet)? loaded,
+ TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loaded != null) {
- return loaded(diet);
+ return loaded(diet, isStarted, hasStartedDiet);
}
return orElse();
}
@@ -624,9 +652,14 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
}
abstract class DietStateLoaded implements AboutDietState {
- const factory DietStateLoaded(final Diet diet) = _$DietStateLoadedImpl;
+ const factory DietStateLoaded(
+ {required final Diet diet,
+ final bool isStarted,
+ final bool hasStartedDiet}) = _$DietStateLoadedImpl;
Diet get diet;
+ bool get isStarted;
+ bool get hasStartedDiet;
/// Create a copy of AboutDietState
/// with the given fields replaced by the non-null parameter values.
@@ -707,7 +740,8 @@ class _$DietStateErrorImpl implements DietStateError {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
- required TResult Function(Diet diet) loaded,
+ required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
+ loaded,
required TResult Function(String message) error,
}) {
return error(message);
@@ -719,7 +753,7 @@ class _$DietStateErrorImpl implements DietStateError {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
- TResult? Function(Diet diet)? loaded,
+ TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return error?.call(message);
@@ -731,7 +765,7 @@ class _$DietStateErrorImpl implements DietStateError {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
- TResult Function(Diet diet)? loaded,
+ TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart
index 8cd3fd84..1c63c8f9 100644
--- a/lib/features/diets/presentation/pages/about_diet_screen.dart
+++ b/lib/features/diets/presentation/pages/about_diet_screen.dart
@@ -12,6 +12,7 @@ import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.da
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_state.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/core/widgets/cpfc_container.dart';
+import 'package:eatvio_app/features/diets/presentation/widgets/cancel_diet_bottom_sheet.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
@@ -75,7 +76,7 @@ class _AboutDietScreenState extends State {
Widget build(BuildContext context) {
final s = S.of(context);
return BlocProvider(
- create: (context) => dietsCubit..getDietWithLoading(widget.diet.id),
+ create: (context) => dietsCubit..initDietPage(widget.diet.id),
child: BlocProvider(
create: (BuildContext context) => commentsCubit
..getComments(
@@ -130,18 +131,37 @@ class _AboutDietScreenState extends State {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
- backgroundColor: appTheme.greenA700,
+ backgroundColor: dietState.isStarted ? appTheme.transparentRed : appTheme.greenA700,
onPressed: () {
final cubit = context.read();
- showModalBottomSheet(
- context: context,
- builder: (context) => StartDietBottomSheet(
- aboutDietCubit: cubit,
- ),
- );
+
+ if (dietState.isStarted) {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => CancelDietBottomSheet(
+ aboutDietCubit: cubit,
+ dietName: dietState.diet.name,
+ ),
+ );
+ } else if (dietState.hasStartedDiet) {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => CancelDietBottomSheet(
+ aboutDietCubit: cubit,
+ title: 'Вы собираетесь начать диету ${dietState.diet.name}, завершить диету ${cubit.userStartedDietName}?',
+ ),
+ );
+ } else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => StartDietBottomSheet(
+ aboutDietCubit: cubit,
+ ),
+ );
+ }
},
label: Text(
- s.startDiet,
+ dietState.isStarted ? 'Отменить диету' : s.startDiet,
style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
),
),
diff --git a/lib/features/diets/presentation/widgets/calendar_widget.dart b/lib/features/diets/presentation/widgets/calendar_widget.dart
index bd3fdca2..3ca86847 100644
--- a/lib/features/diets/presentation/widgets/calendar_widget.dart
+++ b/lib/features/diets/presentation/widgets/calendar_widget.dart
@@ -5,8 +5,8 @@ import 'package:intl/intl.dart';
import 'package:table_calendar/table_calendar.dart';
class CalendarWidget extends StatefulWidget {
- const CalendarWidget({super.key});
-
+ const CalendarWidget({super.key, required this.startDate});
+ final List startDate;
@override
State createState() => _CalendarWidgetState();
}
@@ -56,6 +56,9 @@ class _CalendarWidgetState extends State {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
+ if (widget.startDate.isNotEmpty) {
+ widget.startDate[0] = selectedDay;
+ }
}
},
onFormatChanged: (format) {
@@ -69,6 +72,7 @@ class _CalendarWidgetState extends State {
_focusedDay = focusedDay;
},
calendarStyle: CalendarStyle(
+ outsideDaysVisible: true,
weekendTextStyle: TextStyle(
color: appTheme.gray,
),
@@ -90,6 +94,17 @@ class _CalendarWidgetState extends State {
color: appTheme.blueA400,
shape: BoxShape.circle,
),
+ outsideTextStyle: TextStyle(
+ color: appTheme.gray,
+ ),
+ outsideDecoration: BoxDecoration(
+ color: appTheme.dividerColor,
+ shape: BoxShape.circle,
+ ),
+ withinRangeDecoration: BoxDecoration(
+ color: appTheme.blueA400,
+ shape: BoxShape.circle,
+ ),
todayTextStyle: TextStyle(
color: appTheme.gray,
),
diff --git a/lib/features/diets/presentation/widgets/cancel_diet_bottom_sheet.dart b/lib/features/diets/presentation/widgets/cancel_diet_bottom_sheet.dart
new file mode 100644
index 00000000..08a2dbf2
--- /dev/null
+++ b/lib/features/diets/presentation/widgets/cancel_diet_bottom_sheet.dart
@@ -0,0 +1,79 @@
+import 'package:eatvio_app/core/utils/toasts.dart';
+import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
+import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
+import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart';
+import 'package:eatvio_app/core/theme/custom_text_style.dart';
+import 'package:eatvio_app/core/theme/theme_helper.dart';
+import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
+import 'package:flutter/material.dart';
+
+class CancelDietBottomSheet extends StatelessWidget {
+ const CancelDietBottomSheet({super.key, required this.aboutDietCubit, this.title, this.dietName});
+ final AboutDietCubit aboutDietCubit;
+ final String? title;
+ final String? dietName;
+ @override
+ Widget build(BuildContext context) {
+
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SizedBox(height: 4),
+ BottomSheetHandlerBar(),
+ SizedBox(height: 12),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 25.0),
+ child: Text(
+ title ?? 'Вы действительно хотите отменить прохождение диеты ${dietName ?? ''}?',
+ textAlign: TextAlign.center,
+ style: TextStyles.headingXLSemibold.copyWith(
+ color: appTheme.black,
+ ),
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28),
+ child: CustomOutlinedButton(
+ onPressed: () async {
+ if (title != null) {
+ final result = await aboutDietCubit.cancelDiet();
+ if (result) {
+ Navigator.pop(context);
+ showSuccessToast('предыдущая диета отменена');
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => StartDietBottomSheet(
+ aboutDietCubit: aboutDietCubit,
+ ),
+ );
+ } else {
+ showErrorToast('Не удалось отменить диету');
+ Navigator.pop(context);
+ }
+ } else {
+ final result = await aboutDietCubit.cancelDiet();
+ if (result) {
+ showSuccessToast('Диета отменена');
+ Navigator.pop(context);
+ aboutDietCubit.clearStartedDietsState();
+ } else {
+ showErrorToast('Не удалось отменить диету');
+ Navigator.pop(context);
+ }
+ }
+ },
+ height: 60,
+ text: 'Подтвердить',
+ buttonTextStyle: TextStyles.textLSemibold.copyWith(
+ color: appTheme.whiteA700,
+ ),
+ buttonStyle: ButtonStyle(
+ backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
+ side: WidgetStatePropertyAll(BorderSide.none),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart b/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart
index b7ac41d5..e54fcf9c 100644
--- a/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart
+++ b/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart
@@ -1,3 +1,4 @@
+import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
@@ -8,9 +9,16 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
-class StartDietBottomSheet extends StatelessWidget {
+class StartDietBottomSheet extends StatefulWidget {
const StartDietBottomSheet({super.key, required this.aboutDietCubit});
final AboutDietCubit aboutDietCubit;
+
+ @override
+ State createState() => _StartDietBottomSheetState();
+}
+
+class _StartDietBottomSheetState extends State {
+ final startDate = [DateTime.now()];
@override
Widget build(BuildContext context) {
final s = S.of(context);
@@ -42,11 +50,23 @@ class StartDietBottomSheet extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CustomDivider(),
),
- CalendarWidget(),
+ CalendarWidget(
+ startDate: startDate,
+ ),
Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28),
child: CustomOutlinedButton(
- onPressed: () {},
+ onPressed: () async {
+ final result = await widget.aboutDietCubit.startDiet(startDate[0]);
+ if (result) {
+ showSuccessToast('Диета успешно начата');
+ Navigator.pop(context);
+ widget.aboutDietCubit.emitCurrentStartedDietsState();
+ } else {
+ showErrorToast('Не удалось начать диету');
+ Navigator.pop(context);
+ }
+ },
height: 60,
text: s.startDiet,
buttonTextStyle: TextStyles.textLSemibold.copyWith(
diff --git a/lib/features/diets/repositories/diet_repository.dart b/lib/features/diets/repositories/diet_repository.dart
index d0a48e38..070434ed 100644
--- a/lib/features/diets/repositories/diet_repository.dart
+++ b/lib/features/diets/repositories/diet_repository.dart
@@ -49,6 +49,22 @@ class DietRepository {
}
}
+ Future startDiet(int id, String date) async {
+ try {
+ return await dietsService.startDiet(id: id, body: {'date': date});
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future cancelDiet(int id) async{
+ try {
+ return await dietsService.cancelDiet(id: id);
+ } catch (e) {
+ rethrow;
+ }
+ }
+
void emitLikesStateToFeed(LikesState state) => _dietLikeStateStreamController.add(state);
void emitFavoriteStateToFeed(FavoriteState state) => _dietFavoriteStateStreamController.add(state);
void emitCommentsStateToFeed(CommentsState state) => _dietCommentsStateStreamController.add(state);
diff --git a/lib/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart b/lib/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart
index 5c2f28fd..69c0f2ee 100644
--- a/lib/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart
+++ b/lib/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart
@@ -267,7 +267,7 @@ class PlansCubit extends Cubit {
Duration(milliseconds: 200),
() async {
try {
- await planRepository.addToFavorite(slug);
+ await planRepository.removeFromFavorite(slug);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
diff --git a/lib/features/feed/presentation/pages/feed_screen.dart b/lib/features/feed/presentation/pages/feed_screen.dart
index 4ff555a1..0dbe0b31 100644
--- a/lib/features/feed/presentation/pages/feed_screen.dart
+++ b/lib/features/feed/presentation/pages/feed_screen.dart
@@ -10,7 +10,7 @@ import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile
import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_state.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/blog_posts.dart';
-import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
+import 'package:eatvio_app/features/feed/presentation/widgets/chip_group.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/consultants.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/diets.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/plans.dart';
@@ -18,13 +18,13 @@ import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
+import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../widgets/recipes.dart';
-final bucket = PageStorageBucket();
class FeedScreen extends StatelessWidget {
const FeedScreen({super.key});
@@ -122,6 +122,7 @@ class _FeedScreenContentState extends State {
recipesScrollController.dispose();
dietsScrollController.dispose();
plansScrollController.dispose();
+ consultantsScrollController.dispose();
super.dispose();
}
@@ -179,7 +180,18 @@ class _FeedScreenContentState extends State {
floating: true,
snap: true,
scrolledUnderElevation: 0,
- bottom: _chipGroup(),
+ bottom: PreferredSize(
+ preferredSize: Size(MediaQuery.of(context).size.width, 32),
+ child: ChipGroup(
+ selectedChipIndex: _selectedChipIndex,
+ chipsLabelList: chipsLabelList,
+ setSelectedChipIndex: (index) {
+ setState(() {
+ _selectedChipIndex = index;
+ });
+ },
+ ),
+ ),
title: Row(
children: [
CustomSearchBar(
@@ -245,7 +257,7 @@ class _FeedScreenContentState extends State {
SizedBox(width: 12),
switch (_selectedChipIndex) {
1 => AddItemButton(
- onTap: () => GoRouter.of(context).pushNamed('recipeCreateScreen'),
+ onTap: () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name),
),
2 => AddItemButton(
onTap: state.profile?.role == 'consultant' ? () => GoRouter.of(context).pushNamed(CreateDietScreen.name) : null,
@@ -273,80 +285,4 @@ class _FeedScreenContentState extends State {
),
);
}
-
- _chipGroup() {
- return PreferredSize(
- preferredSize: Size(MediaQuery.of(context).size.width, 32),
- child: Align(
- alignment: Alignment.centerLeft,
- child: SingleChildScrollView(
- padding: EdgeInsets.symmetric(horizontal: 16),
- scrollDirection: Axis.horizontal,
- child: Row(
- children: [
- ChoiceChipWidget(
- index: 0,
- selectedChipIndex: _selectedChipIndex,
- chipsLabelList: chipsLabelList,
- onSelected: (bool selected) {
- setState(() {
- _selectedChipIndex = 0;
- FocusScope.of(context).requestFocus(new FocusNode());
- });
- },
- ),
- SizedBox(width: 8),
- ChoiceChipWidget(
- index: 1,
- selectedChipIndex: _selectedChipIndex,
- chipsLabelList: chipsLabelList,
- onSelected: (bool selected) {
- setState(() {
- _selectedChipIndex = 1;
- FocusScope.of(context).requestFocus(new FocusNode());
- });
- },
- ),
- SizedBox(width: 8),
- ChoiceChipWidget(
- index: 2,
- selectedChipIndex: _selectedChipIndex,
- chipsLabelList: chipsLabelList,
- onSelected: (bool selected) {
- setState(() {
- _selectedChipIndex = 2;
- FocusScope.of(context).requestFocus(new FocusNode());
- });
- },
- ),
- SizedBox(width: 8),
- ChoiceChipWidget(
- index: 3,
- selectedChipIndex: _selectedChipIndex,
- chipsLabelList: chipsLabelList,
- onSelected: (bool selected) {
- setState(() {
- _selectedChipIndex = 3;
- FocusScope.of(context).requestFocus(new FocusNode());
- });
- },
- ),
- SizedBox(width: 8),
- ChoiceChipWidget(
- index: 4,
- selectedChipIndex: _selectedChipIndex,
- chipsLabelList: chipsLabelList,
- onSelected: (bool selected) {
- setState(() {
- _selectedChipIndex = 4;
- FocusScope.of(context).requestFocus(new FocusNode());
- });
- },
- ),
- ],
- ),
- ),
- ),
- );
- }
}
diff --git a/lib/features/feed/presentation/widgets/chip_group.dart b/lib/features/feed/presentation/widgets/chip_group.dart
new file mode 100644
index 00000000..44f8b241
--- /dev/null
+++ b/lib/features/feed/presentation/widgets/chip_group.dart
@@ -0,0 +1,44 @@
+import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
+import 'package:flutter/widgets.dart';
+
+class ChipGroup extends StatelessWidget {
+ const ChipGroup({
+ super.key,
+ required this.selectedChipIndex,
+ required this.chipsLabelList,
+ required this.setSelectedChipIndex,
+ });
+ final int selectedChipIndex;
+ final List chipsLabelList;
+ final void Function(int) setSelectedChipIndex;
+
+ @override
+ Widget build(BuildContext context) {
+ return Align(
+ alignment: Alignment.centerLeft,
+ child: SingleChildScrollView(
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ scrollDirection: Axis.horizontal,
+ child: Row(
+ children: [
+ ...List.generate(
+ 5,
+ (index) => Padding(
+ padding: EdgeInsets.only(right: 8.0),
+ child: ChoiceChipWidget(
+ index: index,
+ selectedChipIndex: selectedChipIndex,
+ chipsLabelList: chipsLabelList,
+ onSelected: (bool selected) {
+ setSelectedChipIndex(index);
+ FocusScope.of(context).requestFocus(new FocusNode());
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart
index e72be541..a1102186 100644
--- a/lib/features/menu/presentation/pages/menu_screen.dart
+++ b/lib/features/menu/presentation/pages/menu_screen.dart
@@ -67,7 +67,7 @@ class MenuScreen extends StatelessWidget {
),
_divider(context),
_tile(
- leading: ImageConstant.icnSupport,
+ leading: ImageConstant.icnDevTools,
title: 'Для разработчиков',
onTap: () {
GoRouter.of(context).pushNamed(DevToolsScreen.name);
diff --git a/lib/features/profile/repositories/profile_repository.dart b/lib/features/profile/repositories/profile_repository.dart
index 21f5e7b3..5a1da306 100644
--- a/lib/features/profile/repositories/profile_repository.dart
+++ b/lib/features/profile/repositories/profile_repository.dart
@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
+import 'package:eatvio_app/features/profile/models/chosen_diets_pagination_model.dart';
import 'package:eatvio_app/features/profile/models/circumference_model.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
import 'package:eatvio_app/features/profile/models/sub_model.dart';
@@ -171,4 +172,12 @@ class ProfileRepository {
rethrow;
}
}
+
+ Future> getChosenDiets() async {
+ try {
+ return (await profileService.getChosenDiets()).diets;
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/recipes/entities/recipe_create_entity.dart b/lib/features/recipes/entities/recipe_create_entity.dart
index 43a44a55..75e7c783 100644
--- a/lib/features/recipes/entities/recipe_create_entity.dart
+++ b/lib/features/recipes/entities/recipe_create_entity.dart
@@ -1,5 +1,8 @@
import 'dart:io';
+import 'package:eatvio_app/features/recipes/models/ingredient.dart';
+import 'package:eatvio_app/features/recipes/models/recipe_model.dart';
+import 'package:eatvio_app/features/recipes/models/step.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'recipe_create_entity.freezed.dart';
@@ -11,11 +14,71 @@ enum TypeOfRecipe {
snacks,
sweets,
drinks,
- other,
+ other;
+
+ String toString() {
+ switch (this) {
+ case TypeOfRecipe.first_meals:
+ return 'Первые блюда';
+ case TypeOfRecipe.second_meals:
+ return 'Вторые блюда';
+ case TypeOfRecipe.salads:
+ return 'Салаты';
+ case TypeOfRecipe.snacks:
+ return 'Закуски';
+ case TypeOfRecipe.sweets:
+ return 'Сладости';
+ case TypeOfRecipe.drinks:
+ return 'Напитки';
+ case TypeOfRecipe.other:
+ return 'Другое';
+ }
+ }
+
+ String toApiType() {
+ switch (this) {
+ case TypeOfRecipe.first_meals:
+ return 'first_meals';
+ case TypeOfRecipe.second_meals:
+ return 'second_meals';
+ case TypeOfRecipe.salads:
+ return 'salads';
+ case TypeOfRecipe.snacks:
+ return 'snacks';
+ case TypeOfRecipe.sweets:
+ return 'sweets';
+ case TypeOfRecipe.drinks:
+ return 'drinks';
+ case TypeOfRecipe.other:
+ return 'other';
+ }
+ }
+
+ static TypeOfRecipe? fromString(String value) {
+ switch (value) {
+ case 'Первые блюда':
+ return TypeOfRecipe.first_meals;
+ case 'Вторые блюда':
+ return TypeOfRecipe.second_meals;
+ case 'Салаты':
+ return TypeOfRecipe.salads;
+ case 'Закуски':
+ return TypeOfRecipe.snacks;
+ case 'Сладости':
+ return TypeOfRecipe.sweets;
+ case 'Напитки':
+ return TypeOfRecipe.drinks;
+ case 'Другое':
+ return TypeOfRecipe.other;
+ default:
+ return null;
+ }
+ }
}
@freezed
class RecipeCreateEntity with _$RecipeCreateEntity {
+ const RecipeCreateEntity._();
const factory RecipeCreateEntity({
String? title,
TypeOfRecipe? typeOfRecipe,
@@ -26,19 +89,25 @@ class RecipeCreateEntity with _$RecipeCreateEntity {
String? description,
File? image,
String? videoUrl,
- required List ingredients,
+ required List ingredients,
required List steps,
required List tags, // tags for search
}) = _RecipeCreateEntity;
-}
-@freezed
-class IngredientEntity with _$IngredientEntity {
- const factory IngredientEntity({
- required String name,
- required String unit,
- required String portion,
- }) = _IngredientEntity;
+ RecipeModel toModel() => RecipeModel(
+ title: title,
+ type: typeOfRecipe?.toApiType(),
+ servings: countOfPortions,
+ energyPerServing: energyInOnePortion?.toInt(),
+ totalCookingTime: totalCookTime?.split(':').fold(0, (value, element) => (int.tryParse(element) ?? 0)),
+ activeCookTime: activeCookTime?.split(':').fold(0, (value, element) => (int.tryParse(element) ?? 0)),
+ description: description,
+ image: image?.path,
+ video_url: videoUrl,
+ ingredients: ingredients,
+ steps: steps.map((step) => Step(image: step.image?.path ?? '', description: step.description ?? '')).toList(),
+ tags: tags,
+ );
}
@freezed
diff --git a/lib/features/recipes/entities/recipe_create_entity.freezed.dart b/lib/features/recipes/entities/recipe_create_entity.freezed.dart
index e42db8f4..aabda874 100644
--- a/lib/features/recipes/entities/recipe_create_entity.freezed.dart
+++ b/lib/features/recipes/entities/recipe_create_entity.freezed.dart
@@ -25,7 +25,7 @@ mixin _$RecipeCreateEntity {
String? get description => throw _privateConstructorUsedError;
File? get image => throw _privateConstructorUsedError;
String? get videoUrl => throw _privateConstructorUsedError;
- List get ingredients => throw _privateConstructorUsedError;
+ List get ingredients => throw _privateConstructorUsedError;
List get steps => throw _privateConstructorUsedError;
List get tags => throw _privateConstructorUsedError;
@@ -52,7 +52,7 @@ abstract class $RecipeCreateEntityCopyWith<$Res> {
String? description,
File? image,
String? videoUrl,
- List ingredients,
+ List ingredients,
List steps,
List tags});
}
@@ -125,7 +125,7 @@ class _$RecipeCreateEntityCopyWithImpl<$Res, $Val extends RecipeCreateEntity>
ingredients: null == ingredients
? _value.ingredients
: ingredients // ignore: cast_nullable_to_non_nullable
- as List,
+ as List,
steps: null == steps
? _value.steps
: steps // ignore: cast_nullable_to_non_nullable
@@ -156,7 +156,7 @@ abstract class _$$RecipeCreateEntityImplCopyWith<$Res>
String? description,
File? image,
String? videoUrl,
- List ingredients,
+ List ingredients,
List steps,
List tags});
}
@@ -227,7 +227,7 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
ingredients: null == ingredients
? _value._ingredients
: ingredients // ignore: cast_nullable_to_non_nullable
- as List,
+ as List,
steps: null == steps
? _value._steps
: steps // ignore: cast_nullable_to_non_nullable
@@ -242,7 +242,7 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
/// @nodoc
-class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
+class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
const _$RecipeCreateEntityImpl(
{this.title,
this.typeOfRecipe,
@@ -253,12 +253,13 @@ class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
this.description,
this.image,
this.videoUrl,
- required final List ingredients,
+ required final List ingredients,
required final List steps,
required final List tags})
: _ingredients = ingredients,
_steps = steps,
- _tags = tags;
+ _tags = tags,
+ super._();
@override
final String? title;
@@ -280,9 +281,9 @@ class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
final File? image;
@override
final String? videoUrl;
- final List _ingredients;
+ final List _ingredients;
@override
- List get ingredients {
+ List get ingredients {
if (_ingredients is EqualUnmodifiableListView) return _ingredients;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_ingredients);
@@ -362,7 +363,7 @@ class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
this, _$identity);
}
-abstract class _RecipeCreateEntity implements RecipeCreateEntity {
+abstract class _RecipeCreateEntity extends RecipeCreateEntity {
const factory _RecipeCreateEntity(
{final String? title,
final TypeOfRecipe? typeOfRecipe,
@@ -373,9 +374,10 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
final String? description,
final File? image,
final String? videoUrl,
- required final List ingredients,
+ required final List ingredients,
required final List steps,
required final List tags}) = _$RecipeCreateEntityImpl;
+ const _RecipeCreateEntity._() : super._();
@override
String? get title;
@@ -396,7 +398,7 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
@override
String? get videoUrl;
@override
- List get ingredients;
+ List get ingredients;
@override
List get steps;
@override
@@ -410,171 +412,6 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
throw _privateConstructorUsedError;
}
-/// @nodoc
-mixin _$IngredientEntity {
- String get name => throw _privateConstructorUsedError;
- String get unit => throw _privateConstructorUsedError;
- String get portion => throw _privateConstructorUsedError;
-
- /// Create a copy of IngredientEntity
- /// with the given fields replaced by the non-null parameter values.
- @JsonKey(includeFromJson: false, includeToJson: false)
- $IngredientEntityCopyWith get copyWith =>
- throw _privateConstructorUsedError;
-}
-
-/// @nodoc
-abstract class $IngredientEntityCopyWith<$Res> {
- factory $IngredientEntityCopyWith(
- IngredientEntity value, $Res Function(IngredientEntity) then) =
- _$IngredientEntityCopyWithImpl<$Res, IngredientEntity>;
- @useResult
- $Res call({String name, String unit, String portion});
-}
-
-/// @nodoc
-class _$IngredientEntityCopyWithImpl<$Res, $Val extends IngredientEntity>
- implements $IngredientEntityCopyWith<$Res> {
- _$IngredientEntityCopyWithImpl(this._value, this._then);
-
- // ignore: unused_field
- final $Val _value;
- // ignore: unused_field
- final $Res Function($Val) _then;
-
- /// Create a copy of IngredientEntity
- /// with the given fields replaced by the non-null parameter values.
- @pragma('vm:prefer-inline')
- @override
- $Res call({
- Object? name = null,
- Object? unit = null,
- Object? portion = null,
- }) {
- return _then(_value.copyWith(
- name: null == name
- ? _value.name
- : name // ignore: cast_nullable_to_non_nullable
- as String,
- unit: null == unit
- ? _value.unit
- : unit // ignore: cast_nullable_to_non_nullable
- as String,
- portion: null == portion
- ? _value.portion
- : portion // ignore: cast_nullable_to_non_nullable
- as String,
- ) as $Val);
- }
-}
-
-/// @nodoc
-abstract class _$$IngredientEntityImplCopyWith<$Res>
- implements $IngredientEntityCopyWith<$Res> {
- factory _$$IngredientEntityImplCopyWith(_$IngredientEntityImpl value,
- $Res Function(_$IngredientEntityImpl) then) =
- __$$IngredientEntityImplCopyWithImpl<$Res>;
- @override
- @useResult
- $Res call({String name, String unit, String portion});
-}
-
-/// @nodoc
-class __$$IngredientEntityImplCopyWithImpl<$Res>
- extends _$IngredientEntityCopyWithImpl<$Res, _$IngredientEntityImpl>
- implements _$$IngredientEntityImplCopyWith<$Res> {
- __$$IngredientEntityImplCopyWithImpl(_$IngredientEntityImpl _value,
- $Res Function(_$IngredientEntityImpl) _then)
- : super(_value, _then);
-
- /// Create a copy of IngredientEntity
- /// with the given fields replaced by the non-null parameter values.
- @pragma('vm:prefer-inline')
- @override
- $Res call({
- Object? name = null,
- Object? unit = null,
- Object? portion = null,
- }) {
- return _then(_$IngredientEntityImpl(
- name: null == name
- ? _value.name
- : name // ignore: cast_nullable_to_non_nullable
- as String,
- unit: null == unit
- ? _value.unit
- : unit // ignore: cast_nullable_to_non_nullable
- as String,
- portion: null == portion
- ? _value.portion
- : portion // ignore: cast_nullable_to_non_nullable
- as String,
- ));
- }
-}
-
-/// @nodoc
-
-class _$IngredientEntityImpl implements _IngredientEntity {
- const _$IngredientEntityImpl(
- {required this.name, required this.unit, required this.portion});
-
- @override
- final String name;
- @override
- final String unit;
- @override
- final String portion;
-
- @override
- String toString() {
- return 'IngredientEntity(name: $name, unit: $unit, portion: $portion)';
- }
-
- @override
- bool operator ==(Object other) {
- return identical(this, other) ||
- (other.runtimeType == runtimeType &&
- other is _$IngredientEntityImpl &&
- (identical(other.name, name) || other.name == name) &&
- (identical(other.unit, unit) || other.unit == unit) &&
- (identical(other.portion, portion) || other.portion == portion));
- }
-
- @override
- int get hashCode => Object.hash(runtimeType, name, unit, portion);
-
- /// Create a copy of IngredientEntity
- /// with the given fields replaced by the non-null parameter values.
- @JsonKey(includeFromJson: false, includeToJson: false)
- @override
- @pragma('vm:prefer-inline')
- _$$IngredientEntityImplCopyWith<_$IngredientEntityImpl> get copyWith =>
- __$$IngredientEntityImplCopyWithImpl<_$IngredientEntityImpl>(
- this, _$identity);
-}
-
-abstract class _IngredientEntity implements IngredientEntity {
- const factory _IngredientEntity(
- {required final String name,
- required final String unit,
- required final String portion}) = _$IngredientEntityImpl;
-
- @override
- String get name;
- @override
- String get unit;
- @override
- String get portion;
-
- /// Create a copy of IngredientEntity
- /// with the given fields replaced by the non-null parameter values.
- @override
- @JsonKey(includeFromJson: false, includeToJson: false)
- _$$IngredientEntityImplCopyWith<_$IngredientEntityImpl> get copyWith =>
- throw _privateConstructorUsedError;
-}
-
/// @nodoc
mixin _$StepEntity {
String? get description => throw _privateConstructorUsedError;
diff --git a/lib/features/recipes/models/ingredient.dart b/lib/features/recipes/models/ingredient.dart
index d7a130e4..480f358a 100644
--- a/lib/features/recipes/models/ingredient.dart
+++ b/lib/features/recipes/models/ingredient.dart
@@ -13,4 +13,4 @@ class Ingredient with _$Ingredient {
}) = _Ingredient;
factory Ingredient.fromJson(Map json) => _$IngredientFromJson(json);
-}
+}
\ No newline at end of file
diff --git a/lib/features/recipes/models/recipe_model.dart b/lib/features/recipes/models/recipe_model.dart
index 04add902..588650dd 100644
--- a/lib/features/recipes/models/recipe_model.dart
+++ b/lib/features/recipes/models/recipe_model.dart
@@ -11,13 +11,17 @@ class RecipeModel with _$RecipeModel {
const factory RecipeModel({
List? ingredients,
List? steps,
+ List? tags,
String? type,
String? state,
String? title,
String? description,
String? video_url,
String? image,
- int? cooking_time,
+ int? totalCookingTime,
+ int? activeCookTime,
+ int? servings,
+ int? energyPerServing,
}) = _RecipeModel;
factory RecipeModel.fromJson(Map json) => _$RecipeModelFromJson(json);
diff --git a/lib/features/recipes/models/recipe_model.freezed.dart b/lib/features/recipes/models/recipe_model.freezed.dart
index 691ac1f3..7b549e53 100644
--- a/lib/features/recipes/models/recipe_model.freezed.dart
+++ b/lib/features/recipes/models/recipe_model.freezed.dart
@@ -22,13 +22,17 @@ RecipeModel _$RecipeModelFromJson(Map json) {
mixin _$RecipeModel {
List? get ingredients => throw _privateConstructorUsedError;
List? get steps => throw _privateConstructorUsedError;
+ List? get tags => throw _privateConstructorUsedError;
String? get type => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
String? get title => throw _privateConstructorUsedError;
String? get description => throw _privateConstructorUsedError;
String? get video_url => throw _privateConstructorUsedError;
String? get image => throw _privateConstructorUsedError;
- int? get cooking_time => throw _privateConstructorUsedError;
+ int? get totalCookingTime => throw _privateConstructorUsedError;
+ int? get activeCookTime => throw _privateConstructorUsedError;
+ int? get servings => throw _privateConstructorUsedError;
+ int? get energyPerServing => throw _privateConstructorUsedError;
/// Serializes this RecipeModel to a JSON map.
Map toJson() => throw _privateConstructorUsedError;
@@ -49,13 +53,17 @@ abstract class $RecipeModelCopyWith<$Res> {
$Res call(
{List? ingredients,
List? steps,
+ List? tags,
String? type,
String? state,
String? title,
String? description,
String? video_url,
String? image,
- int? cooking_time});
+ int? totalCookingTime,
+ int? activeCookTime,
+ int? servings,
+ int? energyPerServing});
}
/// @nodoc
@@ -75,13 +83,17 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
$Res call({
Object? ingredients = freezed,
Object? steps = freezed,
+ Object? tags = freezed,
Object? type = freezed,
Object? state = freezed,
Object? title = freezed,
Object? description = freezed,
Object? video_url = freezed,
Object? image = freezed,
- Object? cooking_time = freezed,
+ Object? totalCookingTime = freezed,
+ Object? activeCookTime = freezed,
+ Object? servings = freezed,
+ Object? energyPerServing = freezed,
}) {
return _then(_value.copyWith(
ingredients: freezed == ingredients
@@ -92,6 +104,10 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
? _value.steps
: steps // ignore: cast_nullable_to_non_nullable
as List?,
+ tags: freezed == tags
+ ? _value.tags
+ : tags // ignore: cast_nullable_to_non_nullable
+ as List?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
@@ -116,9 +132,21 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as String?,
- cooking_time: freezed == cooking_time
- ? _value.cooking_time
- : cooking_time // ignore: cast_nullable_to_non_nullable
+ totalCookingTime: freezed == totalCookingTime
+ ? _value.totalCookingTime
+ : totalCookingTime // ignore: cast_nullable_to_non_nullable
+ as int?,
+ activeCookTime: freezed == activeCookTime
+ ? _value.activeCookTime
+ : activeCookTime // ignore: cast_nullable_to_non_nullable
+ as int?,
+ servings: freezed == servings
+ ? _value.servings
+ : servings // ignore: cast_nullable_to_non_nullable
+ as int?,
+ energyPerServing: freezed == energyPerServing
+ ? _value.energyPerServing
+ : energyPerServing // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
@@ -135,13 +163,17 @@ abstract class _$$RecipeModelImplCopyWith<$Res>
$Res call(
{List? ingredients,
List? steps,
+ List? tags,
String? type,
String? state,
String? title,
String? description,
String? video_url,
String? image,
- int? cooking_time});
+ int? totalCookingTime,
+ int? activeCookTime,
+ int? servings,
+ int? energyPerServing});
}
/// @nodoc
@@ -159,13 +191,17 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
$Res call({
Object? ingredients = freezed,
Object? steps = freezed,
+ Object? tags = freezed,
Object? type = freezed,
Object? state = freezed,
Object? title = freezed,
Object? description = freezed,
Object? video_url = freezed,
Object? image = freezed,
- Object? cooking_time = freezed,
+ Object? totalCookingTime = freezed,
+ Object? activeCookTime = freezed,
+ Object? servings = freezed,
+ Object? energyPerServing = freezed,
}) {
return _then(_$RecipeModelImpl(
ingredients: freezed == ingredients
@@ -176,6 +212,10 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
? _value._steps
: steps // ignore: cast_nullable_to_non_nullable
as List?,
+ tags: freezed == tags
+ ? _value._tags
+ : tags // ignore: cast_nullable_to_non_nullable
+ as List?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
@@ -200,9 +240,21 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as String?,
- cooking_time: freezed == cooking_time
- ? _value.cooking_time
- : cooking_time // ignore: cast_nullable_to_non_nullable
+ totalCookingTime: freezed == totalCookingTime
+ ? _value.totalCookingTime
+ : totalCookingTime // ignore: cast_nullable_to_non_nullable
+ as int?,
+ activeCookTime: freezed == activeCookTime
+ ? _value.activeCookTime
+ : activeCookTime // ignore: cast_nullable_to_non_nullable
+ as int?,
+ servings: freezed == servings
+ ? _value.servings
+ : servings // ignore: cast_nullable_to_non_nullable
+ as int?,
+ energyPerServing: freezed == energyPerServing
+ ? _value.energyPerServing
+ : energyPerServing // ignore: cast_nullable_to_non_nullable
as int?,
));
}
@@ -214,15 +266,20 @@ class _$RecipeModelImpl implements _RecipeModel {
const _$RecipeModelImpl(
{final List? ingredients,
final List? steps,
+ final List? tags,
this.type,
this.state,
this.title,
this.description,
this.video_url,
this.image,
- this.cooking_time})
+ this.totalCookingTime,
+ this.activeCookTime,
+ this.servings,
+ this.energyPerServing})
: _ingredients = ingredients,
- _steps = steps;
+ _steps = steps,
+ _tags = tags;
factory _$RecipeModelImpl.fromJson(Map json) =>
_$$RecipeModelImplFromJson(json);
@@ -247,6 +304,16 @@ class _$RecipeModelImpl implements _RecipeModel {
return EqualUnmodifiableListView(value);
}
+ final List? _tags;
+ @override
+ List? get tags {
+ final value = _tags;
+ if (value == null) return null;
+ if (_tags is EqualUnmodifiableListView) return _tags;
+ // ignore: implicit_dynamic_type
+ return EqualUnmodifiableListView(value);
+ }
+
@override
final String? type;
@override
@@ -260,11 +327,17 @@ class _$RecipeModelImpl implements _RecipeModel {
@override
final String? image;
@override
- final int? cooking_time;
+ final int? totalCookingTime;
+ @override
+ final int? activeCookTime;
+ @override
+ final int? servings;
+ @override
+ final int? energyPerServing;
@override
String toString() {
- return 'RecipeModel(ingredients: $ingredients, steps: $steps, type: $type, state: $state, title: $title, description: $description, video_url: $video_url, image: $image, cooking_time: $cooking_time)';
+ return 'RecipeModel(ingredients: $ingredients, steps: $steps, tags: $tags, type: $type, state: $state, title: $title, description: $description, video_url: $video_url, image: $image, totalCookingTime: $totalCookingTime, activeCookTime: $activeCookTime, servings: $servings, energyPerServing: $energyPerServing)';
}
@override
@@ -275,6 +348,7 @@ class _$RecipeModelImpl implements _RecipeModel {
const DeepCollectionEquality()
.equals(other._ingredients, _ingredients) &&
const DeepCollectionEquality().equals(other._steps, _steps) &&
+ const DeepCollectionEquality().equals(other._tags, _tags) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.title, title) || other.title == title) &&
@@ -283,8 +357,14 @@ class _$RecipeModelImpl implements _RecipeModel {
(identical(other.video_url, video_url) ||
other.video_url == video_url) &&
(identical(other.image, image) || other.image == image) &&
- (identical(other.cooking_time, cooking_time) ||
- other.cooking_time == cooking_time));
+ (identical(other.totalCookingTime, totalCookingTime) ||
+ other.totalCookingTime == totalCookingTime) &&
+ (identical(other.activeCookTime, activeCookTime) ||
+ other.activeCookTime == activeCookTime) &&
+ (identical(other.servings, servings) ||
+ other.servings == servings) &&
+ (identical(other.energyPerServing, energyPerServing) ||
+ other.energyPerServing == energyPerServing));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -293,13 +373,17 @@ class _$RecipeModelImpl implements _RecipeModel {
runtimeType,
const DeepCollectionEquality().hash(_ingredients),
const DeepCollectionEquality().hash(_steps),
+ const DeepCollectionEquality().hash(_tags),
type,
state,
title,
description,
video_url,
image,
- cooking_time);
+ totalCookingTime,
+ activeCookTime,
+ servings,
+ energyPerServing);
/// Create a copy of RecipeModel
/// with the given fields replaced by the non-null parameter values.
@@ -321,13 +405,17 @@ abstract class _RecipeModel implements RecipeModel {
const factory _RecipeModel(
{final List? ingredients,
final List? steps,
+ final List? tags,
final String? type,
final String? state,
final String? title,
final String? description,
final String? video_url,
final String? image,
- final int? cooking_time}) = _$RecipeModelImpl;
+ final int? totalCookingTime,
+ final int? activeCookTime,
+ final int? servings,
+ final int? energyPerServing}) = _$RecipeModelImpl;
factory _RecipeModel.fromJson(Map json) =
_$RecipeModelImpl.fromJson;
@@ -337,6 +425,8 @@ abstract class _RecipeModel implements RecipeModel {
@override
List? get steps;
@override
+ List? get tags;
+ @override
String? get type;
@override
String? get state;
@@ -349,7 +439,13 @@ abstract class _RecipeModel implements RecipeModel {
@override
String? get image;
@override
- int? get cooking_time;
+ int? get totalCookingTime;
+ @override
+ int? get activeCookTime;
+ @override
+ int? get servings;
+ @override
+ int? get energyPerServing;
/// Create a copy of RecipeModel
/// with the given fields replaced by the non-null parameter values.
diff --git a/lib/features/recipes/models/recipe_model.g.dart b/lib/features/recipes/models/recipe_model.g.dart
index d5ab9c96..478765ae 100644
--- a/lib/features/recipes/models/recipe_model.g.dart
+++ b/lib/features/recipes/models/recipe_model.g.dart
@@ -16,24 +16,32 @@ _$RecipeModelImpl _$$RecipeModelImplFromJson(Map json) =>
?.map((e) =>
e == null ? null : Step.fromJson(e as Map))
.toList(),
+ tags: (json['tags'] as List?)?.map((e) => e as String).toList(),
type: json['type'] as String?,
state: json['state'] as String?,
title: json['title'] as String?,
description: json['description'] as String?,
video_url: json['video_url'] as String?,
image: json['image'] as String?,
- cooking_time: (json['cooking_time'] as num?)?.toInt(),
+ totalCookingTime: (json['totalCookingTime'] as num?)?.toInt(),
+ activeCookTime: (json['activeCookTime'] as num?)?.toInt(),
+ servings: (json['servings'] as num?)?.toInt(),
+ energyPerServing: (json['energyPerServing'] as num?)?.toInt(),
);
Map _$$RecipeModelImplToJson(_$RecipeModelImpl instance) =>
{
'ingredients': instance.ingredients,
'steps': instance.steps,
+ 'tags': instance.tags,
'type': instance.type,
'state': instance.state,
'title': instance.title,
'description': instance.description,
'video_url': instance.video_url,
'image': instance.image,
- 'cooking_time': instance.cooking_time,
+ 'totalCookingTime': instance.totalCookingTime,
+ 'activeCookTime': instance.activeCookTime,
+ 'servings': instance.servings,
+ 'energyPerServing': instance.energyPerServing,
};
diff --git a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart b/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart
index 03253f26..78112360 100644
--- a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart
+++ b/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart
@@ -1,14 +1,18 @@
import 'dart:io';
import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.dart';
+import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
+import 'package:eatvio_app/features/recipes/reporitories/recipe_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CreateRecipeCubit extends Cubit {
- CreateRecipeCubit()
+ final RecipeRepository recipeRepository;
+ CreateRecipeCubit({required this.recipeRepository})
: super(
CreateRecipeState(
RecipeCreateEntity(
+ typeOfRecipe: TypeOfRecipe.first_meals,
ingredients: [],
steps: [],
tags: [],
@@ -38,7 +42,7 @@ class CreateRecipeCubit extends Cubit {
);
}
- addIngredient(IngredientEntity entity) {
+ addIngredient(Ingredient entity) {
emit(
CreateRecipeState(
state.entity.copyWith(
@@ -52,7 +56,7 @@ class CreateRecipeCubit extends Cubit {
}
removeIngredient(int index) {
- List ingredients = [...state.entity.ingredients];
+ List ingredients = [...state.entity.ingredients];
ingredients.removeAt(index);
emit(
@@ -65,8 +69,8 @@ class CreateRecipeCubit extends Cubit {
}
replaceIngredientPosition(int oldIndex, int newIndex) {
- List ingredients = [...state.entity.ingredients];
- final IngredientEntity item = ingredients.removeAt(oldIndex);
+ List ingredients = [...state.entity.ingredients];
+ final item = ingredients.removeAt(oldIndex);
ingredients.insert(newIndex, item);
emit(
CreateRecipeState(
@@ -216,4 +220,12 @@ class CreateRecipeCubit extends Cubit {
),
);
}
+
+ Future publishRecipe() async {
+ try {
+ await recipeRepository.publishRecipe(state.entity.toModel());
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/recipes/presentation/pages/recipe_create_screen.dart b/lib/features/recipes/presentation/pages/recipe_create_screen.dart
index efeace26..78da5627 100644
--- a/lib/features/recipes/presentation/pages/recipe_create_screen.dart
+++ b/lib/features/recipes/presentation/pages/recipe_create_screen.dart
@@ -10,6 +10,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class RecipeCreateScreen extends StatefulWidget {
const RecipeCreateScreen({super.key});
+ static const name = 'recipeCreateScreen';
+ static const path = '/recipe-create-screen';
+
@override
State createState() => _RecipeCreateScreenState();
}
@@ -39,28 +42,34 @@ class _RecipeCreateScreenState extends State {
InfoAboutRecipeWidget(),
IngredientsWidget(),
StepsWidget(),
- InkWell(
- onTap: () {},
- borderRadius: BorderRadius.circular(20),
- child: Ink(
- width: MediaQuery.of(context).size.width - 32,
- decoration: BoxDecoration(
- color: appTheme.blueA400,
+ Builder(
+ builder: (context) {
+ return InkWell(
+ onTap: () {
+ context.read().publishRecipe();
+ },
borderRadius: BorderRadius.circular(20),
- ),
- child: Container(
- padding: const EdgeInsets.all(16),
- alignment: Alignment.center,
- child: Text(
- 'Опубликовать',
- style: TextStyle(
- fontSize: 16,
- color: Colors.white,
- fontWeight: FontWeight.w600,
+ child: Ink(
+ width: MediaQuery.of(context).size.width - 32,
+ decoration: BoxDecoration(
+ color: appTheme.blueA400,
+ borderRadius: BorderRadius.circular(20),
+ ),
+ child: Container(
+ padding: const EdgeInsets.all(16),
+ alignment: Alignment.center,
+ child: Text(
+ 'Опубликовать',
+ style: TextStyle(
+ fontSize: 16,
+ color: Colors.white,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
),
),
- ),
- ),
+ );
+ },
),
],
),
diff --git a/lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart b/lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart
index 473cc04a..44ef152e 100644
--- a/lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart
+++ b/lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart
@@ -18,14 +18,7 @@ class InfoAboutRecipeWidget extends StatefulWidget {
}
class _InfoAboutRecipeWidgetState extends State {
- final List items = const [
- SelectionPopupModel(title: 'Первые Блюда'),
- SelectionPopupModel(title: 'Вторые Блюда'),
- SelectionPopupModel(title: 'Салаты'),
- SelectionPopupModel(title: 'Сладости'),
- SelectionPopupModel(title: 'Напитки'),
- SelectionPopupModel(title: 'Прочее'),
- ];
+ final List items = TypeOfRecipe.values.map((e) => SelectionPopupModel(title: e.toString())).toList();
late SelectionPopupModel selectedType;
late final CreateRecipeCubit createRecipeCubit;
@@ -64,14 +57,7 @@ class _InfoAboutRecipeWidgetState extends State {
selectedType = newSelectedType;
});
createRecipeCubit.changeType(
- switch (selectedType.title) {
- 'Первые Блюда' => TypeOfRecipe.first_meals,
- 'Вторые Блюда' => TypeOfRecipe.second_meals,
- 'Салаты' => TypeOfRecipe.salads,
- 'Сладости' => TypeOfRecipe.sweets,
- 'Напитки' => TypeOfRecipe.drinks,
- _ => TypeOfRecipe.other,
- },
+ TypeOfRecipe.fromString(selectedType.title) ?? TypeOfRecipe.other,
);
},
),
diff --git a/lib/features/recipes/presentation/widgets/ingredients_widget.dart b/lib/features/recipes/presentation/widgets/ingredients_widget.dart
index e1ac4748..5f66ed87 100644
--- a/lib/features/recipes/presentation/widgets/ingredients_widget.dart
+++ b/lib/features/recipes/presentation/widgets/ingredients_widget.dart
@@ -1,6 +1,6 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
-import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.dart';
+import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
import 'package:flutter/material.dart';
@@ -161,7 +161,7 @@ class _IngredientsWidgetState extends State {
onTap: () {
if (nameController.text.isNotEmpty && (pcsController.text.isNotEmpty || gramsController.text.isNotEmpty)) {
createRecipeCubit.addIngredient(
- IngredientEntity(
+ Ingredient(
name: nameController.text,
unit: gramsController.text.isEmpty ? pcsController.text : gramsController.text,
portion: gramsController.text.isEmpty ? 'pcs' : 'gr',
diff --git a/lib/features/recipes/reporitories/recipe_repository.dart b/lib/features/recipes/reporitories/recipe_repository.dart
index bea4d06d..b9c49aef 100644
--- a/lib/features/recipes/reporitories/recipe_repository.dart
+++ b/lib/features/recipes/reporitories/recipe_repository.dart
@@ -19,11 +19,14 @@ class RecipeRepository {
}
}
- Future publishRecipe(RecipeModel recipe) async {
+ Future publishRecipe(RecipeModel recipe) async {
try {
- final File file = File(recipe.image ?? '');
+ if (recipe.image == null || (recipe.image?.isEmpty ?? true)) {
+ return null;
+ }
+ final file = File(recipe.image ?? '');
- List