import 'dart:async'; import 'package:eatvio_app/data/models/selectionPopupModel/selection_popup_model.dart'; import 'package:eatvio_app/features/diary/models/diary_model.dart'; import 'package:eatvio_app/features/diary/models/meal.dart'; import 'package:eatvio_app/features/diary/models/product.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; part 'diary_event.dart'; /// A bloc that manages the state of a Diary according to the event that is dispatched to it. class DiaryBloc extends Bloc { final DiaryRepository diaryRepository; DiaryBloc({required this.diaryRepository}) : super(const DiaryState.loading()) { on(loadDiary); on(loadDiaryById); on(_changeSatiety); // пользователь нажимает на шкалу сытости on(_changeMood); // пользователь нажимает на шкалу настроения on(_changeMealTime); // пользовательяет времяа пиши on(_changeMeal); on(_changePortion); on(_changeWakeup); // пользователь выбирает время пробуждения on(_changeRetire); // пользователь выбирает время отхода ко сну on(_toggleMood); on(_toggleWakeup); on(_addPortion); //пользователь нажимает "Добавить продукт" on(_removePortion); on( _removeImage, ); // пользователь нажимает на крестик около продукта on(_uploadImages); //пользователь нажимает "Добавить фото" on( _inputProduct, ); on(_finishDay); on(_sendModeration); on(_clearInput); on(_confirmPortion); } loadDiary( LoadDiary event, Emitter emit, ) async { emit(const DiaryState.loading()); try { Map data = await diaryRepository.getDiaryID(); final int? diaryID = data['id']; final DiaryModel diary = await diaryRepository.getDiary(diaryID ?? 45); final Meal currentMeal = diary.meals[0]; List portionList = [SelectionPopupModel(title: "грамм", value: "gr"), SelectionPopupModel(title: "порций", value: "pcs")]; final Map chartData = await diaryRepository.getChartData(diaryID ?? 45); final Map dailyNutritionIntake = chartData['dailyNutritionIntake']!; final Map dailyNutritionRecommendations = chartData['dailyNutritionRecommendation']!; final double ccal = dailyNutritionIntake['energy']! / dailyNutritionRecommendations['energy']! * 50; final double proteins = dailyNutritionIntake['proteins']! / dailyNutritionRecommendations['proteins']! * 50; final double fats = dailyNutritionIntake['fats']! / dailyNutritionRecommendations['fats']! * 50; final double carbs = dailyNutritionIntake['carbohydrates']! / dailyNutritionRecommendations['carbohydrates']! * 50; Color getColor(double value) { if (value >= 99.1) { return Colors.red; } else { return Colors.blue; } } final List chartDataCounted = [ NutritionData('Калории', ccal, getColor(ccal)), NutritionData('Белки', proteins, getColor(proteins)), NutritionData('Жиры', fats, getColor(fats)), NutritionData('Углеводы', carbs, getColor(carbs)), ]; List mealList = [ SelectionPopupModel(title: "Прием пищи 1", id: 1), SelectionPopupModel(title: "Прием пищи 2", id: 2), SelectionPopupModel(title: "Прием пищи 3", id: 3), SelectionPopupModel(title: "Прием пищи 4", id: 4), SelectionPopupModel(title: "Прием пищи 5", id: 5), SelectionPopupModel(title: "Прием пищи 6", id: 6), ]; emit(DiaryState.loaded( diary: diary, currentMeal: currentMeal, portions: currentMeal.portions, portionList: portionList, portionController: portionList[0], mealList: mealList, chartData: chartData, chartDataCounted: chartDataCounted, selectedMood: currentMeal.mood, selectedSatiety: currentMeal.satiety, wakeupController: TimeOfDay.now(), retireController: TimeOfDay.now(), commentController: TextEditingController(), product_nameController: TextEditingController(), productQuantityController: TextEditingController(), caloriesController: TextEditingController(), fatsController: TextEditingController(), proteinsController: TextEditingController(), carbsController: TextEditingController(), wakeupExpanded: false, moderationExpanded: false, moodExpanded: false, isLoading: false, isAdded: false, autocompleteList: [], recommendedChartValues: [1, 1, 1, 1], consumedChartValues: [1, 1, 1, 1])); } catch (e) { emit(DiaryState.error(errorMessage: e.toString())); } } loadDiaryById( LoadDiaryById event, Emitter emit, ) async { emit(const DiaryState.loading()); try { final int diaryID = int.parse(event.id ?? "0"); final DiaryModel diary = await diaryRepository.getDiary(diaryID); final Meal currentMeal = diary.meals[0]; List portionList = [SelectionPopupModel(title: "gr"), SelectionPopupModel(title: "pcs")]; final Map chartData = await diaryRepository.getChartData(diaryID); final Map dailyNutritionIntake = chartData['dailyNutritionIntake']!; final Map dailyNutritionRecommendations = chartData['dailyNutritionRecommendation']!; final double ccal = dailyNutritionIntake['energy']! / dailyNutritionRecommendations['energy']! * 50; final double proteins = dailyNutritionIntake['proteins']! / dailyNutritionRecommendations['proteins']! * 50; final double fats = dailyNutritionIntake['fats']! / dailyNutritionRecommendations['fats']! * 50; final double carbs = dailyNutritionIntake['carbohydrates']! / dailyNutritionRecommendations['carbohydrates']! * 50; Color getColor(double value) { if (value >= 99.1) { return Colors.red; } else { return Colors.blue; } } final List chartDataCounted = [ NutritionData('Калории', ccal, getColor(ccal)), NutritionData('Белки', proteins, getColor(proteins)), NutritionData('Жиры', fats, getColor(fats)), NutritionData('Углеводы', carbs, getColor(carbs)), ]; List mealList = [ SelectionPopupModel(title: "Прием пищи 1", id: 1), SelectionPopupModel(title: "Прием пищи 2", id: 2), SelectionPopupModel(title: "Прием пищи 3", id: 3), SelectionPopupModel(title: "Прием пищи 4", id: 4), SelectionPopupModel(title: "Прием пищи 5", id: 5), SelectionPopupModel(title: "Прием пищи 6", id: 6), ]; emit(DiaryState.loaded( diary: diary, currentMeal: currentMeal, portions: currentMeal.portions, portionList: portionList, portionController: portionList[0], mealList: mealList, chartData: chartData, chartDataCounted: chartDataCounted, selectedMood: currentMeal.mood, selectedSatiety: currentMeal.satiety, wakeupController: TimeOfDay.now(), retireController: TimeOfDay.now(), commentController: TextEditingController(), product_nameController: TextEditingController(), productQuantityController: TextEditingController(), caloriesController: TextEditingController(), fatsController: TextEditingController(), proteinsController: TextEditingController(), carbsController: TextEditingController(), wakeupExpanded: false, moderationExpanded: false, moodExpanded: false, isLoading: false, isAdded: false, autocompleteList: [], recommendedChartValues: [1, 1, 1, 1], consumedChartValues: [1, 1, 1, 1])); } catch (e) { emit(DiaryState.error(errorMessage: e.toString())); } } Future _changeSatiety( ChangeSatiety event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; var updatedState = currentState.copyWith( selectedSatiety: event.selectedElement, ); diaryRepository.changeSatiety( event.selectedElement, currentState.currentMeal.id, currentState.diary.id, ); emit(updatedState); } } Future _changeMood( ChangeMood event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; var updatedState = currentState.copyWith( selectedMood: event.selectedElement, ); diaryRepository.changeMood( event.selectedElement, currentState.currentMeal.id, currentState.diary.id, ); emit(updatedState); // Передаем обновленное состояние в emit } } FutureOr _changeWakeup( WakeupEvent event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; var updatedState = currentState.copyWith(wakeupController: event.selectedTime); final DateFormat formatter = DateFormat('HH:mm'); final String wakeupString = formatter.format(DateTime(2022, 1, 1, event.selectedTime.hour, event.selectedTime.minute)); await diaryRepository.changeWakeup( currentState.diary.id, wakeupString, ); emit(updatedState); } } FutureOr _changeRetire( RetireEvent event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; var updatedState = currentState.copyWith(retireController: event.selectedTime); final DateFormat formatter = DateFormat('HH:mm'); final String retireString = formatter.format(DateTime(2022, 1, 1, event.selectedTime.hour, event.selectedTime.minute)); await diaryRepository.changeRetire( currentState.diary.id, retireString, ); emit(updatedState); } } FutureOr _changeMealTime( ChangeMealTime event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; var updatedState = currentState.copyWith(mealTimeController: event.time); emit(updatedState); } } FutureOr _changeMeal( ChangeMeal event, Emitter emit, ) async { if (state is DiaryStateLoaded) { final currentState = state as DiaryStateLoaded; final Meal meal = currentState.diary.meals.firstWhere((meal) => meal.type == event.value.id); DiaryStateLoaded updatedState = currentState.copyWith( currentMeal: meal, portions: meal.portions, mealTimeController: toTime(meal.time), selectedMood: meal.mood, selectedSatiety: meal.satiety, ); emit(updatedState); } } Future _inputProduct( ProductInput event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; final Map> productNutritionalValues = await diaryRepository.inputProduct(event.input); var updatedState = currentState.copyWith( product_nameController: TextEditingController(text: event.input), autocompleteList: productNutritionalValues['product_nutritional_value'] ?? [], ); if (event.input.length > 3) { updatedState = updatedState.copyWith(moderationExpanded: true); } else { updatedState = updatedState.copyWith(moderationExpanded: false); } emit(updatedState); } } FutureOr _addPortion( AddPortionEvent event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; await diaryRepository.addPortion( currentState.diary.id, currentState.currentMeal.id, currentState.product_nameController.text, currentState.portionController.value, int.parse(currentState.productQuantityController.text), ); add(LoadDiary()); } } Future _removePortion( RemovePortionEvent event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; await diaryRepository.removePortion( currentState.diary.id, currentState.currentMeal.id, event.portionId, ); } add(LoadDiary()); } Future _uploadImages( UploadImages event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; emit(currentState.copyWith(isLoading: true)); await diaryRepository.uploadImage( event.image.path, currentState.diary.id, currentState.currentMeal.id, ); add(LoadDiary()); } } FutureOr _sendModeration( SendModeration event, Emitter emit, ) async { if (state is DiaryStateLoaded) { await diaryRepository.sendModeration( event.name, event.energyOn100gr, event.proteinsOn100gr, event.fatsOn100gr, event.carbohydratesOn100gr, ); } } FutureOr _clearInput( ClearInput event, Emitter emit, ) { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; DiaryStateLoaded updatedState = currentState.copyWith(); emit(updatedState); } } FutureOr _changePortion( ChangePortion event, Emitter emit, ) { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; DiaryStateLoaded updatedState = currentState.copyWith( portionController: event.value, ); emit(updatedState); } } FutureOr _toggleMood( ToggleMoodExpandedEvent event, Emitter emit, ) { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; emit(currentState.copyWith(moodExpanded: !currentState.moodExpanded)); } } FutureOr _toggleWakeup( ToggleWakeupExpandedEvent event, Emitter emit, ) { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; emit(currentState.copyWith(wakeupExpanded: !currentState.wakeupExpanded)); } } FutureOr _removeImage( RemoveImageEvent event, Emitter emit, ) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; await diaryRepository.removeImage( currentState.diary.id, currentState.currentMeal.id, int.parse(event.photoID), event.path, ); add(LoadDiary()); } } Future _finishDay(FinishDay event, Emitter emit) async { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; await diaryRepository.finishDay( currentState.diary.id, // currentState.wakeupController, // currentState.retireController, // currentState.commentController.text, ); add(LoadDiary()); } } TimeOfDay toTime(String timeStr) { List timeParts = timeStr.split(":"); int hour = int.parse(timeParts[0]); int minute = int.parse(timeParts[1]); TimeOfDay timeOfDay = TimeOfDay(hour: hour, minute: minute); return timeOfDay; } int compareTypes(a, b) { return a['type'].compareTo(b['type']); } List fillPortion() { return [ SelectionPopupModel(id: 0, title: "порций", isSelected: true), SelectionPopupModel(id: 1, title: "грамм"), ]; } FutureOr _confirmPortion(ConfirmPortion event, Emitter emit) { if (state is DiaryStateLoaded) { var currentState = state as DiaryStateLoaded; final updatedPortions = currentState.portions.map((portion) { if (portion.id == event.id) { return portion.copyWith(from_diet: !portion.from_diet); } if (!portion.from_diet) add(RemovePortionEvent(portion.id)); return portion; }).toList(); final updatedState = currentState.copyWith(portions: updatedPortions); emit(updatedState); add(LoadDiary()); } } } int convertToInt(dynamic value) { if (value is int) { return value; } else if (value is double) { return value.toInt(); } else { throw FormatException('Unexpected value type: ${value.runtimeType}'); } }