refatcor(diary): Refactored (doesn't work yet though)

This commit is contained in:
2024-07-11 14:35:29 +03:00
parent def95aeeb5
commit e4cde31d76
27 changed files with 2304 additions and 1712 deletions

View File

@@ -0,0 +1,845 @@
import 'dart:async';
import 'package:eatvio_app/features/diary/models/diary_model.dart';
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
import 'package:intl/intl.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:image_picker/image_picker.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.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<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<AddComment>(_addComment); //пользователь нажимает "Добавить комментарий"
on<ProductInput>(
_inputProduct,
);
on<FinishDay>(_finishDay);
on<SendModeration>(_sendModeration);
on<RefreshEvent>(
reloadScreen,
);
on<ClearInput>(_clearInput);
}
loadDiary(
LoadDiary event,
Emitter<DiaryState> emit,
) async {
emit(const DiaryState.loading());
try {
final diary =
await diaryRepository.getDiary(await diaryRepository.getDiaryID());
emit(DiaryState.loaded(
diary: diary,
commentController: TextEditingController(),
productNameController: TextEditingController(),
productQuantityController: TextEditingController(),
caloriesController: TextEditingController(),
fatsController: TextEditingController(),
proteinsController: TextEditingController(),
carbsController: TextEditingController(),
wakeupExpanded: false,
moderationExpanded: false,
moodExpanded: false,
isLoading: false,
isAdded: false,
recommendedChartValues: [0, 0, 0, 0],
consumedChartValues: [0, 0, 0, 0]));
} catch (e) {
emit(DiaryState.error(errorMessage: e.toString()));
print(e);
}
}
fetchChartValues(
String token,
int diaryID,
Emitter<DiaryState> emit,
) async {
final responseChart = await http.get(
Uri.parse(
dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/dailyNutritionData/',
),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
);
if (responseChart.statusCode == 200) {
final responseBody = jsonDecode(responseChart.body);
final recommendedChartValues =
responseBody['dailyNutritionRecommendation']
.values
.map<int>((value) => convertToInt(value))
.toList();
final consumedChartValues = responseBody['dailyNutritionIntake']
.values
.map<int>((value) => convertToInt(value))
.toList();
print([recommendedChartValues, consumedChartValues]);
return [recommendedChartValues, consumedChartValues];
} else {
emit(DiaryStateError(
errorMessage: 'Ошибка при декодировании данных КБЖУ'));
print('fail');
return [[], []]; // Return empty lists instead of -1
}
}
refreshScreen(
int mealID,
DiaryEvent event,
Emitter<DiaryState> emit,
) async {
await reloadScreen(event, emit);
}
reloadScreen(
DiaryEvent event,
Emitter<DiaryState> emit,
) async {
print("Reloading...");
}
Future<void> _changeSatiety(
ChangeSatiety event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var updatedState =
currentState.copyWith(selectedMood: event.selectedElement);
// Отправляем данные по HTTP маршруту и ждем ответ
var response = await sendSatietyData(
event.selectedElement,
currentState.currentMeal?.id,
currentState.diary.id,
);
if (response.statusCode == 200) {
print(
'Satiety change success! meal_id: ${currentState.currentMeal?.id}, mood: ${event.selectedElement}',
);
emit(updatedState); // Передаем обновленное состояние в emit
}
}
}
Future<http.Response> sendSatietyData(
int selectedSatiety,
int? mealID,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! +
'/diary/$diaryID/meal/$mealID/satiety/fill';
final Map<String, dynamic> credentials = {
'satiety': selectedSatiety,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
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);
// Отправляем данные по HTTP маршруту и ждем ответ
var response = await sendMoodData(
event.selectedElement,
currentState.currentMeal?.id,
currentState.diary.id,
);
if (response.statusCode == 200) {
print(
'Mood change success! meal_id: ${currentState.currentMeal?.id}, mood: ${event.selectedElement}',
);
emit(updatedState); // Передаем обновленное состояние в emit
}
}
}
Future<http.Response> sendMoodData(
int selectedMood,
int? mealID,
int diaryID,
) async {
String url =
dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/mood/fill';
final Map<String, dynamic> credentials = {
'mood': selectedMood,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
FutureOr<void> _changeWakeup(
WakeupEvent event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var response = await sendWakeupData(
event.selectedTime,
currentState.diary.id,
);
if (response.statusCode == 200) {
await reloadScreen(event, emit);
var updatedState =
currentState.copyWith(wakeupController: event.selectedTime);
print("Wakeup time changed to ${event.selectedTime}");
emit(updatedState); // Передаем обновленное состояние в emit
} else
print("Wakeup time change error");
}
}
FutureOr<void> _changeRetire(
RetireEvent event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var response = await sendRetireData(
event.selectedTime,
currentState.diary.id,
);
if (response.statusCode == 200) {
await reloadScreen(event, emit);
var updatedState =
currentState.copyWith(retireController: event.selectedTime);
print("Retire time changed to ${event.selectedTime}");
emit(updatedState); // Передаем обновленное состояние в emit
} else
print("Retire time change error");
}
}
Future<http.Response> sendWakeupData(
TimeOfDay wakeup,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/wakeUp/fill';
final formatter = DateFormat('HH:mm');
final wakeupString =
formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute));
final Map<String, dynamic> credentials = {
'wakeUp': wakeupString,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
Future<http.Response> sendRetireData(
TimeOfDay retire,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
final formatter = DateFormat('HH:mm');
final retireString =
formatter.format(DateTime(2022, 1, 1, retire.hour, retire.minute));
final Map<String, dynamic> credentials = {
'retire': retireString,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
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);
}
}
Future<http.Response> sendMealTimeData(
String? token,
TimeOfDay mealTime,
int? mealID,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
final Map<String, dynamic> credentials = {
'time': mealTime,
'meal_id': mealID,
'diary_id': diaryID,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
FutureOr<void> _changeMeal(
ChangeMeal event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
Meal? meal =
currentState.diary.meals.firstWhere((meal) => meal.id == event.value);
DiaryStateLoaded updatedState = currentState.copyWith(
currentMeal: event.value,
portions: meal.portions,
photos: meal.photos,
mealTimeController: toTime(meal.time),
selectedMood: meal.mood,
selectedSatiety: meal.satiety,
);
emit(updatedState);
}
}
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> _addPortion(
AddPortionEvent event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var response = await sendPortionData(
currentState.productNameController?.text,
currentState.portionController?.id == 0 ? 'pcs' : 'gr',
int.parse(currentState.productQuantityController!.text),
currentState.currentMeal!.id,
currentState.diary.id,
);
if (response.statusCode == 200) {
emit(currentState.copyWith(isAdded: true));
await reloadScreen(event, emit);
} else {
print(
'Failed to add portion. Status Code: ${response.statusCode} ${response.body}',
);
}
}
}
FutureOr<http.Response> sendPortionData(
String? name,
String portion,
int? quantity,
int? mealID,
int diaryID,
) async {
String url =
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/create';
final Map<String, dynamic> credentials = {
'product_name': name,
'portion': portion,
'quantity': quantity,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
Future<void> _inputProduct(
ProductInput event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
final Map<String, dynamic> credentials = {'name': event.input};
final uri = Uri.parse(
'${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/filter?${Uri(queryParameters: credentials).query}',
);
final response = await http.get(
uri,
headers: {
'Content-Type': 'application/json',
},
);
final productNutritionalValues =
jsonDecode(response.body)['product_nutritional_value'];
if (productNutritionalValues is List<dynamic>) {
// Create a new controller with the input text and update the autocomplete list
var updatedState = currentState.copyWith(
productNameController: TextEditingController(text: event.input),
autocompleteList: productNutritionalValues
.map((product) => product['name'])
.toList(),
);
if (event.input.length > 3) {
updatedState = updatedState.copyWith(moderationExpanded: true);
} else {
updatedState = updatedState.copyWith(moderationExpanded: false);
}
emit(updatedState); // Emit the updated state
}
}
}
Future<void> _removePortion(
RemovePortionEvent event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
int? mealId =
currentState.currentMeal?.id; // Rename id to mealId for clarity
var response = await sendRemovePortion(
event.portionId,
mealId,
currentState.diary.id,
);
if (response.statusCode == 204) {
await reloadScreen(event, emit);
} else {
print(
'Failed to add portion. Status Code: ${response.statusCode} ${response.body}',
);
}
}
}
Future<http.Response> sendRemovePortion(
int? portionID,
int? mealID,
int diaryID,
) async {
String url =
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/$portionID/delete';
var response = await http.delete(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
encoding: utf8,
);
return response;
}
FutureOr<void> _removeImage(
RemoveImageEvent event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var response = await sendRemovePhoto(
event.path,
event.photoID,
currentState.currentMeal?.id,
currentState.diary.id,
);
print(response.statusCode);
if (response.statusCode == 204) {
await reloadScreen(event, emit);
} else {
print(
'Failed to remove photo. Status Code: ${response.statusCode} ${response.body}',
);
}
}
}
Future<http.Response> sendRemovePhoto(
String? path,
String? photoID,
int? mealID,
int diaryID,
) async {
String url =
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/photo/delete';
final Map<String, dynamic> credentials = {
'id': photoID,
'path': path,
};
var response = await http.delete(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
Future<void> _finishDay(FinishDay event, Emitter<DiaryState> emit) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var response = await sendFinishDay(
currentState.diary.id,
currentState.wakeupController,
currentState.retireController,
currentState.commentController?.text,
);
if (response.statusCode == 200) {
await reloadScreen(event, emit);
}
}
}
Future<http.Response> sendFinishDay(
int diaryID,
TimeOfDay? wakeup,
TimeOfDay? retire,
String? comment,
) async {
final formatter = DateFormat('HH:mm');
final wakeupString =
formatter.format(DateTime(2022, 1, 1, wakeup!.hour, wakeup.minute));
final retireString =
formatter.format(DateTime(2022, 1, 1, retire!.hour, retire.minute));
final Map<String, dynamic> credentials = {
'wakeup': wakeupString,
'retire': retireString,
'comment': comment ?? " ",
};
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/finishDay';
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
Future<void> _uploadImages(
UploadImages event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
emit(currentState.copyWith(isLoading: true));
await uploadImageToServer(
event.image.path,
currentState.diary.id,
currentState.currentMeal!.id,
);
await reloadScreen(event, emit);
}
}
Future<String> uploadImageToServer(
String imagePath,
int diaryID,
int mealId,
) async {
String url =
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealId/photo/upload';
var request = http.MultipartRequest('POST', Uri.parse(url));
request.files.add(await http.MultipartFile.fromPath('photo', imagePath));
var client = http.Client();
var streamedResponse = await client.send(request);
if (streamedResponse.statusCode == 200) {
var response = await http.Response.fromStream(streamedResponse);
var imageUrl = response.body;
print('Image successfully uploaded');
return imageUrl;
} else {
throw Exception(
'Failed to upload image. Status code: ${streamedResponse.statusCode}}',
);
}
}
FutureOr<void> _sendModeration(
SendModeration event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
final response = await _sendModerationData(
event.name,
event.energyOn100gr,
event.proteinsOn100gr,
event.fatsOn100gr,
event.carbohydratesOn100gr,
);
if (response.statusCode == 200)
print("Moderation success");
else
print("Moderation error");
}
}
Future<http.Response> _sendModerationData(
String name,
String energy_on_100gr,
String proteins_on_100gr,
String fats_on_100gr,
String carbohydrates_on_100gr,
) async {
final Uri url = Uri.parse(
'${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/sendToModeration',
);
final Uri serverURL = Uri.parse('${dotenv.env['SERVER_ROOT']!}');
String host = serverURL.host;
final Map<String, String> credentials = {
'name': name,
'energy_on_100gr': energy_on_100gr,
'proteins_on_100gr': proteins_on_100gr,
'fats_on_100gr': fats_on_100gr,
'carbohydrates_on_100gr': carbohydrates_on_100gr,
};
var headers = {
//'Content-Length': bodyBytes,
'Host': host,
};
var response = await http.post(
url,
headers: headers,
body: credentials,
encoding: utf8,
);
if (response.statusCode == 200) {
return response;
} else {
throw Exception(
'Failed to send a product for moderation ${response.statusCode}',
);
}
}
FutureOr<void> _addComment(
AddComment event,
Emitter<DiaryState> emit,
) async {
if (state is DiaryStateLoaded) {
var currentState = state as DiaryStateLoaded;
var updatedState = currentState.copyWith(
commentController: TextEditingController(text: event.comment),
);
// Отправляем данные по HTTP маршруту и ждем ответ
var response = await sendCommentData(
event.comment,
currentState.diary.id,
);
if (response.statusCode == 200) {
emit(updatedState); // Передаем обновленное состояние в emit
}
}
}
Future<http.Response> sendCommentData(
String comment,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
final Map<String, dynamic> credentials = {
'comment': comment,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
body: json.encode(credentials),
encoding: utf8,
);
return response;
}
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;
}
List<SelectionPopupModel> fillMealList(List<dynamic> meals) {
meals.sort(compareTypes);
return meals.map((meal) {
String title = "Прием пищи ${meal['type']}";
return SelectionPopupModel(id: meal['id'], title: title);
}).toList();
}
int compareTypes(a, b) {
return a['type'].compareTo(b['type']);
}
List<SelectionPopupModel> fillPortion() {
return [
SelectionPopupModel(id: 0, title: "порций", isSelected: true),
SelectionPopupModel(id: 1, title: "грамм"),
];
}
}
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}');
}
}

