forked from eatvio/eatvio_app
Merge pull request 'new test cases' (#218) from misha/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#218
This commit is contained in:
6
assets/images/icn_dev_tools.svg
Normal file
6
assets/images/icn_dev_tools.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M114.2 27.1C89.3 30.4 64.8 44.2 48.8 64C40.4 74.3 32.9 89.1 29.2 102.5L26.5 112.5V256V399.5L29.2 409.5C40.3 449.6 72.4 478.4 112.7 484.5C127.3 486.7 384.7 486.7 399.3 484.5C439.6 478.4 471.7 449.6 482.8 409.5L485.5 399.5V256V112.5L482.8 102.5C472.8 66.5 445.5 39.1 409.5 29.3L399.5 26.5L260 26.4C183.3 26.3 117.7 26.6 114.2 27.1ZM401.5 61.4C426 68.7 443.8 86.7 450.7 111C452.4 117.2 452.5 124.8 452.5 256C452.5 387.2 452.4 394.8 450.7 401C443.8 425.5 425.5 443.8 401 450.7C394.8 452.4 387.2 452.5 256 452.5C124.8 452.5 117.2 452.4 111 450.7C86.8 443.8 68.8 426 61.4 401.5C59.7 395.9 59.5 387.3 59.2 259.5C58.9 111.4 58.8 115.9 64.5 101.9C71 85.7 85.7 71 101.9 64.5C115.5 58.9 110.1 59.1 258.5 59.3C387.4 59.5 395.9 59.7 401.5 61.4Z" fill="#2F80ED"/>
|
||||
<path d="M273.5 164.9C271.9 165.9 269.7 167.6 268.7 168.7C266 171.7 214 328.6 214 333.6C214 341.7 221.6 349 230.2 349C235.6 349 239.3 347.4 242.8 343.7C245.9 340.5 298 184.3 298 178.4C298 167 283.2 158.8 273.5 164.9Z" fill="#2F80ED"/>
|
||||
<path d="M173.6 189.5C170.6 190.5 162.8 197.7 142.2 218.2C127.2 233.2 114.2 246.7 113.4 248.2C111.6 251.7 111.6 260.3 113.4 263.8C114.2 265.3 127.4 279 142.7 294.2C173.2 324.6 174 325.1 183.2 323.1C188.6 321.9 194.4 316 195.4 310.7C197 302.3 195.5 300.2 172.7 277.2L151.6 256L172.7 234.7C195.5 211.9 197 209.6 195.4 201.4C194.7 197.8 190.3 191.7 187.1 190.1C183.8 188.3 177.5 188.1 173.6 189.5Z" fill="#2F80ED"/>
|
||||
<path d="M328 189C323.5 190.1 317.5 196.5 316.6 201.3C315 209.7 316.5 211.8 339.3 234.7L360.4 256L339.3 277.2C321.3 295.3 318 299.1 317 302.8C313.7 315.3 324.7 326.3 337.3 322.9C340.9 322 345.5 317.9 369.3 294.2C384.6 279 397.8 265.3 398.6 263.8C399.4 262.3 400 258.8 400 256C400 253.2 399.4 249.7 398.6 248.2C397.8 246.7 384.6 233.1 369.3 217.8C338.2 186.9 337.9 186.7 328 189Z" fill="#2F80ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -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(
|
||||
|
||||
@@ -91,14 +91,14 @@ Future<void> init() async {
|
||||
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
|
||||
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
||||
sl.registerFactory<DiaryCubit>(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl()));
|
||||
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
|
||||
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
|
||||
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));
|
||||
sl.registerFactory<CreateDietCubit>(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit());
|
||||
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
|
||||
sl.registerFactory<AboutPlanCubit>(() => AboutPlanCubit(planRepository: sl()));
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit());
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit(recipeRepository: sl()));
|
||||
sl.registerFactory<MyDietsCubit>(() => MyDietsCubit(myDietsRepository: sl()));
|
||||
sl.registerFactory<MyPlansCubit>(() => MyPlansCubit(myPlansRepository: sl()));
|
||||
sl.registerFactory<UserDetailsCubit>(() => UserDetailsCubit(profileRepository: sl()));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -154,7 +154,17 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
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,
|
||||
|
||||
@@ -25,7 +25,7 @@ class DevToolsScreenContent extends StatefulWidget {
|
||||
|
||||
class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
String baseUrlForTest = '';
|
||||
final List<TestCases> testCases = [Test850task()];
|
||||
final List<TestCases> testCases = [Test850task(), GetChatRoomPreviews(), GetChatRoom(), SendMessage(), GetChatMessages(), GetChatRoomIdByUserSlug()];
|
||||
final controller = TextEditingController();
|
||||
late final List<TestState> testsStates;
|
||||
|
||||
|
||||
@@ -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<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
// get all of needed services
|
||||
final dio = sl<Dio>();
|
||||
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<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
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<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
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<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
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<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AboutDietState> {
|
||||
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<void> getDiet(int id, {int? index}) async {
|
||||
try {
|
||||
@@ -16,17 +23,30 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
||||
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<void> getDietWithLoading(int id) async {
|
||||
Future<void> 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<AboutDietState> {
|
||||
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<AboutDietState> {
|
||||
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<AboutDietState> {
|
||||
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<AboutDietState> {
|
||||
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<AboutDietState> {
|
||||
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<AboutDietState> {
|
||||
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<AboutDietState> {
|
||||
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<bool> 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<bool> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}) {
|
||||
|
||||
@@ -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<AboutDietScreen> {
|
||||
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<CommentsCubit>(
|
||||
create: (BuildContext context) => commentsCubit
|
||||
..getComments(
|
||||
@@ -130,18 +131,37 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
backgroundColor: appTheme.greenA700,
|
||||
backgroundColor: dietState.isStarted ? appTheme.transparentRed : appTheme.greenA700,
|
||||
onPressed: () {
|
||||
final cubit = context.read<AboutDietCubit>();
|
||||
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),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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<DateTime> startDate;
|
||||
@override
|
||||
State<CalendarWidget> createState() => _CalendarWidgetState();
|
||||
}
|
||||
@@ -56,6 +56,9 @@ class _CalendarWidgetState extends State<CalendarWidget> {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay;
|
||||
});
|
||||
if (widget.startDate.isNotEmpty) {
|
||||
widget.startDate[0] = selectedDay;
|
||||
}
|
||||
}
|
||||
},
|
||||
onFormatChanged: (format) {
|
||||
@@ -69,6 +72,7 @@ class _CalendarWidgetState extends State<CalendarWidget> {
|
||||
_focusedDay = focusedDay;
|
||||
},
|
||||
calendarStyle: CalendarStyle(
|
||||
outsideDaysVisible: true,
|
||||
weekendTextStyle: TextStyle(
|
||||
color: appTheme.gray,
|
||||
),
|
||||
@@ -90,6 +94,17 @@ class _CalendarWidgetState extends State<CalendarWidget> {
|
||||
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,
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<StartDietBottomSheet> createState() => _StartDietBottomSheetState();
|
||||
}
|
||||
|
||||
class _StartDietBottomSheetState extends State<StartDietBottomSheet> {
|
||||
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(
|
||||
|
||||
@@ -49,6 +49,22 @@ class DietRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startDiet(int id, String date) async {
|
||||
try {
|
||||
return await dietsService.startDiet(id: id, body: {'date': date});
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> 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);
|
||||
|
||||
@@ -267,7 +267,7 @@ class PlansCubit extends Cubit<PlansState> {
|
||||
Duration(milliseconds: 200),
|
||||
() async {
|
||||
try {
|
||||
await planRepository.addToFavorite(slug);
|
||||
await planRepository.removeFromFavorite(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
|
||||
@@ -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<FeedScreenContent> {
|
||||
recipesScrollController.dispose();
|
||||
dietsScrollController.dispose();
|
||||
plansScrollController.dispose();
|
||||
consultantsScrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -179,7 +180,18 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
||||
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<FeedScreenContent> {
|
||||
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<FeedScreenContent> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_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());
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
44
lib/features/feed/presentation/widgets/chip_group.dart
Normal file
44
lib/features/feed/presentation/widgets/chip_group.dart
Normal file
@@ -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<String> 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());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<List<ChosenDietModel>> getChosenDiets() async {
|
||||
try {
|
||||
return (await profileService.getChosenDiets()).diets;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IngredientEntity> ingredients,
|
||||
required List<Ingredient> ingredients,
|
||||
required List<StepEntity> steps,
|
||||
required List<String> 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
|
||||
|
||||
@@ -25,7 +25,7 @@ mixin _$RecipeCreateEntity {
|
||||
String? get description => throw _privateConstructorUsedError;
|
||||
File? get image => throw _privateConstructorUsedError;
|
||||
String? get videoUrl => throw _privateConstructorUsedError;
|
||||
List<IngredientEntity> get ingredients => throw _privateConstructorUsedError;
|
||||
List<Ingredient> get ingredients => throw _privateConstructorUsedError;
|
||||
List<StepEntity> get steps => throw _privateConstructorUsedError;
|
||||
List<String> get tags => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -52,7 +52,7 @@ abstract class $RecipeCreateEntityCopyWith<$Res> {
|
||||
String? description,
|
||||
File? image,
|
||||
String? videoUrl,
|
||||
List<IngredientEntity> ingredients,
|
||||
List<Ingredient> ingredients,
|
||||
List<StepEntity> steps,
|
||||
List<String> 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<IngredientEntity>,
|
||||
as List<Ingredient>,
|
||||
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<IngredientEntity> ingredients,
|
||||
List<Ingredient> ingredients,
|
||||
List<StepEntity> steps,
|
||||
List<String> tags});
|
||||
}
|
||||
@@ -227,7 +227,7 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
|
||||
ingredients: null == ingredients
|
||||
? _value._ingredients
|
||||
: ingredients // ignore: cast_nullable_to_non_nullable
|
||||
as List<IngredientEntity>,
|
||||
as List<Ingredient>,
|
||||
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<IngredientEntity> ingredients,
|
||||
required final List<Ingredient> ingredients,
|
||||
required final List<StepEntity> steps,
|
||||
required final List<String> 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<IngredientEntity> _ingredients;
|
||||
final List<Ingredient> _ingredients;
|
||||
@override
|
||||
List<IngredientEntity> get ingredients {
|
||||
List<Ingredient> 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<IngredientEntity> ingredients,
|
||||
required final List<Ingredient> ingredients,
|
||||
required final List<StepEntity> steps,
|
||||
required final List<String> tags}) = _$RecipeCreateEntityImpl;
|
||||
const _RecipeCreateEntity._() : super._();
|
||||
|
||||
@override
|
||||
String? get title;
|
||||
@@ -396,7 +398,7 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
|
||||
@override
|
||||
String? get videoUrl;
|
||||
@override
|
||||
List<IngredientEntity> get ingredients;
|
||||
List<Ingredient> get ingredients;
|
||||
@override
|
||||
List<StepEntity> 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<IngredientEntity> 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;
|
||||
|
||||
@@ -13,4 +13,4 @@ class Ingredient with _$Ingredient {
|
||||
}) = _Ingredient;
|
||||
|
||||
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,17 @@ class RecipeModel with _$RecipeModel {
|
||||
const factory RecipeModel({
|
||||
List<Ingredient?>? ingredients,
|
||||
List<Step?>? steps,
|
||||
List<String>? 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<String, Object?> json) => _$RecipeModelFromJson(json);
|
||||
|
||||
@@ -22,13 +22,17 @@ RecipeModel _$RecipeModelFromJson(Map<String, dynamic> json) {
|
||||
mixin _$RecipeModel {
|
||||
List<Ingredient?>? get ingredients => throw _privateConstructorUsedError;
|
||||
List<Step?>? get steps => throw _privateConstructorUsedError;
|
||||
List<String>? 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<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -49,13 +53,17 @@ abstract class $RecipeModelCopyWith<$Res> {
|
||||
$Res call(
|
||||
{List<Ingredient?>? ingredients,
|
||||
List<Step?>? steps,
|
||||
List<String>? 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<Step?>?,
|
||||
tags: freezed == tags
|
||||
? _value.tags
|
||||
: tags // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>?,
|
||||
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<Ingredient?>? ingredients,
|
||||
List<Step?>? steps,
|
||||
List<String>? 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<Step?>?,
|
||||
tags: freezed == tags
|
||||
? _value._tags
|
||||
: tags // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>?,
|
||||
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<Ingredient?>? ingredients,
|
||||
final List<Step?>? steps,
|
||||
final List<String>? 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<String, dynamic> json) =>
|
||||
_$$RecipeModelImplFromJson(json);
|
||||
@@ -247,6 +304,16 @@ class _$RecipeModelImpl implements _RecipeModel {
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
final List<String>? _tags;
|
||||
@override
|
||||
List<String>? 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<Ingredient?>? ingredients,
|
||||
final List<Step?>? steps,
|
||||
final List<String>? 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<String, dynamic> json) =
|
||||
_$RecipeModelImpl.fromJson;
|
||||
@@ -337,6 +425,8 @@ abstract class _RecipeModel implements RecipeModel {
|
||||
@override
|
||||
List<Step?>? get steps;
|
||||
@override
|
||||
List<String>? 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.
|
||||
|
||||
@@ -16,24 +16,32 @@ _$RecipeModelImpl _$$RecipeModelImplFromJson(Map<String, dynamic> json) =>
|
||||
?.map((e) =>
|
||||
e == null ? null : Step.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
tags: (json['tags'] as List<dynamic>?)?.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<String, dynamic> _$$RecipeModelImplToJson(_$RecipeModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'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,
|
||||
};
|
||||
|
||||
@@ -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<CreateRecipeState> {
|
||||
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<CreateRecipeState> {
|
||||
);
|
||||
}
|
||||
|
||||
addIngredient(IngredientEntity entity) {
|
||||
addIngredient(Ingredient entity) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
@@ -52,7 +56,7 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
}
|
||||
|
||||
removeIngredient(int index) {
|
||||
List<IngredientEntity> ingredients = [...state.entity.ingredients];
|
||||
List<Ingredient> ingredients = [...state.entity.ingredients];
|
||||
ingredients.removeAt(index);
|
||||
|
||||
emit(
|
||||
@@ -65,8 +69,8 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
}
|
||||
|
||||
replaceIngredientPosition(int oldIndex, int newIndex) {
|
||||
List<IngredientEntity> ingredients = [...state.entity.ingredients];
|
||||
final IngredientEntity item = ingredients.removeAt(oldIndex);
|
||||
List<Ingredient> ingredients = [...state.entity.ingredients];
|
||||
final item = ingredients.removeAt(oldIndex);
|
||||
ingredients.insert(newIndex, item);
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
@@ -216,4 +220,12 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> publishRecipe() async {
|
||||
try {
|
||||
await recipeRepository.publishRecipe(state.entity.toModel());
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RecipeCreateScreen> createState() => _RecipeCreateScreenState();
|
||||
}
|
||||
@@ -39,28 +42,34 @@ class _RecipeCreateScreenState extends State<RecipeCreateScreen> {
|
||||
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<CreateRecipeCubit>().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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -18,14 +18,7 @@ class InfoAboutRecipeWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
|
||||
final List<SelectionPopupModel> items = const [
|
||||
SelectionPopupModel(title: 'Первые Блюда'),
|
||||
SelectionPopupModel(title: 'Вторые Блюда'),
|
||||
SelectionPopupModel(title: 'Салаты'),
|
||||
SelectionPopupModel(title: 'Сладости'),
|
||||
SelectionPopupModel(title: 'Напитки'),
|
||||
SelectionPopupModel(title: 'Прочее'),
|
||||
];
|
||||
final List<SelectionPopupModel> 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<InfoAboutRecipeWidget> {
|
||||
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,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -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<IngredientsWidget> {
|
||||
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',
|
||||
|
||||
@@ -19,11 +19,14 @@ class RecipeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<RecipeModel> publishRecipe(RecipeModel recipe) async {
|
||||
Future<RecipeModel?> 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<Map<String, Object>> ingredientsMap = recipe.ingredients?.map((ingredient) {
|
||||
final ingredientsMap = recipe.ingredients?.map((ingredient) {
|
||||
return {
|
||||
'name': ingredient!.name,
|
||||
'unit': ingredient.unit,
|
||||
@@ -32,25 +35,42 @@ class RecipeRepository {
|
||||
}).toList() ??
|
||||
[];
|
||||
|
||||
List<Map<String, Object>> stepsMap = recipe.steps?.map((step) {
|
||||
final stepsMap = recipe.steps?.map((step) {
|
||||
return {
|
||||
'image': File(step?.image ?? ""),
|
||||
'description': step?.description ?? "",
|
||||
};
|
||||
}).toList() ??
|
||||
[];
|
||||
|
||||
return await recipeService.publishRecipe(
|
||||
recipe.type,
|
||||
recipe.state,
|
||||
recipe.title,
|
||||
recipe.description,
|
||||
recipe.video_url,
|
||||
ingredientsMap,
|
||||
stepsMap,
|
||||
recipe.cooking_time,
|
||||
file,
|
||||
);
|
||||
// recipe.steps?.map((step) {
|
||||
// return File(step?.image ?? "");
|
||||
// }).toList() ??
|
||||
// [];
|
||||
|
||||
if (recipe.type != null &&
|
||||
recipe.description != null &&
|
||||
recipe.totalCookingTime != null &&
|
||||
recipe.activeCookTime != null &&
|
||||
recipe.servings != null &&
|
||||
recipe.energyPerServing != null &&
|
||||
ingredientsMap.isNotEmpty) {
|
||||
return await recipeService.publishRecipe(
|
||||
type: recipe.type!,
|
||||
state: 'active',
|
||||
title: recipe.title,
|
||||
tags: recipe.tags,
|
||||
description: recipe.description!,
|
||||
video_url: recipe.video_url,
|
||||
ingredients: ingredientsMap,
|
||||
steps: stepsMap,
|
||||
cooking_time: recipe.totalCookingTime!,
|
||||
active_time: recipe.activeCookTime!,
|
||||
image: file,
|
||||
servings: recipe.servings!,
|
||||
energy_per_serving: recipe.energyPerServing!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
||||
@@ -19,18 +19,25 @@ abstract class RecipeService {
|
||||
@GET('/api/diary/product_nutritional_values/filter')
|
||||
Future<Map<String, List<Product>>> getInputProduct(@Query('name') String name);
|
||||
|
||||
|
||||
@POST('/api/recipes/create')
|
||||
Future<RecipeModel> publishRecipe(
|
||||
@Part() String? type,
|
||||
@Part() String? state,
|
||||
@MultiPart()
|
||||
Future<RecipeModel> publishRecipe({
|
||||
@Part() required String type,
|
||||
@Part() required String state,
|
||||
@Part() String? title,
|
||||
@Part() String? description,
|
||||
@Part() required String description,
|
||||
@Part() String? video_url,
|
||||
@Part() List<Map<String, Object>> ingredients,
|
||||
@Part() List<Map<String, Object>> steps,
|
||||
@Part() List<Map<String, Object>>? ingredients,
|
||||
@Part() List<Map<String, Object>>? steps,
|
||||
@Part() List<String>? tags,
|
||||
@Part() int? cooking_time,
|
||||
@Part() File image,
|
||||
);
|
||||
@Part() int? active_time,
|
||||
@Part() required int servings,
|
||||
@Part() required int energy_per_serving,
|
||||
@Part() File? image,
|
||||
|
||||
});
|
||||
|
||||
@POST('/api/recipes/update/{id}')
|
||||
Future<RecipeModel> editRecipe(@Part() Map<String, Object?> recipe, String id);
|
||||
|
||||
@@ -92,75 +92,95 @@ class _RecipeService implements RecipeService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RecipeModel> publishRecipe(
|
||||
String? type,
|
||||
String? state,
|
||||
Future<RecipeModel> publishRecipe({
|
||||
required String type,
|
||||
required String state,
|
||||
String? title,
|
||||
String? description,
|
||||
required String description,
|
||||
String? video_url,
|
||||
List<Map<String, Object>> ingredients,
|
||||
List<Map<String, Object>> steps,
|
||||
List<Map<String, Object>>? ingredients,
|
||||
List<Map<String, Object>>? steps,
|
||||
List<String>? tags,
|
||||
int? cooking_time,
|
||||
File image,
|
||||
) async {
|
||||
int? active_time,
|
||||
required int servings,
|
||||
required int energy_per_serving,
|
||||
File? image,
|
||||
}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
if (type != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type',
|
||||
type,
|
||||
));
|
||||
}
|
||||
if (state != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'state',
|
||||
state,
|
||||
));
|
||||
}
|
||||
_data.fields.add(MapEntry(
|
||||
'type',
|
||||
type,
|
||||
));
|
||||
_data.fields.add(MapEntry(
|
||||
'state',
|
||||
state,
|
||||
));
|
||||
if (title != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'title',
|
||||
title,
|
||||
));
|
||||
}
|
||||
if (description != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'description',
|
||||
description,
|
||||
));
|
||||
}
|
||||
_data.fields.add(MapEntry(
|
||||
'description',
|
||||
description,
|
||||
));
|
||||
if (video_url != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'video_url',
|
||||
video_url,
|
||||
));
|
||||
}
|
||||
ingredients.forEach((i) {
|
||||
ingredients?.forEach((i) {
|
||||
_data.fields.add(MapEntry('ingredients', jsonEncode(i)));
|
||||
});
|
||||
steps.forEach((i) {
|
||||
steps?.forEach((i) {
|
||||
_data.fields.add(MapEntry('steps', jsonEncode(i)));
|
||||
});
|
||||
tags?.forEach((i) {
|
||||
_data.fields.add(MapEntry('tags', i));
|
||||
});
|
||||
if (cooking_time != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'cooking_time',
|
||||
cooking_time.toString(),
|
||||
));
|
||||
}
|
||||
_data.files.add(MapEntry(
|
||||
'image',
|
||||
MultipartFile.fromFileSync(
|
||||
image.path,
|
||||
filename: image.path.split(Platform.pathSeparator).last,
|
||||
),
|
||||
if (active_time != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'active_time',
|
||||
active_time.toString(),
|
||||
));
|
||||
}
|
||||
_data.fields.add(MapEntry(
|
||||
'servings',
|
||||
servings.toString(),
|
||||
));
|
||||
_data.fields.add(MapEntry(
|
||||
'energy_per_serving',
|
||||
energy_per_serving.toString(),
|
||||
));
|
||||
if (image != null) {
|
||||
if (image != null) {
|
||||
_data.files.add(MapEntry(
|
||||
'image',
|
||||
MultipartFile.fromFileSync(
|
||||
image.path,
|
||||
filename: image.path.split(Platform.pathSeparator).last,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
final _options = _setStreamType<RecipeModel>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
contentType: 'multipart/form-data',
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
|
||||
import 'package:eatvio_app/core/token_storage.dart';
|
||||
@@ -57,7 +59,7 @@ class RegistrationCubit extends Cubit<RegistrationState> {
|
||||
await TokenStorage.saveTokenInStorage(token);
|
||||
AppRouter.navigatorKey.currentContext?.go('/my-profile-screen');
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ abstract class SettingsService {
|
||||
@GET('/api/profile/settings')
|
||||
Future<ProfileSettingsModel> getProfileSettings();
|
||||
|
||||
@POST('/api/profile/settings/update')
|
||||
@PUT('/api/profile/settings/update')
|
||||
Future<ProfileSettingsModel> updateProfileSettings({
|
||||
@Body() required Map<String, dynamic> data,
|
||||
});
|
||||
|
||||
@POST('/api/profile/update')
|
||||
@PUT('/api/profile/update')
|
||||
Future<ProfileDetailsModel> updateProfileDetails({
|
||||
@Body() required Map<String, dynamic> data,
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ class _SettingsService implements SettingsService {
|
||||
final _data = <String, dynamic>{};
|
||||
_data.addAll(data);
|
||||
final _options = _setStreamType<ProfileSettingsModel>(Options(
|
||||
method: 'POST',
|
||||
method: 'PUT',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
@@ -98,7 +98,7 @@ class _SettingsService implements SettingsService {
|
||||
final _data = <String, dynamic>{};
|
||||
_data.addAll(data);
|
||||
final _options = _setStreamType<ProfileDetailsModel>(Options(
|
||||
method: 'POST',
|
||||
method: 'PUT',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
|
||||
@@ -226,5 +226,9 @@
|
||||
"takes_pills": "Принимает лекарства",
|
||||
"sport_hobbies": "Спортивные увлечения",
|
||||
"liked_groceries": "Любимые продукты",
|
||||
"not_liked_groceries": "Не любимые продукты"
|
||||
"not_liked_groceries": "Не любимые продукты",
|
||||
"diet_started": "Диета уже начата",
|
||||
"cancel_diet": "Отменить диету?",
|
||||
"confirm": "Подтвердить",
|
||||
"could_not_cancel_diet": "Не удалось отменить диету"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user