forked from eatvio/eatvio_app
518 lines
18 KiB
Dart
518 lines
18 KiB
Dart
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<DiaryEvent, DiaryState> {
|
|
final DiaryRepository diaryRepository;
|
|
|
|
DiaryBloc({required this.diaryRepository}) : super(const DiaryState.loading()) {
|
|
on<LoadDiary>(loadDiary);
|
|
on<LoadDiaryById>(loadDiaryById);
|
|
on<ChangeSatiety>(_changeSatiety); // пользователь нажимает на шкалу сытости
|
|
on<ChangeMood>(_changeMood); // пользователь нажимает на шкалу настроения
|
|
on<ChangeMealTime>(_changeMealTime); // пользовательяет времяа пиши
|
|
on<ChangeMeal>(_changeMeal);
|
|
on<ChangePortion>(_changePortion);
|
|
on<WakeupEvent>(_changeWakeup); // пользователь выбирает время пробуждения
|
|
on<RetireEvent>(_changeRetire); // пользователь выбирает время отхода ко сну
|
|
on<ToggleMoodExpandedEvent>(_toggleMood);
|
|
on<ToggleWakeupExpandedEvent>(_toggleWakeup);
|
|
on<AddPortionEvent>(_addPortion); //пользователь нажимает "Добавить продукт"
|
|
on<RemovePortionEvent>(_removePortion);
|
|
on<RemoveImageEvent>(
|
|
_removeImage,
|
|
); // пользователь нажимает на крестик около продукта
|
|
on<UploadImages>(_uploadImages); //пользователь нажимает "Добавить фото"
|
|
on<ProductInput>(
|
|
_inputProduct,
|
|
);
|
|
on<FinishDay>(_finishDay);
|
|
on<SendModeration>(_sendModeration);
|
|
on<ClearInput>(_clearInput);
|
|
on<ConfirmPortion>(_confirmPortion);
|
|
}
|
|
|
|
loadDiary(
|
|
LoadDiary event,
|
|
Emitter<DiaryState> emit,
|
|
) async {
|
|
emit(const DiaryState.loading());
|
|
try {
|
|
Map<String, int> data = await diaryRepository.getDiaryID();
|
|
|
|
final int? diaryID = data['id'];
|
|
final DiaryModel diary = await diaryRepository.getDiary(diaryID ?? 45);
|
|
final Meal currentMeal = diary.meals[0];
|
|
|
|
List<SelectionPopupModel> portionList = [SelectionPopupModel(title: "грамм", value: "gr"), SelectionPopupModel(title: "порций", value: "pcs")];
|
|
|
|
final Map<String, dynamic> chartData = await diaryRepository.getChartData(diaryID ?? 45);
|
|
final Map<String, dynamic> dailyNutritionIntake = chartData['dailyNutritionIntake']!;
|
|
final Map<String, dynamic> 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<NutritionData> chartDataCounted = [
|
|
NutritionData('Калории', ccal, getColor(ccal)),
|
|
NutritionData('Белки', proteins, getColor(proteins)),
|
|
NutritionData('Жиры', fats, getColor(fats)),
|
|
NutritionData('Углеводы', carbs, getColor(carbs)),
|
|
];
|
|
|
|
List<SelectionPopupModel> 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<DiaryState> 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<SelectionPopupModel> portionList = [SelectionPopupModel(title: "gr"), SelectionPopupModel(title: "pcs")];
|
|
|
|
final Map<String, dynamic> chartData = await diaryRepository.getChartData(diaryID);
|
|
final Map<String, dynamic> dailyNutritionIntake = chartData['dailyNutritionIntake']!;
|
|
final Map<String, dynamic> 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<NutritionData> chartDataCounted = [
|
|
NutritionData('Калории', ccal, getColor(ccal)),
|
|
NutritionData('Белки', proteins, getColor(proteins)),
|
|
NutritionData('Жиры', fats, getColor(fats)),
|
|
NutritionData('Углеводы', carbs, getColor(carbs)),
|
|
];
|
|
|
|
List<SelectionPopupModel> 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<void> _changeSatiety(
|
|
ChangeSatiety event,
|
|
Emitter<DiaryState> 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<void> _changeMood(
|
|
ChangeMood event,
|
|
Emitter<DiaryState> 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<void> _changeWakeup(
|
|
WakeupEvent event,
|
|
Emitter<DiaryState> 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<void> _changeRetire(
|
|
RetireEvent event,
|
|
Emitter<DiaryState> 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<void> _changeMealTime(
|
|
ChangeMealTime event,
|
|
Emitter<DiaryState> emit,
|
|
) async {
|
|
if (state is DiaryStateLoaded) {
|
|
var currentState = state as DiaryStateLoaded;
|
|
var updatedState = currentState.copyWith(mealTimeController: event.time);
|
|
emit(updatedState);
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _changeMeal(
|
|
ChangeMeal event,
|
|
Emitter<DiaryState> 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<void> _inputProduct(
|
|
ProductInput event,
|
|
Emitter<DiaryState> emit,
|
|
) async {
|
|
if (state is DiaryStateLoaded) {
|
|
var currentState = state as DiaryStateLoaded;
|
|
|
|
final Map<String, List<Product>> 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<void> _addPortion(
|
|
AddPortionEvent event,
|
|
Emitter<DiaryState> 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<void> _removePortion(
|
|
RemovePortionEvent event,
|
|
Emitter<DiaryState> 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<void> _uploadImages(
|
|
UploadImages event,
|
|
Emitter<DiaryState> 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<void> _sendModeration(
|
|
SendModeration event,
|
|
Emitter<DiaryState> emit,
|
|
) async {
|
|
if (state is DiaryStateLoaded) {
|
|
await diaryRepository.sendModeration(
|
|
event.name,
|
|
event.energyOn100gr,
|
|
event.proteinsOn100gr,
|
|
event.fatsOn100gr,
|
|
event.carbohydratesOn100gr,
|
|
);
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _clearInput(
|
|
ClearInput event,
|
|
Emitter<DiaryState> emit,
|
|
) {
|
|
if (state is DiaryStateLoaded) {
|
|
var currentState = state as DiaryStateLoaded;
|
|
DiaryStateLoaded updatedState = currentState.copyWith();
|
|
emit(updatedState);
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _changePortion(
|
|
ChangePortion event,
|
|
Emitter<DiaryState> emit,
|
|
) {
|
|
if (state is DiaryStateLoaded) {
|
|
var currentState = state as DiaryStateLoaded;
|
|
DiaryStateLoaded updatedState = currentState.copyWith(
|
|
portionController: event.value,
|
|
);
|
|
emit(updatedState);
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _toggleMood(
|
|
ToggleMoodExpandedEvent event,
|
|
Emitter<DiaryState> emit,
|
|
) {
|
|
if (state is DiaryStateLoaded) {
|
|
var currentState = state as DiaryStateLoaded;
|
|
|
|
emit(currentState.copyWith(moodExpanded: !currentState.moodExpanded));
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _toggleWakeup(
|
|
ToggleWakeupExpandedEvent event,
|
|
Emitter<DiaryState> emit,
|
|
) {
|
|
if (state is DiaryStateLoaded) {
|
|
var currentState = state as DiaryStateLoaded;
|
|
|
|
emit(currentState.copyWith(wakeupExpanded: !currentState.wakeupExpanded));
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _removeImage(
|
|
RemoveImageEvent event,
|
|
Emitter<DiaryState> 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<void> _finishDay(FinishDay event, Emitter<DiaryState> 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<String> 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<SelectionPopupModel> fillPortion() {
|
|
return [
|
|
SelectionPopupModel(id: 0, title: "порций", isSelected: true),
|
|
SelectionPopupModel(id: 1, title: "грамм"),
|
|
];
|
|
}
|
|
|
|
FutureOr<void> _confirmPortion(ConfirmPortion event, Emitter<DiaryState> 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}');
|
|
}
|
|
}
|