View File

@@ -1,20 +0,0 @@
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
class DiaryCubit extends Cubit<DiaryState> {
final DiaryRepository diaryRepository;
DiaryCubit({required this.diaryRepository}) : super(const DiaryState.loading());
loadDiary(String diaryID) async {
emit(const DiaryState.loading());
try {
final diary = await diaryRepository.getDiary(await diaryRepository.getDiaryID().toString());
emit(DiaryState.loaded(diary: diary));
} catch (e) {
emit(DiaryState.error(errorMessage: e.toString()));
print(e);
}
}
}

View File

@@ -0,0 +1,171 @@
// ignore_for_file: must_be_immutable
part of 'diary_bloc.dart';
/// Abstract class for all events that can be dispatched from the
///Diary widget.
///
/// Events must be immutable and implement the [Equatable] interface.
@immutable
abstract class DiaryEvent extends Equatable {}
/// Event that is dispatched when the Diary widget is first created.
class LoadDiary extends DiaryEvent {
@override
List<Object?> get props => [];
}
class ChangeSatiety extends DiaryEvent {
ChangeSatiety(
this.selectedElement,
);
final int selectedElement;
@override
List<Object> get props => [selectedElement];
}
class ChangeMood extends DiaryEvent {
ChangeMood(
this.selectedElement,
);
final int selectedElement;
@override
List<Object> get props => [selectedElement];
}
class ChangeMealTime extends DiaryEvent {
ChangeMealTime(this.time);
final TimeOfDay time;
@override
List<Object> get props => [time];
}
class AddComment extends DiaryEvent {
AddComment(this.diaryId, this.comment);
final String comment;
final int diaryId;
@override
List<Object> get props => [comment, diaryId];
}
class WakeupEvent extends DiaryEvent {
WakeupEvent(this.selectedTime);
final TimeOfDay selectedTime;
@override
List<Object> get props => [selectedTime];
}
class RetireEvent extends DiaryEvent {
RetireEvent(this.selectedTime);
final TimeOfDay selectedTime;
@override
List<Object> get props => [selectedTime];
}
class ToggleMoodExpandedEvent extends DiaryEvent {
@override
List<Object?> get props => [];
}
class ToggleWakeupExpandedEvent extends DiaryEvent {
@override
List<Object?> get props => [];
}
class AddPortionEvent extends DiaryEvent {
AddPortionEvent();
@override
List<Object> get props => [];
}
class RemovePortionEvent extends DiaryEvent {
RemovePortionEvent(this.portionId);
final int portionId;
@override
List<Object> get props => [portionId];
}
class UploadImages extends DiaryEvent {
UploadImages(this.image);
final XFile image;
@override
List<Object> get props => [image];
}
class RemoveImageEvent extends DiaryEvent {
RemoveImageEvent(this.photoID, this.path);
final String photoID;
final String path;
@override
List<Object> get props => [photoID, path];
}
class ProductInput extends DiaryEvent {
ProductInput(this.input);
final String input;
@override
List<Object> get props => [input];
}
class ChangeMeal extends DiaryEvent {
ChangeMeal({required this.value, required this.meals});
Meal value;
List<Meal>? meals;
@override
List<Object?> get props => [value, meals];
}
class ClearInput extends DiaryEvent {
ClearInput({required this.value});
TextEditingController value;
@override
List<Object?> get props => [value];
}
class ChangePortion extends DiaryEvent {
ChangePortion({required this.value});
SelectionPopupModel value;
@override
List<Object?> get props => [value];
}
class FinishDay extends DiaryEvent {
FinishDay();
@override
List<Object?> get props => [];
}
class RefreshEvent extends DiaryEvent {
RefreshEvent();
@override
List<Object?> get props => [];
}
class SendModeration extends DiaryEvent {
SendModeration({
required this.name,
required this.energyOn100gr,
required this.proteinsOn100gr,
required this.fatsOn100gr,
required this.carbohydratesOn100gr,
});
String name;
String energyOn100gr;
String proteinsOn100gr;
String fatsOn100gr;
String carbohydratesOn100gr;
@override
List<Object?> get props =>
[name, energyOn100gr, proteinsOn100gr, fatsOn100gr, carbohydratesOn100gr];
}

