diff --git a/assets/icons/play_icon.svg b/assets/icons/play_icon.svg new file mode 100644 index 00000000..d12618a2 --- /dev/null +++ b/assets/icons/play_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 646293d0..3c5c98f3 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -30,8 +30,8 @@ import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'; -import 'package:eatvio_app/features/recipes/presentation/pages/about_recipes_screen.dart'; -import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart'; +import 'package:eatvio_app/features/recipe/presentation/about_recipe_screen.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart'; import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart'; import 'package:eatvio_app/features/settings/entities/settings_entity.dart'; import 'package:eatvio_app/features/settings/presentation/pages/change_email_screen.dart'; @@ -58,6 +58,8 @@ class AppRouter { path: '/auth-screen', name: 'authScreen', redirect: (context, state) async { + // TokenStorage.removeTokenInStorage(); + // await TokenStorage.saveTokenInStorage('1|laravel_sanctum_zaU2133RQ0wbxDYdH7IQxTpThmilWtKhzxbSt5X1065fcae3'); if (await TokenStorage.haveTokenInStorage()) { return '/my-profile-screen'; } @@ -128,9 +130,14 @@ class AppRouter { GoRoute( path: '/about-recipes-screen', name: 'aboutRecipesScreen', - builder: (context, state) => AboutRecipesScreen( - recipes: state.extra as Recipes, - ), + builder: (context, state) { + final data = state.extra as Map; + return AboutRecipeScreen( + recipe: data['recipe'] as Recipe, + index: data['index'] as int?, + navigateToComments: data['navigateToComments'] as bool, + ); + }, ), GoRoute( path: RecipeCreateScreen.path, diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index e258d702..39f86908 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -54,9 +54,12 @@ import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/prof import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart'; import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:eatvio_app/features/profile/services/profile_service.dart'; -import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; -import 'package:eatvio_app/features/recipes/reporitories/recipe_repository.dart'; -import 'package:eatvio_app/features/recipes/services/recipe_service.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart'; +import 'package:eatvio_app/features/create_recipe/services/recipe_service.dart'; +import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_cubit.dart'; +import 'package:eatvio_app/features/recipe/repositories/about_recipe_repository.dart'; +import 'package:eatvio_app/features/recipe/services/about_recipe_service.dart'; import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart'; import 'package:eatvio_app/features/registration/repositories/registration_repository.dart'; import 'package:eatvio_app/features/registration/services/registration_service.dart'; @@ -80,7 +83,7 @@ Future init() async { // Cubit factories sl.registerFactory(() => RegistrationCubit(registrationRepository: sl())); sl.registerFactory(() => ChatRoomCubit(chatRepository: sl(), wsManager: sl())); - sl.registerFactory(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl())); + sl.registerFactory(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl())); sl.registerFactory(() => CalendarCubit(calendarRepository: sl())); sl.registerFactory(() => ProfileCubit(profileRepository: sl(), chatRepository: sl())); sl.registerFactory(() => profilePlans.PlansCubit(plansRepository: sl())); @@ -99,10 +102,11 @@ Future init() async { sl.registerFactory(() => FeedProfileCubit(profileRepository: sl())); sl.registerFactory(() => AboutPlanCubit(planRepository: sl())); sl.registerFactory(() => CreateRecipeCubit(recipeRepository: sl())); - sl.registerFactory(() => MyDietsCubit(myDietsRepository: sl())); + sl.registerFactory(() => MyDietsCubit(myDietsRepository: sl(), createDietRepository: sl())); sl.registerFactory(() => MyPlansCubit(myPlansRepository: sl())); sl.registerFactory(() => UserDetailsCubit(profileRepository: sl())); sl.registerFactory(() => ConsultantsCubit(feedRepository: sl())); + sl.registerFactory(() => AboutRecipeCubit(recipeRepository: sl(), profileRepository: sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); @@ -122,6 +126,7 @@ Future init() async { sl.registerLazySingleton(() => PlanRepository(planService: sl())); sl.registerLazySingleton(() => MyDietsRepository(myDietsService: sl())); sl.registerLazySingleton(() => MyPlansRepository(myPlansService: sl())); + sl.registerLazySingleton(() => AboutRecipeRepository(recipeService: sl())); // Services sl.registerLazySingleton(() => ChatService(sl())); @@ -129,7 +134,7 @@ Future init() async { sl.registerLazySingleton(() => RegistrationService(sl())); sl.registerLazySingleton(() => CalendarService(sl())); sl.registerLazySingleton(() => ProfileService(sl())); - sl.registerLazySingleton(() => RecipeService(sl())); + sl.registerLazySingleton(() => CreateRecipeService(sl())); sl.registerLazySingleton(() => SettingsService(sl())); sl.registerLazySingleton(() => ConsultantService(sl())); sl.registerLazySingleton(() => DiaryService(sl())); @@ -142,6 +147,7 @@ Future init() async { sl.registerLazySingleton(() => MyDietsService(sl())); sl.registerLazySingleton(() => MyPlansService(sl())); sl.registerLazySingleton(() => WsService(sl())); + sl.registerLazySingleton(() => AboutRecipeService(sl())); // External sl.registerLazySingleton( diff --git a/lib/core/utils/enums.dart b/lib/core/utils/enums.dart new file mode 100644 index 00000000..9e6eca73 --- /dev/null +++ b/lib/core/utils/enums.dart @@ -0,0 +1,5 @@ +enum ProductType { + diet, + plan, + recipe +} diff --git a/lib/core/utils/localization_utils.dart b/lib/core/utils/localization_utils.dart index 23d846a0..2070f626 100644 --- a/lib/core/utils/localization_utils.dart +++ b/lib/core/utils/localization_utils.dart @@ -41,3 +41,33 @@ String getCommentForm(int number, S s) { return s.comments_plural; } } + +String getMinuteForm(int number) { + if (number % 10 == 1 && number % 100 != 11) { + return S.current.minute; + } else if ([2, 3, 4].contains(number % 10) && ![12, 13, 14].contains(number % 100)) { + return S.current.minutes; + } else { + return S.current.another_form_of_minute; + } +} + +String getHourForm(int number) { + if (number % 10 == 1 && number % 100 != 11) { + return S.current.hour; + } else if ([2, 3, 4].contains(number % 10) && ![12, 13, 14].contains(number % 100)) { + return S.current.another_form_of_hour; + } else { + return S.current.hours; + } +} + +String getStepForm(int number) { + if (number % 10 == 1 && number % 100 != 11) { + return S.current.stage; + } else if ([2, 3, 4].contains(number % 10) && ![12, 13, 14].contains(number % 100)) { + return S.current.another_form_of_stage; + } else { + return S.current.stages; + } +} diff --git a/lib/core/widgets/info_row.dart b/lib/core/widgets/info_row.dart index 564cf015..7dc1ad73 100644 --- a/lib/core/widgets/info_row.dart +++ b/lib/core/widgets/info_row.dart @@ -1,9 +1,11 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/utils/enums.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/type_badges.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; @@ -11,21 +13,35 @@ import 'package:flutter_svg/svg.dart'; class InfoRow extends StatelessWidget { const InfoRow({ + required this.type, super.key, this.diet, this.itemsBackgroundColor, this.disableDescriptionOfTypes = false, this.plan, + this.recipe, }); + final ProductType type; final Color? itemsBackgroundColor; final Diet? diet; final PlanModel? plan; + final Recipe? recipe; final bool disableDescriptionOfTypes; @override Widget build(BuildContext context) { final s = S.of(context); + String? timeTitle; + if (recipe != null) { + final hours = recipe!.cookingTime ~/ 60; + final minutes = recipe!.cookingTime % 60; + if (hours > 0) { + timeTitle = '$hours ${getHourForm(hours)} $minutes ${getMinuteForm(minutes)}'; + } else { + timeTitle = '$minutes ${getMinuteForm(minutes)}'; + } + } return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), scrollDirection: Axis.horizontal, @@ -41,7 +57,11 @@ class InfoRow extends StatelessWidget { ), SizedBox(width: 4), Text( - diet == null ? '${plan?.duration} ${getDayForm(plan?.duration ?? 7, s)}' : '${diet?.duration} ${getDayForm(diet?.duration ?? 7, s)}', + switch (type) { + ProductType.diet => '${diet?.duration} ${getDayForm(diet?.duration ?? 7, s)}', + ProductType.plan => '${plan?.duration} ${getDayForm(plan?.duration ?? 7, s)}', + ProductType.recipe => '${timeTitle ?? ''}', + }, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], @@ -51,18 +71,42 @@ class InfoRow extends StatelessWidget { backgroundColor: itemsBackgroundColor, children: [ SvgPicture.asset( - 'assets/icons/people.svg', + switch (type) { + ProductType.diet => 'assets/icons/people.svg', + ProductType.plan => 'assets/icons/people.svg', + ProductType.recipe => 'assets/icons/calories.svg', + }, width: 13.3, height: 13.3, ), SizedBox(width: 4), Text( - '80 человек прошли', + switch (type) { + ProductType.diet => '80 человек прошли', + ProductType.plan => '80 человек прошли', + ProductType.recipe => (recipe?.calories.toStringAsFixed(0) ?? '0') + ' ккал', + }, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], ), SizedBox(width: 12), + if (type == ProductType.recipe) + InfoBouble( + backgroundColor: itemsBackgroundColor, + children: [ + SvgPicture.asset( + 'assets/icons/steps.svg', + width: 13.3, + height: 13.3, + ), + SizedBox(width: 4), + Text( + '${recipe?.steps?.length ?? 0} ${getStepForm(recipe?.steps?.length ?? 0)}', + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + ), if (plan?.type.hasAtLeastOneType() == true || diet?.type.hasAtLeastOneType() == true) InfoBouble( backgroundColor: itemsBackgroundColor, diff --git a/lib/features/create_diet/models/create_diet_model.dart b/lib/features/create_diet/models/create_diet_model.dart index 84639c77..49f1b752 100644 --- a/lib/features/create_diet/models/create_diet_model.dart +++ b/lib/features/create_diet/models/create_diet_model.dart @@ -19,5 +19,3 @@ class CreateDietModel with _$CreateDietModel { List>>? dailySchedule, }) = _CreateDietModel; } - - diff --git a/lib/features/create_diet/presentation/create_diet_screen.dart b/lib/features/create_diet/presentation/create_diet_screen.dart index cb1cd953..6cd1ea03 100644 --- a/lib/features/create_diet/presentation/create_diet_screen.dart +++ b/lib/features/create_diet/presentation/create_diet_screen.dart @@ -233,7 +233,13 @@ class _CreateDietScreenContentState extends State { else CustomOutlinedButton( onPressed: () { - context.read().publishDiet(); + if (widget.diet != null) { + context.read().updateDiet( + widget.diet!.id, + ); + } else { + context.read().createDiet(); + } }, text: widget.diet != null ? s.save : s.publish, height: 52, diff --git a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart index 771e2531..759cfae6 100644 --- a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart +++ b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart @@ -18,6 +18,7 @@ class CreateDietCubit extends Cubit { final ProfileRepository profileRepository; final GlossaryRepository glossaryRepository; String? tempImagePath; + int? dietId; static const _dietStateValues = DietState.values; CreateDietCubit({required this.profileRepository, required this.createDietRepository, required this.glossaryRepository}) : super(CreateDietState.initial()); @@ -25,10 +26,12 @@ class CreateDietCubit extends Cubit { Future setInitialData(Diet? diet) async { if (diet == null) return; emit(state.copyWith(isLoading: true, isDietBeingEdited: true)); - final countOfDays = diet.dailySchedule.length; + dietId = diet.id; + final dailyScheduleWithNutrition = await createDietRepository.getDietPortionsNutrition(diet.id); + final countOfDays = dailyScheduleWithNutrition.length; final List>> dailySchedule = List.generate(countOfDays, (index) => [[], [], [], [], []]); - for (final dayEntry in diet.dailySchedule.entries) { + for (final dayEntry in dailyScheduleWithNutrition.entries) { for (final mealEntry in dayEntry.value.entries) { for (final product in mealEntry.value.values) { dailySchedule[dayEntry.key - 1][mealEntry.key - 1].add(product); @@ -318,7 +321,7 @@ class CreateDietCubit extends Cubit { return diet; } - Future publishDiet() async { + Future createDiet() async { emit(state.copyWith(isDietPublishing: true)); if (!_isFromValid()) return null; try { @@ -347,6 +350,33 @@ class CreateDietCubit extends Cubit { return null; } + Future updateDiet(int dietId) async { + if (!_isFromValid()) return null; + try { + File? image; + if (state.dietData.image != null) { + image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path)); + } + await createDietRepository.updateDiet( + dietId: dietId, + name: state.dietData.name, + duration: state.dietData.duration, + state: state.dietData.state ?? DietState.active, + image: image, + meatType: state.dietData.type['meat'] == true ? '1' : '0', + carbType: state.dietData.type['carb'] == true ? '1' : '0', + vegType: state.dietData.type['veg'] == true ? '1' : '0', + lightType: state.dietData.type['light'] == true ? '1' : '0', + details: state.dietData.details, + dailySchedule: state.dietData.dailySchedule, + ); + return null; + } catch (e, st) { + log(e.toString(), stackTrace: st); + return 'Произошла непредвиденная ошибка'; + } + } + bool _isFromValid() { bool result = true; if ((state.dietData.name?.length ?? 0) < 3) { diff --git a/lib/features/create_diet/presentation/diet_preview_screen.dart b/lib/features/create_diet/presentation/diet_preview_screen.dart index 879e5446..5a385a16 100644 --- a/lib/features/create_diet/presentation/diet_preview_screen.dart +++ b/lib/features/create_diet/presentation/diet_preview_screen.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:eatvio_app/core/utils/enums.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; @@ -300,7 +301,7 @@ class _DietPreviewScreenState extends State { ), ), SizedBox(height: 12), - InfoRow(diet: widget.diet), + InfoRow(diet: widget.diet, type: ProductType.diet,), ExpandableText( text: widget.diet.details, ), diff --git a/lib/features/create_diet/presentation/widgets/add_meal_widget.dart b/lib/features/create_diet/presentation/widgets/add_meal_widget.dart index c0c76b03..2e79416e 100644 --- a/lib/features/create_diet/presentation/widgets/add_meal_widget.dart +++ b/lib/features/create_diet/presentation/widgets/add_meal_widget.dart @@ -1,5 +1,7 @@ +import 'package:easy_debounce/easy_debounce.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; @@ -33,6 +35,7 @@ class _AddMealWidgetState extends State { GlossaryModel? selectedGlossary; final productNameController = TextEditingController(); final quantityController = TextEditingController(); + List glossariesList = []; @override void initState() { @@ -66,47 +69,27 @@ class _AddMealWidgetState extends State { children: [ SizedBox( width: MediaQuery.of(context).size.width * 0.8 - 32, - child: TypeAheadField( + child: CustomTextFormField( controller: productNameController, - itemBuilder: (context, glossary) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - glossary.name, - style: TextStyle( - height: 1, - fontSize: 16, - ), - ), + autofocus: true, + hintText: 'Введите название продукта/блюда', + onChanged: (query) { + EasyDebounce.debounce( + 'glossary-search', + Duration(milliseconds: 250), + () async { + selectedGlossary = null; + if (query.isEmpty) { + glossariesList = []; + selectedGlossary = null; + setState(() {}); + return; + } + glossariesList = await widget.createDietCubit.getGlossaryListByQuery(query); + setState(() {}); + }, ); }, - itemSeparatorBuilder: (context, index) => _divider(context), - onSelected: (newSelectedGlossary) async { - setState(() { - selectedGlossary = newSelectedGlossary; - productNameController.text = selectedGlossary!.name; - }); - }, - suggestionsCallback: (query) async { - if (query.length < 1) return []; - final suggestionsList = await widget.createDietCubit.getGlossaryListByQuery(query); - return suggestionsList; - }, - builder: (context, controller, focusNode) { - return CustomTextFormField( - controller: controller, - autofocus: true, - focusNode: focusNode, - hintText: 'Введите название продукта/блюда', - ); - }, - emptyBuilder: (context) { - return const SizedBox.shrink(); - }, - errorBuilder: (context, object) => Padding( - padding: const EdgeInsets.all(8.0), - child: Text('Нет подходящих вариантов'), - ), ), ), const SizedBox(width: 4), @@ -129,7 +112,56 @@ class _AddMealWidgetState extends State { ), ], ), - const SizedBox(height: 16), + glossariesList.isEmpty ? const SizedBox(height: 16) : const SizedBox.shrink(), + Container( + constraints: BoxConstraints( + maxHeight: 256, + ), + child: ListView.separated( + physics: const BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, i) { + return ListTile( + onTap: () async { + setState(() { + selectedGlossary = glossariesList[i]; + productNameController.text = selectedGlossary!.name; + }); + glossariesList = []; + setState(() {}); + FocusManager.instance.primaryFocus?.nextFocus(); + }, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + title: Text( + glossariesList[i].name, + style: TextStyle( + height: 1, + ), + ), + subtitle: glossariesList[i].grInPortion == 0 + ? null + : Text( + '${glossariesList[i].grInPortion} г', + style: TextStyle( + height: 1, + color: Colors.grey.shade600, + ), + ), + trailing: Icon( + Icons.add_rounded, + color: Colors.grey.shade400, + ), + contentPadding: EdgeInsets.only(left: 12), + ); + }, + itemCount: glossariesList.length, + separatorBuilder: (BuildContext context, int index) { + return CustomDivider(); + }, + ), + ), Row( children: [ if (widget.portionIndex != null) @@ -188,7 +220,7 @@ class _AddMealWidgetState extends State { Future addPortion() async { final portionIndex = widget.portionIndex; - final measurementUnits = selectedGlossary?.grInPortion == null + var measurementUnits = selectedGlossary?.grInPortion == null ? Portion.PCS : selectedGlossary?.grInPortion == 0 ? Portion.GRAMS @@ -199,7 +231,7 @@ class _AddMealWidgetState extends State { final portion = DailySchedule( dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates), quantity: int.parse(quantityController.text), - name: glossary.name, + name: glossary.name, productNutritionalValueId: glossary.id.toString(), portion: measurementUnits, ); @@ -234,7 +266,8 @@ class _AddMealWidgetState extends State { ), ); if (newGlossary != null) { - final [calories, carbohydrates, proteins, fats] = _getCPFC(newGlossary, Portion.GRAMS); + measurementUnits = Portion.GRAMS; + final [calories, carbohydrates, proteins, fats] = _getCPFC(newGlossary, measurementUnits); final portion = DailySchedule( dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates), quantity: int.parse(quantityController.text), diff --git a/lib/features/create_diet/repositories/create_diet_repository.dart b/lib/features/create_diet/repositories/create_diet_repository.dart index ba9a6fa4..19e14ea6 100644 --- a/lib/features/create_diet/repositories/create_diet_repository.dart +++ b/lib/features/create_diet/repositories/create_diet_repository.dart @@ -58,4 +58,97 @@ class CreateDietRepository { return createDietService.createPersonalDiet(formData: formData); } } + + Future>>> getDietPortionsNutrition(int dietId) async { + try { + final response = await createDietService.getDietPortionsNutrition(diet: dietId); + final data = response.data; + final dailySchedule = (data as Map).map((key, value) { + return MapEntry( + int.parse(key), + (value as Map).map((key, value) { + return MapEntry( + int.parse(key), + (value as Map).map((key, value) { + return MapEntry(int.parse(key), DailySchedule.fromJson(value as Map)); + }), + ); + }), + ); + }); + return dailySchedule; + } catch (e) { + rethrow; + } + } + + Future updateDiet({ + required int dietId, + required DietState state, + String? name, + int? duration, + String? meatType, + String? carbType, + String? vegType, + String? lightType, + String? details, + File? image, + List>>? dailySchedule, + }) { + final dietMap = {}; + if (dailySchedule != null) { + for (final (dayIndex, day) in dailySchedule.indexed) { + for (final (mealIndex, meal) in day.indexed) { + for (final (productIndex, product) in meal.indexed) { + final jsonProduct = product.toJson(); + dietMap.addAll({ + 'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][name]': jsonProduct['name'], + 'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][portion]': jsonProduct['portion'], + 'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][quantity]': jsonProduct['quantity'], + 'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][product_nutritional_value_id]': jsonProduct['product_nutritional_value_id'], + }); + } + } + } + } + if (name != null) { + dietMap.addAll({'name': name}); + } + if (details != null) { + dietMap.addAll({'details': details}); + } + if (duration != null) { + dietMap.addAll({'duration': duration}); + } + if (meatType != null) { + dietMap.addAll({'type[meat]': meatType}); + } + if (carbType != null) { + dietMap.addAll({'type[carb]': carbType}); + } + if (vegType != null) { + dietMap.addAll({'type[veg]': vegType}); + } + if (lightType != null) { + dietMap.addAll({'type[light]': lightType}); + } + if (image != null) { + dietMap.addAll({ + 'image': MultipartFile.fromFileSync( + image.path, + filename: image.path.split(Platform.pathSeparator).last, + ), + }); + } + + final formData = FormData.fromMap(dietMap); + switch (state) { + case DietState.active: + return createDietService.updatePublished(diet: dietId, formData: formData); + case DietState.draft: + return createDietService.updateDrafted(diet: dietId, formData: formData); + case DietState.personal: + return createDietService.updatePersonal(diet: dietId, formData: formData); + } + } } diff --git a/lib/features/create_diet/services/create_diet_service.dart b/lib/features/create_diet/services/create_diet_service.dart index 471681ee..b6537ea9 100644 --- a/lib/features/create_diet/services/create_diet_service.dart +++ b/lib/features/create_diet/services/create_diet_service.dart @@ -25,4 +25,30 @@ abstract class CreateDietService { Future createPersonalDiet({ @Body() required FormData formData, }); + + @PUT('/api/diets/update/published/{diet}') + @MultiPart() + Future updatePublished({ + @Path() required int diet, + @Body() required FormData formData, + }); + + @PUT('/api/diets/update/drafted/{diet}') + @MultiPart() + Future updateDrafted({ + @Path() required int diet, + @Body() required FormData formData, + }); + + @PUT('/api/diets/update/personalized/{diet}') + @MultiPart() + Future updatePersonal({ + @Path() required int diet, + @Body() required FormData formData, + }); + + @GET('/api/diets/show/{diet}/per_portion_nutritions') + Future getDietPortionsNutrition({ + @Path() required int diet, + }); } diff --git a/lib/features/create_diet/services/create_diet_service.g.dart b/lib/features/create_diet/services/create_diet_service.g.dart index 8d039872..73d5ef59 100644 --- a/lib/features/create_diet/services/create_diet_service.g.dart +++ b/lib/features/create_diet/services/create_diet_service.g.dart @@ -113,6 +113,111 @@ class _CreateDietService implements CreateDietService { return _value; } + @override + Future updatePublished({ + required int diet, + required FormData formData, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = formData; + final _options = _setStreamType( + Options( + method: 'PUT', + headers: _headers, + extra: _extra, + contentType: 'multipart/form-data', + ) + .compose( + _dio.options, + '/api/diets/update/published/${diet}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future updateDrafted({ + required int diet, + required FormData formData, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = formData; + final _options = _setStreamType( + Options( + method: 'PUT', + headers: _headers, + extra: _extra, + contentType: 'multipart/form-data', + ) + .compose( + _dio.options, + '/api/diets/update/drafted/${diet}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future updatePersonal({ + required int diet, + required FormData formData, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = formData; + final _options = _setStreamType( + Options( + method: 'PUT', + headers: _headers, + extra: _extra, + contentType: 'multipart/form-data', + ) + .compose( + _dio.options, + '/api/diets/update/personalized/${diet}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future> getDietPortionsNutrition({ + required int diet, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/diets/show/${diet}/per_portion_nutritions', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch(_options); + final _value = _result.data; + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/features/recipes/entities/recipe_create_entity.dart b/lib/features/create_recipe/entities/recipe_create_entity.dart similarity index 93% rename from lib/features/recipes/entities/recipe_create_entity.dart rename to lib/features/create_recipe/entities/recipe_create_entity.dart index 6ca01db3..9792ea63 100644 --- a/lib/features/recipes/entities/recipe_create_entity.dart +++ b/lib/features/create_recipe/entities/recipe_create_entity.dart @@ -1,8 +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:eatvio_app/features/create_recipe/models/ingredient.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_model.dart'; +import 'package:eatvio_app/features/create_recipe/models/step.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'recipe_create_entity.freezed.dart'; diff --git a/lib/features/recipes/entities/recipe_create_entity.freezed.dart b/lib/features/create_recipe/entities/recipe_create_entity.freezed.dart similarity index 100% rename from lib/features/recipes/entities/recipe_create_entity.freezed.dart rename to lib/features/create_recipe/entities/recipe_create_entity.freezed.dart diff --git a/lib/features/recipes/models/ingredient.dart b/lib/features/create_recipe/models/ingredient.dart similarity index 100% rename from lib/features/recipes/models/ingredient.dart rename to lib/features/create_recipe/models/ingredient.dart diff --git a/lib/features/recipes/models/ingredient.freezed.dart b/lib/features/create_recipe/models/ingredient.freezed.dart similarity index 100% rename from lib/features/recipes/models/ingredient.freezed.dart rename to lib/features/create_recipe/models/ingredient.freezed.dart diff --git a/lib/features/recipes/models/ingredient.g.dart b/lib/features/create_recipe/models/ingredient.g.dart similarity index 100% rename from lib/features/recipes/models/ingredient.g.dart rename to lib/features/create_recipe/models/ingredient.g.dart diff --git a/lib/features/recipes/models/product.dart b/lib/features/create_recipe/models/product.dart similarity index 100% rename from lib/features/recipes/models/product.dart rename to lib/features/create_recipe/models/product.dart diff --git a/lib/features/recipes/models/product.freezed.dart b/lib/features/create_recipe/models/product.freezed.dart similarity index 100% rename from lib/features/recipes/models/product.freezed.dart rename to lib/features/create_recipe/models/product.freezed.dart diff --git a/lib/features/recipes/models/product.g.dart b/lib/features/create_recipe/models/product.g.dart similarity index 100% rename from lib/features/recipes/models/product.g.dart rename to lib/features/create_recipe/models/product.g.dart diff --git a/lib/features/recipes/models/recipe_create_model.dart b/lib/features/create_recipe/models/recipe_create_model.dart similarity index 87% rename from lib/features/recipes/models/recipe_create_model.dart rename to lib/features/create_recipe/models/recipe_create_model.dart index e8f4dbf7..747e165f 100644 --- a/lib/features/recipes/models/recipe_create_model.dart +++ b/lib/features/create_recipe/models/recipe_create_model.dart @@ -1,5 +1,5 @@ -import 'package:eatvio_app/features/recipes/models/ingredient.dart'; -import 'package:eatvio_app/features/recipes/models/step.dart'; +import 'package:eatvio_app/features/create_recipe/models/ingredient.dart'; +import 'package:eatvio_app/features/create_recipe/models/step.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'recipe_create_model.freezed.dart'; diff --git a/lib/features/recipes/models/recipe_create_model.freezed.dart b/lib/features/create_recipe/models/recipe_create_model.freezed.dart similarity index 100% rename from lib/features/recipes/models/recipe_create_model.freezed.dart rename to lib/features/create_recipe/models/recipe_create_model.freezed.dart diff --git a/lib/features/recipes/models/recipe_create_model.g.dart b/lib/features/create_recipe/models/recipe_create_model.g.dart similarity index 100% rename from lib/features/recipes/models/recipe_create_model.g.dart rename to lib/features/create_recipe/models/recipe_create_model.g.dart diff --git a/lib/features/recipes/models/recipe_model.dart b/lib/features/create_recipe/models/recipe_model.dart similarity index 81% rename from lib/features/recipes/models/recipe_model.dart rename to lib/features/create_recipe/models/recipe_model.dart index 588650dd..b79425d1 100644 --- a/lib/features/recipes/models/recipe_model.dart +++ b/lib/features/create_recipe/models/recipe_model.dart @@ -1,5 +1,5 @@ -import 'package:eatvio_app/features/recipes/models/ingredient.dart'; -import 'package:eatvio_app/features/recipes/models/step.dart'; +import 'package:eatvio_app/features/create_recipe/models/ingredient.dart'; +import 'package:eatvio_app/features/create_recipe/models/step.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'recipe_model.freezed.dart'; diff --git a/lib/features/recipes/models/recipe_model.freezed.dart b/lib/features/create_recipe/models/recipe_model.freezed.dart similarity index 100% rename from lib/features/recipes/models/recipe_model.freezed.dart rename to lib/features/create_recipe/models/recipe_model.freezed.dart diff --git a/lib/features/recipes/models/recipe_model.g.dart b/lib/features/create_recipe/models/recipe_model.g.dart similarity index 100% rename from lib/features/recipes/models/recipe_model.g.dart rename to lib/features/create_recipe/models/recipe_model.g.dart diff --git a/lib/features/recipes/models/recipe_tag_model.dart b/lib/features/create_recipe/models/recipe_tag_model.dart similarity index 100% rename from lib/features/recipes/models/recipe_tag_model.dart rename to lib/features/create_recipe/models/recipe_tag_model.dart diff --git a/lib/features/recipes/models/recipe_tag_model.freezed.dart b/lib/features/create_recipe/models/recipe_tag_model.freezed.dart similarity index 100% rename from lib/features/recipes/models/recipe_tag_model.freezed.dart rename to lib/features/create_recipe/models/recipe_tag_model.freezed.dart diff --git a/lib/features/recipes/models/recipe_tag_model.g.dart b/lib/features/create_recipe/models/recipe_tag_model.g.dart similarity index 100% rename from lib/features/recipes/models/recipe_tag_model.g.dart rename to lib/features/create_recipe/models/recipe_tag_model.g.dart diff --git a/lib/features/recipes/models/step.dart b/lib/features/create_recipe/models/step.dart similarity index 100% rename from lib/features/recipes/models/step.dart rename to lib/features/create_recipe/models/step.dart diff --git a/lib/features/recipes/models/step.freezed.dart b/lib/features/create_recipe/models/step.freezed.dart similarity index 100% rename from lib/features/recipes/models/step.freezed.dart rename to lib/features/create_recipe/models/step.freezed.dart diff --git a/lib/features/recipes/models/step.g.dart b/lib/features/create_recipe/models/step.g.dart similarity index 100% rename from lib/features/recipes/models/step.g.dart rename to lib/features/create_recipe/models/step.g.dart diff --git a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart similarity index 94% rename from lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart rename to lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart index a91e44dd..c171e720 100644 --- a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart +++ b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart @@ -1,10 +1,10 @@ import 'dart:developer'; 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:eatvio_app/features/create_recipe/entities/recipe_create_entity.dart'; +import 'package:eatvio_app/features/create_recipe/models/ingredient.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; +import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class CreateRecipeCubit extends Cubit { diff --git a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart similarity index 86% rename from lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart rename to lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart index fd95a839..6bf9e961 100644 --- a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart +++ b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart @@ -1,4 +1,4 @@ -import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.dart'; +import 'package:eatvio_app/features/create_recipe/entities/recipe_create_entity.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'create_recipe_state.freezed.dart'; diff --git a/lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.freezed.dart b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.freezed.dart similarity index 100% rename from lib/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.freezed.dart rename to lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.freezed.dart diff --git a/lib/features/recipes/presentation/pages/recipe_create_screen.dart b/lib/features/create_recipe/presentation/pages/recipe_create_screen.dart similarity index 86% rename from lib/features/recipes/presentation/pages/recipe_create_screen.dart rename to lib/features/create_recipe/presentation/pages/recipe_create_screen.dart index f383da68..da31746c 100644 --- a/lib/features/recipes/presentation/pages/recipe_create_screen.dart +++ b/lib/features/create_recipe/presentation/pages/recipe_create_screen.dart @@ -1,11 +1,11 @@ import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/utils/toasts.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:eatvio_app/features/recipes/presentation/widgets/info_about_recipe_widget.dart'; -import 'package:eatvio_app/features/recipes/presentation/widgets/ingredients_widget.dart'; -import 'package:eatvio_app/features/recipes/presentation/widgets/steps_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/info_about_recipe_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/ingredients_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/steps_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/image_widget.dart b/lib/features/create_recipe/presentation/widgets/image_widget.dart similarity index 96% rename from lib/features/recipes/presentation/widgets/image_widget.dart rename to lib/features/create_recipe/presentation/widgets/image_widget.dart index 251e3345..5422d895 100644 --- a/lib/features/recipes/presentation/widgets/image_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/image_widget.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart b/lib/features/create_recipe/presentation/widgets/info_about_recipe_widget.dart similarity index 90% rename from lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart rename to lib/features/create_recipe/presentation/widgets/info_about_recipe_widget.dart index 0a7cb9fe..86a14896 100644 --- a/lib/features/recipes/presentation/widgets/info_about_recipe_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/info_about_recipe_widget.dart @@ -3,13 +3,13 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down.dart'; import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; -import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.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:eatvio_app/features/recipes/presentation/widgets/image_widget.dart'; -import 'package:eatvio_app/features/recipes/presentation/widgets/portion_widget.dart'; -import 'package:eatvio_app/features/recipes/presentation/widgets/tags_widget.dart'; -import 'package:eatvio_app/features/recipes/presentation/widgets/time_of_cook_widget.dart'; +import 'package:eatvio_app/features/create_recipe/entities/recipe_create_entity.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/image_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/portion_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/tags_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/time_of_cook_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/ingredients_widget.dart b/lib/features/create_recipe/presentation/widgets/ingredients_widget.dart similarity index 97% rename from lib/features/recipes/presentation/widgets/ingredients_widget.dart rename to lib/features/create_recipe/presentation/widgets/ingredients_widget.dart index 3983bccf..a3700bb3 100644 --- a/lib/features/recipes/presentation/widgets/ingredients_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/ingredients_widget.dart @@ -1,8 +1,8 @@ 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/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:eatvio_app/features/create_recipe/models/ingredient.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/portion_widget.dart b/lib/features/create_recipe/presentation/widgets/portion_widget.dart similarity index 94% rename from lib/features/recipes/presentation/widgets/portion_widget.dart rename to lib/features/create_recipe/presentation/widgets/portion_widget.dart index 569f2c7c..a6044738 100644 --- a/lib/features/recipes/presentation/widgets/portion_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/portion_widget.dart @@ -1,7 +1,7 @@ import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.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:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/step_image_widget.dart b/lib/features/create_recipe/presentation/widgets/step_image_widget.dart similarity index 96% rename from lib/features/recipes/presentation/widgets/step_image_widget.dart rename to lib/features/create_recipe/presentation/widgets/step_image_widget.dart index ad4c9549..b2922c92 100644 --- a/lib/features/recipes/presentation/widgets/step_image_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/step_image_widget.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/steps_widget.dart b/lib/features/create_recipe/presentation/widgets/steps_widget.dart similarity index 93% rename from lib/features/recipes/presentation/widgets/steps_widget.dart rename to lib/features/create_recipe/presentation/widgets/steps_widget.dart index 6feb9aa6..726438d8 100644 --- a/lib/features/recipes/presentation/widgets/steps_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/steps_widget.dart @@ -1,8 +1,8 @@ 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/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:eatvio_app/features/recipes/presentation/widgets/step_image_widget.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/widgets/step_image_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/presentation/widgets/tags_widget.dart b/lib/features/create_recipe/presentation/widgets/tags_widget.dart similarity index 100% rename from lib/features/recipes/presentation/widgets/tags_widget.dart rename to lib/features/create_recipe/presentation/widgets/tags_widget.dart diff --git a/lib/features/recipes/presentation/widgets/time_of_cook_widget.dart b/lib/features/create_recipe/presentation/widgets/time_of_cook_widget.dart similarity index 97% rename from lib/features/recipes/presentation/widgets/time_of_cook_widget.dart rename to lib/features/create_recipe/presentation/widgets/time_of_cook_widget.dart index e845a923..29c2bdaa 100644 --- a/lib/features/recipes/presentation/widgets/time_of_cook_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/time_of_cook_widget.dart @@ -1,8 +1,8 @@ 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/create_diet/presentation/widgets/error_widget.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:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; +import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/recipes/reporitories/recipe_repository.dart b/lib/features/create_recipe/reporitories/recipe_repository.dart similarity index 86% rename from lib/features/recipes/reporitories/recipe_repository.dart rename to lib/features/create_recipe/reporitories/recipe_repository.dart index 4dec15c0..f1284fd6 100644 --- a/lib/features/recipes/reporitories/recipe_repository.dart +++ b/lib/features/create_recipe/reporitories/recipe_repository.dart @@ -1,16 +1,17 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; - import 'package:dio/dio.dart'; import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart'; -import 'package:eatvio_app/features/recipes/models/step.dart'; -import 'package:eatvio_app/features/recipes/services/recipe_service.dart'; -import 'package:eatvio_app/features/recipes/models/product.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_model.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_tag_model.dart'; +import 'package:eatvio_app/features/create_recipe/models/step.dart'; +import 'package:eatvio_app/features/create_recipe/services/recipe_service.dart'; +import 'package:eatvio_app/features/create_recipe/models/product.dart'; class RecipeRepository { - final RecipeService recipeService; + final CreateRecipeService recipeService; + RecipeRepository({required this.recipeService}); @@ -99,5 +100,5 @@ class RecipeRepository { } catch (e) { rethrow; } - } + } } diff --git a/lib/features/create_recipe/services/recipe_service.dart b/lib/features/create_recipe/services/recipe_service.dart new file mode 100644 index 00000000..4bc9c85f --- /dev/null +++ b/lib/features/create_recipe/services/recipe_service.dart @@ -0,0 +1,47 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_model.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_tag_model.dart'; +import 'package:retrofit/retrofit.dart'; +import 'package:eatvio_app/features/create_recipe/models/product.dart'; + +part 'recipe_service.g.dart'; + +@RestApi() +abstract class CreateRecipeService { + factory CreateRecipeService(Dio dio, {String baseUrl}) = _CreateRecipeService; + + @POST('/api/recipes/{slug}/like') + Future likeRecipe({ + @Path() required String slug, + }); + + @POST('/api/recipes/{slug}/add_to_favorite') + Future addToFavorite({ + @Path() required String slug, + }); + + @POST('/api/recipes/{slug}/remove_from_favorite') + Future removeFromFavorite({ + @Path() required String slug, + }); + + @GET('/api/recipes/show/{id}') + Future getRecipe(@Path() String id); + + @GET('/api/diary/product_nutritional_values/filter') + Future>> getInputProduct(@Query('name') String name); + + @POST('/api/recipes/create') + @MultiPart() + Future publishRecipe({ + @Body() required FormData formData, + }); + + @POST('/api/recipes/update/{id}') + Future editRecipe(@Part() Map recipe, String id); + + @GET('/api/recipes/tags') + Future getTags(); + + +} \ No newline at end of file diff --git a/lib/features/recipes/services/recipe_service.g.dart b/lib/features/create_recipe/services/recipe_service.g.dart similarity index 73% rename from lib/features/recipes/services/recipe_service.g.dart rename to lib/features/create_recipe/services/recipe_service.g.dart index e1e9d280..58b81059 100644 --- a/lib/features/recipes/services/recipe_service.g.dart +++ b/lib/features/create_recipe/services/recipe_service.g.dart @@ -8,8 +8,8 @@ part of 'recipe_service.dart'; // ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations -class _RecipeService implements RecipeService { - _RecipeService(this._dio, {this.baseUrl, this.errorLogger}); +class _CreateRecipeService implements CreateRecipeService { + _CreateRecipeService(this._dio, {this.baseUrl, this.errorLogger}); final Dio _dio; @@ -17,6 +17,63 @@ class _RecipeService implements RecipeService { final ParseErrorLogger? errorLogger; + @override + Future likeRecipe({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/${slug}/like', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future addToFavorite({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/${slug}/add_to_favorite', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future removeFromFavorite({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/${slug}/remove_from_favorite', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + @override Future getRecipe(String id) async { final _extra = {}; diff --git a/lib/features/dev_tools/test_cases.dart b/lib/features/dev_tools/test_cases.dart index 30eb9c6a..8c92a104 100644 --- a/lib/features/dev_tools/test_cases.dart +++ b/lib/features/dev_tools/test_cases.dart @@ -8,7 +8,7 @@ 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'; -import 'package:eatvio_app/features/recipes/services/recipe_service.dart'; +import 'package:eatvio_app/features/create_recipe/services/recipe_service.dart'; sealed class TestCases { String get name => throw UnimplementedError(); @@ -247,7 +247,7 @@ class GetRecipes implements TestCases { try { final dio = sl(); final feedService = FeedService(dio, baseUrl: baseUrl); - final recipeService = RecipeService(dio, baseUrl: baseUrl); + final recipeService = CreateRecipeService(dio, baseUrl: baseUrl); final tagsModel = await recipeService.getTags(); final tags = tagsModel.data; final tagsIds = tags.map((e) => e.id).toList(); @@ -431,7 +431,7 @@ class GetRecipesComments implements TestCases { try { final dio = sl(); final feedService = FeedService(dio, baseUrl: baseUrl); - final recipeService = RecipeService(dio, baseUrl: baseUrl); + final recipeService = CreateRecipeService(dio, baseUrl: baseUrl); final tagsModel = await recipeService.getTags(); final tags = tagsModel.data; final tagsIds = tags.map((e) => e.id).toList(); diff --git a/lib/features/diary/presentation/widgets/add_product_modal.dart b/lib/features/diary/presentation/widgets/add_product_modal.dart index 01c5a64d..88d30914 100644 --- a/lib/features/diary/presentation/widgets/add_product_modal.dart +++ b/lib/features/diary/presentation/widgets/add_product_modal.dart @@ -89,6 +89,12 @@ class _AddProductModalState extends State { 'glossary-search', Duration(milliseconds: 250), () async { + selectedGlossary = null; + if (query.isEmpty) { + glossariesList = []; + setState(() {}); + return; + } glossariesList = await widget.diaryCubit.getGlossaryListByQuery(query); setState(() {}); }, @@ -131,7 +137,7 @@ class _AddProductModalState extends State { selectedGlossary = glossariesList[i]; productNameController.text = selectedGlossary!.name; }); - glossariesList = await widget.diaryCubit.getGlossaryListByQuery(glossariesList[i].name); + glossariesList = []; setState(() {}); FocusManager.instance.primaryFocus?.nextFocus(); }, diff --git a/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart b/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart index 09fd00cb..f9e80c3e 100644 --- a/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart +++ b/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart @@ -3,6 +3,7 @@ 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/glossary/models/glossary_model.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:lottie/lottie.dart'; class GlossaryModerationRequestModalWidget extends StatefulWidget { @@ -31,13 +32,13 @@ class _GlossaryModerationRequestModalWidgetState extends State _alert(), + GlossaryState.addNewProduct => _addNewGlossaryProduct(), + GlossaryState.success => _success(), + }, + ); } @override diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index a701d978..b3397495 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/enums.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; @@ -419,7 +420,7 @@ class _AboutDietScreenState extends State { ), ), SizedBox(height: 12), - InfoRow(diet: widget.diet), + InfoRow(diet: widget.diet, type: ProductType.diet,), ExpandableText( text: widget.diet.details, ), @@ -466,7 +467,7 @@ class _AboutDietScreenState extends State { padding: EdgeInsets.symmetric(horizontal: 16), alignment: Alignment.centerLeft, child: Text( - dietState.diet.totalComments.toString() + ' ' + getCommentForm(widget.diet.totalComments, s), + dietState.diet.totalComments.toString() + ' ' + getCommentForm(dietState.diet.totalComments, s), style: TextStyles.textMMedium.copyWith( color: appTheme.gray500, ), diff --git a/lib/features/diets/presentation/widgets/expandable_text.dart b/lib/features/diets/presentation/widgets/expandable_text.dart index ab490df4..37285909 100644 --- a/lib/features/diets/presentation/widgets/expandable_text.dart +++ b/lib/features/diets/presentation/widgets/expandable_text.dart @@ -40,6 +40,7 @@ class _ExpandableTextState extends State { data: widget.text, style: { "*": Style( + margin: Margins.all(0), color: Colors.black87, maxLines: isExpanded ? null : widget.maxLines, textOverflow: TextOverflow.clip, diff --git a/lib/features/feed/models/diets_pagination_model.dart b/lib/features/feed/models/diets_pagination_model.dart index c67b8e07..3907fe8a 100644 --- a/lib/features/feed/models/diets_pagination_model.dart +++ b/lib/features/feed/models/diets_pagination_model.dart @@ -96,7 +96,7 @@ class DailySchedule with _$DailySchedule { @JsonKey(name: "name") required String name, @JsonKey(name: "portion") required Portion portion, @JsonKey(name: "quantity", fromJson: _parseQuantity) required int quantity, - @JsonKey(includeToJson: false, includeFromJson: false) DailyNutrition? dailyNutrition, + @JsonKey(name: 'nutrition', includeToJson: false) DailyNutrition? dailyNutrition, @JsonKey(name: 'product_nutritional_value_id', includeToJson: true, includeFromJson: false, includeIfNull: false) String? productNutritionalValueId, }) = _DailySchedule; @@ -117,10 +117,21 @@ enum Portion { @JsonValue("milliliters") MILLILITERS, @JsonValue("pcs") - PCS -} + PCS; -final portionValues = EnumValues({"grams": Portion.GRAMS, "milliliters": Portion.MILLILITERS, "pcs": Portion.PCS}); + static Portion? getPortionFromValue(String value) { + switch (value) { + case 'gr': + return Portion.GRAMS; + case 'milliliters': + return Portion.MILLILITERS; + case 'pcs': + return Portion.PCS; + default: + return null; + } + } +} @freezed class DietType with _$DietType { diff --git a/lib/features/feed/models/diets_pagination_model.freezed.dart b/lib/features/feed/models/diets_pagination_model.freezed.dart index cc185651..fcbf3b63 100644 --- a/lib/features/feed/models/diets_pagination_model.freezed.dart +++ b/lib/features/feed/models/diets_pagination_model.freezed.dart @@ -1246,7 +1246,7 @@ mixin _$DailySchedule { Portion get portion => throw _privateConstructorUsedError; @JsonKey(name: "quantity", fromJson: _parseQuantity) int get quantity => throw _privateConstructorUsedError; - @JsonKey(includeToJson: false, includeFromJson: false) + @JsonKey(name: 'nutrition', includeToJson: false) DailyNutrition? get dailyNutrition => throw _privateConstructorUsedError; @JsonKey( name: 'product_nutritional_value_id', @@ -1275,7 +1275,7 @@ abstract class $DailyScheduleCopyWith<$Res> { {@JsonKey(name: "name") String name, @JsonKey(name: "portion") Portion portion, @JsonKey(name: "quantity", fromJson: _parseQuantity) int quantity, - @JsonKey(includeToJson: false, includeFromJson: false) + @JsonKey(name: 'nutrition', includeToJson: false) DailyNutrition? dailyNutrition, @JsonKey( name: 'product_nutritional_value_id', @@ -1359,7 +1359,7 @@ abstract class _$$DailyScheduleImplCopyWith<$Res> {@JsonKey(name: "name") String name, @JsonKey(name: "portion") Portion portion, @JsonKey(name: "quantity", fromJson: _parseQuantity) int quantity, - @JsonKey(includeToJson: false, includeFromJson: false) + @JsonKey(name: 'nutrition', includeToJson: false) DailyNutrition? dailyNutrition, @JsonKey( name: 'product_nutritional_value_id', @@ -1424,8 +1424,7 @@ class _$DailyScheduleImpl implements _DailySchedule { @JsonKey(name: "portion") required this.portion, @JsonKey(name: "quantity", fromJson: _parseQuantity) required this.quantity, - @JsonKey(includeToJson: false, includeFromJson: false) - this.dailyNutrition, + @JsonKey(name: 'nutrition', includeToJson: false) this.dailyNutrition, @JsonKey( name: 'product_nutritional_value_id', includeToJson: true, @@ -1446,7 +1445,7 @@ class _$DailyScheduleImpl implements _DailySchedule { @JsonKey(name: "quantity", fromJson: _parseQuantity) final int quantity; @override - @JsonKey(includeToJson: false, includeFromJson: false) + @JsonKey(name: 'nutrition', includeToJson: false) final DailyNutrition? dailyNutrition; @override @JsonKey( @@ -1504,7 +1503,7 @@ abstract class _DailySchedule implements DailySchedule { @JsonKey(name: "portion") required final Portion portion, @JsonKey(name: "quantity", fromJson: _parseQuantity) required final int quantity, - @JsonKey(includeToJson: false, includeFromJson: false) + @JsonKey(name: 'nutrition', includeToJson: false) final DailyNutrition? dailyNutrition, @JsonKey( name: 'product_nutritional_value_id', @@ -1526,7 +1525,7 @@ abstract class _DailySchedule implements DailySchedule { @JsonKey(name: "quantity", fromJson: _parseQuantity) int get quantity; @override - @JsonKey(includeToJson: false, includeFromJson: false) + @JsonKey(name: 'nutrition', includeToJson: false) DailyNutrition? get dailyNutrition; @override @JsonKey( diff --git a/lib/features/feed/models/diets_pagination_model.g.dart b/lib/features/feed/models/diets_pagination_model.g.dart index 021fbfcf..3e7099a5 100644 --- a/lib/features/feed/models/diets_pagination_model.g.dart +++ b/lib/features/feed/models/diets_pagination_model.g.dart @@ -117,6 +117,9 @@ _$DailyScheduleImpl _$$DailyScheduleImplFromJson(Map json) => name: json['name'] as String, portion: $enumDecode(_$PortionEnumMap, json['portion']), quantity: _parseQuantity(json['quantity']), + dailyNutrition: json['nutrition'] == null + ? null + : DailyNutrition.fromJson(json['nutrition'] as Map), ); Map _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) => diff --git a/lib/features/feed/models/recipes_model.dart b/lib/features/feed/models/recipes_model.dart index ff287211..f32fc011 100644 --- a/lib/features/feed/models/recipes_model.dart +++ b/lib/features/feed/models/recipes_model.dart @@ -5,12 +5,13 @@ part 'recipes_model.freezed.dart'; part 'recipes_model.g.dart'; @freezed -class Recipes with _$Recipes { - const factory Recipes({ +class Recipe with _$Recipe { + const factory Recipe({ required int id, required String image, required String state, @JsonKey(name: 'cooking_time') required int cookingTime, + @JsonKey(name: 'active_time') required int activeCookingTime, required List? ingredients, required String description, required List? steps, @@ -22,13 +23,14 @@ class Recipes with _$Recipes { required String slug, @JsonKey(name: 'total_comments') required int commentsCount, @JsonKey(name: 'was_liked_by_me') required bool wasLikedByMe, + @JsonKey(name: 'is_favorite') required bool isFavorite, @JsonKey(name: 'energy_per_serving') required double calories, @JsonKey(name: 'total_likes') required int likesCount, @JsonKey(name: 'total_views') required int viewsCount, required Author author, }) = _Recipes; - factory Recipes.fromJson(Map json) => _$RecipesFromJson(json); + factory Recipe.fromJson(Map json) => _$RecipeFromJson(json); } @freezed diff --git a/lib/features/feed/models/recipes_model.freezed.dart b/lib/features/feed/models/recipes_model.freezed.dart index 75d6ea7b..6123bc74 100644 --- a/lib/features/feed/models/recipes_model.freezed.dart +++ b/lib/features/feed/models/recipes_model.freezed.dart @@ -14,17 +14,19 @@ T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); -Recipes _$RecipesFromJson(Map json) { +Recipe _$RecipeFromJson(Map json) { return _Recipes.fromJson(json); } /// @nodoc -mixin _$Recipes { +mixin _$Recipe { int get id => throw _privateConstructorUsedError; String get image => throw _privateConstructorUsedError; String get state => throw _privateConstructorUsedError; @JsonKey(name: 'cooking_time') int get cookingTime => throw _privateConstructorUsedError; + @JsonKey(name: 'active_time') + int get activeCookingTime => throw _privateConstructorUsedError; List? get ingredients => throw _privateConstructorUsedError; String get description => throw _privateConstructorUsedError; List? get steps => throw _privateConstructorUsedError; @@ -41,6 +43,8 @@ mixin _$Recipes { int get commentsCount => throw _privateConstructorUsedError; @JsonKey(name: 'was_liked_by_me') bool get wasLikedByMe => throw _privateConstructorUsedError; + @JsonKey(name: 'is_favorite') + bool get isFavorite => throw _privateConstructorUsedError; @JsonKey(name: 'energy_per_serving') double get calories => throw _privateConstructorUsedError; @JsonKey(name: 'total_likes') @@ -49,25 +53,26 @@ mixin _$Recipes { int get viewsCount => throw _privateConstructorUsedError; Author get author => throw _privateConstructorUsedError; - /// Serializes this Recipes to a JSON map. + /// Serializes this Recipe to a JSON map. Map toJson() => throw _privateConstructorUsedError; - /// Create a copy of Recipes + /// Create a copy of Recipe /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - $RecipesCopyWith get copyWith => throw _privateConstructorUsedError; + $RecipeCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $RecipesCopyWith<$Res> { - factory $RecipesCopyWith(Recipes value, $Res Function(Recipes) then) = - _$RecipesCopyWithImpl<$Res, Recipes>; +abstract class $RecipeCopyWith<$Res> { + factory $RecipeCopyWith(Recipe value, $Res Function(Recipe) then) = + _$RecipeCopyWithImpl<$Res, Recipe>; @useResult $Res call( {int id, String image, String state, @JsonKey(name: 'cooking_time') int cookingTime, + @JsonKey(name: 'active_time') int activeCookingTime, List? ingredients, String description, List? steps, @@ -79,6 +84,7 @@ abstract class $RecipesCopyWith<$Res> { String slug, @JsonKey(name: 'total_comments') int commentsCount, @JsonKey(name: 'was_liked_by_me') bool wasLikedByMe, + @JsonKey(name: 'is_favorite') bool isFavorite, @JsonKey(name: 'energy_per_serving') double calories, @JsonKey(name: 'total_likes') int likesCount, @JsonKey(name: 'total_views') int viewsCount, @@ -88,16 +94,16 @@ abstract class $RecipesCopyWith<$Res> { } /// @nodoc -class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> - implements $RecipesCopyWith<$Res> { - _$RecipesCopyWithImpl(this._value, this._then); +class _$RecipeCopyWithImpl<$Res, $Val extends Recipe> + implements $RecipeCopyWith<$Res> { + _$RecipeCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; // ignore: unused_field final $Res Function($Val) _then; - /// Create a copy of Recipes + /// Create a copy of Recipe /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override @@ -106,6 +112,7 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> Object? image = null, Object? state = null, Object? cookingTime = null, + Object? activeCookingTime = null, Object? ingredients = freezed, Object? description = null, Object? steps = freezed, @@ -117,6 +124,7 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> Object? slug = null, Object? commentsCount = null, Object? wasLikedByMe = null, + Object? isFavorite = null, Object? calories = null, Object? likesCount = null, Object? viewsCount = null, @@ -139,6 +147,10 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> ? _value.cookingTime : cookingTime // ignore: cast_nullable_to_non_nullable as int, + activeCookingTime: null == activeCookingTime + ? _value.activeCookingTime + : activeCookingTime // ignore: cast_nullable_to_non_nullable + as int, ingredients: freezed == ingredients ? _value.ingredients : ingredients // ignore: cast_nullable_to_non_nullable @@ -183,6 +195,10 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> ? _value.wasLikedByMe : wasLikedByMe // ignore: cast_nullable_to_non_nullable as bool, + isFavorite: null == isFavorite + ? _value.isFavorite + : isFavorite // ignore: cast_nullable_to_non_nullable + as bool, calories: null == calories ? _value.calories : calories // ignore: cast_nullable_to_non_nullable @@ -202,7 +218,7 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> ) as $Val); } - /// Create a copy of Recipes + /// Create a copy of Recipe /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') @@ -214,7 +230,7 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> } /// @nodoc -abstract class _$$RecipesImplCopyWith<$Res> implements $RecipesCopyWith<$Res> { +abstract class _$$RecipesImplCopyWith<$Res> implements $RecipeCopyWith<$Res> { factory _$$RecipesImplCopyWith( _$RecipesImpl value, $Res Function(_$RecipesImpl) then) = __$$RecipesImplCopyWithImpl<$Res>; @@ -225,6 +241,7 @@ abstract class _$$RecipesImplCopyWith<$Res> implements $RecipesCopyWith<$Res> { String image, String state, @JsonKey(name: 'cooking_time') int cookingTime, + @JsonKey(name: 'active_time') int activeCookingTime, List? ingredients, String description, List? steps, @@ -236,6 +253,7 @@ abstract class _$$RecipesImplCopyWith<$Res> implements $RecipesCopyWith<$Res> { String slug, @JsonKey(name: 'total_comments') int commentsCount, @JsonKey(name: 'was_liked_by_me') bool wasLikedByMe, + @JsonKey(name: 'is_favorite') bool isFavorite, @JsonKey(name: 'energy_per_serving') double calories, @JsonKey(name: 'total_likes') int likesCount, @JsonKey(name: 'total_views') int viewsCount, @@ -247,13 +265,13 @@ abstract class _$$RecipesImplCopyWith<$Res> implements $RecipesCopyWith<$Res> { /// @nodoc class __$$RecipesImplCopyWithImpl<$Res> - extends _$RecipesCopyWithImpl<$Res, _$RecipesImpl> + extends _$RecipeCopyWithImpl<$Res, _$RecipesImpl> implements _$$RecipesImplCopyWith<$Res> { __$$RecipesImplCopyWithImpl( _$RecipesImpl _value, $Res Function(_$RecipesImpl) _then) : super(_value, _then); - /// Create a copy of Recipes + /// Create a copy of Recipe /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override @@ -262,6 +280,7 @@ class __$$RecipesImplCopyWithImpl<$Res> Object? image = null, Object? state = null, Object? cookingTime = null, + Object? activeCookingTime = null, Object? ingredients = freezed, Object? description = null, Object? steps = freezed, @@ -273,6 +292,7 @@ class __$$RecipesImplCopyWithImpl<$Res> Object? slug = null, Object? commentsCount = null, Object? wasLikedByMe = null, + Object? isFavorite = null, Object? calories = null, Object? likesCount = null, Object? viewsCount = null, @@ -295,6 +315,10 @@ class __$$RecipesImplCopyWithImpl<$Res> ? _value.cookingTime : cookingTime // ignore: cast_nullable_to_non_nullable as int, + activeCookingTime: null == activeCookingTime + ? _value.activeCookingTime + : activeCookingTime // ignore: cast_nullable_to_non_nullable + as int, ingredients: freezed == ingredients ? _value._ingredients : ingredients // ignore: cast_nullable_to_non_nullable @@ -339,6 +363,10 @@ class __$$RecipesImplCopyWithImpl<$Res> ? _value.wasLikedByMe : wasLikedByMe // ignore: cast_nullable_to_non_nullable as bool, + isFavorite: null == isFavorite + ? _value.isFavorite + : isFavorite // ignore: cast_nullable_to_non_nullable + as bool, calories: null == calories ? _value.calories : calories // ignore: cast_nullable_to_non_nullable @@ -367,6 +395,7 @@ class _$RecipesImpl implements _Recipes { required this.image, required this.state, @JsonKey(name: 'cooking_time') required this.cookingTime, + @JsonKey(name: 'active_time') required this.activeCookingTime, required final List? ingredients, required this.description, required final List? steps, @@ -378,6 +407,7 @@ class _$RecipesImpl implements _Recipes { required this.slug, @JsonKey(name: 'total_comments') required this.commentsCount, @JsonKey(name: 'was_liked_by_me') required this.wasLikedByMe, + @JsonKey(name: 'is_favorite') required this.isFavorite, @JsonKey(name: 'energy_per_serving') required this.calories, @JsonKey(name: 'total_likes') required this.likesCount, @JsonKey(name: 'total_views') required this.viewsCount, @@ -397,6 +427,9 @@ class _$RecipesImpl implements _Recipes { @override @JsonKey(name: 'cooking_time') final int cookingTime; + @override + @JsonKey(name: 'active_time') + final int activeCookingTime; final List? _ingredients; @override List? get ingredients { @@ -441,6 +474,9 @@ class _$RecipesImpl implements _Recipes { @JsonKey(name: 'was_liked_by_me') final bool wasLikedByMe; @override + @JsonKey(name: 'is_favorite') + final bool isFavorite; + @override @JsonKey(name: 'energy_per_serving') final double calories; @override @@ -454,7 +490,7 @@ class _$RecipesImpl implements _Recipes { @override String toString() { - return 'Recipes(id: $id, image: $image, state: $state, cookingTime: $cookingTime, ingredients: $ingredients, description: $description, steps: $steps, videoUrl: $videoUrl, createdAt: $createdAt, updatedAt: $updatedAt, title: $title, type: $type, slug: $slug, commentsCount: $commentsCount, wasLikedByMe: $wasLikedByMe, calories: $calories, likesCount: $likesCount, viewsCount: $viewsCount, author: $author)'; + return 'Recipe(id: $id, image: $image, state: $state, cookingTime: $cookingTime, activeCookingTime: $activeCookingTime, ingredients: $ingredients, description: $description, steps: $steps, videoUrl: $videoUrl, createdAt: $createdAt, updatedAt: $updatedAt, title: $title, type: $type, slug: $slug, commentsCount: $commentsCount, wasLikedByMe: $wasLikedByMe, isFavorite: $isFavorite, calories: $calories, likesCount: $likesCount, viewsCount: $viewsCount, author: $author)'; } @override @@ -467,6 +503,8 @@ class _$RecipesImpl implements _Recipes { (identical(other.state, state) || other.state == state) && (identical(other.cookingTime, cookingTime) || other.cookingTime == cookingTime) && + (identical(other.activeCookingTime, activeCookingTime) || + other.activeCookingTime == activeCookingTime) && const DeepCollectionEquality() .equals(other._ingredients, _ingredients) && (identical(other.description, description) || @@ -485,6 +523,8 @@ class _$RecipesImpl implements _Recipes { other.commentsCount == commentsCount) && (identical(other.wasLikedByMe, wasLikedByMe) || other.wasLikedByMe == wasLikedByMe) && + (identical(other.isFavorite, isFavorite) || + other.isFavorite == isFavorite) && (identical(other.calories, calories) || other.calories == calories) && (identical(other.likesCount, likesCount) || @@ -502,6 +542,7 @@ class _$RecipesImpl implements _Recipes { image, state, cookingTime, + activeCookingTime, const DeepCollectionEquality().hash(_ingredients), description, const DeepCollectionEquality().hash(_steps), @@ -513,13 +554,14 @@ class _$RecipesImpl implements _Recipes { slug, commentsCount, wasLikedByMe, + isFavorite, calories, likesCount, viewsCount, author ]); - /// Create a copy of Recipes + /// Create a copy of Recipe /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @@ -535,12 +577,13 @@ class _$RecipesImpl implements _Recipes { } } -abstract class _Recipes implements Recipes { +abstract class _Recipes implements Recipe { const factory _Recipes( {required final int id, required final String image, required final String state, @JsonKey(name: 'cooking_time') required final int cookingTime, + @JsonKey(name: 'active_time') required final int activeCookingTime, required final List? ingredients, required final String description, required final List? steps, @@ -552,6 +595,7 @@ abstract class _Recipes implements Recipes { required final String slug, @JsonKey(name: 'total_comments') required final int commentsCount, @JsonKey(name: 'was_liked_by_me') required final bool wasLikedByMe, + @JsonKey(name: 'is_favorite') required final bool isFavorite, @JsonKey(name: 'energy_per_serving') required final double calories, @JsonKey(name: 'total_likes') required final int likesCount, @JsonKey(name: 'total_views') required final int viewsCount, @@ -569,6 +613,9 @@ abstract class _Recipes implements Recipes { @JsonKey(name: 'cooking_time') int get cookingTime; @override + @JsonKey(name: 'active_time') + int get activeCookingTime; + @override List? get ingredients; @override String get description; @@ -596,6 +643,9 @@ abstract class _Recipes implements Recipes { @JsonKey(name: 'was_liked_by_me') bool get wasLikedByMe; @override + @JsonKey(name: 'is_favorite') + bool get isFavorite; + @override @JsonKey(name: 'energy_per_serving') double get calories; @override @@ -607,7 +657,7 @@ abstract class _Recipes implements Recipes { @override Author get author; - /// Create a copy of Recipes + /// Create a copy of Recipe /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) diff --git a/lib/features/feed/models/recipes_model.g.dart b/lib/features/feed/models/recipes_model.g.dart index 00da0654..c0a684d9 100644 --- a/lib/features/feed/models/recipes_model.g.dart +++ b/lib/features/feed/models/recipes_model.g.dart @@ -12,6 +12,7 @@ _$RecipesImpl _$$RecipesImplFromJson(Map json) => image: json['image'] as String, state: json['state'] as String, cookingTime: (json['cooking_time'] as num).toInt(), + activeCookingTime: (json['active_time'] as num).toInt(), ingredients: (json['ingredients'] as List?) ?.map((e) => Ingredient.fromJson(e as Map)) .toList(), @@ -27,6 +28,7 @@ _$RecipesImpl _$$RecipesImplFromJson(Map json) => slug: json['slug'] as String, commentsCount: (json['total_comments'] as num).toInt(), wasLikedByMe: json['was_liked_by_me'] as bool, + isFavorite: json['is_favorite'] as bool, calories: (json['energy_per_serving'] as num).toDouble(), likesCount: (json['total_likes'] as num).toInt(), viewsCount: (json['total_views'] as num).toInt(), @@ -39,6 +41,7 @@ Map _$$RecipesImplToJson(_$RecipesImpl instance) => 'image': instance.image, 'state': instance.state, 'cooking_time': instance.cookingTime, + 'active_time': instance.activeCookingTime, 'ingredients': instance.ingredients, 'description': instance.description, 'steps': instance.steps, @@ -50,6 +53,7 @@ Map _$$RecipesImplToJson(_$RecipesImpl instance) => 'slug': instance.slug, 'total_comments': instance.commentsCount, 'was_liked_by_me': instance.wasLikedByMe, + 'is_favorite': instance.isFavorite, 'energy_per_serving': instance.calories, 'total_likes': instance.likesCount, 'total_views': instance.viewsCount, diff --git a/lib/features/feed/models/recipes_pagination_model.dart b/lib/features/feed/models/recipes_pagination_model.dart index 25384082..315266a1 100644 --- a/lib/features/feed/models/recipes_pagination_model.dart +++ b/lib/features/feed/models/recipes_pagination_model.dart @@ -8,7 +8,7 @@ part 'recipes_pagination_model.g.dart'; @freezed class RecipesPagination with _$RecipesPagination { const factory RecipesPagination({ - @JsonKey(name: 'data') required List recipesList, + @JsonKey(name: 'data') required List recipesList, // @JsonKey(name: "current_page") required int currentPage, // @JsonKey(name: "first_page_url") required String firstPageUrl, // required int from, diff --git a/lib/features/feed/models/recipes_pagination_model.freezed.dart b/lib/features/feed/models/recipes_pagination_model.freezed.dart index ca8e9f92..4e05ed5a 100644 --- a/lib/features/feed/models/recipes_pagination_model.freezed.dart +++ b/lib/features/feed/models/recipes_pagination_model.freezed.dart @@ -21,7 +21,7 @@ RecipesPagination _$RecipesPaginationFromJson(Map json) { /// @nodoc mixin _$RecipesPagination { @JsonKey(name: 'data') - List get recipesList => throw _privateConstructorUsedError; + List get recipesList => throw _privateConstructorUsedError; /// Serializes this RecipesPagination to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -39,7 +39,7 @@ abstract class $RecipesPaginationCopyWith<$Res> { RecipesPagination value, $Res Function(RecipesPagination) then) = _$RecipesPaginationCopyWithImpl<$Res, RecipesPagination>; @useResult - $Res call({@JsonKey(name: 'data') List recipesList}); + $Res call({@JsonKey(name: 'data') List recipesList}); } /// @nodoc @@ -63,7 +63,7 @@ class _$RecipesPaginationCopyWithImpl<$Res, $Val extends RecipesPagination> recipesList: null == recipesList ? _value.recipesList : recipesList // ignore: cast_nullable_to_non_nullable - as List, + as List, ) as $Val); } } @@ -76,7 +76,7 @@ abstract class _$$RecipesPaginationImplCopyWith<$Res> __$$RecipesPaginationImplCopyWithImpl<$Res>; @override @useResult - $Res call({@JsonKey(name: 'data') List recipesList}); + $Res call({@JsonKey(name: 'data') List recipesList}); } /// @nodoc @@ -98,7 +98,7 @@ class __$$RecipesPaginationImplCopyWithImpl<$Res> recipesList: null == recipesList ? _value._recipesList : recipesList // ignore: cast_nullable_to_non_nullable - as List, + as List, )); } } @@ -107,16 +107,16 @@ class __$$RecipesPaginationImplCopyWithImpl<$Res> @JsonSerializable() class _$RecipesPaginationImpl implements _RecipesPagination { const _$RecipesPaginationImpl( - {@JsonKey(name: 'data') required final List recipesList}) + {@JsonKey(name: 'data') required final List recipesList}) : _recipesList = recipesList; factory _$RecipesPaginationImpl.fromJson(Map json) => _$$RecipesPaginationImplFromJson(json); - final List _recipesList; + final List _recipesList; @override @JsonKey(name: 'data') - List get recipesList { + List get recipesList { if (_recipesList is EqualUnmodifiableListView) return _recipesList; // ignore: implicit_dynamic_type return EqualUnmodifiableListView(_recipesList); @@ -160,7 +160,7 @@ class _$RecipesPaginationImpl implements _RecipesPagination { abstract class _RecipesPagination implements RecipesPagination { const factory _RecipesPagination( - {@JsonKey(name: 'data') required final List recipesList}) = + {@JsonKey(name: 'data') required final List recipesList}) = _$RecipesPaginationImpl; factory _RecipesPagination.fromJson(Map json) = @@ -168,7 +168,7 @@ abstract class _RecipesPagination implements RecipesPagination { @override @JsonKey(name: 'data') - List get recipesList; + List get recipesList; /// Create a copy of RecipesPagination /// with the given fields replaced by the non-null parameter values. diff --git a/lib/features/feed/models/recipes_pagination_model.g.dart b/lib/features/feed/models/recipes_pagination_model.g.dart index 109f39d5..ab5e7326 100644 --- a/lib/features/feed/models/recipes_pagination_model.g.dart +++ b/lib/features/feed/models/recipes_pagination_model.g.dart @@ -10,7 +10,7 @@ _$RecipesPaginationImpl _$$RecipesPaginationImplFromJson( Map json) => _$RecipesPaginationImpl( recipesList: (json['data'] as List) - .map((e) => Recipes.fromJson(e as Map)) + .map((e) => Recipe.fromJson(e as Map)) .toList(), ); diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart index ad9c01ad..58e36983 100644 --- a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart @@ -1,23 +1,46 @@ +import 'dart:async'; + +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart'; import 'package:eatvio_app/features/feed/repositories/feed_repository.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart'; -import 'package:eatvio_app/features/recipes/reporitories/recipe_repository.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_tag_model.dart'; +import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart'; +import 'package:eatvio_app/features/recipe/repositories/about_recipe_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'dart:developer'; +import 'package:go_router/go_router.dart'; + class RecipesCubit extends Cubit { final FeedRepository feedRepository; final RecipeRepository recipeRepository; + final AboutRecipeRepository aboutRecipeRepository; Set _selectedTags = {}; int currentPage = 1; late int lastPage; bool isLoading = false; String? _query; + late final StreamSubscription likesStateSubscription; + late final StreamSubscription favoriteStateSubscription; + late final StreamSubscription commentsStateSubscription; RecipesCubit({ required this.feedRepository, required this.recipeRepository, - }) : super(const RecipesState.loading()); + required this.aboutRecipeRepository, + }) : super(RecipesState.initial()) { + likesStateSubscription = aboutRecipeRepository.recipeLikeState.listen((likesState) { + _changeRecipeLikeState(likesState); + }); + favoriteStateSubscription = aboutRecipeRepository.recipeFavoriteState.listen((favoriteState) { + _changeRecipeFavoriteState(favoriteState); + }); + commentsStateSubscription = aboutRecipeRepository.recipeCommentsState.listen((commentsState) { + _changeRecipeCommentsState(commentsState); + }); + } loadRecipesList({ String? query, @@ -28,9 +51,10 @@ class RecipesCubit extends Cubit { tags: _selectedTags.toList(), query: query, ); + final recipesTags = (await recipeRepository.getRecipesTags()); emit( - RecipesState.loaded( + state.copyWith( recipesList: recipesList, recipesTags: recipesTags, selectedTags: _selectedTags, @@ -41,7 +65,7 @@ class RecipesCubit extends Cubit { lastPage = currentPage; } catch (e, st) { log(e.toString(), stackTrace: st); - emit(RecipesState.error(errorMessage: e.toString())); + emit(state.copyWith(errorMessage: e.toString())); } } @@ -49,7 +73,7 @@ class RecipesCubit extends Cubit { if (currentPage < lastPage && !isLoading) { isLoading = true; - emit((state as RecipesStateLoaded).copyWith(isLoading: isLoading)); + emit(state.copyWith(isLoading: isLoading)); try { if (currentPage < lastPage) { @@ -61,9 +85,9 @@ class RecipesCubit extends Cubit { ); isLoading = false; emit( - (state as RecipesStateLoaded).copyWith( + state.copyWith( recipesList: [ - ...(state as RecipesStateLoaded).recipesList, + ...state.recipesList, ...recipesList, ], isLoading: isLoading, @@ -72,7 +96,7 @@ class RecipesCubit extends Cubit { } lastPage = currentPage; } catch (e) { - emit(RecipesState.error(errorMessage: e.toString())); + emit(state.copyWith(errorMessage: e.toString())); } } } @@ -80,14 +104,202 @@ class RecipesCubit extends Cubit { updateTags(RecipeTag tag) { _selectedTags.contains(tag.id) ? _selectedTags.remove(tag.id) : _selectedTags.add(tag.id); emit( - (state as RecipesStateLoaded).copyWith( + state.copyWith( selectedTags: _selectedTags, selectedTagsCounter: _selectedTags.length, ), ); } - likeRecipe(String slug) async => await feedRepository.likeRecipe(slug: slug); + Future addRecipeToFavorite(String slug, int index) async { + final currentRecipes = state.recipesList; + final recipesForModification = [...currentRecipes]; + final currentRecipe = currentRecipes[index]; - unlikeRecipe(String slug) async => await feedRepository.unlikeRecipe(slug: slug); + recipesForModification[index] = currentRecipe.copyWith( + isFavorite: true, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + EasyDebounce.debounce( + 'add_or_remove_plan_from_favorite', + Duration(milliseconds: 200), + () async { + try { + await aboutRecipeRepository.addToFavorite(slug); + } catch (e) { + log(e.toString()); + showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); + emit( + state.copyWith( + recipesList: [ + ...currentRecipes, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } + }, + ); + } + + Future removeRecipeFromFavorite(String slug, int index) async { + final currentRecipes = state.recipesList; + final recipesForModification = [...currentRecipes]; + final currentRecipe = currentRecipes[index]; + + recipesForModification[index] = currentRecipe.copyWith( + isFavorite: false, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + EasyDebounce.debounce( + 'add_or_remove_plan_from_favorite', + Duration(milliseconds: 200), + () async { + try { + await aboutRecipeRepository.removeFromFavorite(slug); + } catch (e) { + log(e.toString()); + showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); + emit( + state.copyWith( + recipesList: [ + ...currentRecipes, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } + }, + ); + } + + Future likeRecipe(String slug, int index) async { + final currentRecipes = state.recipesList; + final recipesForModification = [...currentRecipes]; + final currentRecipe = currentRecipes[index]; + if (currentRecipe.wasLikedByMe) { + recipesForModification[index] = currentRecipe.copyWith( + likesCount: currentRecipe.likesCount - 1, + wasLikedByMe: false, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } else { + recipesForModification[index] = currentRecipe.copyWith( + likesCount: currentRecipe.likesCount + 1, + wasLikedByMe: true, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } + EasyDebounce.debounce( + 'like_plan', + Duration(milliseconds: 200), + () async { + try { + await aboutRecipeRepository.likeRecipe(slug); + } catch (e) { + log(e.toString()); + emit( + state.copyWith( + recipesList: [ + ...currentRecipes, + ], + isLoading: isLoading, + ), + ); + } + }, + ); + } + + void _changeRecipeLikeState(LikesState likesState) { + final currentRecipes = state.recipesList; + final recipesForModification = [...currentRecipes]; + final currentRecipe = currentRecipes[likesState.index]; + + recipesForModification[likesState.index] = currentRecipe.copyWith( + likesCount: likesState.totalLikes, + wasLikedByMe: likesState.wasLikedByMe, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } + + void _changeRecipeFavoriteState(FavoriteState favoriteState) { + final currentRecipes = state.recipesList; + final recipesForModification = [...currentRecipes]; + final currentRecipe = currentRecipes[favoriteState.index]; + + recipesForModification[favoriteState.index] = currentRecipe.copyWith( + isFavorite: favoriteState.isFavorite, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } + + void _changeRecipeCommentsState(CommentsState commentsState) { + final currentRecipes = state.recipesList; + final recipesForModification = [...currentRecipes]; + final currentRecipe = currentRecipes[commentsState.index]; + + recipesForModification[commentsState.index] = currentRecipe.copyWith( + commentsCount: commentsState.totalComments, + ); + emit( + state.copyWith( + recipesList: [ + ...recipesForModification, + ], + isLoading: isLoading, + errorMessage: null, + ), + ); + } } diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart index dc6dcb66..74db94f7 100644 --- a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart @@ -1,24 +1,26 @@ import 'package:eatvio_app/features/feed/models/recipes_model.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_tag_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'recipes_state.freezed.dart'; @freezed abstract class RecipesState with _$RecipesState { - const factory RecipesState() = _RecipesState; - - const factory RecipesState.loading() = RecipesStateLoading; - - const factory RecipesState.loaded({ - required List recipesList, + const factory RecipesState({ + required List recipesList, required List recipesTags, required Set selectedTags, required int selectedTagsCounter, required bool isLoading, - }) = RecipesStateLoaded; + String? errorMessage, + }) = _RecipesState; - const factory RecipesState.error({ - required String errorMessage, - }) = RecipesStateError; + factory RecipesState.initial() => RecipesState( + recipesList: [], + recipesTags: [], + selectedTags: {}, + selectedTagsCounter: 0, + isLoading: true, + errorMessage: null, + ); } diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart index 34100adb..b5afc8b1 100644 --- a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart @@ -16,65 +16,17 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$RecipesState { - @optionalTypeArgs - TResult when( - TResult Function() $default, { - required TResult Function() loading, - required TResult Function( - List recipesList, - List recipesTags, - Set selectedTags, - int selectedTagsCounter, - bool isLoading) - loaded, - required TResult Function(String errorMessage) error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function()? $default, { - TResult? Function()? loading, - TResult? Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult? Function(String errorMessage)? error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen( - TResult Function()? $default, { - TResult Function()? loading, - TResult Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult Function(String errorMessage)? error, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map( - TResult Function(_RecipesState value) $default, { - required TResult Function(RecipesStateLoading value) loading, - required TResult Function(RecipesStateLoaded value) loaded, - required TResult Function(RecipesStateError value) error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(_RecipesState value)? $default, { - TResult? Function(RecipesStateLoading value)? loading, - TResult? Function(RecipesStateLoaded value)? loaded, - TResult? Function(RecipesStateError value)? error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap( - TResult Function(_RecipesState value)? $default, { - TResult Function(RecipesStateLoading value)? loading, - TResult Function(RecipesStateLoaded value)? loaded, - TResult Function(RecipesStateError value)? error, - required TResult orElse(), - }) => + List get recipesList => throw _privateConstructorUsedError; + List get recipesTags => throw _privateConstructorUsedError; + Set get selectedTags => throw _privateConstructorUsedError; + int get selectedTagsCounter => throw _privateConstructorUsedError; + bool get isLoading => throw _privateConstructorUsedError; + String? get errorMessage => throw _privateConstructorUsedError; + + /// Create a copy of RecipesState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $RecipesStateCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -83,6 +35,14 @@ abstract class $RecipesStateCopyWith<$Res> { factory $RecipesStateCopyWith( RecipesState value, $Res Function(RecipesState) then) = _$RecipesStateCopyWithImpl<$Res, RecipesState>; + @useResult + $Res call( + {List recipesList, + List recipesTags, + Set selectedTags, + int selectedTagsCounter, + bool isLoading, + String? errorMessage}); } /// @nodoc @@ -97,13 +57,60 @@ class _$RecipesStateCopyWithImpl<$Res, $Val extends RecipesState> /// Create a copy of RecipesState /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recipesList = null, + Object? recipesTags = null, + Object? selectedTags = null, + Object? selectedTagsCounter = null, + Object? isLoading = null, + Object? errorMessage = freezed, + }) { + return _then(_value.copyWith( + recipesList: null == recipesList + ? _value.recipesList + : recipesList // ignore: cast_nullable_to_non_nullable + as List, + recipesTags: null == recipesTags + ? _value.recipesTags + : recipesTags // ignore: cast_nullable_to_non_nullable + as List, + selectedTags: null == selectedTags + ? _value.selectedTags + : selectedTags // ignore: cast_nullable_to_non_nullable + as Set, + selectedTagsCounter: null == selectedTagsCounter + ? _value.selectedTagsCounter + : selectedTagsCounter // ignore: cast_nullable_to_non_nullable + as int, + isLoading: null == isLoading + ? _value.isLoading + : isLoading // ignore: cast_nullable_to_non_nullable + as bool, + errorMessage: freezed == errorMessage + ? _value.errorMessage + : errorMessage // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } } /// @nodoc -abstract class _$$RecipesStateImplCopyWith<$Res> { +abstract class _$$RecipesStateImplCopyWith<$Res> + implements $RecipesStateCopyWith<$Res> { factory _$$RecipesStateImplCopyWith( _$RecipesStateImpl value, $Res Function(_$RecipesStateImpl) then) = __$$RecipesStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {List recipesList, + List recipesTags, + Set selectedTags, + int selectedTagsCounter, + bool isLoading, + String? errorMessage}); } /// @nodoc @@ -116,268 +123,6 @@ class __$$RecipesStateImplCopyWithImpl<$Res> /// Create a copy of RecipesState /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc - -class _$RecipesStateImpl implements _RecipesState { - const _$RecipesStateImpl(); - - @override - String toString() { - return 'RecipesState()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$RecipesStateImpl); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when( - TResult Function() $default, { - required TResult Function() loading, - required TResult Function( - List recipesList, - List recipesTags, - Set selectedTags, - int selectedTagsCounter, - bool isLoading) - loaded, - required TResult Function(String errorMessage) error, - }) { - return $default(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function()? $default, { - TResult? Function()? loading, - TResult? Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult? Function(String errorMessage)? error, - }) { - return $default?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function()? $default, { - TResult Function()? loading, - TResult Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult Function(String errorMessage)? error, - required TResult orElse(), - }) { - if ($default != null) { - return $default(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(_RecipesState value) $default, { - required TResult Function(RecipesStateLoading value) loading, - required TResult Function(RecipesStateLoaded value) loaded, - required TResult Function(RecipesStateError value) error, - }) { - return $default(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(_RecipesState value)? $default, { - TResult? Function(RecipesStateLoading value)? loading, - TResult? Function(RecipesStateLoaded value)? loaded, - TResult? Function(RecipesStateError value)? error, - }) { - return $default?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(_RecipesState value)? $default, { - TResult Function(RecipesStateLoading value)? loading, - TResult Function(RecipesStateLoaded value)? loaded, - TResult Function(RecipesStateError value)? error, - required TResult orElse(), - }) { - if ($default != null) { - return $default(this); - } - return orElse(); - } -} - -abstract class _RecipesState implements RecipesState { - const factory _RecipesState() = _$RecipesStateImpl; -} - -/// @nodoc -abstract class _$$RecipesStateLoadingImplCopyWith<$Res> { - factory _$$RecipesStateLoadingImplCopyWith(_$RecipesStateLoadingImpl value, - $Res Function(_$RecipesStateLoadingImpl) then) = - __$$RecipesStateLoadingImplCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$RecipesStateLoadingImplCopyWithImpl<$Res> - extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateLoadingImpl> - implements _$$RecipesStateLoadingImplCopyWith<$Res> { - __$$RecipesStateLoadingImplCopyWithImpl(_$RecipesStateLoadingImpl _value, - $Res Function(_$RecipesStateLoadingImpl) _then) - : super(_value, _then); - - /// Create a copy of RecipesState - /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc - -class _$RecipesStateLoadingImpl implements RecipesStateLoading { - const _$RecipesStateLoadingImpl(); - - @override - String toString() { - return 'RecipesState.loading()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$RecipesStateLoadingImpl); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when( - TResult Function() $default, { - required TResult Function() loading, - required TResult Function( - List recipesList, - List recipesTags, - Set selectedTags, - int selectedTagsCounter, - bool isLoading) - loaded, - required TResult Function(String errorMessage) error, - }) { - return loading(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function()? $default, { - TResult? Function()? loading, - TResult? Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult? Function(String errorMessage)? error, - }) { - return loading?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function()? $default, { - TResult Function()? loading, - TResult Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult Function(String errorMessage)? error, - required TResult orElse(), - }) { - if (loading != null) { - return loading(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(_RecipesState value) $default, { - required TResult Function(RecipesStateLoading value) loading, - required TResult Function(RecipesStateLoaded value) loaded, - required TResult Function(RecipesStateError value) error, - }) { - return loading(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(_RecipesState value)? $default, { - TResult? Function(RecipesStateLoading value)? loading, - TResult? Function(RecipesStateLoaded value)? loaded, - TResult? Function(RecipesStateError value)? error, - }) { - return loading?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(_RecipesState value)? $default, { - TResult Function(RecipesStateLoading value)? loading, - TResult Function(RecipesStateLoaded value)? loaded, - TResult Function(RecipesStateError value)? error, - required TResult orElse(), - }) { - if (loading != null) { - return loading(this); - } - return orElse(); - } -} - -abstract class RecipesStateLoading implements RecipesState { - const factory RecipesStateLoading() = _$RecipesStateLoadingImpl; -} - -/// @nodoc -abstract class _$$RecipesStateLoadedImplCopyWith<$Res> { - factory _$$RecipesStateLoadedImplCopyWith(_$RecipesStateLoadedImpl value, - $Res Function(_$RecipesStateLoadedImpl) then) = - __$$RecipesStateLoadedImplCopyWithImpl<$Res>; - @useResult - $Res call( - {List recipesList, - List recipesTags, - Set selectedTags, - int selectedTagsCounter, - bool isLoading}); -} - -/// @nodoc -class __$$RecipesStateLoadedImplCopyWithImpl<$Res> - extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateLoadedImpl> - implements _$$RecipesStateLoadedImplCopyWith<$Res> { - __$$RecipesStateLoadedImplCopyWithImpl(_$RecipesStateLoadedImpl _value, - $Res Function(_$RecipesStateLoadedImpl) _then) - : super(_value, _then); - - /// Create a copy of RecipesState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -386,12 +131,13 @@ class __$$RecipesStateLoadedImplCopyWithImpl<$Res> Object? selectedTags = null, Object? selectedTagsCounter = null, Object? isLoading = null, + Object? errorMessage = freezed, }) { - return _then(_$RecipesStateLoadedImpl( + return _then(_$RecipesStateImpl( recipesList: null == recipesList ? _value._recipesList : recipesList // ignore: cast_nullable_to_non_nullable - as List, + as List, recipesTags: null == recipesTags ? _value._recipesTags : recipesTags // ignore: cast_nullable_to_non_nullable @@ -408,26 +154,31 @@ class __$$RecipesStateLoadedImplCopyWithImpl<$Res> ? _value.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool, + errorMessage: freezed == errorMessage + ? _value.errorMessage + : errorMessage // ignore: cast_nullable_to_non_nullable + as String?, )); } } /// @nodoc -class _$RecipesStateLoadedImpl implements RecipesStateLoaded { - const _$RecipesStateLoadedImpl( - {required final List recipesList, +class _$RecipesStateImpl implements _RecipesState { + const _$RecipesStateImpl( + {required final List recipesList, required final List recipesTags, required final Set selectedTags, required this.selectedTagsCounter, - required this.isLoading}) + required this.isLoading, + this.errorMessage}) : _recipesList = recipesList, _recipesTags = recipesTags, _selectedTags = selectedTags; - final List _recipesList; + final List _recipesList; @override - List get recipesList { + List get recipesList { if (_recipesList is EqualUnmodifiableListView) return _recipesList; // ignore: implicit_dynamic_type return EqualUnmodifiableListView(_recipesList); @@ -453,17 +204,19 @@ class _$RecipesStateLoadedImpl implements RecipesStateLoaded { final int selectedTagsCounter; @override final bool isLoading; + @override + final String? errorMessage; @override String toString() { - return 'RecipesState.loaded(recipesList: $recipesList, recipesTags: $recipesTags, selectedTags: $selectedTags, selectedTagsCounter: $selectedTagsCounter, isLoading: $isLoading)'; + return 'RecipesState(recipesList: $recipesList, recipesTags: $recipesTags, selectedTags: $selectedTags, selectedTagsCounter: $selectedTagsCounter, isLoading: $isLoading, errorMessage: $errorMessage)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$RecipesStateLoadedImpl && + other is _$RecipesStateImpl && const DeepCollectionEquality() .equals(other._recipesList, _recipesList) && const DeepCollectionEquality() @@ -473,7 +226,9 @@ class _$RecipesStateLoadedImpl implements RecipesStateLoaded { (identical(other.selectedTagsCounter, selectedTagsCounter) || other.selectedTagsCounter == selectedTagsCounter) && (identical(other.isLoading, isLoading) || - other.isLoading == isLoading)); + other.isLoading == isLoading) && + (identical(other.errorMessage, errorMessage) || + other.errorMessage == errorMessage)); } @override @@ -483,287 +238,44 @@ class _$RecipesStateLoadedImpl implements RecipesStateLoaded { const DeepCollectionEquality().hash(_recipesTags), const DeepCollectionEquality().hash(_selectedTags), selectedTagsCounter, - isLoading); + isLoading, + errorMessage); /// Create a copy of RecipesState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') - _$$RecipesStateLoadedImplCopyWith<_$RecipesStateLoadedImpl> get copyWith => - __$$RecipesStateLoadedImplCopyWithImpl<_$RecipesStateLoadedImpl>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when( - TResult Function() $default, { - required TResult Function() loading, - required TResult Function( - List recipesList, - List recipesTags, - Set selectedTags, - int selectedTagsCounter, - bool isLoading) - loaded, - required TResult Function(String errorMessage) error, - }) { - return loaded( - recipesList, recipesTags, selectedTags, selectedTagsCounter, isLoading); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function()? $default, { - TResult? Function()? loading, - TResult? Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult? Function(String errorMessage)? error, - }) { - return loaded?.call( - recipesList, recipesTags, selectedTags, selectedTagsCounter, isLoading); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function()? $default, { - TResult Function()? loading, - TResult Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult Function(String errorMessage)? error, - required TResult orElse(), - }) { - if (loaded != null) { - return loaded(recipesList, recipesTags, selectedTags, selectedTagsCounter, - isLoading); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(_RecipesState value) $default, { - required TResult Function(RecipesStateLoading value) loading, - required TResult Function(RecipesStateLoaded value) loaded, - required TResult Function(RecipesStateError value) error, - }) { - return loaded(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(_RecipesState value)? $default, { - TResult? Function(RecipesStateLoading value)? loading, - TResult? Function(RecipesStateLoaded value)? loaded, - TResult? Function(RecipesStateError value)? error, - }) { - return loaded?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(_RecipesState value)? $default, { - TResult Function(RecipesStateLoading value)? loading, - TResult Function(RecipesStateLoaded value)? loaded, - TResult Function(RecipesStateError value)? error, - required TResult orElse(), - }) { - if (loaded != null) { - return loaded(this); - } - return orElse(); - } + _$$RecipesStateImplCopyWith<_$RecipesStateImpl> get copyWith => + __$$RecipesStateImplCopyWithImpl<_$RecipesStateImpl>(this, _$identity); } -abstract class RecipesStateLoaded implements RecipesState { - const factory RecipesStateLoaded( - {required final List recipesList, +abstract class _RecipesState implements RecipesState { + const factory _RecipesState( + {required final List recipesList, required final List recipesTags, required final Set selectedTags, required final int selectedTagsCounter, - required final bool isLoading}) = _$RecipesStateLoadedImpl; + required final bool isLoading, + final String? errorMessage}) = _$RecipesStateImpl; - List get recipesList; + @override + List get recipesList; + @override List get recipesTags; + @override Set get selectedTags; + @override int get selectedTagsCounter; + @override bool get isLoading; + @override + String? get errorMessage; /// Create a copy of RecipesState /// with the given fields replaced by the non-null parameter values. + @override @JsonKey(includeFromJson: false, includeToJson: false) - _$$RecipesStateLoadedImplCopyWith<_$RecipesStateLoadedImpl> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$RecipesStateErrorImplCopyWith<$Res> { - factory _$$RecipesStateErrorImplCopyWith(_$RecipesStateErrorImpl value, - $Res Function(_$RecipesStateErrorImpl) then) = - __$$RecipesStateErrorImplCopyWithImpl<$Res>; - @useResult - $Res call({String errorMessage}); -} - -/// @nodoc -class __$$RecipesStateErrorImplCopyWithImpl<$Res> - extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateErrorImpl> - implements _$$RecipesStateErrorImplCopyWith<$Res> { - __$$RecipesStateErrorImplCopyWithImpl(_$RecipesStateErrorImpl _value, - $Res Function(_$RecipesStateErrorImpl) _then) - : super(_value, _then); - - /// Create a copy of RecipesState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? errorMessage = null, - }) { - return _then(_$RecipesStateErrorImpl( - errorMessage: null == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc - -class _$RecipesStateErrorImpl implements RecipesStateError { - const _$RecipesStateErrorImpl({required this.errorMessage}); - - @override - final String errorMessage; - - @override - String toString() { - return 'RecipesState.error(errorMessage: $errorMessage)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$RecipesStateErrorImpl && - (identical(other.errorMessage, errorMessage) || - other.errorMessage == errorMessage)); - } - - @override - int get hashCode => Object.hash(runtimeType, errorMessage); - - /// Create a copy of RecipesState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$RecipesStateErrorImplCopyWith<_$RecipesStateErrorImpl> get copyWith => - __$$RecipesStateErrorImplCopyWithImpl<_$RecipesStateErrorImpl>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when( - TResult Function() $default, { - required TResult Function() loading, - required TResult Function( - List recipesList, - List recipesTags, - Set selectedTags, - int selectedTagsCounter, - bool isLoading) - loaded, - required TResult Function(String errorMessage) error, - }) { - return error(errorMessage); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function()? $default, { - TResult? Function()? loading, - TResult? Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult? Function(String errorMessage)? error, - }) { - return error?.call(errorMessage); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function()? $default, { - TResult Function()? loading, - TResult Function(List recipesList, List recipesTags, - Set selectedTags, int selectedTagsCounter, bool isLoading)? - loaded, - TResult Function(String errorMessage)? error, - required TResult orElse(), - }) { - if (error != null) { - return error(errorMessage); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(_RecipesState value) $default, { - required TResult Function(RecipesStateLoading value) loading, - required TResult Function(RecipesStateLoaded value) loaded, - required TResult Function(RecipesStateError value) error, - }) { - return error(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(_RecipesState value)? $default, { - TResult? Function(RecipesStateLoading value)? loading, - TResult? Function(RecipesStateLoaded value)? loaded, - TResult? Function(RecipesStateError value)? error, - }) { - return error?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(_RecipesState value)? $default, { - TResult Function(RecipesStateLoading value)? loading, - TResult Function(RecipesStateLoaded value)? loaded, - TResult Function(RecipesStateError value)? error, - required TResult orElse(), - }) { - if (error != null) { - return error(this); - } - return orElse(); - } -} - -abstract class RecipesStateError implements RecipesState { - const factory RecipesStateError({required final String errorMessage}) = - _$RecipesStateErrorImpl; - - String get errorMessage; - - /// Create a copy of RecipesState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$RecipesStateErrorImplCopyWith<_$RecipesStateErrorImpl> get copyWith => + _$$RecipesStateImplCopyWith<_$RecipesStateImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/features/feed/presentation/pages/feed_screen.dart b/lib/features/feed/presentation/pages/feed_screen.dart index 4527c0af..a6b8aff7 100644 --- a/lib/features/feed/presentation/pages/feed_screen.dart +++ b/lib/features/feed/presentation/pages/feed_screen.dart @@ -20,7 +20,7 @@ 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:eatvio_app/features/create_recipe/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'; diff --git a/lib/features/feed/presentation/widgets/diets_widget.dart b/lib/features/feed/presentation/widgets/diets_widget.dart index d42d7da3..cbb233a2 100644 --- a/lib/features/feed/presentation/widgets/diets_widget.dart +++ b/lib/features/feed/presentation/widgets/diets_widget.dart @@ -1,5 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/enums.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; @@ -30,21 +31,27 @@ class DietsWidget extends StatefulWidget { class _DietsWidgetState extends State { @override Widget build(BuildContext context) { - return DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, + return InkWell( + onTap: () => GoRouter.of(context).pushNamed( + 'aboutDietScreen', + extra: { + 'diet': widget.diet, + 'index': widget.index, + 'navigateToComments': false, + }, ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - InkWell( - onTap: () => - GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'diet': widget.diet, 'index': widget.index, 'navigateToComments': false}), - child: ClipRRect( + child: Ink( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + ClipRRect( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), child: CachedNetworkImage( imageUrl: baseUrl + '/storage/' + widget.diet.image, @@ -52,123 +59,124 @@ class _DietsWidgetState extends State { width: MediaQuery.of(context).size.width, ), ), - ), - Positioned( - right: 14, - child: BookmarkButton( - onTap: () async { - if (widget.diet.isFavorite) { - await context.read().removeDietFromFavorite(widget.diet.id, widget.index); - } else { - await context.read().addDietToFavorite(widget.diet.id, widget.index); - } - }, - color: appTheme.whiteA700, - isFavorite: widget.diet.isFavorite, - borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), - width: 40, - height: 44, - size: 19, - ), - ), - Positioned( - bottom: 16, - child: SizedBox( - width: MediaQuery.sizeOf(context).width, - child: InfoRow( - diet: widget.diet, - itemsBackgroundColor: appTheme.whiteA700, - disableDescriptionOfTypes: true, - ), - ), - ), - ], - ), - SizedBox(height: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - widget.diet.name, - style: TextStyles.textMSemibold.copyWith(color: appTheme.black), - ), - ), - if (widget.diet.details.isNotEmpty) ...[ - SizedBox( - height: 12, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Html( - data: widget.diet.details.length > 200 ? widget.diet.details.substring(0, 200) + '...' : widget.diet.details, - style: { - "*": Style( - margin: Margins.zero, - color: appTheme.black, - fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14), - fontWeight: TextStyles.textSMedium.fontWeight ?? FontWeight.w400, - ), + Positioned( + right: 14, + child: BookmarkButton( + onTap: () async { + if (widget.diet.isFavorite) { + await context.read().removeDietFromFavorite(widget.diet.id, widget.index); + } else { + await context.read().addDietToFavorite(widget.diet.id, widget.index); + } }, + color: appTheme.whiteA700, + isFavorite: widget.diet.isFavorite, + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + width: 40, + height: 44, + size: 19, ), ), - ], - ], - ), - Padding( - padding: const EdgeInsets.only(left: 16, top: 12), - child: Row( - children: [ - FeedIconButton( - icon: widget.diet.wasLikedByMe ? Icons.favorite_rounded : Icons.favorite_border_rounded, - text: widget.diet.totalLikes.toString(), - iconColor: widget.diet.wasLikedByMe ? appTheme.blueA400 : null, - backgroundColor: widget.diet.wasLikedByMe ? appTheme.transparentBlue : null, - onTap: () async { - await context.read().likeDiet(widget.diet.id, widget.index); - }, - ), - SizedBox( - width: 12, - ), - FeedIconButton( - icon: Icons.chat_bubble_outline, - text: widget.diet.totalComments.toString(), - onTap: () => - GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'diet': widget.diet, 'index': widget.index, 'navigateToComments': true}), - ), - SizedBox( - width: 12, - ), - FeedIconButton( - svgIcon: SvgPicture.asset('assets/icons/repost.svg'), - onTap: () async { - final box = context.findRenderObject() as RenderBox?; - final result = await Share.shareUri( - Uri.parse( - baseUrl + '/ru/diet/' + widget.diet.id.toString(), - ), - sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, - ); - switch (result.status) { - case ShareResultStatus.success: - case ShareResultStatus.dismissed: - case ShareResultStatus.unavailable: - } - }, - ), - Spacer(), - FeedIconButton( - svgIcon: SvgPicture.asset('assets/icons/eye2.svg'), - text: widget.diet.totalViews.toString(), - backgroundColor: Colors.transparent, + Positioned( + bottom: 16, + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + child: InfoRow( + diet: widget.diet, + itemsBackgroundColor: appTheme.whiteA700, + disableDescriptionOfTypes: true, + type: ProductType.diet + ), + ), ), ], ), - ), - SizedBox(height: 12), - ], + SizedBox(height: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + widget.diet.name, + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), + ), + ), + if (widget.diet.details.isNotEmpty) ...[ + SizedBox( + height: 12, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Html( + data: widget.diet.details.length > 200 ? widget.diet.details.substring(0, 200) + '...' : widget.diet.details, + style: { + "*": Style( + margin: Margins.zero, + color: appTheme.black, + fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14), + fontWeight: TextStyles.textSMedium.fontWeight ?? FontWeight.w400, + ), + }, + ), + ), + ], + ], + ), + Padding( + padding: const EdgeInsets.only(left: 16, top: 12), + child: Row( + children: [ + FeedIconButton( + icon: widget.diet.wasLikedByMe ? Icons.favorite_rounded : Icons.favorite_border_rounded, + text: widget.diet.totalLikes.toString(), + iconColor: widget.diet.wasLikedByMe ? appTheme.blueA400 : null, + backgroundColor: widget.diet.wasLikedByMe ? appTheme.transparentBlue : null, + onTap: () async { + await context.read().likeDiet(widget.diet.id, widget.index); + }, + ), + SizedBox( + width: 12, + ), + FeedIconButton( + icon: Icons.chat_bubble_outline, + text: widget.diet.totalComments.toString(), + onTap: () => + GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'diet': widget.diet, 'index': widget.index, 'navigateToComments': true}), + ), + SizedBox( + width: 12, + ), + FeedIconButton( + svgIcon: SvgPicture.asset('assets/icons/repost.svg'), + onTap: () async { + final box = context.findRenderObject() as RenderBox?; + final result = await Share.shareUri( + Uri.parse( + baseUrl + '/ru/diet/' + widget.diet.id.toString(), + ), + sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + ); + switch (result.status) { + case ShareResultStatus.success: + case ShareResultStatus.dismissed: + case ShareResultStatus.unavailable: + } + }, + ), + Spacer(), + FeedIconButton( + svgIcon: SvgPicture.asset('assets/icons/eye2.svg'), + text: widget.diet.totalViews.toString(), + backgroundColor: Colors.transparent, + ), + ], + ), + ), + SizedBox(height: 12), + ], + ), ), ); } diff --git a/lib/features/feed/presentation/widgets/plan_widget.dart b/lib/features/feed/presentation/widgets/plan_widget.dart index a77c9b3e..49515f87 100644 --- a/lib/features/feed/presentation/widgets/plan_widget.dart +++ b/lib/features/feed/presentation/widgets/plan_widget.dart @@ -1,5 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/enums.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; @@ -33,21 +34,27 @@ class _PlanWidgetState extends State { @override Widget build(BuildContext context) { final s = S.of(context); - return DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, + return InkWell( + onTap: () => GoRouter.of(context).pushNamed( + 'aboutPlanScreen', + extra: { + 'plan': widget.plan, + 'index': widget.index, + 'navigateToComments': false, + }, ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - InkWell( - onTap: () => - GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'index': widget.index, 'navigateToComments': false}), - child: ClipRRect( + child: Ink( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + ClipRRect( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), child: CachedNetworkImage( imageUrl: baseUrl + '/storage/' + widget.plan.image, @@ -55,176 +62,177 @@ class _PlanWidgetState extends State { width: MediaQuery.of(context).size.width, ), ), - ), - Positioned( - right: 14, - child: BookmarkButton( - onTap: () async { - if (widget.plan.isFavorite) { - await context.read().removePlanFromFavorite(widget.plan.slug, widget.index); - } else { - await context.read().addPlanToFavorite(widget.plan.slug, widget.index); - } - }, - color: appTheme.whiteA700, - isFavorite: widget.plan.isFavorite, - borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), - width: 40, - height: 44, - size: 19, - ), - ), - Positioned( - bottom: 16, - child: SizedBox( - width: MediaQuery.sizeOf(context).width, - child: InfoRow( - plan: widget.plan, - itemsBackgroundColor: appTheme.whiteA700, - disableDescriptionOfTypes: true, + Positioned( + right: 14, + child: BookmarkButton( + onTap: () async { + if (widget.plan.isFavorite) { + await context.read().removePlanFromFavorite(widget.plan.slug, widget.index); + } else { + await context.read().addPlanToFavorite(widget.plan.slug, widget.index); + } + }, + color: appTheme.whiteA700, + isFavorite: widget.plan.isFavorite, + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + width: 40, + height: 44, + size: 19, ), ), - ), - ], - ), - SizedBox(height: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - widget.plan.name, - style: TextStyles.textMSemibold.copyWith(color: appTheme.black), - ), - ), - if (widget.plan.details.isNotEmpty) ...[ - SizedBox( - height: 12, + Positioned( + bottom: 16, + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + child: InfoRow( + plan: widget.plan, + itemsBackgroundColor: appTheme.whiteA700, + disableDescriptionOfTypes: true, + type: ProductType.plan + ), + ), ), + ], + ), + SizedBox(height: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: Html( - data: widget.plan.details.length > 200 ? widget.plan.details.substring(0, 200) + '...' : widget.plan.details, - style: { - "*": Style( - margin: Margins.zero, - color: appTheme.black, - fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14), - fontWeight: TextStyles.textSMedium.fontWeight ?? FontWeight.w400, - ), + child: Text( + widget.plan.name, + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), + ), + ), + if (widget.plan.details.isNotEmpty) ...[ + SizedBox( + height: 12, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Html( + data: widget.plan.details.length > 200 ? widget.plan.details.substring(0, 200) + '...' : widget.plan.details, + style: { + "*": Style( + margin: Margins.zero, + color: appTheme.black, + fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14), + fontWeight: TextStyles.textSMedium.fontWeight ?? FontWeight.w400, + ), + }, + ), + ), + ], + ], + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: Container( + width: 46, + height: 46, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: NetworkImage(widget.plan.author.authorProfileIcon), + fit: BoxFit.cover, + ), + ), + ), + subtitle: Row( + children: [ + InfoBouble( + radius: 12, + backgroundColor: appTheme.gray50, + padding: EdgeInsets.symmetric(vertical: 4, horizontal: 10), + children: [ + Text( + '${widget.plan.author.authorPlaceInRating}-й в рейтинге', + style: TextStyles.textXSSemibold.copyWith( + color: appTheme.blueA400, + ), + ), + ], + ), + Spacer(), + Text( + '${widget.plan.packetsCount == 1 ? widget.plan.packet1Options?.price : '${s.from} ${widget.plan.minPriceOfOptions.toStringAsFixed(0)}'}₽', + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.greenA700), + ), + SizedBox(width: 16), + ], + ), + title: Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Text( + widget.plan.author.authorName, + style: TextStyles.textSSemibold.copyWith( + color: appTheme.black, + ), + ), + ), + ), + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.only( + left: 16, + ), + child: Row( + children: [ + FeedIconButton( + icon: widget.plan.wasLikedByMe ? Icons.favorite_rounded : Icons.favorite_border_rounded, + text: widget.plan.totalLikes.toString(), + iconColor: widget.plan.wasLikedByMe ? appTheme.blueA400 : null, + backgroundColor: widget.plan.wasLikedByMe ? appTheme.transparentBlue : null, + onTap: () async { + await context.read().likePlan(widget.plan.slug, widget.index); }, ), - ), - ], - ], - ), - SizedBox(height: 16), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: Container( - width: 46, - height: 46, - decoration: BoxDecoration( - shape: BoxShape.circle, - image: DecorationImage( - image: NetworkImage(widget.plan.author.authorProfileIcon), - fit: BoxFit.cover, + SizedBox( + width: 12, ), - ), - ), - subtitle: Row( - children: [ - InfoBouble( - radius: 12, - backgroundColor: appTheme.gray50, - padding: EdgeInsets.symmetric(vertical: 4, horizontal: 10), - children: [ - Text( - '${widget.plan.author.authorPlaceInRating}-й в рейтинге', - style: TextStyles.textXSSemibold.copyWith( - color: appTheme.blueA400, + FeedIconButton( + icon: Icons.chat_bubble_outline, + text: widget.plan.totalComments.toString(), + onTap: () => + GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'index': widget.index, 'navigateToComments': true}), + ), + SizedBox( + width: 12, + ), + FeedIconButton( + svgIcon: SvgPicture.asset('assets/icons/repost.svg'), + onTap: () async { + final box = context.findRenderObject() as RenderBox?; + final result = await Share.shareUri( + Uri.parse( + baseUrl + '/ru/diet/' + widget.plan.id.toString(), ), - ), - ], + sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + ); + switch (result.status) { + case ShareResultStatus.success: + case ShareResultStatus.dismissed: + case ShareResultStatus.unavailable: + } + }, ), Spacer(), - Text( - '${widget.plan.packetsCount == 1 ? widget.plan.packet1Options?.price : '${s.from} ${widget.plan.minPriceOfOptions.toStringAsFixed(0)}'}₽', - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.greenA700), + FeedIconButton( + svgIcon: SvgPicture.asset('assets/icons/eye2.svg'), + text: widget.plan.totalViews.toString(), + backgroundColor: Colors.transparent, ), - SizedBox(width: 16), ], ), - title: Padding( - padding: const EdgeInsets.only(bottom: 4.0), - child: Text( - widget.plan.author.authorName, - style: TextStyles.textSSemibold.copyWith( - color: appTheme.black, - ), - ), - ), ), - ), - SizedBox(height: 16), - Padding( - padding: const EdgeInsets.only( - left: 16, - ), - child: Row( - children: [ - FeedIconButton( - icon: widget.plan.wasLikedByMe ? Icons.favorite_rounded : Icons.favorite_border_rounded, - text: widget.plan.totalLikes.toString(), - iconColor: widget.plan.wasLikedByMe ? appTheme.blueA400 : null, - backgroundColor: widget.plan.wasLikedByMe ? appTheme.transparentBlue : null, - onTap: () async { - await context.read().likePlan(widget.plan.slug, widget.index); - }, - ), - SizedBox( - width: 12, - ), - FeedIconButton( - icon: Icons.chat_bubble_outline, - text: widget.plan.totalComments.toString(), - onTap: () => - GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'index': widget.index, 'navigateToComments': true}), - ), - SizedBox( - width: 12, - ), - FeedIconButton( - svgIcon: SvgPicture.asset('assets/icons/repost.svg'), - onTap: () async { - final box = context.findRenderObject() as RenderBox?; - final result = await Share.shareUri( - Uri.parse( - baseUrl + '/ru/diet/' + widget.plan.id.toString(), - ), - sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, - ); - switch (result.status) { - case ShareResultStatus.success: - case ShareResultStatus.dismissed: - case ShareResultStatus.unavailable: - } - }, - ), - Spacer(), - FeedIconButton( - svgIcon: SvgPicture.asset('assets/icons/eye2.svg'), - text: widget.plan.totalViews.toString(), - backgroundColor: Colors.transparent, - ), - ], - ), - ), - SizedBox(height: 12), - ], + SizedBox(height: 12), + ], + ), ), ); } diff --git a/lib/features/feed/presentation/widgets/recipes.dart b/lib/features/feed/presentation/widgets/recipes.dart index e5dc0fa5..78ad76e6 100644 --- a/lib/features/feed/presentation/widgets/recipes.dart +++ b/lib/features/feed/presentation/widgets/recipes.dart @@ -14,38 +14,39 @@ class Recipes extends StatelessWidget { onRefresh: () => context.read().loadRecipesList(), child: BlocBuilder( builder: (BuildContext context, RecipesState state) { - if (state is RecipesStateLoaded) { - return ListView.separated( - padding: EdgeInsets.symmetric(vertical: 8), - shrinkWrap: true, - physics: BouncingScrollPhysics(), - itemBuilder: (context, index) { - return RecipesWidget( - recipes: state.recipesList[index], - index: index, - ); - }, - separatorBuilder: (context, index) { - return SizedBox(height: 8); - }, - itemCount: state.recipesList.length, - ); - } else if (state is RecipesStateError) { - return Scaffold( - backgroundColor: Colors.grey.shade200, - body: Padding( - padding: const EdgeInsets.all(16), - child: Center( - child: Text(state.errorMessage), - ), - ), - ); - } else { + if (state.isLoading) { return Scaffold( backgroundColor: Colors.grey.shade200, body: const Loader(), ); } + if (state.errorMessage != null) { + return Scaffold( + backgroundColor: Colors.grey.shade200, + body: Padding( + padding: const EdgeInsets.all(16), + child: Center( + child: Text(state.errorMessage ?? ''), + ), + ), + ); + } + + return ListView.separated( + padding: EdgeInsets.symmetric(vertical: 8), + shrinkWrap: true, + physics: BouncingScrollPhysics(), + itemBuilder: (context, index) { + return RecipesWidget( + recipe: state.recipesList[index], + index: index, + ); + }, + separatorBuilder: (context, index) { + return SizedBox(height: 8); + }, + itemCount: state.recipesList.length, + ); }, ), ); diff --git a/lib/features/feed/presentation/widgets/recipes_widget.dart b/lib/features/feed/presentation/widgets/recipes_widget.dart index 16681a77..8e08c05a 100644 --- a/lib/features/feed/presentation/widgets/recipes_widget.dart +++ b/lib/features/feed/presentation/widgets/recipes_widget.dart @@ -2,11 +2,15 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/utils/localization_utils.dart'; +import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart'; +import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; @@ -16,11 +20,11 @@ import 'package:share_plus/share_plus.dart'; class RecipesWidget extends StatefulWidget { final int index; - final Recipes recipes; + final Recipe recipe; const RecipesWidget({ super.key, - required this.recipes, + required this.recipe, required this.index, }); @@ -29,177 +33,207 @@ class RecipesWidget extends StatefulWidget { } class _RecipesWidgetState extends State { - late bool isLiked; - late int totalLikes; - - @override - void initState() { - super.initState(); - isLiked = false; - } - @override Widget build(BuildContext context) { + final s = S.of(context); return BlocBuilder( builder: (BuildContext context, RecipesState state) { - if (state is RecipesStateLoaded) { - isLiked = state.recipesList[widget.index].wasLikedByMe; - totalLikes = state.recipesList[widget.index].likesCount; - return InkWell( - borderRadius: BorderRadius.circular(12), - onTap: () => GoRouter.of(context).pushNamed('aboutRecipesScreen', extra: widget.recipes), - child: Ink( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - ClipRRect( - borderRadius: BorderRadius.vertical(top: Radius.circular(12)), - child: CachedNetworkImage( - imageUrl: baseUrl + '/storage/' + widget.recipes.image, - fit: BoxFit.fitWidth, - width: MediaQuery.of(context).size.width, - ), - ), - Positioned( - left: 8, - bottom: 8, - child: Wrap( - runSpacing: 8, - children: [ - _iconButton('assets/icons/clock.svg', widget.recipes.cookingTime.toString() + ' минут'), - SizedBox(width: 8), - _iconButton('assets/icons/steps.svg', '${widget.recipes.steps?.length.toString()}' + ' этапов'), - SizedBox(width: 8), - _iconButton('assets/icons/calories.svg', widget.recipes.calories.toStringAsFixed(0) + ' ккал'), - ], - ), - ), - ], - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text( - widget.recipes.title ?? '', - style: TextStyle( - fontWeight: FontWeight.w600, - color: Colors.black87, + final isLiked = widget.recipe.wasLikedByMe; + final totalLikes = widget.recipe.likesCount; + final isFavorite = widget.recipe.isFavorite; + final hours = widget.recipe.cookingTime ~/ 60; + final minutes = widget.recipe.cookingTime % 60; + final String timeTitle; + if (hours > 0) { + timeTitle = '$hours ${getHourForm(hours)} $minutes ${getMinuteForm(minutes)}'; + } else { + timeTitle = '$minutes ${getMinuteForm(minutes)}'; + } + return InkWell( + borderRadius: BorderRadius.circular(12), + onTap: () => GoRouter.of(context).pushNamed( + 'aboutRecipesScreen', + extra: { + 'recipe': widget.recipe, + 'index': widget.index, + 'navigateToComments': false, + }, + ), + child: Ink( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.vertical(top: Radius.circular(12)), + child: CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + widget.recipe.image, + fit: BoxFit.fitWidth, + width: MediaQuery.of(context).size.width, ), ), - ), - if (widget.recipes.description.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Html( - data: widget.recipes.description.length > 200 ? widget.recipes.description.substring(0, 200) : widget.recipes.description, - style: { - "*": Style( - color: Colors.black87, - ), + Positioned( + left: 8, + bottom: 8, + child: Wrap( + runSpacing: 8, + children: [ + _iconButton('assets/icons/clock.svg', timeTitle), + SizedBox(width: 8), + _iconButton('assets/icons/steps.svg', '${widget.recipe.steps?.length ?? 0} ${getStepForm(widget.recipe.steps?.length ?? 0)}'), + SizedBox(width: 8), + _iconButton('assets/icons/calories.svg', widget.recipe.calories.toStringAsFixed(0) + ' ккал'), + ], + ), + ), + Positioned( + right: 14, + child: BookmarkButton( + onTap: () async { + if (isFavorite) { + await context.read().removeRecipeFromFavorite(widget.recipe.slug, widget.index); + } else { + await context.read().addRecipeToFavorite(widget.recipe.slug, widget.index); + } }, + color: appTheme.whiteA700, + isFavorite: isFavorite, + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + width: 40, + height: 44, + size: 19, ), ), - if (widget.recipes.description.length > 200) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'Читать полностью...', - style: TextStyle( - height: 1, - color: appTheme.blueA400, - decoration: TextDecoration.underline, - ), - ), - ), + ], + ), + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + widget.recipe.title ?? '', + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), + ), + ), + if (widget.recipe.videoUrl != null && widget.recipe.videoUrl!.isNotEmpty) ...[ SizedBox(height: 12), Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), + padding: const EdgeInsets.only(left: 15), child: Row( children: [ - FeedIconButton( - icon: isLiked ? Icons.favorite_rounded : Icons.favorite_border_rounded, - text: widget.recipes.likesCount.toString(), - iconColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null, - backgroundColor: isLiked ? Colors.red.withValues(alpha: 0.1) : null, - textColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null, - onTap: () async { - setState(() { - isLiked = !isLiked; - isLiked ? totalLikes++ : totalLikes--; - }); - isLiked - ? await context.read().likeRecipe(state.recipesList[widget.index].slug) - : await context.read().unlikeRecipe(state.recipesList[widget.index].slug); - await context.read().loadRecipesList(); - }, - ), + SvgPicture.asset('assets/icons/play_icon.svg', width: 16, height: 16), SizedBox(width: 8), - FeedIconButton( - svgIcon: SvgPicture.asset( - 'assets/icons/comment.svg', - height: 20, - width: 20, - colorFilter: ColorFilter.mode( - appTheme.gray600, - BlendMode.srcIn, - ), - ), - text: widget.recipes.commentsCount.toString(), - onTap: () => GoRouter.of(context).pushNamed( - 'aboutRecipesScreen', - extra: widget.recipes, - ), - ), - SizedBox(width: 8), - FeedIconButton( - svgIcon: SvgPicture.asset( - height: 20, - width: 20, - 'assets/icons/share.svg', - colorFilter: ColorFilter.mode(appTheme.gray600, BlendMode.srcIn), - ), - onTap: () { - final box = context.findRenderObject() as RenderBox?; - Share.shareUri( - Uri.parse( - baseUrl + '/ru/blog_post/' + widget.recipes.id.toString(), - ), - sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, - ); - }, - ), - Spacer(), - Icon( - Icons.visibility_rounded, - size: 16, - color: appTheme.gray500, - ), - SizedBox(width: 4), Text( - widget.recipes.viewsCount.toString(), - style: TextStyle( - fontSize: 12, - color: appTheme.gray500, - fontWeight: FontWeight.w400, - ), + s.there_is_a_video, + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], ), ), - SizedBox(height: 12), ], - ), + SizedBox(height: 12), + if (widget.recipe.description.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Html( + data: widget.recipe.description.length > 200 ? widget.recipe.description.substring(0, 200) : widget.recipe.description, + style: { + "*": Style( + color: appTheme.black, + margin: Margins.all(0), + fontFamily: TextStyles.textSMedium.fontFamily, + fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14), + fontWeight: TextStyles.textSMedium.fontWeight, + ), + }, + ), + ), + if (widget.recipe.description.length > 200) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'Читать полностью...', + style: TextStyle( + height: 1, + color: appTheme.blueA400, + decoration: TextDecoration.underline, + ), + ), + ), + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + FeedIconButton( + icon: isLiked ? Icons.favorite_rounded : Icons.favorite_border_rounded, + text: totalLikes.toString(), + iconColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null, + backgroundColor: isLiked ? Colors.red.withValues(alpha: 0.1) : null, + textColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null, + onTap: () async { + await context.read().likeRecipe(widget.recipe.slug, widget.index); + }, + ), + SizedBox(width: 8), + FeedIconButton( + icon: Icons.chat_bubble_outline, + text: widget.recipe.commentsCount.toString(), + onTap: () => GoRouter.of(context).pushNamed( + 'aboutRecipesScreen', + extra: { + 'recipe': widget.recipe, + 'index': widget.index, + 'navigateToComments': true, + }, + ), + ), + SizedBox(width: 8), + FeedIconButton( + svgIcon: SvgPicture.asset( + height: 15, + width: 12, + 'assets/icons/repost.svg', + colorFilter: ColorFilter.mode(appTheme.gray600, BlendMode.srcIn), + ), + onTap: () { + final box = context.findRenderObject() as RenderBox?; + Share.shareUri( + Uri.parse( + baseUrl + '/ru/blog_post/' + widget.recipe.id.toString(), + ), + sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + ); + }, + ), + Spacer(), + Icon( + Icons.visibility_rounded, + size: 16, + color: appTheme.gray500, + ), + SizedBox(width: 4), + Text( + widget.recipe.viewsCount.toString(), + style: TextStyle( + fontSize: 12, + color: appTheme.gray500, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + SizedBox(height: 12), + ], ), - ); - } - return Container(); + ), + ); }, ); } @@ -208,7 +242,7 @@ class _RecipesWidgetState extends State { return Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.8), + color: appTheme.whiteA700, borderRadius: BorderRadius.circular(16), ), height: 32, diff --git a/lib/features/feed/presentation/widgets/tags_modal_widget.dart b/lib/features/feed/presentation/widgets/tags_modal_widget.dart index 51b2fca0..f9382909 100644 --- a/lib/features/feed/presentation/widgets/tags_modal_widget.dart +++ b/lib/features/feed/presentation/widgets/tags_modal_widget.dart @@ -1,6 +1,6 @@ import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart'; +import 'package:eatvio_app/features/create_recipe/models/recipe_tag_model.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -23,25 +23,21 @@ class _TagsModalWidgetState extends State { return BlocBuilder( bloc: widget.recipesCubit, builder: (BuildContext context, RecipesState state) { - if (state is RecipesStateLoaded) { - return Container( - padding: EdgeInsets.all(16), - width: MediaQuery.of(context).size.width, - child: Wrap( - spacing: 8, - runSpacing: -8, - children: [ - for (int i = 0; i < state.recipesTags.length; i++) - _tagChip( - tag: state.recipesTags[i], - isSelected: state.selectedTags.contains(state.recipesTags[i].id), - ), - ], - ), - ); - } else { - return Container(); - } + return Container( + padding: EdgeInsets.all(16), + width: MediaQuery.of(context).size.width, + child: Wrap( + spacing: 8, + runSpacing: -8, + children: [ + for (int i = 0; i < state.recipesTags.length; i++) + _tagChip( + tag: state.recipesTags[i], + isSelected: state.selectedTags.contains(state.recipesTags[i].id), + ), + ], + ), + ); }, ); } diff --git a/lib/features/feed/repositories/feed_repository.dart b/lib/features/feed/repositories/feed_repository.dart index 1f13becf..854ce4d3 100644 --- a/lib/features/feed/repositories/feed_repository.dart +++ b/lib/features/feed/repositories/feed_repository.dart @@ -27,7 +27,7 @@ class FeedRepository { } } - Future> getRecipes({ + Future> getRecipes({ String? query, List? tags, int page = 1, @@ -64,7 +64,7 @@ class FeedRepository { } } - Future getPlans({ + Future getPlans({ int page = 1, String? query, String? types, @@ -85,7 +85,7 @@ class FeedRepository { rethrow; } } - + Future getBlogPostComments({ required String id, }) async { @@ -217,24 +217,4 @@ class FeedRepository { rethrow; } } - - Future likeRecipe({ - required String slug, - }) async { - try { - await feedService.likeRecipe(slug: slug); - } catch (e) { - rethrow; - } - } - - Future unlikeRecipe({ - required String slug, - }) async { - try { - await feedService.unlikeRecipe(slug: slug); - } catch (e) { - rethrow; - } - } } diff --git a/lib/features/feed/services/feed_service.dart b/lib/features/feed/services/feed_service.dart index 73ec0bcb..9551f16b 100644 --- a/lib/features/feed/services/feed_service.dart +++ b/lib/features/feed/services/feed_service.dart @@ -103,16 +103,6 @@ abstract class FeedService { @Path() required int id, }); - @POST('/api/recipes/{slug}/like') - Future likeRecipe({ - @Path() required String slug, - }); - - @POST('/api/recipes/{slug}/unliked') - Future unlikeRecipe({ - @Path() required String slug, - }); - @GET('/api/consultants/index') Future getConsultants(); } diff --git a/lib/features/feed/services/feed_service.g.dart b/lib/features/feed/services/feed_service.g.dart index 951463e3..17825316 100644 --- a/lib/features/feed/services/feed_service.g.dart +++ b/lib/features/feed/services/feed_service.g.dart @@ -427,44 +427,6 @@ class _FeedService implements FeedService { await _dio.fetch(_options); } - @override - Future likeRecipe({required String slug}) async { - final _extra = {}; - final queryParameters = {}; - final _headers = {}; - const Map? _data = null; - final _options = _setStreamType( - Options(method: 'POST', headers: _headers, extra: _extra) - .compose( - _dio.options, - '/api/recipes/${slug}/like', - queryParameters: queryParameters, - data: _data, - ) - .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), - ); - await _dio.fetch(_options); - } - - @override - Future unlikeRecipe({required String slug}) async { - final _extra = {}; - final queryParameters = {}; - final _headers = {}; - const Map? _data = null; - final _options = _setStreamType( - Options(method: 'POST', headers: _headers, extra: _extra) - .compose( - _dio.options, - '/api/recipes/${slug}/unliked', - queryParameters: queryParameters, - data: _data, - ) - .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), - ); - await _dio.fetch(_options); - } - @override Future getConsultants() async { final _extra = {}; diff --git a/lib/features/my_diets/presentation/cubit/my_diets_cubit.dart b/lib/features/my_diets/presentation/cubit/my_diets_cubit.dart index 09753d80..7a86efcc 100644 --- a/lib/features/my_diets/presentation/cubit/my_diets_cubit.dart +++ b/lib/features/my_diets/presentation/cubit/my_diets_cubit.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_state.dart'; import 'package:eatvio_app/features/my_diets/repositories/my_diets_repository.dart'; @@ -9,6 +10,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class MyDietsCubit extends Cubit { final MyDietsRepository myDietsRepository; + final CreateDietRepository createDietRepository; int currentDraftPage = 1; int currentPersonalPage = 1; @@ -35,7 +37,10 @@ class MyDietsCubit extends Cubit { static const List _durationValues = [null, ...durationValues]; static const List _types = [null, ...dietTypeValues]; - MyDietsCubit({required this.myDietsRepository}) : super(MyDietsState()); + MyDietsCubit({ + required this.myDietsRepository, + required this.createDietRepository, + }) : super(MyDietsState()); void changeTypeFilter(int index) { if (state.selectedStateIndex == 0) { @@ -283,4 +288,17 @@ class MyDietsCubit extends Cubit { return false; } } + + Future publishDiet(int dietId) async { + try { + await createDietRepository.updateDiet( + dietId: dietId, + state: DietState.active, + ); + return null; + } catch (e, st) { + log(e.toString(), stackTrace: st); + return 'Произошла непредвиденная ошибка'; + } + } } diff --git a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart index ff8d060a..496a3dea 100644 --- a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart +++ b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.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/utils/enums.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; @@ -63,6 +64,7 @@ class _MyDietWidgetState extends State { diet: widget.diet, itemsBackgroundColor: appTheme.whiteA700, disableDescriptionOfTypes: true, + type: ProductType.diet ), ), ), @@ -113,6 +115,7 @@ class _MyDietWidgetState extends State { child: CustomOutlinedButton( onPressed: () { if (state.selectedStateIndex == 1) { + context.read().publishDiet(widget.diet.id); } else if (state.selectedStateIndex == 2) { } else { GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: widget.diet); diff --git a/lib/features/my_plans/presentation/widgets/my_plan_widget.dart b/lib/features/my_plans/presentation/widgets/my_plan_widget.dart index 9132624d..6b8bee6f 100644 --- a/lib/features/my_plans/presentation/widgets/my_plan_widget.dart +++ b/lib/features/my_plans/presentation/widgets/my_plan_widget.dart @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.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/utils/enums.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; @@ -62,6 +63,7 @@ class _MyPlanWidgetState extends State { plan: widget.plan, itemsBackgroundColor: appTheme.whiteA700, disableDescriptionOfTypes: true, + type: ProductType.plan, ), ), ), diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index db415e15..5922b0ff 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/enums.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; @@ -400,9 +401,7 @@ class _AboutPlanScreenState extends State { ), ), SizedBox(height: 12), - - /// TODO глянуть как заменить - InfoRow(plan: state.planModel), + InfoRow(plan: state.planModel, type: ProductType.plan), ExpandableText( text: widget.plan.details, ), diff --git a/lib/features/plans/presentation/widgets/item_widget.dart b/lib/features/plans/presentation/widgets/item_widget.dart index 4cf41e70..5ec3b288 100644 --- a/lib/features/plans/presentation/widgets/item_widget.dart +++ b/lib/features/plans/presentation/widgets/item_widget.dart @@ -10,11 +10,12 @@ class ItemWidget extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Text( - text, - style: TextStyles.textMMedium.copyWith(color: appTheme.black), + Expanded( + child: Text( + text, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), ), - Spacer(), SvgPicture.asset( 'assets/icons/ok.svg', width: 20, diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 0b8b7af6..eee9645b 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -567,6 +567,7 @@ class _ProfileScreenState extends State { Uri.parse( state.profile.educationResource?.vkLink ?? '', ), + mode: LaunchMode.externalApplication, ); }, 'assets/icons/vk.svg', @@ -577,8 +578,9 @@ class _ProfileScreenState extends State { () { launchUrl( Uri.parse( - state.profile.educationResource?.vkLink ?? '', + state.profile.educationResource?.vkLink ?? '', ), + mode: LaunchMode.externalApplication, ); }, 'assets/icons/telegram.svg', @@ -591,6 +593,7 @@ class _ProfileScreenState extends State { Uri.parse( state.profile.educationResource?.personalSiteLink ?? '', ), + mode: LaunchMode.externalApplication, ); }, 'assets/icons/web.svg', diff --git a/lib/features/recipe/presentation/about_recipe_screen.dart b/lib/features/recipe/presentation/about_recipe_screen.dart new file mode 100644 index 00000000..64f201d9 --- /dev/null +++ b/lib/features/recipe/presentation/about_recipe_screen.dart @@ -0,0 +1,473 @@ +import 'dart:developer'; +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/enums.dart'; +import 'package:eatvio_app/core/utils/localization_utils.dart'; +import 'package:eatvio_app/core/utils/string_extentions.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/info_row.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/author_card.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'; +import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; +import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; +import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_cubit.dart'; +import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_state.dart'; +import 'package:eatvio_app/features/recipe/presentation/widgets/ingredients_widget.dart'; +import 'package:eatvio_app/features/recipe/presentation/widgets/steps_widget.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/core/locator_service.dart'; +import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_portal/flutter_portal.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class AboutRecipeScreen extends StatefulWidget { + final Recipe recipe; + final int? index; + final bool navigateToComments; + + AboutRecipeScreen({super.key, required this.recipe, required this.index, required this.navigateToComments}); + + @override + State createState() => _AboutRecipeScreenState(); +} + +class _AboutRecipeScreenState extends State { + final CommentsCubit commentsCubit = sl(); + final AboutRecipeCubit recipeCubit = sl(); + int? replyId; + final ScrollController scrollController = ScrollController(); + final textEditingController = TextEditingController(); + bool isMoreOptionsVisible = false; + bool _jumped = false; + @override + void initState() { + super.initState(); + scrollController.addListener(() { + if (isMoreOptionsVisible) { + setState(() { + isMoreOptionsVisible = false; + }); + } + }); + } + + void _scrollToBottom() { + _jumped = true; + scrollController.jumpTo( + scrollController.position.maxScrollExtent, + ); + } + + @override + Widget build(BuildContext context) { + final s = S.of(context); + return BlocProvider( + create: (context) => recipeCubit..getRecipe(widget.recipe.slug), + child: BlocProvider( + create: (BuildContext context) => commentsCubit + ..getComments( + widget.recipe.slug.toString(), + CommentSourceType.recipe, + true, + ), + child: BlocConsumer( + listener: (BuildContext context, CommentsState commentsState) { + if (commentsState is CommentsStateLoaded) { + setState(() { + replyId = commentsState.replyId; + }); + } + }, + builder: (BuildContext context, CommentsState state) { + return BlocBuilder( + builder: (context, recipeState) { + if (recipeState is AboutRecipeStateLoaded) { + Future.delayed(Duration(milliseconds: 500), () { + if (widget.navigateToComments && !_jumped) { + _scrollToBottom(); + } + }); + return Scaffold( + backgroundColor: appTheme.gray100, + resizeToAvoidBottomInset: true, + bottomSheet: Container( + padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), + decoration: BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: Colors.black.withValues(alpha: 0.1), + ), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (replyId != null) + GestureDetector( + onTap: () { + context.read().setReplyId(null); + setState(() { + replyId = null; + textEditingController.clear(); + }); + FocusManager.instance.primaryFocus?.unfocus(); + }, + child: Container( + decoration: BoxDecoration( + color: appTheme.blueA400, + shape: BoxShape.circle, + ), + height: 32, + width: 32, + child: Icon( + Icons.close_rounded, + size: 18, + color: Colors.white, + ), + ), + ), + if (replyId != null) SizedBox(width: 8), + Expanded( + child: SizedBox( + child: TextFormField( + onChanged: (_) { + setState(() {}); + }, + controller: textEditingController, + maxLines: 5, + minLines: 1, + keyboardType: TextInputType.multiline, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + hintText: s.comment.capitalize(), + hintStyle: TextStyles.textMMedium.copyWith(color: appTheme.lightGray600), + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + ), + ), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + if (textEditingController.text.length > 2) { + commentsCubit.sendComment( + id: widget.recipe.slug, + commentText: textEditingController.text, + parentCommentId: replyId, + commentSourceType: CommentSourceType.recipe, + ); + context.read().setReplyId(null); + setState(() { + textEditingController.clear(); + replyId = null; + }); + Future.delayed(Duration(milliseconds: 500), () { + recipeCubit.getRecipe(widget.recipe.slug, index: widget.index); + }); + FocusManager.instance.primaryFocus?.unfocus(); + } + }, + child: Container( + decoration: BoxDecoration( + color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50, + shape: BoxShape.circle, + ), + height: 32, + width: 32, + child: Icon( + Icons.send_rounded, + size: 18, + color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.black, + ), + ), + ), + ], + ), + ], + ), + ), + ), + appBar: AppBar( + centerTitle: true, + title: Text( + widget.recipe.title ?? '', + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + actions: [ + PortalTarget( + visible: isMoreOptionsVisible, + portalFollower: MoreOptions( + path: 'recipe/${widget.recipe.slug}', + ), + anchor: Aligned( + shiftToWithinBound: AxisFlag(x: true, y: true), + follower: Alignment.topRight, + target: Alignment.bottomRight, + ), + child: IconButton( + onPressed: () { + setState(() { + isMoreOptionsVisible = !isMoreOptionsVisible; + }); + }, + icon: SvgPicture.asset( + 'assets/icons/more_button.svg', + width: 20, + height: 20, + ), + ), + ), + ], + ), + body: SingleChildScrollView( + controller: scrollController, + padding: EdgeInsets.only(bottom: 160), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 8), + MainBackgroundBouble( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + widget.recipe.image, + height: 300, + width: MediaQuery.sizeOf(context).width, + fit: BoxFit.fill, + ), + Positioned( + bottom: 16, + left: 0, + right: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InfoBouble( + children: [ + SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), + SizedBox(width: 8), + Text( + widget.recipe.viewsCount.toString(), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + backgroundColor: appTheme.whiteA700, + ), + SizedBox(width: 12), + GestureDetector( + onTap: () { + recipeCubit.likeRecipe(widget.recipe.slug, widget.index); + }, + child: InfoBouble( + children: [ + SvgPicture.asset( + 'assets/icons/heart.svg', + width: 14, + height: 12, + colorFilter: recipeState.recipe.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, + ), + SizedBox(width: 8), + Text( + recipeState.recipe.likesCount.toString(), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + backgroundColor: appTheme.whiteA700, + ), + ), + SizedBox(width: 12), + InfoBouble( + children: [ + SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), + SizedBox(width: 8), + Text( + recipeState.recipe.commentsCount.toString(), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + backgroundColor: appTheme.whiteA700, + ), + ], + ), + ), + Positioned( + right: 14, + child: BookmarkButton( + onTap: () async { + if (recipeState.recipe.isFavorite) { + await context.read().removeFromFavorite(widget.recipe.slug, widget.index); + } else { + await context.read().addToFavorite(widget.recipe.slug, widget.index); + } + }, + color: appTheme.whiteA700, + isFavorite: recipeState.recipe.isFavorite, + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + width: 40, + height: 44, + size: 19, + ), + ), + ], + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + s.about_recipe, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + ), + SizedBox(height: 12), + InfoRow( + recipe: widget.recipe, + type: ProductType.recipe, + ), + if (widget.recipe.videoUrl != null) + Padding( + padding: const EdgeInsets.only(left: 16, bottom: 12, top: 12), + child: GestureDetector( + onTap: () async { + try { + final uri = Uri.parse(widget.recipe.videoUrl!); + if (await canLaunchUrl(uri)) { + await launchUrl(uri, mode: LaunchMode.externalApplication); + } + } catch (e) { + log(e.toString()); + showErrorToast(s.could_not_launch_video); + } + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + s.check_out_video_recipe, + style: TextStyles.textMSemibold.copyWith( + color: appTheme.black, + ), + ), + SizedBox(width: 12), + SvgPicture.asset('assets/icons/play_icon.svg', width: 16, height: 16), + ], + ), + ), + ), + ExpandableText( + text: widget.recipe.description, + ), + SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: AuthorCard( + author: widget.recipe.author, + title: s.recipe_author, + ), + ), + SizedBox(height: 16), + ], + ), + ), + SizedBox(height: 12), + if (widget.recipe.ingredients?.isNotEmpty == true) + IngredientsWidget( + recipe: widget.recipe, + ), + if (widget.recipe.steps?.isNotEmpty == true) ...[ + SizedBox(height: 12), + StepsWidget( + recipe: widget.recipe, + ), + ], + SizedBox(height: 8), + MainBackgroundBouble( + padding: EdgeInsets.symmetric(vertical: 16), + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 16), + alignment: Alignment.centerLeft, + child: Text( + recipeState.recipe.commentsCount.toString() + ' ' + getCommentForm(widget.recipe.commentsCount, s), + style: TextStyles.textMMedium.copyWith( + color: appTheme.gray500, + ), + ), + ), + SizedBox(height: 16), + if (state is CommentsStateLoading) const Loader(), + if (state is CommentsStateLoaded) + CommentsWidget( + id: widget.recipe.slug, + commentSourceType: CommentSourceType.recipe, + comment: state.comment, + replyId: replyId, + ), + ], + ), + ), + ], + ), + ), + ); + } + return Scaffold( + body: const Loader(), + ); + }, + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/recipe/presentation/cubit/about_recipe_cubit.dart b/lib/features/recipe/presentation/cubit/about_recipe_cubit.dart new file mode 100644 index 00000000..87d12c38 --- /dev/null +++ b/lib/features/recipe/presentation/cubit/about_recipe_cubit.dart @@ -0,0 +1,149 @@ +import 'dart:async'; +import 'dart:developer'; +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; +import 'package:eatvio_app/features/recipe/repositories/about_recipe_repository.dart'; +import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; +import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AboutRecipeCubit extends Cubit { + AboutRecipeCubit({required this.recipeRepository, required this.profileRepository }) + : super(AboutRecipeState.initial()); + final AboutRecipeRepository recipeRepository; + final ProfileRepository profileRepository; + bool isCurrentDietStarted = false; + bool hasStartedDiet = false; + String? userStartedDietName; + + Future getRecipe(String id, {int? index}) async { + try { + final recipe = await recipeRepository.getRecipe(id); + if (index != null) { + recipeRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: recipe.commentsCount)); + } + emit(AboutRecipeState.loaded(recipe: recipe)); + } catch (e) { + log(e.toString()); + } + } + + Future initRecipe(String id, {int? index}) async { + try { + emit(AboutRecipeState.loading()); + final recipe = await recipeRepository.getRecipe(id); + if (index != null) { + recipeRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: recipe.commentsCount)); + } + emit(AboutRecipeState.loaded(recipe: recipe)); + } catch (e) { + log(e.toString()); + } + } + + Future likeRecipe(String id, int? recipeIndexInFeed) async { + if (state is! AboutRecipeStateLoaded) return; + + final currentRecipe = (state as AboutRecipeStateLoaded).recipe; + + if (currentRecipe.wasLikedByMe) { + final updatedRecipe = currentRecipe.copyWith( + likesCount: currentRecipe.likesCount - 1, + wasLikedByMe: false, + ); + if (recipeIndexInFeed != null) { + recipeRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedRecipe.wasLikedByMe, totalLikes: updatedRecipe.likesCount, index: recipeIndexInFeed)); + } + emit(AboutRecipeState.loaded(recipe: updatedRecipe)); + } else { + final updatedRecipe = currentRecipe.copyWith( + likesCount: currentRecipe.likesCount + 1, + wasLikedByMe: true, + ); + if (recipeIndexInFeed != null) { + recipeRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedRecipe.wasLikedByMe, totalLikes: updatedRecipe.likesCount, index: recipeIndexInFeed)); + } + emit(AboutRecipeState.loaded(recipe: updatedRecipe)); + } + + EasyDebounce.debounce( + 'like_recipe', + Duration(seconds: 1), + () async { + try { + await recipeRepository.likeRecipe(id); + } catch (e) { + log(e.toString()); + if (recipeIndexInFeed != null) { + recipeRepository + .emitLikesStateToFeed(LikesState(wasLikedByMe: currentRecipe.wasLikedByMe, totalLikes: currentRecipe.likesCount, index: recipeIndexInFeed)); + } + emit(AboutRecipeState.loaded(recipe: currentRecipe)); + } + }, + ); + } + + Future addToFavorite(String id, int? recipeIndex) async { + if (state is! AboutRecipeStateLoaded) return; + + final currentRecipe = (state as AboutRecipeStateLoaded).recipe; + + final updatedRecipe = currentRecipe.copyWith( + isFavorite: true, + ); + if (recipeIndex != null) { + recipeRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedRecipe.isFavorite, index: recipeIndex)); + } + emit(AboutRecipeState.loaded(recipe: updatedRecipe)); + + EasyDebounce.debounce( + 'add_or_remove_diet_to_favorites', + Duration(seconds: 1), + () async { + try { + await recipeRepository.addToFavorite(id); + } catch (e) { + log(e.toString()); + showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); + if (recipeIndex != null) { + recipeRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentRecipe.isFavorite, index: recipeIndex)); + } + emit(AboutRecipeState.loaded(recipe: currentRecipe)); + } + }, + ); + } + + Future removeFromFavorite(String id, int? recipeIndex) async { + if (state is! AboutRecipeStateLoaded) return; + + final currentRecipe = (state as AboutRecipeStateLoaded).recipe; + + final updatedRecipe = currentRecipe.copyWith( + isFavorite: false, + ); + if (recipeIndex != null) { + recipeRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedRecipe.isFavorite, index: recipeIndex)); + } + emit(AboutRecipeState.loaded(recipe: updatedRecipe)); + + EasyDebounce.debounce( + 'add_or_remove_diet_to_favorites', + Duration(seconds: 1), + () async { + try { + await recipeRepository.removeFromFavorite(id); + } catch (e) { + log(e.toString()); + showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); + if (recipeIndex != null) { + recipeRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentRecipe.isFavorite, index: recipeIndex)); + } + emit(AboutRecipeState.loaded(recipe: currentRecipe)); + } + }, + ); + } +} diff --git a/lib/features/recipe/presentation/cubit/about_recipe_state.dart b/lib/features/recipe/presentation/cubit/about_recipe_state.dart new file mode 100644 index 00000000..9a96b569 --- /dev/null +++ b/lib/features/recipe/presentation/cubit/about_recipe_state.dart @@ -0,0 +1,16 @@ +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'about_recipe_state.freezed.dart'; + +@freezed +class AboutRecipeState with _$AboutRecipeState { + const factory AboutRecipeState() = _AboutRecipeState; + + const factory AboutRecipeState.initial() = AboutRecipeStateInitial; + const factory AboutRecipeState.loading() = AboutRecipeStateLoading; + const factory AboutRecipeState.loaded({ + required Recipe recipe, + }) = AboutRecipeStateLoaded; + const factory AboutRecipeState.error({required String message}) = RecipeStateError; +} diff --git a/lib/features/recipe/presentation/cubit/about_recipe_state.freezed.dart b/lib/features/recipe/presentation/cubit/about_recipe_state.freezed.dart new file mode 100644 index 00000000..850c5386 --- /dev/null +++ b/lib/features/recipe/presentation/cubit/about_recipe_state.freezed.dart @@ -0,0 +1,805 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'about_recipe_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$AboutRecipeState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Recipe recipe) loaded, + required TResult Function(String message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Recipe recipe)? loaded, + TResult? Function(String message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Recipe recipe)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(_AboutRecipeState value) $default, { + required TResult Function(AboutRecipeStateInitial value) initial, + required TResult Function(AboutRecipeStateLoading value) loading, + required TResult Function(AboutRecipeStateLoaded value) loaded, + required TResult Function(RecipeStateError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutRecipeState value)? $default, { + TResult? Function(AboutRecipeStateInitial value)? initial, + TResult? Function(AboutRecipeStateLoading value)? loading, + TResult? Function(AboutRecipeStateLoaded value)? loaded, + TResult? Function(RecipeStateError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutRecipeState value)? $default, { + TResult Function(AboutRecipeStateInitial value)? initial, + TResult Function(AboutRecipeStateLoading value)? loading, + TResult Function(AboutRecipeStateLoaded value)? loaded, + TResult Function(RecipeStateError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AboutRecipeStateCopyWith<$Res> { + factory $AboutRecipeStateCopyWith( + AboutRecipeState value, $Res Function(AboutRecipeState) then) = + _$AboutRecipeStateCopyWithImpl<$Res, AboutRecipeState>; +} + +/// @nodoc +class _$AboutRecipeStateCopyWithImpl<$Res, $Val extends AboutRecipeState> + implements $AboutRecipeStateCopyWith<$Res> { + _$AboutRecipeStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$AboutRecipeStateImplCopyWith<$Res> { + factory _$$AboutRecipeStateImplCopyWith(_$AboutRecipeStateImpl value, + $Res Function(_$AboutRecipeStateImpl) then) = + __$$AboutRecipeStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AboutRecipeStateImplCopyWithImpl<$Res> + extends _$AboutRecipeStateCopyWithImpl<$Res, _$AboutRecipeStateImpl> + implements _$$AboutRecipeStateImplCopyWith<$Res> { + __$$AboutRecipeStateImplCopyWithImpl(_$AboutRecipeStateImpl _value, + $Res Function(_$AboutRecipeStateImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$AboutRecipeStateImpl implements _AboutRecipeState { + const _$AboutRecipeStateImpl(); + + @override + String toString() { + return 'AboutRecipeState()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$AboutRecipeStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Recipe recipe) loaded, + required TResult Function(String message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Recipe recipe)? loaded, + TResult? Function(String message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Recipe recipe)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutRecipeState value) $default, { + required TResult Function(AboutRecipeStateInitial value) initial, + required TResult Function(AboutRecipeStateLoading value) loading, + required TResult Function(AboutRecipeStateLoaded value) loaded, + required TResult Function(RecipeStateError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutRecipeState value)? $default, { + TResult? Function(AboutRecipeStateInitial value)? initial, + TResult? Function(AboutRecipeStateLoading value)? loading, + TResult? Function(AboutRecipeStateLoaded value)? loaded, + TResult? Function(RecipeStateError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutRecipeState value)? $default, { + TResult Function(AboutRecipeStateInitial value)? initial, + TResult Function(AboutRecipeStateLoading value)? loading, + TResult Function(AboutRecipeStateLoaded value)? loaded, + TResult Function(RecipeStateError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class _AboutRecipeState implements AboutRecipeState { + const factory _AboutRecipeState() = _$AboutRecipeStateImpl; +} + +/// @nodoc +abstract class _$$AboutRecipeStateInitialImplCopyWith<$Res> { + factory _$$AboutRecipeStateInitialImplCopyWith( + _$AboutRecipeStateInitialImpl value, + $Res Function(_$AboutRecipeStateInitialImpl) then) = + __$$AboutRecipeStateInitialImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AboutRecipeStateInitialImplCopyWithImpl<$Res> + extends _$AboutRecipeStateCopyWithImpl<$Res, _$AboutRecipeStateInitialImpl> + implements _$$AboutRecipeStateInitialImplCopyWith<$Res> { + __$$AboutRecipeStateInitialImplCopyWithImpl( + _$AboutRecipeStateInitialImpl _value, + $Res Function(_$AboutRecipeStateInitialImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$AboutRecipeStateInitialImpl implements AboutRecipeStateInitial { + const _$AboutRecipeStateInitialImpl(); + + @override + String toString() { + return 'AboutRecipeState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AboutRecipeStateInitialImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Recipe recipe) loaded, + required TResult Function(String message) error, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Recipe recipe)? loaded, + TResult? Function(String message)? error, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Recipe recipe)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutRecipeState value) $default, { + required TResult Function(AboutRecipeStateInitial value) initial, + required TResult Function(AboutRecipeStateLoading value) loading, + required TResult Function(AboutRecipeStateLoaded value) loaded, + required TResult Function(RecipeStateError value) error, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutRecipeState value)? $default, { + TResult? Function(AboutRecipeStateInitial value)? initial, + TResult? Function(AboutRecipeStateLoading value)? loading, + TResult? Function(AboutRecipeStateLoaded value)? loaded, + TResult? Function(RecipeStateError value)? error, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutRecipeState value)? $default, { + TResult Function(AboutRecipeStateInitial value)? initial, + TResult Function(AboutRecipeStateLoading value)? loading, + TResult Function(AboutRecipeStateLoaded value)? loaded, + TResult Function(RecipeStateError value)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class AboutRecipeStateInitial implements AboutRecipeState { + const factory AboutRecipeStateInitial() = _$AboutRecipeStateInitialImpl; +} + +/// @nodoc +abstract class _$$AboutRecipeStateLoadingImplCopyWith<$Res> { + factory _$$AboutRecipeStateLoadingImplCopyWith( + _$AboutRecipeStateLoadingImpl value, + $Res Function(_$AboutRecipeStateLoadingImpl) then) = + __$$AboutRecipeStateLoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AboutRecipeStateLoadingImplCopyWithImpl<$Res> + extends _$AboutRecipeStateCopyWithImpl<$Res, _$AboutRecipeStateLoadingImpl> + implements _$$AboutRecipeStateLoadingImplCopyWith<$Res> { + __$$AboutRecipeStateLoadingImplCopyWithImpl( + _$AboutRecipeStateLoadingImpl _value, + $Res Function(_$AboutRecipeStateLoadingImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$AboutRecipeStateLoadingImpl implements AboutRecipeStateLoading { + const _$AboutRecipeStateLoadingImpl(); + + @override + String toString() { + return 'AboutRecipeState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AboutRecipeStateLoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Recipe recipe) loaded, + required TResult Function(String message) error, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Recipe recipe)? loaded, + TResult? Function(String message)? error, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Recipe recipe)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutRecipeState value) $default, { + required TResult Function(AboutRecipeStateInitial value) initial, + required TResult Function(AboutRecipeStateLoading value) loading, + required TResult Function(AboutRecipeStateLoaded value) loaded, + required TResult Function(RecipeStateError value) error, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutRecipeState value)? $default, { + TResult? Function(AboutRecipeStateInitial value)? initial, + TResult? Function(AboutRecipeStateLoading value)? loading, + TResult? Function(AboutRecipeStateLoaded value)? loaded, + TResult? Function(RecipeStateError value)? error, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutRecipeState value)? $default, { + TResult Function(AboutRecipeStateInitial value)? initial, + TResult Function(AboutRecipeStateLoading value)? loading, + TResult Function(AboutRecipeStateLoaded value)? loaded, + TResult Function(RecipeStateError value)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class AboutRecipeStateLoading implements AboutRecipeState { + const factory AboutRecipeStateLoading() = _$AboutRecipeStateLoadingImpl; +} + +/// @nodoc +abstract class _$$AboutRecipeStateLoadedImplCopyWith<$Res> { + factory _$$AboutRecipeStateLoadedImplCopyWith( + _$AboutRecipeStateLoadedImpl value, + $Res Function(_$AboutRecipeStateLoadedImpl) then) = + __$$AboutRecipeStateLoadedImplCopyWithImpl<$Res>; + @useResult + $Res call({Recipe recipe}); + + $RecipeCopyWith<$Res> get recipe; +} + +/// @nodoc +class __$$AboutRecipeStateLoadedImplCopyWithImpl<$Res> + extends _$AboutRecipeStateCopyWithImpl<$Res, _$AboutRecipeStateLoadedImpl> + implements _$$AboutRecipeStateLoadedImplCopyWith<$Res> { + __$$AboutRecipeStateLoadedImplCopyWithImpl( + _$AboutRecipeStateLoadedImpl _value, + $Res Function(_$AboutRecipeStateLoadedImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recipe = null, + }) { + return _then(_$AboutRecipeStateLoadedImpl( + recipe: null == recipe + ? _value.recipe + : recipe // ignore: cast_nullable_to_non_nullable + as Recipe, + )); + } + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $RecipeCopyWith<$Res> get recipe { + return $RecipeCopyWith<$Res>(_value.recipe, (value) { + return _then(_value.copyWith(recipe: value)); + }); + } +} + +/// @nodoc + +class _$AboutRecipeStateLoadedImpl implements AboutRecipeStateLoaded { + const _$AboutRecipeStateLoadedImpl({required this.recipe}); + + @override + final Recipe recipe; + + @override + String toString() { + return 'AboutRecipeState.loaded(recipe: $recipe)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AboutRecipeStateLoadedImpl && + (identical(other.recipe, recipe) || other.recipe == recipe)); + } + + @override + int get hashCode => Object.hash(runtimeType, recipe); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$AboutRecipeStateLoadedImplCopyWith<_$AboutRecipeStateLoadedImpl> + get copyWith => __$$AboutRecipeStateLoadedImplCopyWithImpl< + _$AboutRecipeStateLoadedImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Recipe recipe) loaded, + required TResult Function(String message) error, + }) { + return loaded(recipe); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Recipe recipe)? loaded, + TResult? Function(String message)? error, + }) { + return loaded?.call(recipe); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Recipe recipe)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(recipe); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutRecipeState value) $default, { + required TResult Function(AboutRecipeStateInitial value) initial, + required TResult Function(AboutRecipeStateLoading value) loading, + required TResult Function(AboutRecipeStateLoaded value) loaded, + required TResult Function(RecipeStateError value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutRecipeState value)? $default, { + TResult? Function(AboutRecipeStateInitial value)? initial, + TResult? Function(AboutRecipeStateLoading value)? loading, + TResult? Function(AboutRecipeStateLoaded value)? loaded, + TResult? Function(RecipeStateError value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutRecipeState value)? $default, { + TResult Function(AboutRecipeStateInitial value)? initial, + TResult Function(AboutRecipeStateLoading value)? loading, + TResult Function(AboutRecipeStateLoaded value)? loaded, + TResult Function(RecipeStateError value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class AboutRecipeStateLoaded implements AboutRecipeState { + const factory AboutRecipeStateLoaded({required final Recipe recipe}) = + _$AboutRecipeStateLoadedImpl; + + Recipe get recipe; + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$AboutRecipeStateLoadedImplCopyWith<_$AboutRecipeStateLoadedImpl> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$RecipeStateErrorImplCopyWith<$Res> { + factory _$$RecipeStateErrorImplCopyWith(_$RecipeStateErrorImpl value, + $Res Function(_$RecipeStateErrorImpl) then) = + __$$RecipeStateErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String message}); +} + +/// @nodoc +class __$$RecipeStateErrorImplCopyWithImpl<$Res> + extends _$AboutRecipeStateCopyWithImpl<$Res, _$RecipeStateErrorImpl> + implements _$$RecipeStateErrorImplCopyWith<$Res> { + __$$RecipeStateErrorImplCopyWithImpl(_$RecipeStateErrorImpl _value, + $Res Function(_$RecipeStateErrorImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + }) { + return _then(_$RecipeStateErrorImpl( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$RecipeStateErrorImpl implements RecipeStateError { + const _$RecipeStateErrorImpl({required this.message}); + + @override + final String message; + + @override + String toString() { + return 'AboutRecipeState.error(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecipeStateErrorImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$RecipeStateErrorImplCopyWith<_$RecipeStateErrorImpl> get copyWith => + __$$RecipeStateErrorImplCopyWithImpl<_$RecipeStateErrorImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Recipe recipe) loaded, + required TResult Function(String message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Recipe recipe)? loaded, + TResult? Function(String message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Recipe recipe)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutRecipeState value) $default, { + required TResult Function(AboutRecipeStateInitial value) initial, + required TResult Function(AboutRecipeStateLoading value) loading, + required TResult Function(AboutRecipeStateLoaded value) loaded, + required TResult Function(RecipeStateError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutRecipeState value)? $default, { + TResult? Function(AboutRecipeStateInitial value)? initial, + TResult? Function(AboutRecipeStateLoading value)? loading, + TResult? Function(AboutRecipeStateLoaded value)? loaded, + TResult? Function(RecipeStateError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutRecipeState value)? $default, { + TResult Function(AboutRecipeStateInitial value)? initial, + TResult Function(AboutRecipeStateLoading value)? loading, + TResult Function(AboutRecipeStateLoaded value)? loaded, + TResult Function(RecipeStateError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class RecipeStateError implements AboutRecipeState { + const factory RecipeStateError({required final String message}) = + _$RecipeStateErrorImpl; + + String get message; + + /// Create a copy of AboutRecipeState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$RecipeStateErrorImplCopyWith<_$RecipeStateErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/recipe/presentation/widgets/ingredients_widget.dart b/lib/features/recipe/presentation/widgets/ingredients_widget.dart new file mode 100644 index 00000000..6f83d6c1 --- /dev/null +++ b/lib/features/recipe/presentation/widgets/ingredients_widget.dart @@ -0,0 +1,59 @@ +import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/utils/localization_utils.dart'; +import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; + +class IngredientsWidget extends StatelessWidget { + const IngredientsWidget({super.key, required this.recipe}); + final Recipe recipe; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return MainBackgroundBouble( + child: ExpansionTile( + initiallyExpanded: true, + shape: Border(), + title: Text( + s.ingredients, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + children: List.generate(recipe.ingredients!.length, (index) { + return Padding( + padding: const EdgeInsets.only(left: 16, bottom: 8), + child: Row( + children: [ + Container( + width: 4, + height: 4, + decoration: BoxDecoration( + color: appTheme.black, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 4), + Builder( + builder: (context) { + final portion = Portion.getPortionFromValue(recipe.ingredients![index].unit ?? ''); + + String? portionName; + if (portion != null) { + portionName = getLocalizedMeasurementUnits(s, portion); + } + return Text( + '${recipe.ingredients![index].name} - ${recipe.ingredients![index].portion} ${portionName}', + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ); + }, + ), + ], + ), + ); + }), + ), + ); + } +} diff --git a/lib/features/recipe/presentation/widgets/steps_widget.dart b/lib/features/recipe/presentation/widgets/steps_widget.dart new file mode 100644 index 00000000..3e41164d --- /dev/null +++ b/lib/features/recipe/presentation/widgets/steps_widget.dart @@ -0,0 +1,63 @@ +import 'package:eatvio_app/core/const.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/main_background_bouble.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/widgets.dart'; + +class StepsWidget extends StatelessWidget { + const StepsWidget({super.key, required this.recipe}); + final Recipe recipe; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return MainBackgroundBouble( + padding: EdgeInsets.all(16), + child: Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + s.step_by_step_photo_recipe, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + ...List.generate(recipe.steps?.length ?? 0, (index) { + return Column( + spacing: 8, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: appTheme.blueA400, + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Text( + '${s.step} ${index + 1}', + style: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700), + ), + ), + if (recipe.steps?[index].image != null && recipe.steps![index].image.isNotEmpty) + AspectRatio( + aspectRatio: 16 / 12, + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Image.network( + baseUrl + '/storage/' + recipe.steps![index].image, + fit: BoxFit.cover, + ), + ), + ), + Text( + recipe.steps![index].description, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), + ], + ); + }), + ], + ), + ); + } +} diff --git a/lib/features/recipe/repositories/about_recipe_repository.dart b/lib/features/recipe/repositories/about_recipe_repository.dart new file mode 100644 index 00000000..a9178a65 --- /dev/null +++ b/lib/features/recipe/repositories/about_recipe_repository.dart @@ -0,0 +1,53 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:async'; +import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:eatvio_app/features/recipe/services/about_recipe_service.dart'; + +class AboutRecipeRepository { + final AboutRecipeService recipeService; + final _recipeLikeStateStreamController = StreamController.broadcast(); + final _recipeFavoriteStateStreamController = StreamController.broadcast(); + final _recipeCommentsStateStreamController = StreamController.broadcast(); + Stream get recipeLikeState => _recipeLikeStateStreamController.stream; + Stream get recipeFavoriteState => _recipeFavoriteStateStreamController.stream; + Stream get recipeCommentsState => _recipeCommentsStateStreamController.stream; + + AboutRecipeRepository({required this.recipeService}); + + Future getRecipe(String recipeID) async { + try { + return await recipeService.getRecipe(recipeID); + } catch (e) { + rethrow; + } + } + + Future likeRecipe(String slug) async { + try { + await recipeService.likeRecipe(slug: slug); + } catch (e) { + rethrow; + } + } + + Future addToFavorite(String slug) async { + try { + await recipeService.addToFavorite(slug: slug); + } catch (e) { + rethrow; + } + } + + Future removeFromFavorite(String slug) async { + try { + await recipeService.removeFromFavorite(slug: slug); + } catch (e) { + rethrow; + } + } + + void emitLikesStateToFeed(LikesState state) => _recipeLikeStateStreamController.add(state); + void emitFavoriteStateToFeed(FavoriteState state) => _recipeFavoriteStateStreamController.add(state); + void emitCommentsStateToFeed(CommentsState state) => _recipeCommentsStateStreamController.add(state); +} diff --git a/lib/features/recipe/services/about_recipe_service.dart b/lib/features/recipe/services/about_recipe_service.dart new file mode 100644 index 00000000..b326f970 --- /dev/null +++ b/lib/features/recipe/services/about_recipe_service.dart @@ -0,0 +1,28 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:retrofit/retrofit.dart'; + +part 'about_recipe_service.g.dart'; + +@RestApi() +abstract class AboutRecipeService { + factory AboutRecipeService(Dio dio, {String baseUrl}) = _AboutRecipeService; + + @POST('/api/recipes/{slug}/like') + Future likeRecipe({ + @Path() required String slug, + }); + + @POST('/api/recipes/{slug}/add_to_favorite') + Future addToFavorite({ + @Path() required String slug, + }); + + @POST('/api/recipes/{slug}/remove_from_favorite') + Future removeFromFavorite({ + @Path() required String slug, + }); + + @GET('/api/recipes/show/{id}') + Future getRecipe(@Path() String id); +} diff --git a/lib/features/recipe/services/about_recipe_service.g.dart b/lib/features/recipe/services/about_recipe_service.g.dart new file mode 100644 index 00000000..d8ed1366 --- /dev/null +++ b/lib/features/recipe/services/about_recipe_service.g.dart @@ -0,0 +1,130 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'about_recipe_service.dart'; + +// ************************************************************************** +// RetrofitGenerator +// ************************************************************************** + +// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations + +class _AboutRecipeService implements AboutRecipeService { + _AboutRecipeService(this._dio, {this.baseUrl, this.errorLogger}); + + final Dio _dio; + + String? baseUrl; + + final ParseErrorLogger? errorLogger; + + @override + Future likeRecipe({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/${slug}/like', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future addToFavorite({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/${slug}/add_to_favorite', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future removeFromFavorite({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/${slug}/remove_from_favorite', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + + @override + Future getRecipe(String id) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/recipes/show/${id}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late Recipe _value; + try { + _value = Recipe.fromJson(_result.data!); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + + RequestOptions _setStreamType(RequestOptions requestOptions) { + if (T != dynamic && + !(requestOptions.responseType == ResponseType.bytes || + requestOptions.responseType == ResponseType.stream)) { + if (T == String) { + requestOptions.responseType = ResponseType.plain; + } else { + requestOptions.responseType = ResponseType.json; + } + } + return requestOptions; + } + + String _combineBaseUrls(String dioBaseUrl, String? baseUrl) { + if (baseUrl == null || baseUrl.trim().isEmpty) { + return dioBaseUrl; + } + + final url = Uri.parse(baseUrl); + + if (url.isAbsolute) { + return url.toString(); + } + + return Uri.parse(dioBaseUrl).resolveUri(url).toString(); + } +} diff --git a/lib/features/recipes/presentation/pages/about_recipes_screen.dart b/lib/features/recipes/presentation/pages/about_recipes_screen.dart deleted file mode 100644 index 3bb3d35f..00000000 --- a/lib/features/recipes/presentation/pages/about_recipes_screen.dart +++ /dev/null @@ -1,430 +0,0 @@ -import 'dart:io'; -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:eatvio_app/core/const.dart'; -import 'package:eatvio_app/core/widgets/loader.dart'; - -import 'package:eatvio_app/features/feed/models/recipes_model.dart' as step; -import 'package:eatvio_app/features/feed/models/recipes_model.dart'; -import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; -import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; -import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; -import 'package:eatvio_app/core/locator_service.dart'; -import 'package:eatvio_app/core/theme/theme_helper.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_html/flutter_html.dart'; -import 'package:go_router/go_router.dart'; - -class AboutRecipesScreen extends StatefulWidget { - final Recipes recipes; - - const AboutRecipesScreen({super.key, required this.recipes}); - - @override - State createState() => _AboutRecipesScreenState(); -} - -class _AboutRecipesScreenState extends State { - final CommentsCubit commentsCubit = sl(); - final textEditingController = TextEditingController(); - int? replyId; - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (BuildContext context) => commentsCubit - ..getComments( - widget.recipes.slug, - CommentSourceType.recipe, - true, - ), - child: BlocConsumer( - listener: (BuildContext context, CommentsState state) { - if (state is CommentsStateLoaded) { - setState(() { - replyId = state.replyId; - }); - } - }, - builder: (BuildContext context, CommentsState state) { - return Scaffold( - resizeToAvoidBottomInset: true, - bottomSheet: Container( - padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), - decoration: BoxDecoration( - color: Colors.white, - border: Border( - top: BorderSide( - color: Colors.black.withValues(alpha: 0.1), - ), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (replyId != null) - GestureDetector( - onTap: () { - context.read().setReplyId(null); - setState(() { - replyId = null; - textEditingController.clear(); - }); - FocusManager.instance.primaryFocus?.unfocus(); - }, - child: Container( - decoration: BoxDecoration( - color: appTheme.blueA400, - shape: BoxShape.circle, - ), - height: 32, - width: 32, - child: Icon( - Icons.close_rounded, - size: 18, - color: Colors.white, - ), - ), - ), - if (replyId != null) SizedBox(width: 8), - SizedBox( - width: MediaQuery.of(context).size.width - (replyId == null ? 64 : 104), - child: TextFormField( - onChanged: (_) { - setState(() {}); - }, - controller: textEditingController, - maxLines: 5, - minLines: 1, - keyboardType: TextInputType.multiline, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black, - height: 1, - ), - decoration: InputDecoration( - isDense: true, - contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), - hintText: 'Комментарий', - hintStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - height: 1, - color: Color(0xFFAEAEB2), - ), - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - ), - ), - ), - SizedBox(width: 8), - GestureDetector( - onTap: () { - if (textEditingController.text.length > 2) { - commentsCubit.sendComment( - id: widget.recipes.slug, - commentText: textEditingController.text, - parentCommentId: replyId, - commentSourceType: CommentSourceType.recipe, - ); - context.read().setReplyId(null); - setState(() { - textEditingController.clear(); - replyId = null; - }); - FocusManager.instance.primaryFocus?.unfocus(); - } - }, - child: Container( - decoration: BoxDecoration( - color: textEditingController.text.length > 2 ? appTheme.blueA400 : Colors.grey, - shape: BoxShape.circle, - ), - height: 32, - width: 32, - child: Icon( - Icons.send_rounded, - size: 18, - color: Colors.white, - ), - ), - ), - ], - ), - ), - appBar: AppBar( - actions: [ - InkWell( - borderRadius: BorderRadius.circular(16), - onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: widget.recipes.author), - child: Ink( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircleAvatar( - maxRadius: 16, - minRadius: 16, - backgroundImage: NetworkImage(widget.recipes.author.authorProfileIcon), - ), - SizedBox(height: 4), - Text( - widget.recipes.author.authorName, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - ], - ), - ), - ), - ], - ), - body: SingleChildScrollView( - padding: EdgeInsets.only(bottom: 80), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - widget.recipes.title ?? '', - style: TextStyle( - color: Colors.black, - fontSize: 24, - fontWeight: FontWeight.w500, - height: 1, - ), - ), - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - children: [ - Icon(Icons.timer_outlined), - SizedBox(width: 4), - Text( - widget.recipes.cookingTime.toString() + ' минут', - style: TextStyle( - color: Colors.black, - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - SizedBox(width: 16), - Icon(Icons.fast_forward_outlined), - SizedBox(width: 4), - Text( - '${widget.recipes.steps?.length ?? 0}' + ' этапов', - style: TextStyle( - color: Colors.black, - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - ], - ), - ), - if (widget.recipes.description.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Html( - data: widget.recipes.description, - style: { - "*": Style( - color: Colors.black87, - ), - }, - ), - ), - SizedBox(height: 12), - CachedNetworkImage( - imageUrl: baseUrl + '/storage/' + widget.recipes.image, - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'Ингредиенты', - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w500, - height: 1, - ), - ), - ), - SizedBox(height: 12), - ListView.separated( - padding: const EdgeInsets.symmetric(horizontal: 16), - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - if (widget.recipes.ingredients != null) { - return _ingredient(widget.recipes.ingredients![index]); - } - return SizedBox.shrink(); - }, - separatorBuilder: (context, index) => SizedBox(height: 4), - itemCount: widget.recipes.ingredients?.length ?? 0, - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'Пошаговый рецепт', - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w500, - height: 1, - ), - ), - ), - SizedBox(height: 12), - ListView.separated( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - if (widget.recipes.steps == null) return SizedBox.shrink(); - return _step(widget.recipes.steps![index], index + 1); - }, - separatorBuilder: (context, index) => Padding( - padding: const EdgeInsets.all(16), - child: Container( - height: 0.4, - decoration: BoxDecoration( - color: Colors.grey, - borderRadius: BorderRadius.circular(16), - ), - ), - ), - itemCount: widget.recipes.steps?.length ?? 0, - ), - SizedBox(height: 8), - Padding( - padding: const EdgeInsets.all(16), - child: Container( - height: 0.4, - decoration: BoxDecoration( - color: Colors.grey, - borderRadius: BorderRadius.circular(16), - ), - ), - ), - SizedBox(height: 8), - Container( - padding: EdgeInsets.symmetric(horizontal: 16), - alignment: Alignment.centerLeft, - child: Text( - widget.recipes.commentsCount.toString() + ' КОММЕНТАРИЕВ', - style: TextStyle( - fontSize: 12, - color: appTheme.gray500, - fontWeight: FontWeight.w400, - ), - ), - ), - SizedBox(height: 16), - if (state is CommentsStateLoading) - Padding( - padding: const EdgeInsets.all(32), - child: const Loader(), - ), - if (state is CommentsStateLoaded) - CommentsWidget( - id: widget.recipes.slug, - commentSourceType: CommentSourceType.recipe, - comment: state.comment, - replyId: replyId, - ), - ], - ), - ), - ); - }, - ), - ); - } - - _ingredient(Ingredient ingredient) { - return Text( - '• ' + ingredient.name + ' ' + ingredient.portion + ' ' + (ingredient.unit ?? ''), - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1.2, - ), - ); - } - - _step(step.Step step, int stepNumber) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'Шаг ' + stepNumber.toString(), - style: TextStyle( - color: Colors.black54, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - step.description, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1.2, - ), - ), - ), - SizedBox(height: 8), - if (step.image.length > 1) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: CachedNetworkImage(imageUrl: baseUrl + '/storage/' + step.image), - ), - ), - ], - ); - } -} diff --git a/lib/features/recipes/services/recipe_service.dart b/lib/features/recipes/services/recipe_service.dart deleted file mode 100644 index 12e9c78b..00000000 --- a/lib/features/recipes/services/recipe_service.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; -import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart'; -import 'package:retrofit/retrofit.dart'; -import 'package:eatvio_app/features/recipes/models/product.dart'; - -part 'recipe_service.g.dart'; - -@RestApi() -abstract class RecipeService { - factory RecipeService(Dio dio, {String baseUrl}) = _RecipeService; - - @GET('/api/recipes/show/{id}') - Future getRecipe(@Path() String id); - - @GET('/api/diary/product_nutritional_values/filter') - Future>> getInputProduct(@Query('name') String name); - - @POST('/api/recipes/create') - @MultiPart() - Future publishRecipe({ - @Body() required FormData formData, - }); - - @POST('/api/recipes/update/{id}') - Future editRecipe(@Part() Map recipe, String id); - - @GET('/api/recipes/tags') - Future getTags(); -} \ No newline at end of file diff --git a/lib/features/support/presentation/pages/support_screen.dart b/lib/features/support/presentation/pages/support_screen.dart index 00e51c42..dcac5568 100644 --- a/lib/features/support/presentation/pages/support_screen.dart +++ b/lib/features/support/presentation/pages/support_screen.dart @@ -24,7 +24,10 @@ class SupportScreen extends StatelessWidget { ), InkWell( onTap: () { - launchUrl(Uri.parse('https://t.me/eatvio')); + launchUrl( + Uri.parse('https://t.me/eatvio'), + mode: LaunchMode.externalApplication, + ); }, borderRadius: BorderRadius.circular(16), child: Ink( @@ -53,7 +56,10 @@ class SupportScreen extends StatelessWidget { const SizedBox(height: 8), InkWell( onTap: () { - launchUrl(Uri.parse('mailto:help@eatv.io')); + launchUrl( + Uri.parse('mailto:help@eatv.io'), + mode: LaunchMode.externalApplication, + ); }, borderRadius: BorderRadius.circular(16), child: Ink( diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart index 3139e159..abae25a5 100644 --- a/lib/generated/intl/messages_all.dart +++ b/lib/generated/intl/messages_all.dart @@ -39,10 +39,8 @@ MessageLookupByLibrary? _findExact(String localeName) { /// User programs should call this before using [localeName] for messages. Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale( - localeName, - (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null, - ); + localeName, (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); if (availableLocale == null) { return new SynchronousFuture(false); } @@ -62,11 +60,8 @@ bool _messagesExistFor(String locale) { } MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { - var actualLocale = Intl.verifiedLocale( - locale, - _messagesExistFor, - onFailure: (_) => null, - ); + var actualLocale = + Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); if (actualLocale == null) return null; return _findExact(actualLocale); } diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 7fbaf5bc..0d06adf7 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -22,375 +22,350 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), - "Russia": MessageLookupByLibrary.simpleMessage("Россия"), - "about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"), - "about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"), - "active": MessageLookupByLibrary.simpleMessage("Активен"), - "active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"), - "add": MessageLookupByLibrary.simpleMessage("Добавить"), - "add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"), - "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), - "adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"), - "additional_information": MessageLookupByLibrary.simpleMessage( - "Дополнительная информация", - ), - "admin": MessageLookupByLibrary.simpleMessage("Админ"), - "all": MessageLookupByLibrary.simpleMessage("Все"), - "allergies_presence": MessageLookupByLibrary.simpleMessage( - "Наличие аллергии", - ), - "another_form_of_comment": MessageLookupByLibrary.simpleMessage( - "комментария", - ), - "another_form_of_day": MessageLookupByLibrary.simpleMessage("дня"), - "answer": MessageLookupByLibrary.simpleMessage("Ответить"), - "archived": MessageLookupByLibrary.simpleMessage("В архиве"), - "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), - "average_pressure": MessageLookupByLibrary.simpleMessage( - "Средние показатели давления:", - ), - "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), - "by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"), - "calories": MessageLookupByLibrary.simpleMessage("Калории"), - "carb_type": MessageLookupByLibrary.simpleMessage("Мучное"), - "carb_type_desc": MessageLookupByLibrary.simpleMessage( - "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", - ), - "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), - "chat": MessageLookupByLibrary.simpleMessage("Чат"), - "chest": MessageLookupByLibrary.simpleMessage("Грудь"), - "chest_circumference": MessageLookupByLibrary.simpleMessage("Обхват груди"), - "choose_diet_duration": MessageLookupByLibrary.simpleMessage( - "Выберите срок диеты", - ), - "choose_plan_duration": MessageLookupByLibrary.simpleMessage( - "Выберите срок плана", - ), - "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( - "Выберите дату старты диеты. Она отобразиться у Вас в дневнике", - ), - "circumference_empty": MessageLookupByLibrary.simpleMessage( - "Все замеры обязательны к заполнению", - ), - "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все замеры должны быть числами", - ), - "circumferences_chart": MessageLookupByLibrary.simpleMessage( - "График обхватов", - ), - "city": MessageLookupByLibrary.simpleMessage("Город"), - "city_of_living": MessageLookupByLibrary.simpleMessage("Город проживания"), - "comfort": MessageLookupByLibrary.simpleMessage("Комфорт"), - "comment": MessageLookupByLibrary.simpleMessage("комментарий"), - "comments": MessageLookupByLibrary.simpleMessage("Комментарии"), - "comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"), - "complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"), - "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), - "continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), - "copied": MessageLookupByLibrary.simpleMessage("cкопирован"), - "copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), - "country": MessageLookupByLibrary.simpleMessage("Страна"), - "create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"), - "customer_price": MessageLookupByLibrary.simpleMessage( - "Цена для покупателя", - ), - "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), - "day": MessageLookupByLibrary.simpleMessage("День"), - "days": MessageLookupByLibrary.simpleMessage("дней"), - "deleted": MessageLookupByLibrary.simpleMessage("Удалена"), - "diary": MessageLookupByLibrary.simpleMessage("Дневник"), - "diet": MessageLookupByLibrary.simpleMessage("Диета"), - "diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"), - "diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"), - "diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"), - "diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"), - "do_you_have_hypertension_tendency": MessageLookupByLibrary.simpleMessage( - "Есть ли у вас гипертония?", - ), - "do_you_have_hypotension_tendency": MessageLookupByLibrary.simpleMessage( - "Есть ли у вас гипотония?", - ), - "do_you_take_pills": MessageLookupByLibrary.simpleMessage( - "Принимаете ли вы лекарства или бады?", - ), - "down_pressure": MessageLookupByLibrary.simpleMessage( - "Нижние показатели давления:", - ), - "draft": MessageLookupByLibrary.simpleMessage("Черновик"), - "drafted": MessageLookupByLibrary.simpleMessage("Черновики"), - "eating_behavior": MessageLookupByLibrary.simpleMessage( - "Пищевое поведение", - ), - "eating_behavior_type": MessageLookupByLibrary.simpleMessage( - "Формирование пищевого поведения", - ), - "eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage( - "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;", - ), - "economy": MessageLookupByLibrary.simpleMessage("Эконом"), - "edit": MessageLookupByLibrary.simpleMessage("Редактировать"), - "email": MessageLookupByLibrary.simpleMessage("Электронная почта"), - "email_empty": MessageLookupByLibrary.simpleMessage( - "Необходимо ввести адрес электронной почты", - ), - "email_invalid": MessageLookupByLibrary.simpleMessage( - "Некорректный адрес электронной почты", - ), - "enter_additional_information": MessageLookupByLibrary.simpleMessage( - "Введите любую дополнительную информацию на своё усмотрение:", - ), - "enter_diet_description": MessageLookupByLibrary.simpleMessage( - "Введите описание диеты", - ), - "enter_diet_name": MessageLookupByLibrary.simpleMessage( - "Выведите название диеты", - ), - "enter_information": MessageLookupByLibrary.simpleMessage( - "Введите информацию...", - ), - "exit": MessageLookupByLibrary.simpleMessage("Выйти"), - "expand": MessageLookupByLibrary.simpleMessage("Показать полностью"), - "fats": MessageLookupByLibrary.simpleMessage("Жиры"), - "favorite_foods": MessageLookupByLibrary.simpleMessage("Любимые продукты"), - "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Ваши любимые/нелюбимые продукты питания?", - ), - "female": MessageLookupByLibrary.simpleMessage("Женский"), - "fill_in_information": MessageLookupByLibrary.simpleMessage( - "Заполните информацию:", - ), - "finish": MessageLookupByLibrary.simpleMessage("Завершить"), - "first_name": MessageLookupByLibrary.simpleMessage("Имя"), - "first_name_empty": MessageLookupByLibrary.simpleMessage( - "Имя обязательно для заполнения", - ), - "first_name_short": MessageLookupByLibrary.simpleMessage( - "В имени должен быть минимум 1 символ", - ), - "fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"), - "fit_type_desc": MessageLookupByLibrary.simpleMessage( - "План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;", - ), - "fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"), - "from": MessageLookupByLibrary.simpleMessage("от"), - "from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"), - "from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"), - "gallery": MessageLookupByLibrary.simpleMessage("Галерея"), - "general_info": MessageLookupByLibrary.simpleMessage("Общая информация"), - "gr": MessageLookupByLibrary.simpleMessage("гр"), - "grams": MessageLookupByLibrary.simpleMessage("гр"), - "has_alergy": MessageLookupByLibrary.simpleMessage("Наличие аллергии"), - "has_hyper": MessageLookupByLibrary.simpleMessage( - "Склонность к гипертонии", - ), - "has_hypo": MessageLookupByLibrary.simpleMessage("Склонность к гипотонии"), - "height": MessageLookupByLibrary.simpleMessage("Рост"), - "height_measurements": MessageLookupByLibrary.simpleMessage("Замер роста"), - "hide": MessageLookupByLibrary.simpleMessage("Свернуть"), - "hip_circumference": MessageLookupByLibrary.simpleMessage("Обхват бедер"), - "hips": MessageLookupByLibrary.simpleMessage("Бедра"), - "in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), - "inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"), - "initial_measurements": MessageLookupByLibrary.simpleMessage( - "Первичные замеры", - ), - "label": MessageLookupByLibrary.simpleMessage("Название"), - "language": MessageLookupByLibrary.simpleMessage("Язык"), - "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), - "last_name_empty": MessageLookupByLibrary.simpleMessage( - "Фамилия обязательна для заполнения", - ), - "last_name_short": MessageLookupByLibrary.simpleMessage( - "В фамилии должен быть минимум 6 символов", - ), - "lbl_english": MessageLookupByLibrary.simpleMessage("English"), - "light_type": MessageLookupByLibrary.simpleMessage("Легкая"), - "light_type_desc": MessageLookupByLibrary.simpleMessage( - "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.", - ), - "liked_groceries": MessageLookupByLibrary.simpleMessage("Любимые продукты"), - "link_was_copied": MessageLookupByLibrary.simpleMessage( - "Ссылка успешно скопирована", - ), - "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), - "lower": MessageLookupByLibrary.simpleMessage("Нижнее"), - "male": MessageLookupByLibrary.simpleMessage("Мужской"), - "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), - "meat": MessageLookupByLibrary.simpleMessage("Мясо"), - "meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), - "meat_type_desc": MessageLookupByLibrary.simpleMessage( - "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.", - ), - "milliliter": MessageLookupByLibrary.simpleMessage("мл"), - "minimal_customer_price": MessageLookupByLibrary.simpleMessage( - "Минимальная цена для покупателя: 1200₽", - ), - "most_new": MessageLookupByLibrary.simpleMessage("Самые новые"), - "most_old": MessageLookupByLibrary.simpleMessage("Самые старые"), - "msg_network_err": MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), - "msg_something_went_wrong": MessageLookupByLibrary.simpleMessage( - "Что то пошло не так!", - ), - "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), - "my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"), - "my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"), - "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), - "name_empty": MessageLookupByLibrary.simpleMessage( - "Имя пользователя обязательно для заполнения", - ), - "name_short": MessageLookupByLibrary.simpleMessage( - "В имени пользователя должно быть минимум 6 символов", - ), - "no": MessageLookupByLibrary.simpleMessage("Нет"), - "no_account": MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), - "not_liked_groceries": MessageLookupByLibrary.simpleMessage( - "Не любимые продукты", - ), - "one_option": MessageLookupByLibrary.simpleMessage("1 опция"), - "option": MessageLookupByLibrary.simpleMessage("Опция"), - "option_content": MessageLookupByLibrary.simpleMessage("Содержание опции"), - "option_content_hint_text": MessageLookupByLibrary.simpleMessage( - "На связи 7 дней в неделю", - ), - "option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"), - "option_desc_hint_text": MessageLookupByLibrary.simpleMessage( - "Этот план питания для похудения на 7 дней сбалансированный и разнообразный", - ), - "option_duration": MessageLookupByLibrary.simpleMessage( - "Продолжительность опции", - ), - "option_personal_diet_desc": MessageLookupByLibrary.simpleMessage( - "Опция подразумевает создание индивидуальной диеты", - ), - "option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"), - "order": MessageLookupByLibrary.simpleMessage("Порядок"), - "other_info": MessageLookupByLibrary.simpleMessage("Прочая информация"), - "password": MessageLookupByLibrary.simpleMessage("Пароль"), - "password_confirmation": MessageLookupByLibrary.simpleMessage( - "Подтвердите пароль", - ), - "password_confirmation_mismatch": MessageLookupByLibrary.simpleMessage( - "Пароли не совпадают", - ), - "password_empty": MessageLookupByLibrary.simpleMessage( - "Пароль обязателен для заполнения", - ), - "password_short": MessageLookupByLibrary.simpleMessage( - "В пароле должно быть минимум 6 символов", - ), - "pcs": MessageLookupByLibrary.simpleMessage("шт"), - "personal": MessageLookupByLibrary.simpleMessage("Персональные"), - "personal_diet_state": MessageLookupByLibrary.simpleMessage("Персональная"), - "photo": MessageLookupByLibrary.simpleMessage("Фото"), - "physical_fitness": MessageLookupByLibrary.simpleMessage( - "Физическая форма", - ), - "place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"), - "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), - "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), - "plan_desc_example": MessageLookupByLibrary.simpleMessage( - "Большое подробное, привлекательное описание плана", - ), - "plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"), - "plan_label_desc": MessageLookupByLibrary.simpleMessage( - "План для поддержания формы", - ), - "plan_type": MessageLookupByLibrary.simpleMessage("Тип плана"), - "plans_complete": MessageLookupByLibrary.simpleMessage("Пройденные планы"), - "popular": MessageLookupByLibrary.simpleMessage("Популярные"), - "preferred_language": MessageLookupByLibrary.simpleMessage( - "Предпочитаемый язык общения", - ), - "pressure": MessageLookupByLibrary.simpleMessage("Давление"), - "pressure_empty": MessageLookupByLibrary.simpleMessage( - "Все показатели давления обязательны к заполнению", - ), - "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все показатели давления должны быть числами", - ), - "preview": MessageLookupByLibrary.simpleMessage("Превью"), - "price": MessageLookupByLibrary.simpleMessage("Цена"), - "price_explanation": MessageLookupByLibrary.simpleMessage( - "Разница в ценах, это комиссия Итвио", - ), - "profile": MessageLookupByLibrary.simpleMessage("Профиль"), - "proteins": MessageLookupByLibrary.simpleMessage("Белки"), - "publish": MessageLookupByLibrary.simpleMessage("Опубликовать"), - "published": MessageLookupByLibrary.simpleMessage("Опубликованные"), - "removeFromActive": MessageLookupByLibrary.simpleMessage( - "Снять с публикации", - ), - "save": MessageLookupByLibrary.simpleMessage("Сохранить"), - "services": MessageLookupByLibrary.simpleMessage("Оказание услуг"), - "settings": MessageLookupByLibrary.simpleMessage("Настройки"), - "sex": MessageLookupByLibrary.simpleMessage("Пол"), - "share": MessageLookupByLibrary.simpleMessage("Поделиться"), - "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), - "slim": MessageLookupByLibrary.simpleMessage("Похудение"), - "slim_type": MessageLookupByLibrary.simpleMessage("Похудение"), - "slim_type_desc": MessageLookupByLibrary.simpleMessage( - "План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;", - ), - "sport_hobbies": MessageLookupByLibrary.simpleMessage( - "Спортивные увлечения", - ), - "standard": MessageLookupByLibrary.simpleMessage("Стандарт"), - "startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"), - "takes_pills": MessageLookupByLibrary.simpleMessage("Принимает лекарства"), - "tell_your_schedule": MessageLookupByLibrary.simpleMessage( - "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:", - ), - "three_options": MessageLookupByLibrary.simpleMessage("3 опции"), - "time": MessageLookupByLibrary.simpleMessage("Время прохождения"), - "toOffer": MessageLookupByLibrary.simpleMessage("Предложить"), - "to_draft": MessageLookupByLibrary.simpleMessage("В черновик"), - "unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Нелюбимые продукты", - ), - "up_pressure": MessageLookupByLibrary.simpleMessage( - "Верхние показатели давления:", - ), - "upper": MessageLookupByLibrary.simpleMessage("Верхнее"), - "user_authorization": MessageLookupByLibrary.simpleMessage( - "Аутентификация", - ), - "user_data": MessageLookupByLibrary.simpleMessage("Данные пользователя"), - "user_registration": MessageLookupByLibrary.simpleMessage( - "Регистрация пользователя", - ), - "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), - "vegan": MessageLookupByLibrary.simpleMessage("Веган."), - "vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"), - "vegan_type_desc": MessageLookupByLibrary.simpleMessage( - "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.", - ), - "waist": MessageLookupByLibrary.simpleMessage("Талия"), - "waist_circumference": MessageLookupByLibrary.simpleMessage("Обхват талии"), - "weight": MessageLookupByLibrary.simpleMessage("Вес"), - "weight_chart": MessageLookupByLibrary.simpleMessage("График веса"), - "weight_measurements": MessageLookupByLibrary.simpleMessage("Замер веса"), - "wellness": MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"), - "wellness_type": MessageLookupByLibrary.simpleMessage( - "Улучшение самочувствия", - ), - "wellness_type_desc": MessageLookupByLibrary.simpleMessage( - "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", - ), - "what_is_your_name": MessageLookupByLibrary.simpleMessage("Как вас зовут?"), - "wrong_credentials": MessageLookupByLibrary.simpleMessage( - "Неверные данные пользователя", - ), - "you_will_get": MessageLookupByLibrary.simpleMessage("Вы получите"), - "your_blood_pressure": MessageLookupByLibrary.simpleMessage( - "Ваше артериальное давление?", - ), - "your_favorite_sports": MessageLookupByLibrary.simpleMessage( - "Ваши спортивные увлечения?", - ), - "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), - "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), - "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), - "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:"), - }; + "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), + "Russia": MessageLookupByLibrary.simpleMessage("Россия"), + "about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"), + "about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"), + "about_recipe": MessageLookupByLibrary.simpleMessage("Об этом рецепте"), + "active": MessageLookupByLibrary.simpleMessage("Активен"), + "active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"), + "add": MessageLookupByLibrary.simpleMessage("Добавить"), + "add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"), + "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), + "adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"), + "additional_information": + MessageLookupByLibrary.simpleMessage("Дополнительная информация"), + "admin": MessageLookupByLibrary.simpleMessage("Админ"), + "all": MessageLookupByLibrary.simpleMessage("Все"), + "allergies_presence": + MessageLookupByLibrary.simpleMessage("Наличие аллергии"), + "another_form_of_comment": + MessageLookupByLibrary.simpleMessage("комментария"), + "another_form_of_day": MessageLookupByLibrary.simpleMessage("дня"), + "another_form_of_hour": MessageLookupByLibrary.simpleMessage("часа"), + "another_form_of_minute": MessageLookupByLibrary.simpleMessage("минут"), + "another_form_of_stage": MessageLookupByLibrary.simpleMessage("этапа"), + "answer": MessageLookupByLibrary.simpleMessage("Ответить"), + "archived": MessageLookupByLibrary.simpleMessage("В архиве"), + "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), + "average_pressure": MessageLookupByLibrary.simpleMessage( + "Средние показатели давления:"), + "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), + "by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"), + "calories": MessageLookupByLibrary.simpleMessage("Калории"), + "cancel_diet": MessageLookupByLibrary.simpleMessage("Отменить диету?"), + "carb_type": MessageLookupByLibrary.simpleMessage("Мучное"), + "carb_type_desc": MessageLookupByLibrary.simpleMessage( + "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."), + "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), + "chat": MessageLookupByLibrary.simpleMessage("Чат"), + "check_out_video_recipe": + MessageLookupByLibrary.simpleMessage("Посмотреть видео рецепт"), + "chest": MessageLookupByLibrary.simpleMessage("Грудь"), + "chest_circumference": + MessageLookupByLibrary.simpleMessage("Обхват груди"), + "choose_diet_duration": + MessageLookupByLibrary.simpleMessage("Выберите срок диеты"), + "choose_plan_duration": + MessageLookupByLibrary.simpleMessage("Выберите срок плана"), + "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( + "Выберите дату старты диеты. Она отобразиться у Вас в дневнике"), + "circumference_empty": MessageLookupByLibrary.simpleMessage( + "Все замеры обязательны к заполнению"), + "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( + "Все замеры должны быть числами"), + "circumferences_chart": + MessageLookupByLibrary.simpleMessage("График обхватов"), + "city": MessageLookupByLibrary.simpleMessage("Город"), + "city_of_living": + MessageLookupByLibrary.simpleMessage("Город проживания"), + "comfort": MessageLookupByLibrary.simpleMessage("Комфорт"), + "comment": MessageLookupByLibrary.simpleMessage("комментарий"), + "comments": MessageLookupByLibrary.simpleMessage("Комментарии"), + "comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"), + "complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"), + "confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"), + "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), + "continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), + "copied": MessageLookupByLibrary.simpleMessage("cкопирован"), + "copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), + "could_not_cancel_diet": + MessageLookupByLibrary.simpleMessage("Не удалось отменить диету"), + "could_not_launch_video": + MessageLookupByLibrary.simpleMessage("Не удалось запустить видео"), + "country": MessageLookupByLibrary.simpleMessage("Страна"), + "create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"), + "customer_price": + MessageLookupByLibrary.simpleMessage("Цена для покупателя"), + "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), + "day": MessageLookupByLibrary.simpleMessage("День"), + "days": MessageLookupByLibrary.simpleMessage("дней"), + "deleted": MessageLookupByLibrary.simpleMessage("Удалена"), + "diary": MessageLookupByLibrary.simpleMessage("Дневник"), + "diet": MessageLookupByLibrary.simpleMessage("Диета"), + "diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"), + "diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"), + "diet_started": + MessageLookupByLibrary.simpleMessage("Диета уже начата"), + "diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"), + "diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"), + "do_you_have_hypertension_tendency": + MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"), + "do_you_have_hypotension_tendency": + MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"), + "do_you_take_pills": MessageLookupByLibrary.simpleMessage( + "Принимаете ли вы лекарства или бады?"), + "down_pressure": + MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"), + "draft": MessageLookupByLibrary.simpleMessage("Черновик"), + "drafted": MessageLookupByLibrary.simpleMessage("Черновики"), + "eating_behavior": + MessageLookupByLibrary.simpleMessage("Пищевое поведение"), + "eating_behavior_type": MessageLookupByLibrary.simpleMessage( + "Формирование пищевого поведения"), + "eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage( + "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"), + "economy": MessageLookupByLibrary.simpleMessage("Эконом"), + "edit": MessageLookupByLibrary.simpleMessage("Редактировать"), + "email": MessageLookupByLibrary.simpleMessage("Электронная почта"), + "email_empty": MessageLookupByLibrary.simpleMessage( + "Необходимо ввести адрес электронной почты"), + "email_invalid": MessageLookupByLibrary.simpleMessage( + "Некорректный адрес электронной почты"), + "enter_additional_information": MessageLookupByLibrary.simpleMessage( + "Введите любую дополнительную информацию на своё усмотрение:"), + "enter_diet_description": + MessageLookupByLibrary.simpleMessage("Введите описание диеты"), + "enter_diet_name": + MessageLookupByLibrary.simpleMessage("Выведите название диеты"), + "enter_information": + MessageLookupByLibrary.simpleMessage("Введите информацию..."), + "exit": MessageLookupByLibrary.simpleMessage("Выйти"), + "expand": MessageLookupByLibrary.simpleMessage("Показать полностью"), + "fats": MessageLookupByLibrary.simpleMessage("Жиры"), + "favorite_foods": + MessageLookupByLibrary.simpleMessage("Любимые продукты"), + "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( + "Ваши любимые/нелюбимые продукты питания?"), + "female": MessageLookupByLibrary.simpleMessage("Женский"), + "fill_in_information": + MessageLookupByLibrary.simpleMessage("Заполните информацию:"), + "finish": MessageLookupByLibrary.simpleMessage("Завершить"), + "first_name": MessageLookupByLibrary.simpleMessage("Имя"), + "first_name_empty": MessageLookupByLibrary.simpleMessage( + "Имя обязательно для заполнения"), + "first_name_short": MessageLookupByLibrary.simpleMessage( + "В имени должен быть минимум 1 символ"), + "fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"), + "fit_type_desc": MessageLookupByLibrary.simpleMessage( + "План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;"), + "fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"), + "from": MessageLookupByLibrary.simpleMessage("от"), + "from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"), + "from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"), + "gallery": MessageLookupByLibrary.simpleMessage("Галерея"), + "general_info": + MessageLookupByLibrary.simpleMessage("Общая информация"), + "gr": MessageLookupByLibrary.simpleMessage("гр"), + "grams": MessageLookupByLibrary.simpleMessage("гр"), + "has_alergy": MessageLookupByLibrary.simpleMessage("Наличие аллергии"), + "has_hyper": + MessageLookupByLibrary.simpleMessage("Склонность к гипертонии"), + "has_hypo": + MessageLookupByLibrary.simpleMessage("Склонность к гипотонии"), + "height": MessageLookupByLibrary.simpleMessage("Рост"), + "height_measurements": + MessageLookupByLibrary.simpleMessage("Замер роста"), + "hide": MessageLookupByLibrary.simpleMessage("Свернуть"), + "hip_circumference": + MessageLookupByLibrary.simpleMessage("Обхват бедер"), + "hips": MessageLookupByLibrary.simpleMessage("Бедра"), + "hour": MessageLookupByLibrary.simpleMessage("час"), + "hours": MessageLookupByLibrary.simpleMessage("часов"), + "in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), + "inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"), + "ingredients": MessageLookupByLibrary.simpleMessage("Ингредиенты"), + "initial_measurements": + MessageLookupByLibrary.simpleMessage("Первичные замеры"), + "label": MessageLookupByLibrary.simpleMessage("Название"), + "language": MessageLookupByLibrary.simpleMessage("Язык"), + "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), + "last_name_empty": MessageLookupByLibrary.simpleMessage( + "Фамилия обязательна для заполнения"), + "last_name_short": MessageLookupByLibrary.simpleMessage( + "В фамилии должен быть минимум 6 символов"), + "lbl_english": MessageLookupByLibrary.simpleMessage("English"), + "light_type": MessageLookupByLibrary.simpleMessage("Легкая"), + "light_type_desc": MessageLookupByLibrary.simpleMessage( + "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."), + "liked_groceries": + MessageLookupByLibrary.simpleMessage("Любимые продукты"), + "link_was_copied": + MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"), + "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), + "lower": MessageLookupByLibrary.simpleMessage("Нижнее"), + "male": MessageLookupByLibrary.simpleMessage("Мужской"), + "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), + "meat": MessageLookupByLibrary.simpleMessage("Мясо"), + "meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), + "meat_type_desc": MessageLookupByLibrary.simpleMessage( + "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."), + "milliliter": MessageLookupByLibrary.simpleMessage("мл"), + "minimal_customer_price": MessageLookupByLibrary.simpleMessage( + "Минимальная цена для покупателя: 1200₽"), + "minute": MessageLookupByLibrary.simpleMessage("минута"), + "minutes": MessageLookupByLibrary.simpleMessage("минуты"), + "most_new": MessageLookupByLibrary.simpleMessage("Самые новые"), + "most_old": MessageLookupByLibrary.simpleMessage("Самые старые"), + "msg_network_err": + MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), + "msg_something_went_wrong": + MessageLookupByLibrary.simpleMessage("Что то пошло не так!"), + "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), + "my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"), + "my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"), + "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), + "name_empty": MessageLookupByLibrary.simpleMessage( + "Имя пользователя обязательно для заполнения"), + "name_short": MessageLookupByLibrary.simpleMessage( + "В имени пользователя должно быть минимум 6 символов"), + "no": MessageLookupByLibrary.simpleMessage("Нет"), + "no_account": + MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), + "not_liked_groceries": + MessageLookupByLibrary.simpleMessage("Не любимые продукты"), + "one_option": MessageLookupByLibrary.simpleMessage("1 опция"), + "option": MessageLookupByLibrary.simpleMessage("Опция"), + "option_content": + MessageLookupByLibrary.simpleMessage("Содержание опции"), + "option_content_hint_text": + MessageLookupByLibrary.simpleMessage("На связи 7 дней в неделю"), + "option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"), + "option_desc_hint_text": MessageLookupByLibrary.simpleMessage( + "Этот план питания для похудения на 7 дней сбалансированный и разнообразный"), + "option_duration": + MessageLookupByLibrary.simpleMessage("Продолжительность опции"), + "option_personal_diet_desc": MessageLookupByLibrary.simpleMessage( + "Опция подразумевает создание индивидуальной диеты"), + "option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"), + "order": MessageLookupByLibrary.simpleMessage("Порядок"), + "other_info": MessageLookupByLibrary.simpleMessage("Прочая информация"), + "password": MessageLookupByLibrary.simpleMessage("Пароль"), + "password_confirmation": + MessageLookupByLibrary.simpleMessage("Подтвердите пароль"), + "password_confirmation_mismatch": + MessageLookupByLibrary.simpleMessage("Пароли не совпадают"), + "password_empty": MessageLookupByLibrary.simpleMessage( + "Пароль обязателен для заполнения"), + "password_short": MessageLookupByLibrary.simpleMessage( + "В пароле должно быть минимум 6 символов"), + "pcs": MessageLookupByLibrary.simpleMessage("шт"), + "personal": MessageLookupByLibrary.simpleMessage("Персональные"), + "personal_diet_state": + MessageLookupByLibrary.simpleMessage("Персональная"), + "photo": MessageLookupByLibrary.simpleMessage("Фото"), + "physical_fitness": + MessageLookupByLibrary.simpleMessage("Физическая форма"), + "place_of_living": + MessageLookupByLibrary.simpleMessage("Место проживания"), + "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), + "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), + "plan_desc_example": MessageLookupByLibrary.simpleMessage( + "Большое подробное, привлекательное описание плана"), + "plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"), + "plan_label_desc": + MessageLookupByLibrary.simpleMessage("План для поддержания формы"), + "plan_type": MessageLookupByLibrary.simpleMessage("Тип плана"), + "plans_complete": + MessageLookupByLibrary.simpleMessage("Пройденные планы"), + "popular": MessageLookupByLibrary.simpleMessage("Популярные"), + "preferred_language": + MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"), + "pressure": MessageLookupByLibrary.simpleMessage("Давление"), + "pressure_empty": MessageLookupByLibrary.simpleMessage( + "Все показатели давления обязательны к заполнению"), + "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( + "Все показатели давления должны быть числами"), + "preview": MessageLookupByLibrary.simpleMessage("Превью"), + "price": MessageLookupByLibrary.simpleMessage("Цена"), + "price_explanation": MessageLookupByLibrary.simpleMessage( + "Разница в ценах, это комиссия Итвио"), + "profile": MessageLookupByLibrary.simpleMessage("Профиль"), + "proteins": MessageLookupByLibrary.simpleMessage("Белки"), + "publish": MessageLookupByLibrary.simpleMessage("Опубликовать"), + "published": MessageLookupByLibrary.simpleMessage("Опубликованные"), + "recipe_author": MessageLookupByLibrary.simpleMessage("Автор рецепта"), + "removeFromActive": + MessageLookupByLibrary.simpleMessage("Снять с публикации"), + "save": MessageLookupByLibrary.simpleMessage("Сохранить"), + "services": MessageLookupByLibrary.simpleMessage("Оказание услуг"), + "settings": MessageLookupByLibrary.simpleMessage("Настройки"), + "sex": MessageLookupByLibrary.simpleMessage("Пол"), + "share": MessageLookupByLibrary.simpleMessage("Поделиться"), + "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), + "slim": MessageLookupByLibrary.simpleMessage("Похудение"), + "slim_type": MessageLookupByLibrary.simpleMessage("Похудение"), + "slim_type_desc": MessageLookupByLibrary.simpleMessage( + "План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;"), + "sport_hobbies": + MessageLookupByLibrary.simpleMessage("Спортивные увлечения"), + "stage": MessageLookupByLibrary.simpleMessage("этап"), + "stages": MessageLookupByLibrary.simpleMessage("этапов"), + "standard": MessageLookupByLibrary.simpleMessage("Стандарт"), + "startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"), + "step": MessageLookupByLibrary.simpleMessage("Шаг"), + "step_by_step_photo_recipe": + MessageLookupByLibrary.simpleMessage("Пошаговый фоторецепт"), + "takes_pills": + MessageLookupByLibrary.simpleMessage("Принимает лекарства"), + "tell_your_schedule": MessageLookupByLibrary.simpleMessage( + "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"), + "there_is_a_video": MessageLookupByLibrary.simpleMessage("Есть видео"), + "three_options": MessageLookupByLibrary.simpleMessage("3 опции"), + "time": MessageLookupByLibrary.simpleMessage("Время прохождения"), + "toOffer": MessageLookupByLibrary.simpleMessage("Предложить"), + "to_draft": MessageLookupByLibrary.simpleMessage("В черновик"), + "unfavorite_foods": + MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"), + "up_pressure": MessageLookupByLibrary.simpleMessage( + "Верхние показатели давления:"), + "upper": MessageLookupByLibrary.simpleMessage("Верхнее"), + "user_authorization": + MessageLookupByLibrary.simpleMessage("Аутентификация"), + "user_data": + MessageLookupByLibrary.simpleMessage("Данные пользователя"), + "user_registration": + MessageLookupByLibrary.simpleMessage("Регистрация пользователя"), + "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), + "vegan": MessageLookupByLibrary.simpleMessage("Веган."), + "vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"), + "vegan_type_desc": MessageLookupByLibrary.simpleMessage( + "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."), + "waist": MessageLookupByLibrary.simpleMessage("Талия"), + "waist_circumference": + MessageLookupByLibrary.simpleMessage("Обхват талии"), + "weight": MessageLookupByLibrary.simpleMessage("Вес"), + "weight_chart": MessageLookupByLibrary.simpleMessage("График веса"), + "weight_measurements": + MessageLookupByLibrary.simpleMessage("Замер веса"), + "wellness": + MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"), + "wellness_type": + MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"), + "wellness_type_desc": MessageLookupByLibrary.simpleMessage( + "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."), + "what_is_your_name": + MessageLookupByLibrary.simpleMessage("Как вас зовут?"), + "wrong_credentials": MessageLookupByLibrary.simpleMessage( + "Неверные данные пользователя"), + "you_will_get": MessageLookupByLibrary.simpleMessage("Вы получите"), + "your_blood_pressure": + MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"), + "your_favorite_sports": + MessageLookupByLibrary.simpleMessage("Ваши спортивные увлечения?"), + "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), + "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), + "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), + "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:") + }; } diff --git a/lib/generated/intl/messages_ru.dart b/lib/generated/intl/messages_ru.dart index 1d41b34b..c3bbcbf6 100644 --- a/lib/generated/intl/messages_ru.dart +++ b/lib/generated/intl/messages_ru.dart @@ -22,350 +22,299 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), - "Russia": MessageLookupByLibrary.simpleMessage("Россия"), - "about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"), - "about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"), - "active": MessageLookupByLibrary.simpleMessage("Активен"), - "active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"), - "add": MessageLookupByLibrary.simpleMessage("Добавить"), - "add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"), - "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), - "adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"), - "additional_information": MessageLookupByLibrary.simpleMessage( - "Дополнительная информация", - ), - "admin": MessageLookupByLibrary.simpleMessage("Админ"), - "all": MessageLookupByLibrary.simpleMessage("Все"), - "allergies_presence": MessageLookupByLibrary.simpleMessage( - "Наличие аллергии", - ), - "another_form_of_comment": MessageLookupByLibrary.simpleMessage( - "комментария", - ), - "another_form_of_day": MessageLookupByLibrary.simpleMessage("дня"), - "answer": MessageLookupByLibrary.simpleMessage("Ответить"), - "archived": MessageLookupByLibrary.simpleMessage("В архиве"), - "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), - "average_pressure": MessageLookupByLibrary.simpleMessage( - "Средние показатели давления:", - ), - "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), - "by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"), - "calories": MessageLookupByLibrary.simpleMessage("Калории"), - "carb_type": MessageLookupByLibrary.simpleMessage("Мучное"), - "carb_type_desc": MessageLookupByLibrary.simpleMessage( - "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", - ), - "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), - "chat": MessageLookupByLibrary.simpleMessage("Чат"), - "chest": MessageLookupByLibrary.simpleMessage("Грудь"), - "chest_circumference": MessageLookupByLibrary.simpleMessage("Обхват груди"), - "choose_diet_duration": MessageLookupByLibrary.simpleMessage( - "Выберите срок диеты", - ), - "choose_plan_duration": MessageLookupByLibrary.simpleMessage( - "Выберите срок плана", - ), - "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( - "Выберите дату старты диеты. Она отобразиться у Вас в дневнике", - ), - "circumference_empty": MessageLookupByLibrary.simpleMessage( - "Все замеры обязательны к заполнению", - ), - "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все замеры должны быть числами", - ), - "circumferences_chart": MessageLookupByLibrary.simpleMessage( - "График обхватов", - ), - "city": MessageLookupByLibrary.simpleMessage("Город"), - "city_of_living": MessageLookupByLibrary.simpleMessage("Город проживания"), - "comfort": MessageLookupByLibrary.simpleMessage("Комфорт"), - "comment": MessageLookupByLibrary.simpleMessage("комментарий"), - "comments": MessageLookupByLibrary.simpleMessage("Комментарии"), - "comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"), - "complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"), - "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), - "continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), - "copied": MessageLookupByLibrary.simpleMessage("cкопирован"), - "copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), - "country": MessageLookupByLibrary.simpleMessage("Страна"), - "create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"), - "customer_price": MessageLookupByLibrary.simpleMessage( - "Цена для покупателя", - ), - "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), - "day": MessageLookupByLibrary.simpleMessage("День"), - "days": MessageLookupByLibrary.simpleMessage("дней"), - "deleted": MessageLookupByLibrary.simpleMessage("Удалена"), - "diary": MessageLookupByLibrary.simpleMessage("Дневник"), - "diet": MessageLookupByLibrary.simpleMessage("Диета"), - "diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"), - "diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"), - "diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"), - "diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"), - "do_you_have_hypertension_tendency": MessageLookupByLibrary.simpleMessage( - "Есть ли у вас гипертония?", - ), - "do_you_have_hypotension_tendency": MessageLookupByLibrary.simpleMessage( - "Есть ли у вас гипотония?", - ), - "do_you_take_pills": MessageLookupByLibrary.simpleMessage( - "Принимаете ли вы лекарства или бады?", - ), - "down_pressure": MessageLookupByLibrary.simpleMessage( - "Нижние показатели давления:", - ), - "draft": MessageLookupByLibrary.simpleMessage("Черновик"), - "drafted": MessageLookupByLibrary.simpleMessage("Черновики"), - "eating_behavior": MessageLookupByLibrary.simpleMessage( - "Пищевое поведение", - ), - "eating_behavior_type": MessageLookupByLibrary.simpleMessage( - "Формирование пищевого поведения", - ), - "eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage( - "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;", - ), - "economy": MessageLookupByLibrary.simpleMessage("Эконом"), - "edit": MessageLookupByLibrary.simpleMessage("Редактировать"), - "email": MessageLookupByLibrary.simpleMessage("Электронная почта"), - "email_empty": MessageLookupByLibrary.simpleMessage( - "Необходимо ввести адрес электронной почты", - ), - "email_invalid": MessageLookupByLibrary.simpleMessage( - "Некорректный адрес электронной почты", - ), - "enter_additional_information": MessageLookupByLibrary.simpleMessage( - "Введите любую дополнительную информацию на своё усмотрение:", - ), - "enter_diet_description": MessageLookupByLibrary.simpleMessage( - "Введите описание диеты", - ), - "enter_diet_name": MessageLookupByLibrary.simpleMessage( - "Выведите название диеты", - ), - "enter_information": MessageLookupByLibrary.simpleMessage( - "Введите информацию...", - ), - "exit": MessageLookupByLibrary.simpleMessage("Выйти"), - "expand": MessageLookupByLibrary.simpleMessage("Показать полностью"), - "fats": MessageLookupByLibrary.simpleMessage("Жиры"), - "favorite_foods": MessageLookupByLibrary.simpleMessage("Любимые продукты"), - "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Ваши любимые/нелюбимые продукты питания?", - ), - "female": MessageLookupByLibrary.simpleMessage("Женский"), - "fill_in_information": MessageLookupByLibrary.simpleMessage( - "Заполните информацию:", - ), - "finish": MessageLookupByLibrary.simpleMessage("Завершить"), - "first_name": MessageLookupByLibrary.simpleMessage("Имя"), - "first_name_empty": MessageLookupByLibrary.simpleMessage( - "Имя обязательно для заполнения", - ), - "first_name_short": MessageLookupByLibrary.simpleMessage( - "В имени должен быть минимум 1 символ", - ), - "fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"), - "fit_type_desc": MessageLookupByLibrary.simpleMessage( - "План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;", - ), - "fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"), - "from": MessageLookupByLibrary.simpleMessage("от"), - "from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"), - "from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"), - "gallery": MessageLookupByLibrary.simpleMessage("Галерея"), - "gr": MessageLookupByLibrary.simpleMessage("гр"), - "grams": MessageLookupByLibrary.simpleMessage("гр"), - "height_measurements": MessageLookupByLibrary.simpleMessage("Замер роста"), - "hide": MessageLookupByLibrary.simpleMessage("Свернуть"), - "hip_circumference": MessageLookupByLibrary.simpleMessage("Обхват бедер"), - "hips": MessageLookupByLibrary.simpleMessage("Бедра"), - "in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), - "inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"), - "initial_measurements": MessageLookupByLibrary.simpleMessage( - "Первичные замеры", - ), - "label": MessageLookupByLibrary.simpleMessage("Название"), - "language": MessageLookupByLibrary.simpleMessage("Язык"), - "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), - "last_name_empty": MessageLookupByLibrary.simpleMessage( - "Фамилия обязательна для заполнения", - ), - "last_name_short": MessageLookupByLibrary.simpleMessage( - "В фамилии должен быть минимум 6 символов", - ), - "lbl_english": MessageLookupByLibrary.simpleMessage("English"), - "light_type": MessageLookupByLibrary.simpleMessage("Легкая"), - "light_type_desc": MessageLookupByLibrary.simpleMessage( - "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.", - ), - "link_was_copied": MessageLookupByLibrary.simpleMessage( - "Ссылка успешно скопирована", - ), - "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), - "male": MessageLookupByLibrary.simpleMessage("Мужской"), - "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), - "meat": MessageLookupByLibrary.simpleMessage("Мясо"), - "meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), - "meat_type_desc": MessageLookupByLibrary.simpleMessage( - "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.", - ), - "milliliter": MessageLookupByLibrary.simpleMessage("мл"), - "minimal_customer_price": MessageLookupByLibrary.simpleMessage( - "Минимальная цена для покупателя: 1200₽", - ), - "most_new": MessageLookupByLibrary.simpleMessage("Самые новые"), - "most_old": MessageLookupByLibrary.simpleMessage("Самые старые"), - "msg_network_err": MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), - "msg_something_went_wrong": MessageLookupByLibrary.simpleMessage( - "Что то пошло не так!", - ), - "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), - "my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"), - "my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"), - "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), - "name_empty": MessageLookupByLibrary.simpleMessage( - "Имя пользователя обязательно для заполнения", - ), - "name_short": MessageLookupByLibrary.simpleMessage( - "В имени пользователя должно быть минимум 6 символов", - ), - "no": MessageLookupByLibrary.simpleMessage("Нет"), - "no_account": MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), - "one_option": MessageLookupByLibrary.simpleMessage("1 опция"), - "option": MessageLookupByLibrary.simpleMessage("Опция"), - "option_content": MessageLookupByLibrary.simpleMessage("Содержание опции"), - "option_content_hint_text": MessageLookupByLibrary.simpleMessage( - "На связи 7 дней в неделю", - ), - "option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"), - "option_desc_hint_text": MessageLookupByLibrary.simpleMessage( - "Этот план питания для похудения на 7 дней сбалансированный и разнообразный", - ), - "option_duration": MessageLookupByLibrary.simpleMessage( - "Продолжительность опции", - ), - "option_personal_diet_desc": MessageLookupByLibrary.simpleMessage( - "Опция подразумевает создание индивидуальной диеты", - ), - "option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"), - "order": MessageLookupByLibrary.simpleMessage("Порядок"), - "password": MessageLookupByLibrary.simpleMessage("Пароль"), - "password_confirmation": MessageLookupByLibrary.simpleMessage( - "Подтвердите пароль", - ), - "password_confirmation_mismatch": MessageLookupByLibrary.simpleMessage( - "Пароли не совпадают", - ), - "password_empty": MessageLookupByLibrary.simpleMessage( - "Пароль обязателен для заполнения", - ), - "password_short": MessageLookupByLibrary.simpleMessage( - "В пароле должно быть минимум 6 символов", - ), - "pcs": MessageLookupByLibrary.simpleMessage("шт"), - "personal": MessageLookupByLibrary.simpleMessage("Персональные"), - "personal_diet_state": MessageLookupByLibrary.simpleMessage("Персональная"), - "photo": MessageLookupByLibrary.simpleMessage("Фото"), - "place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"), - "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), - "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), - "plan_desc_example": MessageLookupByLibrary.simpleMessage( - "Большое подробное, привлекательное описание плана", - ), - "plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"), - "plan_label_desc": MessageLookupByLibrary.simpleMessage( - "План для поддержания формы", - ), - "plans_complete": MessageLookupByLibrary.simpleMessage("Пройденные планы"), - "popular": MessageLookupByLibrary.simpleMessage("Популярные"), - "preferred_language": MessageLookupByLibrary.simpleMessage( - "Предпочитаемый язык общения", - ), - "pressure_empty": MessageLookupByLibrary.simpleMessage( - "Все показатели давления обязательны к заполнению", - ), - "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все показатели давления должны быть числами", - ), - "preview": MessageLookupByLibrary.simpleMessage("Превью"), - "price": MessageLookupByLibrary.simpleMessage("Цена"), - "price_explanation": MessageLookupByLibrary.simpleMessage( - "Разница в ценах, это комиссия Итвио", - ), - "profile": MessageLookupByLibrary.simpleMessage("Профиль"), - "proteins": MessageLookupByLibrary.simpleMessage("Белки"), - "publish": MessageLookupByLibrary.simpleMessage("Опубликовать"), - "published": MessageLookupByLibrary.simpleMessage("Опубликованные"), - "removeFromActive": MessageLookupByLibrary.simpleMessage( - "Снять с публикации", - ), - "save": MessageLookupByLibrary.simpleMessage("Сохранить"), - "services": MessageLookupByLibrary.simpleMessage("Оказание услуг"), - "settings": MessageLookupByLibrary.simpleMessage("Настройки"), - "share": MessageLookupByLibrary.simpleMessage("Поделиться"), - "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), - "slim": MessageLookupByLibrary.simpleMessage("Похудение"), - "slim_type": MessageLookupByLibrary.simpleMessage("Похудение"), - "slim_type_desc": MessageLookupByLibrary.simpleMessage( - "План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;", - ), - "standard": MessageLookupByLibrary.simpleMessage("Стандарт"), - "startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"), - "tell_your_schedule": MessageLookupByLibrary.simpleMessage( - "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:", - ), - "three_options": MessageLookupByLibrary.simpleMessage("3 опции"), - "toOffer": MessageLookupByLibrary.simpleMessage("Предложить"), - "to_draft": MessageLookupByLibrary.simpleMessage("В черновик"), - "unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Нелюбимые продукты", - ), - "up_pressure": MessageLookupByLibrary.simpleMessage( - "Верхние показатели давления:", - ), - "user_authorization": MessageLookupByLibrary.simpleMessage( - "Аутентификация", - ), - "user_data": MessageLookupByLibrary.simpleMessage("Данные пользователя"), - "user_registration": MessageLookupByLibrary.simpleMessage( - "Регистрация пользователя", - ), - "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), - "vegan": MessageLookupByLibrary.simpleMessage("Веган."), - "vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"), - "vegan_type_desc": MessageLookupByLibrary.simpleMessage( - "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.", - ), - "waist": MessageLookupByLibrary.simpleMessage("Талия"), - "waist_circumference": MessageLookupByLibrary.simpleMessage("Обхват талии"), - "weight": MessageLookupByLibrary.simpleMessage("Вес"), - "weight_chart": MessageLookupByLibrary.simpleMessage("График веса"), - "weight_measurements": MessageLookupByLibrary.simpleMessage("Замер веса"), - "wellness": MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"), - "wellness_type": MessageLookupByLibrary.simpleMessage( - "Улучшение самочувствия", - ), - "wellness_type_desc": MessageLookupByLibrary.simpleMessage( - "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", - ), - "what_is_your_name": MessageLookupByLibrary.simpleMessage("Как вас зовут?"), - "wrong_credentials": MessageLookupByLibrary.simpleMessage( - "Неверные данные пользователя", - ), - "you_will_get": MessageLookupByLibrary.simpleMessage("Вы получите"), - "your_blood_pressure": MessageLookupByLibrary.simpleMessage( - "Ваше артериальное давление?", - ), - "your_favorite_sports": MessageLookupByLibrary.simpleMessage( - "Ваши спортивные увлечения?", - ), - "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), - "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), - "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), - "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:"), - }; + "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), + "Russia": MessageLookupByLibrary.simpleMessage("Россия"), + "about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"), + "about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"), + "active": MessageLookupByLibrary.simpleMessage("Активен"), + "active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"), + "add": MessageLookupByLibrary.simpleMessage("Добавить"), + "add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"), + "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), + "adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"), + "additional_information": + MessageLookupByLibrary.simpleMessage("Дополнительная информация"), + "admin": MessageLookupByLibrary.simpleMessage("Админ"), + "all": MessageLookupByLibrary.simpleMessage("Все"), + "allergies_presence": + MessageLookupByLibrary.simpleMessage("Наличие аллергии"), + "another_form_of_comment": + MessageLookupByLibrary.simpleMessage("комментария"), + "another_form_of_day": MessageLookupByLibrary.simpleMessage("дня"), + "answer": MessageLookupByLibrary.simpleMessage("Ответить"), + "archived": MessageLookupByLibrary.simpleMessage("В архиве"), + "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), + "average_pressure": MessageLookupByLibrary.simpleMessage( + "Средние показатели давления:"), + "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), + "by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"), + "calories": MessageLookupByLibrary.simpleMessage("Калории"), + "carb_type": MessageLookupByLibrary.simpleMessage("Мучное"), + "carb_type_desc": MessageLookupByLibrary.simpleMessage( + "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."), + "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), + "chat": MessageLookupByLibrary.simpleMessage("Чат"), + "chest": MessageLookupByLibrary.simpleMessage("Грудь"), + "chest_circumference": + MessageLookupByLibrary.simpleMessage("Обхват груди"), + "choose_diet_duration": + MessageLookupByLibrary.simpleMessage("Выберите срок диеты"), + "choose_plan_duration": + MessageLookupByLibrary.simpleMessage("Выберите срок плана"), + "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( + "Выберите дату старты диеты. Она отобразиться у Вас в дневнике"), + "circumference_empty": MessageLookupByLibrary.simpleMessage( + "Все замеры обязательны к заполнению"), + "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( + "Все замеры должны быть числами"), + "circumferences_chart": + MessageLookupByLibrary.simpleMessage("График обхватов"), + "city": MessageLookupByLibrary.simpleMessage("Город"), + "city_of_living": + MessageLookupByLibrary.simpleMessage("Город проживания"), + "comfort": MessageLookupByLibrary.simpleMessage("Комфорт"), + "comment": MessageLookupByLibrary.simpleMessage("комментарий"), + "comments": MessageLookupByLibrary.simpleMessage("Комментарии"), + "comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"), + "complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"), + "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), + "continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), + "copied": MessageLookupByLibrary.simpleMessage("cкопирован"), + "copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), + "country": MessageLookupByLibrary.simpleMessage("Страна"), + "create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"), + "customer_price": + MessageLookupByLibrary.simpleMessage("Цена для покупателя"), + "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), + "day": MessageLookupByLibrary.simpleMessage("День"), + "days": MessageLookupByLibrary.simpleMessage("дней"), + "deleted": MessageLookupByLibrary.simpleMessage("Удалена"), + "diary": MessageLookupByLibrary.simpleMessage("Дневник"), + "diet": MessageLookupByLibrary.simpleMessage("Диета"), + "diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"), + "diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"), + "diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"), + "diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"), + "do_you_have_hypertension_tendency": + MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"), + "do_you_have_hypotension_tendency": + MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"), + "do_you_take_pills": MessageLookupByLibrary.simpleMessage( + "Принимаете ли вы лекарства или бады?"), + "down_pressure": + MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"), + "draft": MessageLookupByLibrary.simpleMessage("Черновик"), + "drafted": MessageLookupByLibrary.simpleMessage("Черновики"), + "eating_behavior": + MessageLookupByLibrary.simpleMessage("Пищевое поведение"), + "eating_behavior_type": MessageLookupByLibrary.simpleMessage( + "Формирование пищевого поведения"), + "eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage( + "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"), + "economy": MessageLookupByLibrary.simpleMessage("Эконом"), + "edit": MessageLookupByLibrary.simpleMessage("Редактировать"), + "email": MessageLookupByLibrary.simpleMessage("Электронная почта"), + "email_empty": MessageLookupByLibrary.simpleMessage( + "Необходимо ввести адрес электронной почты"), + "email_invalid": MessageLookupByLibrary.simpleMessage( + "Некорректный адрес электронной почты"), + "enter_additional_information": MessageLookupByLibrary.simpleMessage( + "Введите любую дополнительную информацию на своё усмотрение:"), + "enter_diet_description": + MessageLookupByLibrary.simpleMessage("Введите описание диеты"), + "enter_diet_name": + MessageLookupByLibrary.simpleMessage("Выведите название диеты"), + "enter_information": + MessageLookupByLibrary.simpleMessage("Введите информацию..."), + "exit": MessageLookupByLibrary.simpleMessage("Выйти"), + "expand": MessageLookupByLibrary.simpleMessage("Показать полностью"), + "fats": MessageLookupByLibrary.simpleMessage("Жиры"), + "favorite_foods": + MessageLookupByLibrary.simpleMessage("Любимые продукты"), + "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( + "Ваши любимые/нелюбимые продукты питания?"), + "female": MessageLookupByLibrary.simpleMessage("Женский"), + "fill_in_information": + MessageLookupByLibrary.simpleMessage("Заполните информацию:"), + "finish": MessageLookupByLibrary.simpleMessage("Завершить"), + "first_name": MessageLookupByLibrary.simpleMessage("Имя"), + "first_name_empty": MessageLookupByLibrary.simpleMessage( + "Имя обязательно для заполнения"), + "first_name_short": MessageLookupByLibrary.simpleMessage( + "В имени должен быть минимум 1 символ"), + "fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"), + "fit_type_desc": MessageLookupByLibrary.simpleMessage( + "План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;"), + "fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"), + "from": MessageLookupByLibrary.simpleMessage("от"), + "from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"), + "from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"), + "gallery": MessageLookupByLibrary.simpleMessage("Галерея"), + "gr": MessageLookupByLibrary.simpleMessage("гр"), + "grams": MessageLookupByLibrary.simpleMessage("гр"), + "height_measurements": + MessageLookupByLibrary.simpleMessage("Замер роста"), + "hide": MessageLookupByLibrary.simpleMessage("Свернуть"), + "hip_circumference": + MessageLookupByLibrary.simpleMessage("Обхват бедер"), + "hips": MessageLookupByLibrary.simpleMessage("Бедра"), + "in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), + "inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"), + "initial_measurements": + MessageLookupByLibrary.simpleMessage("Первичные замеры"), + "label": MessageLookupByLibrary.simpleMessage("Название"), + "language": MessageLookupByLibrary.simpleMessage("Язык"), + "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), + "last_name_empty": MessageLookupByLibrary.simpleMessage( + "Фамилия обязательна для заполнения"), + "last_name_short": MessageLookupByLibrary.simpleMessage( + "В фамилии должен быть минимум 6 символов"), + "lbl_english": MessageLookupByLibrary.simpleMessage("English"), + "light_type": MessageLookupByLibrary.simpleMessage("Легкая"), + "light_type_desc": MessageLookupByLibrary.simpleMessage( + "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."), + "link_was_copied": + MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"), + "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), + "male": MessageLookupByLibrary.simpleMessage("Мужской"), + "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), + "meat": MessageLookupByLibrary.simpleMessage("Мясо"), + "meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), + "meat_type_desc": MessageLookupByLibrary.simpleMessage( + "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."), + "milliliter": MessageLookupByLibrary.simpleMessage("мл"), + "minimal_customer_price": MessageLookupByLibrary.simpleMessage( + "Минимальная цена для покупателя: 1200₽"), + "most_new": MessageLookupByLibrary.simpleMessage("Самые новые"), + "most_old": MessageLookupByLibrary.simpleMessage("Самые старые"), + "msg_network_err": + MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), + "msg_something_went_wrong": + MessageLookupByLibrary.simpleMessage("Что то пошло не так!"), + "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), + "my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"), + "my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"), + "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), + "name_empty": MessageLookupByLibrary.simpleMessage( + "Имя пользователя обязательно для заполнения"), + "name_short": MessageLookupByLibrary.simpleMessage( + "В имени пользователя должно быть минимум 6 символов"), + "no": MessageLookupByLibrary.simpleMessage("Нет"), + "no_account": + MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), + "one_option": MessageLookupByLibrary.simpleMessage("1 опция"), + "option": MessageLookupByLibrary.simpleMessage("Опция"), + "option_content": + MessageLookupByLibrary.simpleMessage("Содержание опции"), + "option_content_hint_text": + MessageLookupByLibrary.simpleMessage("На связи 7 дней в неделю"), + "option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"), + "option_desc_hint_text": MessageLookupByLibrary.simpleMessage( + "Этот план питания для похудения на 7 дней сбалансированный и разнообразный"), + "option_duration": + MessageLookupByLibrary.simpleMessage("Продолжительность опции"), + "option_personal_diet_desc": MessageLookupByLibrary.simpleMessage( + "Опция подразумевает создание индивидуальной диеты"), + "option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"), + "order": MessageLookupByLibrary.simpleMessage("Порядок"), + "password": MessageLookupByLibrary.simpleMessage("Пароль"), + "password_confirmation": + MessageLookupByLibrary.simpleMessage("Подтвердите пароль"), + "password_confirmation_mismatch": + MessageLookupByLibrary.simpleMessage("Пароли не совпадают"), + "password_empty": MessageLookupByLibrary.simpleMessage( + "Пароль обязателен для заполнения"), + "password_short": MessageLookupByLibrary.simpleMessage( + "В пароле должно быть минимум 6 символов"), + "pcs": MessageLookupByLibrary.simpleMessage("шт"), + "personal": MessageLookupByLibrary.simpleMessage("Персональные"), + "personal_diet_state": + MessageLookupByLibrary.simpleMessage("Персональная"), + "photo": MessageLookupByLibrary.simpleMessage("Фото"), + "place_of_living": + MessageLookupByLibrary.simpleMessage("Место проживания"), + "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), + "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), + "plan_desc_example": MessageLookupByLibrary.simpleMessage( + "Большое подробное, привлекательное описание плана"), + "plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"), + "plan_label_desc": + MessageLookupByLibrary.simpleMessage("План для поддержания формы"), + "plans_complete": + MessageLookupByLibrary.simpleMessage("Пройденные планы"), + "popular": MessageLookupByLibrary.simpleMessage("Популярные"), + "preferred_language": + MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"), + "pressure_empty": MessageLookupByLibrary.simpleMessage( + "Все показатели давления обязательны к заполнению"), + "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( + "Все показатели давления должны быть числами"), + "preview": MessageLookupByLibrary.simpleMessage("Превью"), + "price": MessageLookupByLibrary.simpleMessage("Цена"), + "price_explanation": MessageLookupByLibrary.simpleMessage( + "Разница в ценах, это комиссия Итвио"), + "profile": MessageLookupByLibrary.simpleMessage("Профиль"), + "proteins": MessageLookupByLibrary.simpleMessage("Белки"), + "publish": MessageLookupByLibrary.simpleMessage("Опубликовать"), + "published": MessageLookupByLibrary.simpleMessage("Опубликованные"), + "removeFromActive": + MessageLookupByLibrary.simpleMessage("Снять с публикации"), + "save": MessageLookupByLibrary.simpleMessage("Сохранить"), + "services": MessageLookupByLibrary.simpleMessage("Оказание услуг"), + "settings": MessageLookupByLibrary.simpleMessage("Настройки"), + "share": MessageLookupByLibrary.simpleMessage("Поделиться"), + "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), + "slim": MessageLookupByLibrary.simpleMessage("Похудение"), + "slim_type": MessageLookupByLibrary.simpleMessage("Похудение"), + "slim_type_desc": MessageLookupByLibrary.simpleMessage( + "План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;"), + "standard": MessageLookupByLibrary.simpleMessage("Стандарт"), + "startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"), + "tell_your_schedule": MessageLookupByLibrary.simpleMessage( + "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"), + "three_options": MessageLookupByLibrary.simpleMessage("3 опции"), + "toOffer": MessageLookupByLibrary.simpleMessage("Предложить"), + "to_draft": MessageLookupByLibrary.simpleMessage("В черновик"), + "unfavorite_foods": + MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"), + "up_pressure": MessageLookupByLibrary.simpleMessage( + "Верхние показатели давления:"), + "user_authorization": + MessageLookupByLibrary.simpleMessage("Аутентификация"), + "user_data": + MessageLookupByLibrary.simpleMessage("Данные пользователя"), + "user_registration": + MessageLookupByLibrary.simpleMessage("Регистрация пользователя"), + "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), + "vegan": MessageLookupByLibrary.simpleMessage("Веган."), + "vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"), + "vegan_type_desc": MessageLookupByLibrary.simpleMessage( + "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."), + "waist": MessageLookupByLibrary.simpleMessage("Талия"), + "waist_circumference": + MessageLookupByLibrary.simpleMessage("Обхват талии"), + "weight": MessageLookupByLibrary.simpleMessage("Вес"), + "weight_chart": MessageLookupByLibrary.simpleMessage("График веса"), + "weight_measurements": + MessageLookupByLibrary.simpleMessage("Замер веса"), + "wellness": + MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"), + "wellness_type": + MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"), + "wellness_type_desc": MessageLookupByLibrary.simpleMessage( + "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."), + "what_is_your_name": + MessageLookupByLibrary.simpleMessage("Как вас зовут?"), + "wrong_credentials": MessageLookupByLibrary.simpleMessage( + "Неверные данные пользователя"), + "you_will_get": MessageLookupByLibrary.simpleMessage("Вы получите"), + "your_blood_pressure": + MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"), + "your_favorite_sports": + MessageLookupByLibrary.simpleMessage("Ваши спортивные увлечения?"), + "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), + "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), + "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), + "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:") + }; } diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 68b55718..22e67fef 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -18,20 +18,17 @@ class S { static S? _current; static S get current { - assert( - _current != null, - 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.', - ); + assert(_current != null, + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); return _current!; } static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = - (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; @@ -44,10 +41,8 @@ class S { static S of(BuildContext context) { final instance = S.maybeOf(context); - assert( - instance != null, - 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?', - ); + assert(instance != null, + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); return instance!; } @@ -67,7 +62,12 @@ class S { /// `Электронная почта` String get email { - return Intl.message('Электронная почта', name: 'email', desc: '', args: []); + return Intl.message( + 'Электронная почта', + name: 'email', + desc: '', + args: [], + ); } /// `Имя пользователя` @@ -82,7 +82,12 @@ class S { /// `Пароль` String get password { - return Intl.message('Пароль', name: 'password', desc: '', args: []); + return Intl.message( + 'Пароль', + name: 'password', + desc: '', + args: [], + ); } /// `Подтвердите пароль` @@ -107,12 +112,22 @@ class S { /// `Имя` String get first_name { - return Intl.message('Имя', name: 'first_name', desc: '', args: []); + return Intl.message( + 'Имя', + name: 'first_name', + desc: '', + args: [], + ); } /// `Фамилия` String get last_name { - return Intl.message('Фамилия', name: 'last_name', desc: '', args: []); + return Intl.message( + 'Фамилия', + name: 'last_name', + desc: '', + args: [], + ); } /// `Как вас зовут?` @@ -137,22 +152,42 @@ class S { /// `Страна` String get country { - return Intl.message('Страна', name: 'country', desc: '', args: []); + return Intl.message( + 'Страна', + name: 'country', + desc: '', + args: [], + ); } /// `Россия` String get Russia { - return Intl.message('Россия', name: 'Russia', desc: '', args: []); + return Intl.message( + 'Россия', + name: 'Russia', + desc: '', + args: [], + ); } /// `Город` String get city { - return Intl.message('Город', name: 'city', desc: '', args: []); + return Intl.message( + 'Город', + name: 'city', + desc: '', + args: [], + ); } /// `Москва` String get Moscow { - return Intl.message('Москва', name: 'Moscow', desc: '', args: []); + return Intl.message( + 'Москва', + name: 'Moscow', + desc: '', + args: [], + ); } /// `Место проживания` @@ -207,12 +242,22 @@ class S { /// `Мужской` String get male { - return Intl.message('Мужской', name: 'male', desc: '', args: []); + return Intl.message( + 'Мужской', + name: 'male', + desc: '', + args: [], + ); } /// `Женский` String get female { - return Intl.message('Женский', name: 'female', desc: '', args: []); + return Intl.message( + 'Женский', + name: 'female', + desc: '', + args: [], + ); } /// `Замер веса` @@ -227,7 +272,12 @@ class S { /// `Ваш вес:` String get your_weight { - return Intl.message('Ваш вес:', name: 'your_weight', desc: '', args: []); + return Intl.message( + 'Ваш вес:', + name: 'your_weight', + desc: '', + args: [], + ); } /// `Замер роста` @@ -242,7 +292,12 @@ class S { /// `Ваш рост:` String get your_height { - return Intl.message('Ваш рост:', name: 'your_height', desc: '', args: []); + return Intl.message( + 'Ваш рост:', + name: 'your_height', + desc: '', + args: [], + ); } /// `Наличие аллергии` @@ -267,12 +322,22 @@ class S { /// `Ваш пол?` String get your_gender { - return Intl.message('Ваш пол?', name: 'your_gender', desc: '', args: []); + return Intl.message( + 'Ваш пол?', + name: 'your_gender', + desc: '', + args: [], + ); } /// `Нет` String get no { - return Intl.message('Нет', name: 'no', desc: '', args: []); + return Intl.message( + 'Нет', + name: 'no', + desc: '', + args: [], + ); } /// `Есть ли у вас гипертония?` @@ -417,12 +482,22 @@ class S { /// `English` String get lbl_english { - return Intl.message('English', name: 'lbl_english', desc: '', args: []); + return Intl.message( + 'English', + name: 'lbl_english', + desc: '', + args: [], + ); } /// `Завершить` String get finish { - return Intl.message('Завершить', name: 'finish', desc: '', args: []); + return Intl.message( + 'Завершить', + name: 'finish', + desc: '', + args: [], + ); } /// `Предпочитаемый язык общения` @@ -447,7 +522,12 @@ class S { /// `Пропустить` String get skip { - return Intl.message('Пропустить', name: 'skip', desc: '', args: []); + return Intl.message( + 'Пропустить', + name: 'skip', + desc: '', + args: [], + ); } /// `Заполните информацию:` @@ -652,7 +732,12 @@ class S { /// `Войти` String get authentificate { - return Intl.message('Войти', name: 'authentificate', desc: '', args: []); + return Intl.message( + 'Войти', + name: 'authentificate', + desc: '', + args: [], + ); } /// `Аутентификация` @@ -667,7 +752,12 @@ class S { /// `Загрузка...` String get loading { - return Intl.message('Загрузка...', name: 'loading', desc: '', args: []); + return Intl.message( + 'Загрузка...', + name: 'loading', + desc: '', + args: [], + ); } /// `График веса` @@ -712,52 +802,102 @@ class S { /// `Грудь` String get chest { - return Intl.message('Грудь', name: 'chest', desc: '', args: []); + return Intl.message( + 'Грудь', + name: 'chest', + desc: '', + args: [], + ); } /// `Талия` String get waist { - return Intl.message('Талия', name: 'waist', desc: '', args: []); + return Intl.message( + 'Талия', + name: 'waist', + desc: '', + args: [], + ); } /// `Бедра` String get hips { - return Intl.message('Бедра', name: 'hips', desc: '', args: []); + return Intl.message( + 'Бедра', + name: 'hips', + desc: '', + args: [], + ); } /// `Выйти` String get exit { - return Intl.message('Выйти', name: 'exit', desc: '', args: []); + return Intl.message( + 'Выйти', + name: 'exit', + desc: '', + args: [], + ); } /// `Активен` String get active { - return Intl.message('Активен', name: 'active', desc: '', args: []); + return Intl.message( + 'Активен', + name: 'active', + desc: '', + args: [], + ); } /// `В архиве` String get archived { - return Intl.message('В архиве', name: 'archived', desc: '', args: []); + return Intl.message( + 'В архиве', + name: 'archived', + desc: '', + args: [], + ); } /// `Черновик` String get draft { - return Intl.message('Черновик', name: 'draft', desc: '', args: []); + return Intl.message( + 'Черновик', + name: 'draft', + desc: '', + args: [], + ); } /// `Мой блог` String get my_blog { - return Intl.message('Мой блог', name: 'my_blog', desc: '', args: []); + return Intl.message( + 'Мой блог', + name: 'my_blog', + desc: '', + args: [], + ); } /// `Язык` String get language { - return Intl.message('Язык', name: 'language', desc: '', args: []); + return Intl.message( + 'Язык', + name: 'language', + desc: '', + args: [], + ); } /// `В процессе` String get in_progress { - return Intl.message('В процессе', name: 'in_progress', desc: '', args: []); + return Intl.message( + 'В процессе', + name: 'in_progress', + desc: '', + args: [], + ); } /// `Добавить замер` @@ -772,7 +912,12 @@ class S { /// `Вес` String get weight { - return Intl.message('Вес', name: 'weight', desc: '', args: []); + return Intl.message( + 'Вес', + name: 'weight', + desc: '', + args: [], + ); } /// `Пройденные планы` @@ -797,67 +942,132 @@ class S { /// `Настройки` String get settings { - return Intl.message('Настройки', name: 'settings', desc: '', args: []); + return Intl.message( + 'Настройки', + name: 'settings', + desc: '', + args: [], + ); } /// `Профиль` String get profile { - return Intl.message('Профиль', name: 'profile', desc: '', args: []); + return Intl.message( + 'Профиль', + name: 'profile', + desc: '', + args: [], + ); } /// `Чат` String get chat { - return Intl.message('Чат', name: 'chat', desc: '', args: []); + return Intl.message( + 'Чат', + name: 'chat', + desc: '', + args: [], + ); } /// `Админ` String get admin { - return Intl.message('Админ', name: 'admin', desc: '', args: []); + return Intl.message( + 'Админ', + name: 'admin', + desc: '', + args: [], + ); } /// `Дневник` String get diary { - return Intl.message('Дневник', name: 'diary', desc: '', args: []); + return Intl.message( + 'Дневник', + name: 'diary', + desc: '', + args: [], + ); } /// `Сохранить` String get save { - return Intl.message('Сохранить', name: 'save', desc: '', args: []); + return Intl.message( + 'Сохранить', + name: 'save', + desc: '', + args: [], + ); } /// `шт` String get pcs { - return Intl.message('шт', name: 'pcs', desc: '', args: []); + return Intl.message( + 'шт', + name: 'pcs', + desc: '', + args: [], + ); } /// `гр` String get gr { - return Intl.message('гр', name: 'gr', desc: '', args: []); + return Intl.message( + 'гр', + name: 'gr', + desc: '', + args: [], + ); } /// `мл` String get milliliter { - return Intl.message('мл', name: 'milliliter', desc: '', args: []); + return Intl.message( + 'мл', + name: 'milliliter', + desc: '', + args: [], + ); } /// `гр` String get grams { - return Intl.message('гр', name: 'grams', desc: '', args: []); + return Intl.message( + 'гр', + name: 'grams', + desc: '', + args: [], + ); } /// `Начать диету` String get startDiet { - return Intl.message('Начать диету', name: 'startDiet', desc: '', args: []); + return Intl.message( + 'Начать диету', + name: 'startDiet', + desc: '', + args: [], + ); } /// `Комментарии` String get comments { - return Intl.message('Комментарии', name: 'comments', desc: '', args: []); + return Intl.message( + 'Комментарии', + name: 'comments', + desc: '', + args: [], + ); } /// `комментарий` String get comment { - return Intl.message('комментарий', name: 'comment', desc: '', args: []); + return Intl.message( + 'комментарий', + name: 'comment', + desc: '', + args: [], + ); } /// `комментария` @@ -882,17 +1092,32 @@ class S { /// `дней` String get days { - return Intl.message('дней', name: 'days', desc: '', args: []); + return Intl.message( + 'дней', + name: 'days', + desc: '', + args: [], + ); } /// `День` String get day { - return Intl.message('День', name: 'day', desc: '', args: []); + return Intl.message( + 'День', + name: 'day', + desc: '', + args: [], + ); } /// `дня` String get another_form_of_day { - return Intl.message('дня', name: 'another_form_of_day', desc: '', args: []); + return Intl.message( + 'дня', + name: 'another_form_of_day', + desc: '', + args: [], + ); } /// `комментариев` @@ -907,17 +1132,32 @@ class S { /// `Приём пищи` String get meal { - return Intl.message('Приём пищи', name: 'meal', desc: '', args: []); + return Intl.message( + 'Приём пищи', + name: 'meal', + desc: '', + args: [], + ); } /// `Автор диеты` String get diet_author { - return Intl.message('Автор диеты', name: 'diet_author', desc: '', args: []); + return Intl.message( + 'Автор диеты', + name: 'diet_author', + desc: '', + args: [], + ); } /// `Свернуть` String get hide { - return Intl.message('Свернуть', name: 'hide', desc: '', args: []); + return Intl.message( + 'Свернуть', + name: 'hide', + desc: '', + args: [], + ); } /// `Показать полностью` @@ -942,12 +1182,22 @@ class S { /// `Поделиться` String get share { - return Intl.message('Поделиться', name: 'share', desc: '', args: []); + return Intl.message( + 'Поделиться', + name: 'share', + desc: '', + args: [], + ); } /// `Пожаловаться` String get complain { - return Intl.message('Пожаловаться', name: 'complain', desc: '', args: []); + return Intl.message( + 'Пожаловаться', + name: 'complain', + desc: '', + args: [], + ); } /// `Вегетарианская` @@ -962,17 +1212,32 @@ class S { /// `Легкая` String get light_type { - return Intl.message('Легкая', name: 'light_type', desc: '', args: []); + return Intl.message( + 'Легкая', + name: 'light_type', + desc: '', + args: [], + ); } /// `С мясом` String get meat_type { - return Intl.message('С мясом', name: 'meat_type', desc: '', args: []); + return Intl.message( + 'С мясом', + name: 'meat_type', + desc: '', + args: [], + ); } /// `Мучное` String get carb_type { - return Intl.message('Мучное', name: 'carb_type', desc: '', args: []); + return Intl.message( + 'Мучное', + name: 'carb_type', + desc: '', + args: [], + ); } /// `Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.` @@ -1017,32 +1282,62 @@ class S { /// `План диеты` String get diet_plan { - return Intl.message('План диеты', name: 'diet_plan', desc: '', args: []); + return Intl.message( + 'План диеты', + name: 'diet_plan', + desc: '', + args: [], + ); } /// `Калории` String get calories { - return Intl.message('Калории', name: 'calories', desc: '', args: []); + return Intl.message( + 'Калории', + name: 'calories', + desc: '', + args: [], + ); } /// `Белки` String get proteins { - return Intl.message('Белки', name: 'proteins', desc: '', args: []); + return Intl.message( + 'Белки', + name: 'proteins', + desc: '', + args: [], + ); } /// `Жиры` String get fats { - return Intl.message('Жиры', name: 'fats', desc: '', args: []); + return Intl.message( + 'Жиры', + name: 'fats', + desc: '', + args: [], + ); } /// `Углеводы` String get carbohydrates { - return Intl.message('Углеводы', name: 'carbohydrates', desc: '', args: []); + return Intl.message( + 'Углеводы', + name: 'carbohydrates', + desc: '', + args: [], + ); } /// `Ответить` String get answer { - return Intl.message('Ответить', name: 'answer', desc: '', args: []); + return Intl.message( + 'Ответить', + name: 'answer', + desc: '', + args: [], + ); } /// `Ссылка успешно скопирована` @@ -1067,47 +1362,92 @@ class S { /// `Продолжить` String get continue_ { - return Intl.message('Продолжить', name: 'continue_', desc: '', args: []); + return Intl.message( + 'Продолжить', + name: 'continue_', + desc: '', + args: [], + ); } /// `Порядок` String get order { - return Intl.message('Порядок', name: 'order', desc: '', args: []); + return Intl.message( + 'Порядок', + name: 'order', + desc: '', + args: [], + ); } /// `Время прохождения` String get time { - return Intl.message('Время прохождения', name: 'time', desc: '', args: []); + return Intl.message( + 'Время прохождения', + name: 'time', + desc: '', + args: [], + ); } /// `Все` String get all { - return Intl.message('Все', name: 'all', desc: '', args: []); + return Intl.message( + 'Все', + name: 'all', + desc: '', + args: [], + ); } /// `Тип диеты` String get diet_type { - return Intl.message('Тип диеты', name: 'diet_type', desc: '', args: []); + return Intl.message( + 'Тип диеты', + name: 'diet_type', + desc: '', + args: [], + ); } /// `Тип плана` String get plan_type { - return Intl.message('Тип плана', name: 'plan_type', desc: '', args: []); + return Intl.message( + 'Тип плана', + name: 'plan_type', + desc: '', + args: [], + ); } /// `Популярные` String get popular { - return Intl.message('Популярные', name: 'popular', desc: '', args: []); + return Intl.message( + 'Популярные', + name: 'popular', + desc: '', + args: [], + ); } /// `Самые новые` String get most_new { - return Intl.message('Самые новые', name: 'most_new', desc: '', args: []); + return Intl.message( + 'Самые новые', + name: 'most_new', + desc: '', + args: [], + ); } /// `Самые старые` String get most_old { - return Intl.message('Самые старые', name: 'most_old', desc: '', args: []); + return Intl.message( + 'Самые старые', + name: 'most_old', + desc: '', + args: [], + ); } /// `Название: А-Я` @@ -1132,7 +1472,12 @@ class S { /// `По умолчянию` String get by_default { - return Intl.message('По умолчянию', name: 'by_default', desc: '', args: []); + return Intl.message( + 'По умолчянию', + name: 'by_default', + desc: '', + args: [], + ); } /// `Создание диеты` @@ -1177,7 +1522,12 @@ class S { /// `Статус диеты` String get diet_state { - return Intl.message('Статус диеты', name: 'diet_state', desc: '', args: []); + return Intl.message( + 'Статус диеты', + name: 'diet_state', + desc: '', + args: [], + ); } /// `Активная` @@ -1212,22 +1562,42 @@ class S { /// `Мясо` String get meat { - return Intl.message('Мясо', name: 'meat', desc: '', args: []); + return Intl.message( + 'Мясо', + name: 'meat', + desc: '', + args: [], + ); } /// `Веган.` String get vegan { - return Intl.message('Веган.', name: 'vegan', desc: '', args: []); + return Intl.message( + 'Веган.', + name: 'vegan', + desc: '', + args: [], + ); } /// `Фото` String get photo { - return Intl.message('Фото', name: 'photo', desc: '', args: []); + return Intl.message( + 'Фото', + name: 'photo', + desc: '', + args: [], + ); } /// `Галерея` String get gallery { - return Intl.message('Галерея', name: 'gallery', desc: '', args: []); + return Intl.message( + 'Галерея', + name: 'gallery', + desc: '', + args: [], + ); } /// `Добавьте обложку` @@ -1242,27 +1612,52 @@ class S { /// `Добавить` String get add { - return Intl.message('Добавить', name: 'add', desc: '', args: []); + return Intl.message( + 'Добавить', + name: 'add', + desc: '', + args: [], + ); } /// `cкопирован` String get copied { - return Intl.message('cкопирован', name: 'copied', desc: '', args: []); + return Intl.message( + 'cкопирован', + name: 'copied', + desc: '', + args: [], + ); } /// `В черновик` String get to_draft { - return Intl.message('В черновик', name: 'to_draft', desc: '', args: []); + return Intl.message( + 'В черновик', + name: 'to_draft', + desc: '', + args: [], + ); } /// `Превью` String get preview { - return Intl.message('Превью', name: 'preview', desc: '', args: []); + return Intl.message( + 'Превью', + name: 'preview', + desc: '', + args: [], + ); } /// `Опубликовать` String get publish { - return Intl.message('Опубликовать', name: 'publish', desc: '', args: []); + return Intl.message( + 'Опубликовать', + name: 'publish', + desc: '', + args: [], + ); } /// `Добавление плана` @@ -1277,7 +1672,12 @@ class S { /// `Название` String get label { - return Intl.message('Название', name: 'label', desc: '', args: []); + return Intl.message( + 'Название', + name: 'label', + desc: '', + args: [], + ); } /// `План для поддержания формы` @@ -1302,12 +1702,22 @@ class S { /// `Похудение` String get slim { - return Intl.message('Похудение', name: 'slim', desc: '', args: []); + return Intl.message( + 'Похудение', + name: 'slim', + desc: '', + args: [], + ); } /// `Поддержка формы` String get fitness { - return Intl.message('Поддержка формы', name: 'fitness', desc: '', args: []); + return Intl.message( + 'Поддержка формы', + name: 'fitness', + desc: '', + args: [], + ); } /// `Улучшение самочувствия` @@ -1362,37 +1772,72 @@ class S { /// `1 опция` String get one_option { - return Intl.message('1 опция', name: 'one_option', desc: '', args: []); + return Intl.message( + '1 опция', + name: 'one_option', + desc: '', + args: [], + ); } /// `3 опции` String get three_options { - return Intl.message('3 опции', name: 'three_options', desc: '', args: []); + return Intl.message( + '3 опции', + name: 'three_options', + desc: '', + args: [], + ); } /// `Опция` String get option { - return Intl.message('Опция', name: 'option', desc: '', args: []); + return Intl.message( + 'Опция', + name: 'option', + desc: '', + args: [], + ); } /// `Оказание услуг` String get services { - return Intl.message('Оказание услуг', name: 'services', desc: '', args: []); + return Intl.message( + 'Оказание услуг', + name: 'services', + desc: '', + args: [], + ); } /// `Эконом` String get economy { - return Intl.message('Эконом', name: 'economy', desc: '', args: []); + return Intl.message( + 'Эконом', + name: 'economy', + desc: '', + args: [], + ); } /// `Стандарт` String get standard { - return Intl.message('Стандарт', name: 'standard', desc: '', args: []); + return Intl.message( + 'Стандарт', + name: 'standard', + desc: '', + args: [], + ); } /// `Комфорт` String get comfort { - return Intl.message('Комфорт', name: 'comfort', desc: '', args: []); + return Intl.message( + 'Комфорт', + name: 'comfort', + desc: '', + args: [], + ); } /// `Заголовок опции` @@ -1507,7 +1952,12 @@ class S { /// `Автор плана` String get plan_author { - return Intl.message('Автор плана', name: 'plan_author', desc: '', args: []); + return Intl.message( + 'Автор плана', + name: 'plan_author', + desc: '', + args: [], + ); } /// `Продолжительность опции` @@ -1542,7 +1992,12 @@ class S { /// `Похудение` String get slim_type { - return Intl.message('Похудение', name: 'slim_type', desc: '', args: []); + return Intl.message( + 'Похудение', + name: 'slim_type', + desc: '', + args: [], + ); } /// `План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;` @@ -1597,12 +2052,22 @@ class S { /// `Цена` String get price { - return Intl.message('Цена', name: 'price', desc: '', args: []); + return Intl.message( + 'Цена', + name: 'price', + desc: '', + args: [], + ); } /// `Мои диеты` String get my_diets { - return Intl.message('Мои диеты', name: 'my_diets', desc: '', args: []); + return Intl.message( + 'Мои диеты', + name: 'my_diets', + desc: '', + args: [], + ); } /// `Опубликованные` @@ -1617,22 +2082,42 @@ class S { /// `Черновики` String get drafted { - return Intl.message('Черновики', name: 'drafted', desc: '', args: []); + return Intl.message( + 'Черновики', + name: 'drafted', + desc: '', + args: [], + ); } /// `Персональные` String get personal { - return Intl.message('Персональные', name: 'personal', desc: '', args: []); + return Intl.message( + 'Персональные', + name: 'personal', + desc: '', + args: [], + ); } /// `Редактировать` String get edit { - return Intl.message('Редактировать', name: 'edit', desc: '', args: []); + return Intl.message( + 'Редактировать', + name: 'edit', + desc: '', + args: [], + ); } /// `Предложить` String get toOffer { - return Intl.message('Предложить', name: 'toOffer', desc: '', args: []); + return Intl.message( + 'Предложить', + name: 'toOffer', + desc: '', + args: [], + ); } /// `Снять с публикации` @@ -1647,22 +2132,42 @@ class S { /// `Диета` String get diet { - return Intl.message('Диета', name: 'diet', desc: '', args: []); + return Intl.message( + 'Диета', + name: 'diet', + desc: '', + args: [], + ); } /// `Удалена` String get deleted { - return Intl.message('Удалена', name: 'deleted', desc: '', args: []); + return Intl.message( + 'Удалена', + name: 'deleted', + desc: '', + args: [], + ); } /// `Мои планы` String get my_plans { - return Intl.message('Мои планы', name: 'my_plans', desc: '', args: []); + return Intl.message( + 'Мои планы', + name: 'my_plans', + desc: '', + args: [], + ); } /// `от` String get from { - return Intl.message('от', name: 'from', desc: '', args: []); + return Intl.message( + 'от', + name: 'from', + desc: '', + args: [], + ); } /// `Данные пользователя` @@ -1687,7 +2192,12 @@ class S { /// `Пол` String get sex { - return Intl.message('Пол', name: 'sex', desc: '', args: []); + return Intl.message( + 'Пол', + name: 'sex', + desc: '', + args: [], + ); } /// `Физическая форма` @@ -1702,22 +2212,42 @@ class S { /// `Рост` String get height { - return Intl.message('Рост', name: 'height', desc: '', args: []); + return Intl.message( + 'Рост', + name: 'height', + desc: '', + args: [], + ); } /// `Давление` String get pressure { - return Intl.message('Давление', name: 'pressure', desc: '', args: []); + return Intl.message( + 'Давление', + name: 'pressure', + desc: '', + args: [], + ); } /// `Верхнее` String get upper { - return Intl.message('Верхнее', name: 'upper', desc: '', args: []); + return Intl.message( + 'Верхнее', + name: 'upper', + desc: '', + args: [], + ); } /// `Нижнее` String get lower { - return Intl.message('Нижнее', name: 'lower', desc: '', args: []); + return Intl.message( + 'Нижнее', + name: 'lower', + desc: '', + args: [], + ); } /// `Склонность к гипотонии` @@ -1799,6 +2329,216 @@ class S { args: [], ); } + + /// `Диета уже начата` + String get diet_started { + return Intl.message( + 'Диета уже начата', + name: 'diet_started', + desc: '', + args: [], + ); + } + + /// `Отменить диету?` + String get cancel_diet { + return Intl.message( + 'Отменить диету?', + name: 'cancel_diet', + desc: '', + args: [], + ); + } + + /// `Подтвердить` + String get confirm { + return Intl.message( + 'Подтвердить', + name: 'confirm', + desc: '', + args: [], + ); + } + + /// `Не удалось отменить диету` + String get could_not_cancel_diet { + return Intl.message( + 'Не удалось отменить диету', + name: 'could_not_cancel_diet', + desc: '', + args: [], + ); + } + + /// `часов` + String get hours { + return Intl.message( + 'часов', + name: 'hours', + desc: '', + args: [], + ); + } + + /// `час` + String get hour { + return Intl.message( + 'час', + name: 'hour', + desc: '', + args: [], + ); + } + + /// `часа` + String get another_form_of_hour { + return Intl.message( + 'часа', + name: 'another_form_of_hour', + desc: '', + args: [], + ); + } + + /// `минуты` + String get minutes { + return Intl.message( + 'минуты', + name: 'minutes', + desc: '', + args: [], + ); + } + + /// `минута` + String get minute { + return Intl.message( + 'минута', + name: 'minute', + desc: '', + args: [], + ); + } + + /// `минут` + String get another_form_of_minute { + return Intl.message( + 'минут', + name: 'another_form_of_minute', + desc: '', + args: [], + ); + } + + /// `Есть видео` + String get there_is_a_video { + return Intl.message( + 'Есть видео', + name: 'there_is_a_video', + desc: '', + args: [], + ); + } + + /// `Автор рецепта` + String get recipe_author { + return Intl.message( + 'Автор рецепта', + name: 'recipe_author', + desc: '', + args: [], + ); + } + + /// `Об этом рецепте` + String get about_recipe { + return Intl.message( + 'Об этом рецепте', + name: 'about_recipe', + desc: '', + args: [], + ); + } + + /// `этапов` + String get stages { + return Intl.message( + 'этапов', + name: 'stages', + desc: '', + args: [], + ); + } + + /// `этап` + String get stage { + return Intl.message( + 'этап', + name: 'stage', + desc: '', + args: [], + ); + } + + /// `этапа` + String get another_form_of_stage { + return Intl.message( + 'этапа', + name: 'another_form_of_stage', + desc: '', + args: [], + ); + } + + /// `Посмотреть видео рецепт` + String get check_out_video_recipe { + return Intl.message( + 'Посмотреть видео рецепт', + name: 'check_out_video_recipe', + desc: '', + args: [], + ); + } + + /// `Не удалось запустить видео` + String get could_not_launch_video { + return Intl.message( + 'Не удалось запустить видео', + name: 'could_not_launch_video', + desc: '', + args: [], + ); + } + + /// `Ингредиенты` + String get ingredients { + return Intl.message( + 'Ингредиенты', + name: 'ingredients', + desc: '', + args: [], + ); + } + + /// `Шаг` + String get step { + return Intl.message( + 'Шаг', + name: 'step', + desc: '', + args: [], + ); + } + + /// `Пошаговый фоторецепт` + String get step_by_step_photo_recipe { + return Intl.message( + 'Пошаговый фоторецепт', + name: 'step_by_step_photo_recipe', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 3fb9e4f4..1b8e2952 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -230,5 +230,22 @@ "diet_started": "Диета уже начата", "cancel_diet": "Отменить диету?", "confirm": "Подтвердить", - "could_not_cancel_diet": "Не удалось отменить диету" + "could_not_cancel_diet": "Не удалось отменить диету", + "hours": "часов", + "hour": "час", + "another_form_of_hour": "часа", + "minutes": "минуты", + "minute": "минута", + "another_form_of_minute": "минут", + "there_is_a_video": "Есть видео", + "recipe_author": "Автор рецепта", + "about_recipe": "Об этом рецепте", + "stages": "этапов", + "stage": "этап", + "another_form_of_stage": "этапа", + "check_out_video_recipe": "Посмотреть видео рецепт", + "could_not_launch_video": "Не удалось запустить видео", + "ingredients": "Ингредиенты", + "step": "Шаг", + "step_by_step_photo_recipe": "Пошаговый фоторецепт" }