From c36aabd9a4769d25bc625abf85930d7b82832dae Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 27 May 2025 23:30:36 +0700 Subject: [PATCH] added diets to diary --- lib/core/locator_service.dart | 2 +- lib/core/theme/custom_text_style.dart | 6 + .../diary/models/diary_day_model.dart | 21 +++- .../diary/presentation/bloc/diary_cubit.dart | 103 ++++++++++++++++-- .../presentation/widgets/calendar_widget.dart | 10 +- .../widgets/chosen_diet_widget.dart | 82 ++++++++++++++ .../presentation/widgets/meal_widget.dart | 4 +- .../presentation/widgets/product_widget.dart | 2 +- .../widgets/time_of_meal_picker_widget.dart | 1 + lib/generated/intl/messages_en.dart | 2 +- lib/generated/l10n.dart | 4 +- lib/l10n/intl_en.arb | 2 +- 12 files changed, 216 insertions(+), 23 deletions(-) create mode 100644 lib/features/diary/presentation/widgets/chosen_diet_widget.dart diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 15bb5270..e7030939 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -114,7 +114,7 @@ Future init() async { sl.registerFactory(() => PlansCubit(feedRepository: sl(), planRepository: sl())); sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl())); sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl())); - sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl())); + sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl(), dietRepository: sl())); sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl())); sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl())); sl.registerFactory(() => SubCubit(profileRepository: sl())); diff --git a/lib/core/theme/custom_text_style.dart b/lib/core/theme/custom_text_style.dart index 2e39baf8..88851233 100644 --- a/lib/core/theme/custom_text_style.dart +++ b/lib/core/theme/custom_text_style.dart @@ -196,6 +196,12 @@ abstract class TextStyles { height: 1.255, ); + static const textLMedium = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + height: 1.255, + ); + static const textMSemibold = TextStyle( fontSize: 16, fontWeight: FontWeight.w600, diff --git a/lib/features/diary/models/diary_day_model.dart b/lib/features/diary/models/diary_day_model.dart index fb3a0255..ed223d95 100644 --- a/lib/features/diary/models/diary_day_model.dart +++ b/lib/features/diary/models/diary_day_model.dart @@ -44,7 +44,8 @@ class DiaryDayModel with _$DiaryDayModel { @JsonKey(name: "state") required TypeOfDiaryModel typeOfDiary, @JsonKey(name: "meals") required List meals, @JsonKey(name: "chosen_plan_id") required int? chosenPlanId, - @JsonKey(name: "day_of_chosen_diet") required int? dayOfChosenDiet, + @JsonKey(name: "day_of_chosen_diet") required int? dayOfChosenDiet, + @JsonKey(name: "chosen_diet", fromJson: _parseDiet) required DiaryDietModel? diet, }) = _DiaryDayModel; factory DiaryDayModel.fromJson(Map json) => _$DiaryDayModelFromJson(json); @@ -56,11 +57,29 @@ class DiaryFutureDietDayModel with _$DiaryFutureDietDayModel { @JsonKey(name: "date") required String date, @JsonKey(name: "state", fromJson: _parseDayTypeForFutureDay) required TypeOfDiaryModel typeOfDiary, @JsonKey(name: "meals") required List meals, + @JsonKey(name: "chosen_diet", fromJson: _parseDiet) required DiaryDietModel? diet, + @JsonKey(name: "day_of_chosen_diet") required int? dayOfChosenDiet, }) = _DiaryFutureDietDayModel; factory DiaryFutureDietDayModel.fromJson(Map json) => _$DiaryFutureDietDayModelFromJson(json); } +DiaryDietModel? _parseDiet(json) { + if (json == null) return null; + return DiaryDietModel.fromJson(json['diet']); +} + +@freezed +class DiaryDietModel with _$DiaryDietModel { + const factory DiaryDietModel({ + required int id, + required int duration, + required String name, + }) = _DiaryDietModel; + + factory DiaryDietModel.fromJson(Map json) => _$DiaryDietModelFromJson(json); +} + TypeOfDiaryModel _parseDayTypeForFutureDay(json) { return TypeOfDiaryModel( hasChosenDiet: json['has_chosen_diet'] as bool, diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 1e70c096..a07f6611 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -9,6 +9,7 @@ import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/models/nutrition_model.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; +import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart'; import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; @@ -21,12 +22,10 @@ class DiaryCubit extends Cubit { final DiaryRepository diaryRepository; final GlossaryRepository glossaryRepository; final ProfileRepository profileRepository; + final DietRepository dietRepository; int? diaryId; - DiaryCubit({ - required this.diaryRepository, - required this.glossaryRepository, - required this.profileRepository, - }) : super(const DiaryState.loading()) { + DiaryCubit({required this.diaryRepository, required this.glossaryRepository, required this.profileRepository, required this.dietRepository}) + : super(const DiaryState.loading()) { today = DateTime.now(); selectedDay = today; diaryRepository.forceFetchStream.listen((_) { @@ -36,13 +35,14 @@ class DiaryCubit extends Cubit { String comment = ''; - setSelectedDay(DateTime day, {bool? forDietOnly}) async { + Future setSelectedDay(DateTime day, {bool? forDietOnly}) async { selectedDay = day; if (forDietOnly ?? false) { - await fetchDiaryForDietOnly(); - return; + final result = await fetchDiaryForDietOnly(); + return result; } await fetchDiary(true); + return null; } setSelectedMeal(int meal) { @@ -55,10 +55,93 @@ class DiaryCubit extends Cubit { } } - Future fetchDiaryForDietOnly() async { + Future fetchDiaryForDietOnly() async { final stringDate = selectedDay.format('yyyy-MM-dd'); final diaryDay = await diaryRepository.getDiaryByDateForDietOnly(stringDate); - emit(DiaryState.loadedTomorrowWithDiet(diary: diaryDay, selectedDay: selectedDay, selectedMeal: selectedMeal)); + if (!diaryDay.typeOfDiary.hasChosenDiet) { + return false; + } + emit(const DiaryState.loading()); + if (diaryDay.diet?.id != null && diaryDay.dayOfChosenDiet != null) { + final diet = await dietRepository.getDiet((diaryDay.diet?.id)!); + final meals = diet.dailySchedule[diaryDay.dayOfChosenDiet]; + final firstMealPortions = meals?[1]?.values.map((elem) { + return PortionModel( + id: 0, + productName: elem.name, + quantity: elem.quantity, + portion: elem.portion.getValue(), + fromDiet: true, + portionState: PortionState.filledDiet, + ); + }).toList(); + + final secondMealPortions = meals?[2]?.values.map((elem) { + return PortionModel( + id: 0, + productName: elem.name, + quantity: elem.quantity, + portion: elem.portion.getValue(), + fromDiet: true, + portionState: PortionState.filledDiet, + ); + }).toList(); + + final thirdMealPortions = meals?[3]?.values.map((elem) { + return PortionModel( + id: 0, + productName: elem.name, + quantity: elem.quantity, + portion: elem.portion.getValue(), + fromDiet: true, + portionState: PortionState.filledDiet, + ); + }).toList(); + + final fourthMealPortions = meals?[4]?.values.map((elem) { + return PortionModel( + id: 0, + productName: elem.name, + quantity: elem.quantity, + portion: elem.portion.getValue(), + fromDiet: true, + portionState: PortionState.filledDiet, + ); + }).toList(); + + final fifthMealPortions = meals?[5]?.values.map((elem) { + return PortionModel( + id: 0, + productName: elem.name, + quantity: elem.quantity, + portion: elem.portion.getValue(), + fromDiet: true, + portionState: PortionState.filledDiet, + ); + }).toList(); + final firstMeal = MealModel(id: 0, time: '0', typeNumber: 1, satiety: 0, mood: 0, portions: firstMealPortions ?? [], photos: []); + final secondMeal = MealModel(id: 0, time: '0', typeNumber: 2, satiety: 0, mood: 0, portions: secondMealPortions ?? [], photos: []); + final thirdMeal = MealModel(id: 0, time: '0', typeNumber: 3, satiety: 0, mood: 0, portions: thirdMealPortions ?? [], photos: []); + final fourthMeal = MealModel(id: 0, time: '0', typeNumber: 4, satiety: 0, mood: 0, portions: fourthMealPortions ?? [], photos: []); + final fifthMeal = MealModel(id: 0, time: '0', typeNumber: 5, satiety: 0, mood: 0, portions: fifthMealPortions ?? [], photos: []); + emit( + DiaryState.loadedTomorrowWithDiet( + diary: diaryDay.copyWith( + meals: [ + firstMeal, + secondMeal, + thirdMeal, + fourthMeal, + fifthMeal, + ], + ), + selectedDay: selectedDay, + selectedMeal: selectedMeal, + ), + ); + return true; + } + return false; } fetchDiary([bool needLoading = false, int? diaryId]) async { diff --git a/lib/features/diary/presentation/widgets/calendar_widget.dart b/lib/features/diary/presentation/widgets/calendar_widget.dart index d7d3e2f4..bc55d33a 100644 --- a/lib/features/diary/presentation/widgets/calendar_widget.dart +++ b/lib/features/diary/presentation/widgets/calendar_widget.dart @@ -49,12 +49,13 @@ class _CalendarWidgetState extends State { leftChevronVisible: false, rightChevronVisible: false, ), - onDaySelected: (selectedDay, focusedDay) { + onDaySelected: (selectedDay, focusedDay) async { if (!isSameDay(_selectedDay, selectedDay)) { final today = DateTime.now(); if (selectedDay.isAfter(today)) { if ((state is DiaryStateLoaded)) { - if (!state.diary.typeOfDiary.hasChosenDiet) { + final result = await context.read().setSelectedDay(_selectedDay, forDietOnly: true); + if (result == false && context.mounted) { showErrorDialog(context, title: s.want_to_see_tomorrow, description: s.can_not_see_next_day); return; } @@ -62,11 +63,11 @@ class _CalendarWidgetState extends State { _selectedDay = selectedDay; _focusedDay = focusedDay; }); - context.read().setSelectedDay(_selectedDay, forDietOnly: true); return; } if (state is DiaryStateLoadedTomorrowWithDiet) { - if (!state.diary.typeOfDiary.hasChosenDiet) { + final result = await context.read().setSelectedDay(_selectedDay, forDietOnly: true); + if (result == false && context.mounted) { showErrorDialog(context, title: s.want_to_see_tomorrow, description: s.can_not_see_next_day); return; } @@ -74,7 +75,6 @@ class _CalendarWidgetState extends State { _selectedDay = selectedDay; _focusedDay = focusedDay; }); - context.read().setSelectedDay(_selectedDay, forDietOnly: true); return; } } diff --git a/lib/features/diary/presentation/widgets/chosen_diet_widget.dart b/lib/features/diary/presentation/widgets/chosen_diet_widget.dart new file mode 100644 index 00000000..3b5f23fd --- /dev/null +++ b/lib/features/diary/presentation/widgets/chosen_diet_widget.dart @@ -0,0 +1,82 @@ +import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ChosenDietWidget extends StatelessWidget { + const ChosenDietWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder(builder: (context, state) { + if (state is DiaryStateLoaded) { + if (state.diary.typeOfDiary.hasChosenDiet) { + return ChosenDietWidgetContent( + currentDietDay: state.diary.dayOfChosenDiet ?? 0, + dietName: state.diary.diet?.name ?? '', + dietDuration: state.diary.diet?.duration ?? 0, + ); + } + } + if (state is DiaryStateLoadedTomorrowWithDiet) { + if (state.diary.typeOfDiary.hasChosenDiet) { + return Column( + children: [ + const CustomDivider(), + const SizedBox(height: 16), + ChosenDietWidgetContent( + currentDietDay: state.diary.dayOfChosenDiet ?? 0, + dietName: state.diary.diet?.name ?? '', + dietDuration: state.diary.diet?.duration ?? 0, + ), + ], + ); + } + } + + return const SizedBox.shrink(); + }); + } +} + +class ChosenDietWidgetContent extends StatelessWidget { + const ChosenDietWidgetContent({ + super.key, + required this.currentDietDay, + required this.dietName, + required this.dietDuration, + }); + final int currentDietDay; + final String dietName; + final int dietDuration; + @override + Widget build(BuildContext context) { + final size = MediaQuery.sizeOf(context); + final s = S.of(context); + return Column( + children: [ + Row( + children: [ + SizedBox( + width: size.width * 0.4, + child: Text(dietName, style: TextStyles.textLMedium.copyWith(color: appTheme.black)), + ), + const Spacer(), + Container( + decoration: BoxDecoration(color: appTheme.dividerColor, borderRadius: BorderRadius.circular(12)), + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), + child: Text('${s.day} $currentDietDay из $dietDuration', style: TextStyles.textMSemibold.copyWith(color: appTheme.gray)), + ) + ], + ), + const SizedBox( + height: 16, + ) + ], + ); + } +} diff --git a/lib/features/diary/presentation/widgets/meal_widget.dart b/lib/features/diary/presentation/widgets/meal_widget.dart index 93f9fb2d..dc34a7c0 100644 --- a/lib/features/diary/presentation/widgets/meal_widget.dart +++ b/lib/features/diary/presentation/widgets/meal_widget.dart @@ -4,6 +4,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/add_product_button_widget.dart'; +import 'package:eatvio_app/features/diary/presentation/widgets/chosen_diet_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/meal_button_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/meal_comment_widget.dart'; @@ -57,7 +58,8 @@ class _MealWidgetState extends State { ), const SizedBox(height: 12), const TimeOfMealPickerWidget(), - const SizedBox(height: 12), + + const ChosenDietWidget(), const ProductsListWidget(), if (widget.showAddButton) ...[ const SizedBox(height: 12), diff --git a/lib/features/diary/presentation/widgets/product_widget.dart b/lib/features/diary/presentation/widgets/product_widget.dart index 62276496..921a92ff 100644 --- a/lib/features/diary/presentation/widgets/product_widget.dart +++ b/lib/features/diary/presentation/widgets/product_widget.dart @@ -67,7 +67,7 @@ class _ProductWidgetState extends State { if (state is DiaryStateLoadedTomorrowWithDiet) { return ListTile( horizontalTitleGap: 0, - contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 4) : null, + contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 12) : null, title: Text(widget.portion.productName), subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'), shape: RoundedRectangleBorder( diff --git a/lib/features/diary/presentation/widgets/time_of_meal_picker_widget.dart b/lib/features/diary/presentation/widgets/time_of_meal_picker_widget.dart index b73bb6d5..c9da23ed 100644 --- a/lib/features/diary/presentation/widgets/time_of_meal_picker_widget.dart +++ b/lib/features/diary/presentation/widgets/time_of_meal_picker_widget.dart @@ -124,6 +124,7 @@ class _TimeOfMealPickerWidgetState extends State { ), ), const CustomDivider(), + const SizedBox(height: 12), ], ); } diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index dc980241..f53d5c35 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -693,7 +693,7 @@ class MessageLookup extends MessageLookupByLibrary { "Ожидайте начала плана питания", ), "want_to_see_tomorrow": MessageLookupByLibrary.simpleMessage( - "Хотите заглянуть в завтра?", + "Хотите заглянуть в будущее?", ), "weapon": MessageLookupByLibrary.simpleMessage("Оружие"), "weight": MessageLookupByLibrary.simpleMessage("Вес"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8da944a3..5ae91933 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2865,10 +2865,10 @@ class S { ); } - /// `Хотите заглянуть в завтра?` + /// `Хотите заглянуть в будущее?` String get want_to_see_tomorrow { return Intl.message( - 'Хотите заглянуть в завтра?', + 'Хотите заглянуть в будущее?', name: 'want_to_see_tomorrow', desc: '', args: [], diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d6038cb7..6881baf1 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -349,7 +349,7 @@ "try_again_2": "Попробуйте ещё раз", "sorry_we_did_not_find_the_plan": "Извините, план где-то потерялся, мы уже ищем его", "can_not_see_next_day": "Просмотр последующего дня возможен только при прохождении диеты", - "want_to_see_tomorrow": "Хотите заглянуть в завтра?", + "want_to_see_tomorrow": "Хотите заглянуть в будущее?", "you_can_not_start_plan_until_end_of_prev_one": "Вы не можете начать план, пока не закончите прохождение предыдущего", "end_previous_plan": "Закончите прохождение предыдущего плана", "you_can_not_open_this_day": "Вы не можете перейти в этот день",