View File

@@ -1,6 +1,9 @@
import 'package:eatvio_app/features/diary/models/diary_model.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../../../data/models/selectionPopupModel/selection_popup_model.dart';
part 'diary_state.freezed.dart';
@freezed
@@ -11,6 +14,31 @@ abstract class DiaryState with _$DiaryState {
const factory DiaryState.loaded({
required DiaryModel diary,
String? diaryDate,
List<dynamic>? autocompleteList,
required List<int> recommendedChartValues,
required List<int> consumedChartValues,
List<Portion>? portions,
List<Photo>? photos,
Meal? currentMeal,
SelectionPopupModel? portionController,
TimeOfDay? wakeupController,
TimeOfDay? retireController,
TimeOfDay? mealTimeController,
required TextEditingController? commentController,
required TextEditingController? productNameController,
required TextEditingController? productQuantityController,
required TextEditingController? caloriesController,
required TextEditingController? fatsController,
required TextEditingController? proteinsController,
required TextEditingController? carbsController,
int? selectedMood,
int? selectedSatiety,
required bool wakeupExpanded,
required bool moderationExpanded,
required bool moodExpanded,
required bool isLoading,
required bool isAdded,
}) = DiaryStateLoaded;
const factory DiaryState.error({

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,11 @@
// ignore_for_file: unused_local_variable
/*import 'bloc/diary_edit_bloc.dart';
import 'models/diary_edit_model.dart';
import 'package:eatvio_app/features/diary/models/diary_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_bloc.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/locator_service.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
@@ -16,42 +20,45 @@ import 'package:eatvio_app/widgets/custom_scale_element.dart';
import 'package:carousel_slider/carousel_slider.dart';
// ignore: must_be_immutable
class DiaryEditScreen extends StatelessWidget {
DiaryEditScreen({Key? key}) : super(key: key);
static Widget builder(BuildContext context) {
return BlocProvider<DiaryEditBloc>(
create: (context) =>
DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))
..add(DiaryEditInitialEvent()),
child: DiaryEditScreen(),
);
}
class DiaryScreen extends StatelessWidget {
DiaryScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return BlocBuilder<DiaryEditBloc, DiaryEditState>(
builder: (context, state) {
if (state is DiaryEditInitialState) {
// Это начальное состояние, данные ещё не загружены.
return Container(
color: Colors.white,
);
} else if (state is DiaryEditLoadedState) {
// Данные загружены, можно строить экран.
return _buildDiaryEditScreen(context, state);
} else if (state is DiaryEditErrorState) {
// Произошла ошибка при загрузке данных.
return Center(child: Text(state.errorMessage));
} else {
// Другие состояния, если они есть.
return Container(); // Пустой контейнер для необработанных состояний.
}
},
return BlocProvider<DiaryBloc>(
create: (context) => sl<DiaryBloc>()..add(LoadDiary()),
child: BlocBuilder<DiaryBloc, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
return _buildDiary(context, state);
} else if (state is DiaryStateLoading) {
return Scaffold(
body: Center(
child: CupertinoActivityIndicator(),
),
);
} else if (state is DiaryStateError) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text(state.errorMessage),
),
),
);
} else
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text("Неопределенная ошибка"),
),
),
);
},
),
);
}
Widget _buildDiaryEditScreen(
BuildContext context, DiaryEditLoadedState state) {
Widget _buildDiary(BuildContext context, DiaryStateLoaded state) {
return RefreshIndicator(
onRefresh: refreshPage(context),
child: Stack(
@@ -121,13 +128,13 @@ class DiaryEditScreen extends StatelessWidget {
refreshPage(BuildContext context) {
return () async {
context.read<DiaryEditBloc>().add(RefreshEvent());
context.read<DiaryBloc>().add(RefreshEvent());
};
}
/// Section Widget
_buildHeaderRow(BuildContext context, DiaryEditLoadedState state) {
final day = state.diaryId;
_buildHeaderRow(BuildContext context, DiaryStateLoaded state) {
final day = state.diary.id;
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.h),
padding: EdgeInsets.symmetric(
@@ -162,7 +169,7 @@ class DiaryEditScreen extends StatelessWidget {
),
SizedBox(height: 2.v),
Text(
state.diaryDate.toString(),
state.diary.date.toString(),
style: CustomTextStyles.bodyMediumBluegray800,
),
],
@@ -174,7 +181,7 @@ class DiaryEditScreen extends StatelessWidget {
}
/// Section Widget
_buildAddProduct(BuildContext context, DiaryEditLoadedState state) {
_buildAddProduct(BuildContext context, DiaryState state) {
return CustomOutlinedButton(
text: "Добавить продукт",
margin: EdgeInsets.symmetric(horizontal: 10.h),
@@ -185,12 +192,12 @@ class DiaryEditScreen extends StatelessWidget {
addProduct(BuildContext context) {
return () {
context.read<DiaryEditBloc>().add(AddPortionEvent());
context.read<DiaryBloc>().add(AddPortionEvent());
};
}
/// Section Widget
_buildAddPhoto(BuildContext context, DiaryEditLoadedState state) {
_buildAddPhoto(BuildContext context, DiaryStateLoaded state) {
bool isLoading = state.isLoading;
return GestureDetector(
onTap: addImage(context, isLoading),
@@ -258,14 +265,14 @@ class DiaryEditScreen extends StatelessWidget {
List<XFile>? images = await picker.pickMultiImage();
if (images.isNotEmpty) {
for (var image in images) {
context.read<DiaryEditBloc>().add(UploadImages(image));
context.read<DiaryBloc>().add(UploadImages(image));
}
}
};
}
}
_buildMeal(BuildContext context, DiaryEditLoadedState state) {
_buildMeal(BuildContext context, DiaryStateLoaded state) {
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
List<String> _options =
state.autocompleteList!.map((dynamic item) => item as String).toList();
@@ -329,17 +336,22 @@ class DiaryEditScreen extends StatelessWidget {
SizedBox(height: 20.v),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.h),
child: BlocSelector<DiaryEditBloc, DiaryEditState, DiaryEditModel?>(
selector: (state) => state.diaryEditModelObj,
builder: (context, diaryEditModelObj) {
child: BlocSelector<DiaryBloc, DiaryState, DiaryModel?>(
selector: (state) {
if (state is DiaryStateLoaded)
return state.diary;
else
return null;
},
builder: (context, diary) {
return CustomDropDown(
hintText: "Прием пищи",
items: diaryEditModelObj?.mealList,
items: mealsToDropdown(diary!.meals),
contentPadding: EdgeInsets.symmetric(
horizontal: 10.h,
vertical: 9.v,
),
defaultValue: diaryEditModelObj?.mealList[0],
defaultValue: mealsToDropdown(diary.meals)[0],
onChanged: changeMeal(context, state),
);
},
@@ -424,13 +436,20 @@ class DiaryEditScreen extends StatelessWidget {
),
Padding(
padding: EdgeInsets.only(left: 30.h),
child: BlocSelector<DiaryEditBloc, DiaryEditState,
DiaryEditModel?>(
selector: (state) => state.diaryEditModelObj,
builder: (context, diaryEditModelObj) {
child: BlocSelector<DiaryBloc, DiaryState, DiaryModel?>(
selector: (state) {
if (state is DiaryStateLoaded) {
return state.diary;
} else
return null;
},
builder: (context, diary) {
return CustomDropDown(
items: diaryEditModelObj?.portionList,
defaultValue: diaryEditModelObj?.portionList?[0],
items: [
SelectionPopupModel(title: "gr"),
SelectionPopupModel(title: "pcs")
],
defaultValue: SelectionPopupModel(title: "gr"),
onChanged: changePortion(context, state),
);
},
@@ -463,9 +482,9 @@ class DiaryEditScreen extends StatelessWidget {
style: CustomTextStyles.bodyMediumBlack900,
),
SizedBox(height: 15.v),
BlocBuilder<DiaryEditBloc, DiaryEditState>(
BlocBuilder<DiaryBloc, DiaryState>(
builder: (context, state) {
if (state is DiaryEditLoadedState) {
if (state is DiaryStateLoaded) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -505,7 +524,7 @@ class DiaryEditScreen extends StatelessWidget {
],
);
} else {
return Container(); // Возвращаем пустой контейнер, если состояние не является DiaryEditLoadedState
return Container(); // Возвращаем пустой контейнер, если состояние не является DiaryState
}
},
),
@@ -514,7 +533,7 @@ class DiaryEditScreen extends StatelessWidget {
);
}
openCaloriesDialog(BuildContext context, DiaryEditLoadedState state) {
openCaloriesDialog(BuildContext context, DiaryStateLoaded state) {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
@@ -587,7 +606,7 @@ class DiaryEditScreen extends StatelessWidget {
) {
return () {
// Call function to send moderation with the entered values
context.read<DiaryEditBloc>().add(
context.read<DiaryBloc>().add(
SendModeration(
name: name,
energyOn100gr: energyOn100gr,
@@ -609,12 +628,12 @@ class DiaryEditScreen extends StatelessWidget {
changeSatiety(BuildContext context, int satiety) {
return () {
context.read<DiaryEditBloc>().add(ChangeSatiety(satiety));
context.read<DiaryBloc>().add(ChangeSatiety(satiety));
};
}
selectProduct(AutocompleteOnSelected<String> onSelected, String option,
DiaryEditLoadedState state) {
DiaryStateLoaded state) {
state.productNameController?.text = option;
return () {
onSelected(option);
@@ -627,7 +646,7 @@ class DiaryEditScreen extends StatelessWidget {
?.map((dynamic item) => item as String)
.toList());*/
if (value.length > 2) {
context.read<DiaryEditBloc>().add(ProductInput(value));
context.read<DiaryBloc>().add(ProductInput(value));
}
};
}
@@ -640,27 +659,27 @@ class DiaryEditScreen extends StatelessWidget {
};
}
changeMeal(BuildContext context, DiaryEditLoadedState state) {
changeMeal(BuildContext context, DiaryStateLoaded state) {
return (value) {
context
.read<DiaryEditBloc>()
.add(ChangeMeal(meals: state.meals, value: value));
.read<DiaryBloc>()
.add(ChangeMeal(meals: state.diary.meals, value: value));
};
}
changePortion(BuildContext context, DiaryEditLoadedState state) {
changePortion(BuildContext context, DiaryState state) {
return (value) {
context.read<DiaryEditBloc>().add(ChangePortion(value: value));
context.read<DiaryBloc>().add(ChangePortion(value: value));
};
}
_buildCarousel(BuildContext context, DiaryEditLoadedState state) {
if (state.photos!.isEmpty) {
_buildCarousel(BuildContext context, DiaryStateLoaded state) {
if (state.currentMeal!.photos!.isEmpty) {
return Text("Фото нет");
} else {
return CarouselSlider(
options: CarouselOptions(height: 340.0),
items: state.photos!.map<Widget>((photo) {
items: state.currentMeal!.photos!.map<Widget>((photo) {
return _buildPhoto(
context, photo); // Assuming photos are stored as strings
}).toList(),
@@ -668,7 +687,7 @@ class DiaryEditScreen extends StatelessWidget {
}
}
_buildProduct(BuildContext context, Map<String, dynamic> portion) {
_buildProduct(BuildContext context, Portion portion) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
@@ -688,9 +707,9 @@ class DiaryEditScreen extends StatelessWidget {
top: 5.v,
),
child: Text(
portion['product_name'].length > 8
? portion['product_name'].substring(0, 8)
: portion['product_name'],
portion.productName.length > 8
? portion.productName.substring(0, 8)
: portion.productName,
style: CustomTextStyles.titleMedium16,
),
),
@@ -701,9 +720,7 @@ class DiaryEditScreen extends StatelessWidget {
bottom: 3.v,
),
child: Text(
portion['quantity'].toString() +
' ' +
portion['portion'].toString().tr,
portion.quantity.toString() + ' ' + portion.portion.toString().tr,
),
),
Spacer(),
@@ -722,7 +739,7 @@ class DiaryEditScreen extends StatelessWidget {
);
}
_buildPhoto(BuildContext context, Map<String, dynamic> photo) {
_buildPhoto(BuildContext context, Photo photo) {
return Stack(
children: [
Container(
@@ -731,7 +748,7 @@ class DiaryEditScreen extends StatelessWidget {
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Image.network(
dotenv.env['STORAGE_URL']! + photo['path'],
dotenv.env['STORAGE_URL']! + photo.path,
),
),
Positioned(
@@ -756,21 +773,21 @@ class DiaryEditScreen extends StatelessWidget {
);
}
removePortion(BuildContext context, Map<String, dynamic> portion) {
removePortion(BuildContext context, Portion portion) {
return () {
context.read<DiaryEditBloc>().add(RemovePortionEvent(portion['id']));
context.read<DiaryBloc>().add(RemovePortionEvent(portion.id));
};
}
removePhoto(BuildContext context, Map<String, dynamic> photo) {
removePhoto(BuildContext context, Photo photo) {
return () {
context
.read<DiaryEditBloc>()
.add(RemoveImageEvent(photo['id'].toString(), photo['path']));
.read<DiaryBloc>()
.add(RemoveImageEvent(photo.id.toString(), photo.path));
};
}
_buildNutritionChart(BuildContext context, DiaryEditLoadedState state) {
_buildNutritionChart(BuildContext context, DiaryStateLoaded state) {
final List<int> recommendedValues = state
.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы
final List<int> consumedValues = state.consumedChartValues; //
@@ -992,7 +1009,7 @@ class DiaryEditScreen extends StatelessWidget {
}
/// Section Widget
_buildComment(BuildContext context, DiaryEditLoadedState state) {
_buildComment(BuildContext context, DiaryStateLoaded state) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.h),
padding: EdgeInsets.symmetric(
@@ -1036,7 +1053,7 @@ class DiaryEditScreen extends StatelessWidget {
);
}
_buildWakupRetire(BuildContext context, DiaryEditLoadedState state) {
_buildWakupRetire(BuildContext context, DiaryStateLoaded state) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.h),
padding: EdgeInsets.symmetric(
@@ -1112,7 +1129,7 @@ class DiaryEditScreen extends StatelessWidget {
toggleWakeup(BuildContext context) {
return () {
BlocProvider.of<DiaryEditBloc>(context).add(ToggleWakeupExpandedEvent());
BlocProvider.of<DiaryBloc>(context).add(ToggleWakeupExpandedEvent());
};
}
@@ -1146,7 +1163,7 @@ class DiaryEditScreen extends StatelessWidget {
BuildContext dialogContext, BuildContext context) {
return () {
closeDialog(dialogContext);
context.read<DiaryEditBloc>().add(FinishDay());
context.read<DiaryBloc>().add(FinishDay());
};
}
@@ -1156,7 +1173,7 @@ class DiaryEditScreen extends StatelessWidget {
};
}
_buildMood(BuildContext context, DiaryEditLoadedState state) {
_buildMood(BuildContext context, DiaryStateLoaded state) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.h),
padding: EdgeInsets.symmetric(
@@ -1238,49 +1255,46 @@ class DiaryEditScreen extends StatelessWidget {
changeMood(BuildContext context, int satiety) {
return () {
context.read<DiaryEditBloc>().add(ChangeMood(satiety));
context.read<DiaryBloc>().add(ChangeMood(satiety));
};
}
toggleMood(BuildContext context) {
return () {
BlocProvider.of<DiaryEditBloc>(context).add(ToggleMoodExpandedEvent());
BlocProvider.of<DiaryBloc>(context).add(ToggleMoodExpandedEvent());
};
}
Future<void> _selectWakeupTime(
BuildContext context, DiaryEditLoadedState state) async {
BuildContext context, DiaryStateLoaded state) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: state.wakeupController ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != state.wakeupController) {
state.wakeupController = pickedTime;
context.read<DiaryEditBloc>().add(WakeupEvent(pickedTime));
context.read<DiaryBloc>().add(WakeupEvent(pickedTime));
}
}
Future<void> _selectRetireTime(
BuildContext context, DiaryEditLoadedState state) async {
BuildContext context, DiaryStateLoaded state) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: state.retireController ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != state.retireController) {
state.retireController = pickedTime;
context.read<DiaryEditBloc>().add(RetireEvent(pickedTime));
context.read<DiaryBloc>().add(RetireEvent(pickedTime));
}
}
Future<void> _selectMealTime(
BuildContext context, DiaryEditLoadedState state) async {
BuildContext context, DiaryStateLoaded state) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: state.mealTimeController ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != state.mealTimeController) {
state.mealTimeController = pickedTime;
context.read<DiaryEditBloc>().add(ChangeMealTime(pickedTime));
context.read<DiaryBloc>().add(ChangeMealTime(pickedTime));
}
}
}
@@ -1296,4 +1310,13 @@ bool checkConsumedValues(
// Если ни одно потребленное значение не превышает рекомендованное с соответствующим индексом
return false; // Возвращаем true
}
*/
List<SelectionPopupModel> mealsToDropdown(List<Meal> meals) {
return meals
.map((meal) => SelectionPopupModel(
id: meal.id,
title: "Прием пищи ${meal.id}",
isSelected: false,
))
.toList();
}