diff --git a/lib/features/diary/models/photo.freezed.dart b/lib/features/diary/models/photo.freezed.dart index 81cbdd37..2d9c70bd 100644 --- a/lib/features/diary/models/photo.freezed.dart +++ b/lib/features/diary/models/photo.freezed.dart @@ -21,33 +21,33 @@ Photo _$PhotoFromJson(Map json) { /// @nodoc mixin _$Photo { int get id => throw _privateConstructorUsedError; - @JsonKey(name: 'original_name') String get originalName => throw _privateConstructorUsedError; - String get path => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) $PhotoCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc abstract class $PhotoCopyWith<$Res> { - factory $PhotoCopyWith(Photo value, $Res Function(Photo) then) = _$PhotoCopyWithImpl<$Res, Photo>; - + factory $PhotoCopyWith(Photo value, $Res Function(Photo) then) = + _$PhotoCopyWithImpl<$Res, Photo>; @useResult - $Res call({int id, @JsonKey(name: 'original_name') String originalName, String path}); + $Res call( + {int id, + @JsonKey(name: 'original_name') String originalName, + String path}); } /// @nodoc -class _$PhotoCopyWithImpl<$Res, $Val extends Photo> implements $PhotoCopyWith<$Res> { +class _$PhotoCopyWithImpl<$Res, $Val extends Photo> + implements $PhotoCopyWith<$Res> { _$PhotoCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; - // ignore: unused_field final $Res Function($Val) _then; @@ -77,16 +77,24 @@ class _$PhotoCopyWithImpl<$Res, $Val extends Photo> implements $PhotoCopyWith<$R /// @nodoc abstract class _$$PhotoImplCopyWith<$Res> implements $PhotoCopyWith<$Res> { - factory _$$PhotoImplCopyWith(_$PhotoImpl value, $Res Function(_$PhotoImpl) then) = __$$PhotoImplCopyWithImpl<$Res>; - + factory _$$PhotoImplCopyWith( + _$PhotoImpl value, $Res Function(_$PhotoImpl) then) = + __$$PhotoImplCopyWithImpl<$Res>; @override @useResult - $Res call({int id, @JsonKey(name: 'original_name') String originalName, String path}); + $Res call( + {int id, + @JsonKey(name: 'original_name') String originalName, + String path}); } /// @nodoc -class __$$PhotoImplCopyWithImpl<$Res> extends _$PhotoCopyWithImpl<$Res, _$PhotoImpl> implements _$$PhotoImplCopyWith<$Res> { - __$$PhotoImplCopyWithImpl(_$PhotoImpl _value, $Res Function(_$PhotoImpl) _then) : super(_value, _then); +class __$$PhotoImplCopyWithImpl<$Res> + extends _$PhotoCopyWithImpl<$Res, _$PhotoImpl> + implements _$$PhotoImplCopyWith<$Res> { + __$$PhotoImplCopyWithImpl( + _$PhotoImpl _value, $Res Function(_$PhotoImpl) _then) + : super(_value, _then); @pragma('vm:prefer-inline') @override @@ -115,9 +123,13 @@ class __$$PhotoImplCopyWithImpl<$Res> extends _$PhotoCopyWithImpl<$Res, _$PhotoI /// @nodoc @JsonSerializable() class _$PhotoImpl implements _Photo { - const _$PhotoImpl({required this.id, @JsonKey(name: 'original_name') required this.originalName, required this.path}); + const _$PhotoImpl( + {required this.id, + @JsonKey(name: 'original_name') required this.originalName, + required this.path}); - factory _$PhotoImpl.fromJson(Map json) => _$$PhotoImplFromJson(json); + factory _$PhotoImpl.fromJson(Map json) => + _$$PhotoImplFromJson(json); @override final int id; @@ -138,7 +150,8 @@ class _$PhotoImpl implements _Photo { (other.runtimeType == runtimeType && other is _$PhotoImpl && (identical(other.id, id) || other.id == id) && - (identical(other.originalName, originalName) || other.originalName == originalName) && + (identical(other.originalName, originalName) || + other.originalName == originalName) && (identical(other.path, path) || other.path == path)); } @@ -149,7 +162,8 @@ class _$PhotoImpl implements _Photo { @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$PhotoImplCopyWith<_$PhotoImpl> get copyWith => __$$PhotoImplCopyWithImpl<_$PhotoImpl>(this, _$identity); + _$$PhotoImplCopyWith<_$PhotoImpl> get copyWith => + __$$PhotoImplCopyWithImpl<_$PhotoImpl>(this, _$identity); @override Map toJson() { @@ -160,21 +174,22 @@ class _$PhotoImpl implements _Photo { } abstract class _Photo implements Photo { - const factory _Photo({required final int id, @JsonKey(name: 'original_name') required final String originalName, required final String path}) = _$PhotoImpl; + const factory _Photo( + {required final int id, + @JsonKey(name: 'original_name') required final String originalName, + required final String path}) = _$PhotoImpl; factory _Photo.fromJson(Map json) = _$PhotoImpl.fromJson; @override int get id; - @override @JsonKey(name: 'original_name') String get originalName; - @override String get path; - @override @JsonKey(ignore: true) - _$$PhotoImplCopyWith<_$PhotoImpl> get copyWith => throw _privateConstructorUsedError; + _$$PhotoImplCopyWith<_$PhotoImpl> get copyWith => + throw _privateConstructorUsedError; } diff --git a/lib/features/diary/models/photo.g.dart b/lib/features/diary/models/photo.g.dart index 1efd15ed..cacf65ac 100644 --- a/lib/features/diary/models/photo.g.dart +++ b/lib/features/diary/models/photo.g.dart @@ -12,7 +12,8 @@ _$PhotoImpl _$$PhotoImplFromJson(Map json) => _$PhotoImpl( path: json['path'] as String, ); -Map _$$PhotoImplToJson(_$PhotoImpl instance) => { +Map _$$PhotoImplToJson(_$PhotoImpl instance) => + { 'id': instance.id, 'original_name': instance.originalName, 'path': instance.path, diff --git a/lib/features/recipes/models/image.dart b/lib/features/recipes/models/image.dart index f10e8db7..4be7357b 100644 --- a/lib/features/recipes/models/image.dart +++ b/lib/features/recipes/models/image.dart @@ -7,7 +7,7 @@ part 'image.g.dart'; @freezed class Image with _$Image { const factory Image({ - required String path, + String? path, }) = _Image; factory Image.fromJson(Map json) => _$ImageFromJson(json); diff --git a/lib/features/recipes/models/image.freezed.dart b/lib/features/recipes/models/image.freezed.dart index 3d4cc2ed..1b73b841 100644 --- a/lib/features/recipes/models/image.freezed.dart +++ b/lib/features/recipes/models/image.freezed.dart @@ -20,7 +20,7 @@ Image _$ImageFromJson(Map json) { /// @nodoc mixin _$Image { - String get path => throw _privateConstructorUsedError; + String? get path => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -32,7 +32,7 @@ abstract class $ImageCopyWith<$Res> { factory $ImageCopyWith(Image value, $Res Function(Image) then) = _$ImageCopyWithImpl<$Res, Image>; @useResult - $Res call({String path}); + $Res call({String? path}); } /// @nodoc @@ -48,13 +48,13 @@ class _$ImageCopyWithImpl<$Res, $Val extends Image> @pragma('vm:prefer-inline') @override $Res call({ - Object? path = null, + Object? path = freezed, }) { return _then(_value.copyWith( - path: null == path + path: freezed == path ? _value.path : path // ignore: cast_nullable_to_non_nullable - as String, + as String?, ) as $Val); } } @@ -66,7 +66,7 @@ abstract class _$$ImageImplCopyWith<$Res> implements $ImageCopyWith<$Res> { __$$ImageImplCopyWithImpl<$Res>; @override @useResult - $Res call({String path}); + $Res call({String? path}); } /// @nodoc @@ -80,13 +80,13 @@ class __$$ImageImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? path = null, + Object? path = freezed, }) { return _then(_$ImageImpl( - path: null == path + path: freezed == path ? _value.path : path // ignore: cast_nullable_to_non_nullable - as String, + as String?, )); } } @@ -94,13 +94,13 @@ class __$$ImageImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() class _$ImageImpl implements _Image { - const _$ImageImpl({required this.path}); + const _$ImageImpl({this.path}); factory _$ImageImpl.fromJson(Map json) => _$$ImageImplFromJson(json); @override - final String path; + final String? path; @override String toString() { @@ -134,12 +134,12 @@ class _$ImageImpl implements _Image { } abstract class _Image implements Image { - const factory _Image({required final String path}) = _$ImageImpl; + const factory _Image({final String? path}) = _$ImageImpl; factory _Image.fromJson(Map json) = _$ImageImpl.fromJson; @override - String get path; + String? get path; @override @JsonKey(ignore: true) _$$ImageImplCopyWith<_$ImageImpl> get copyWith => diff --git a/lib/features/recipes/models/image.g.dart b/lib/features/recipes/models/image.g.dart index 3e44a28c..d311f9c9 100644 --- a/lib/features/recipes/models/image.g.dart +++ b/lib/features/recipes/models/image.g.dart @@ -7,9 +7,10 @@ part of 'image.dart'; // ************************************************************************** _$ImageImpl _$$ImageImplFromJson(Map json) => _$ImageImpl( - path: json['path'] as String, + path: json['path'] as String?, ); -Map _$$ImageImplToJson(_$ImageImpl instance) => { +Map _$$ImageImplToJson(_$ImageImpl instance) => + { 'path': instance.path, }; diff --git a/lib/features/recipes/models/recipe_model.dart b/lib/features/recipes/models/recipe_model.dart index 2ab3ea81..7f52cabf 100644 --- a/lib/features/recipes/models/recipe_model.dart +++ b/lib/features/recipes/models/recipe_model.dart @@ -12,6 +12,7 @@ class RecipeModel with _$RecipeModel { int? id, required List ingredients, required List steps, + required String title, required String state, required String description, required String type, @@ -20,6 +21,5 @@ class RecipeModel with _$RecipeModel { num? cooking_time, }) = _RecipeModel; - factory RecipeModel.fromJson(Map json) => - _$RecipeModelFromJson(json); + factory RecipeModel.fromJson(Map json) => _$RecipeModelFromJson(json); } diff --git a/lib/features/recipes/models/recipe_model.freezed.dart b/lib/features/recipes/models/recipe_model.freezed.dart index 984d3bd7..c1cb2cac 100644 --- a/lib/features/recipes/models/recipe_model.freezed.dart +++ b/lib/features/recipes/models/recipe_model.freezed.dart @@ -23,6 +23,7 @@ mixin _$RecipeModel { int? get id => throw _privateConstructorUsedError; List get ingredients => throw _privateConstructorUsedError; List get steps => throw _privateConstructorUsedError; + String get title => throw _privateConstructorUsedError; String get state => throw _privateConstructorUsedError; String get description => throw _privateConstructorUsedError; String get type => throw _privateConstructorUsedError; @@ -46,6 +47,7 @@ abstract class $RecipeModelCopyWith<$Res> { {int? id, List ingredients, List steps, + String title, String state, String description, String type, @@ -72,6 +74,7 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel> Object? id = freezed, Object? ingredients = null, Object? steps = null, + Object? title = null, Object? state = null, Object? description = null, Object? type = null, @@ -92,6 +95,10 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel> ? _value.steps : steps // ignore: cast_nullable_to_non_nullable as List, + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, state: null == state ? _value.state : state // ignore: cast_nullable_to_non_nullable @@ -140,6 +147,7 @@ abstract class _$$RecipeModelImplCopyWith<$Res> {int? id, List ingredients, List steps, + String title, String state, String description, String type, @@ -165,6 +173,7 @@ class __$$RecipeModelImplCopyWithImpl<$Res> Object? id = freezed, Object? ingredients = null, Object? steps = null, + Object? title = null, Object? state = null, Object? description = null, Object? type = null, @@ -185,6 +194,10 @@ class __$$RecipeModelImplCopyWithImpl<$Res> ? _value._steps : steps // ignore: cast_nullable_to_non_nullable as List, + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, state: null == state ? _value.state : state // ignore: cast_nullable_to_non_nullable @@ -220,6 +233,7 @@ class _$RecipeModelImpl implements _RecipeModel { {this.id, required final List ingredients, required final List steps, + required this.title, required this.state, required this.description, required this.type, @@ -250,6 +264,8 @@ class _$RecipeModelImpl implements _RecipeModel { return EqualUnmodifiableListView(_steps); } + @override + final String title; @override final String state; @override @@ -265,7 +281,7 @@ class _$RecipeModelImpl implements _RecipeModel { @override String toString() { - return 'RecipeModel(id: $id, ingredients: $ingredients, steps: $steps, state: $state, description: $description, type: $type, image: $image, video_url: $video_url, cooking_time: $cooking_time)'; + return 'RecipeModel(id: $id, ingredients: $ingredients, steps: $steps, title: $title, state: $state, description: $description, type: $type, image: $image, video_url: $video_url, cooking_time: $cooking_time)'; } @override @@ -277,6 +293,7 @@ class _$RecipeModelImpl implements _RecipeModel { const DeepCollectionEquality() .equals(other._ingredients, _ingredients) && const DeepCollectionEquality().equals(other._steps, _steps) && + (identical(other.title, title) || other.title == title) && (identical(other.state, state) || other.state == state) && (identical(other.description, description) || other.description == description) && @@ -295,6 +312,7 @@ class _$RecipeModelImpl implements _RecipeModel { id, const DeepCollectionEquality().hash(_ingredients), const DeepCollectionEquality().hash(_steps), + title, state, description, type, @@ -321,6 +339,7 @@ abstract class _RecipeModel implements RecipeModel { {final int? id, required final List ingredients, required final List steps, + required final String title, required final String state, required final String description, required final String type, @@ -338,6 +357,8 @@ abstract class _RecipeModel implements RecipeModel { @override List get steps; @override + String get title; + @override String get state; @override String get description; diff --git a/lib/features/recipes/models/recipe_model.g.dart b/lib/features/recipes/models/recipe_model.g.dart index 35cd7d47..d058e9c6 100644 --- a/lib/features/recipes/models/recipe_model.g.dart +++ b/lib/features/recipes/models/recipe_model.g.dart @@ -15,6 +15,7 @@ _$RecipeModelImpl _$$RecipeModelImplFromJson(Map json) => steps: (json['steps'] as List) .map((e) => Step.fromJson(e as Map)) .toList(), + title: json['title'] as String, state: json['state'] as String, description: json['description'] as String, type: json['type'] as String, @@ -28,6 +29,7 @@ Map _$$RecipeModelImplToJson(_$RecipeModelImpl instance) => 'id': instance.id, 'ingredients': instance.ingredients, 'steps': instance.steps, + 'title': instance.title, 'state': instance.state, 'description': instance.description, 'type': instance.type, diff --git a/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart b/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart index 0790f2a4..f2c9446d 100644 --- a/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart +++ b/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart @@ -1,58 +1,69 @@ import 'package:eatvio_app/core/app_export.dart'; 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/presentation/bloc/recipe_create_state.dart'; import 'package:eatvio_app/features/recipes/reporitories/recipe_repository.dart'; +import 'package:eatvio_app/features/diary/models/product.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; class RecipeCreateCubit extends Cubit { final RecipeRepository recipeRepository; - RecipeCreateCubit({required this.recipeRepository}) - : super(const RecipeCreateState.loading()); + RecipeCreateCubit({required this.recipeRepository}) : super(const RecipeCreateState.loading()); loadRecipeCreate() { emit(const RecipeCreateStateLoading()); try { + final List types = [ + SelectionPopupModel(title: 'Первые блюда', value: 'first_meals'), + SelectionPopupModel(title: 'Вторые блюда', value: 'second_meals'), + SelectionPopupModel(title: 'Cалаты', value: 'salads'), + SelectionPopupModel(title: 'Перекусы/закуски', value: 'snacks'), + SelectionPopupModel(title: 'Сладости', value: 'sweets'), + SelectionPopupModel(title: 'Напитки', value: 'drinks'), + SelectionPopupModel(title: 'Прочее', value: 'other'), + ]; + final List cookingTimes = [ + SelectionPopupModel(title: '10 минут', value: 10), + SelectionPopupModel(title: '20 минут', value: 20), + SelectionPopupModel(title: '30 минут', value: 30), + SelectionPopupModel(title: '1 час', value: 60), + SelectionPopupModel(title: '2 часа', value: 120), + SelectionPopupModel(title: '10 дней', value: 14400), + ]; + final List statuses = [ + SelectionPopupModel(title: 'Активна', value: 'active'), + SelectionPopupModel(title: 'Неактивна', value: 'inactive'), + ]; emit(RecipeCreateState.loaded( - picker: ImagePicker(), - titleController: TextEditingController(), - productNameController: TextEditingController(), - ingredientController: TextEditingController(), - stepDescriptionController: TextEditingController(), - youtubeLinkController: TextEditingController(), - stepControllers: [TextEditingController()], - stepImages: [null], - ingredients: [], - numberOfSteps: 1, - ingredientAsItems: false, - types: [ - SelectionPopupModel(title: 'Первые блюда'), - SelectionPopupModel(title: 'Вторые блюда'), - SelectionPopupModel(title: 'Cалаты'), - SelectionPopupModel(title: 'Перекусы/закуски'), - SelectionPopupModel(title: 'Сладости'), - SelectionPopupModel(title: 'Напитки'), - SelectionPopupModel(title: 'Прочее'), - ], - cookingTimes: [ - SelectionPopupModel(title: '10 минут'), - SelectionPopupModel(title: '20 минут'), - SelectionPopupModel(title: '30 минут'), - SelectionPopupModel(title: '1 час'), - SelectionPopupModel(title: '2 часа'), - SelectionPopupModel(title: '10 дней'), - ], - statuses: [ - SelectionPopupModel(title: 'Активна'), - SelectionPopupModel(title: 'Неактивна'), - ], - )); + picker: ImagePicker(), + titleController: TextEditingController(), + productNameController: TextEditingController(), + ingredientController: TextEditingController(), + stepDescriptionController: TextEditingController(), + youtubeLinkController: TextEditingController(), + stepControllers: [TextEditingController()], + stepImages: [null], + autocompleteList: [], + ingredients: [], + numberOfSteps: 1, + ingredientAsItems: false, + types: types, + cookingTimes: cookingTimes, + statuses: statuses, + selectedType: types[0], + selectedCookingTime: cookingTimes[0], + selectedStatus: statuses[0])); } catch (e) { emit(RecipeCreateState.error(errorMessage: e.toString())); } } + publishRecipe(RecipeModel recipe) async { + await recipeRepository.publishRecipe(recipe); + } + uploadRecipeImage(XFile? pickedFile) { try { if (state is RecipeCreateStateLoaded) { @@ -106,18 +117,14 @@ class RecipeCreateCubit extends Cubit { if (state is RecipeCreateStateLoaded) { var currentState = state as RecipeCreateStateLoaded; - bool ingredientExists = currentState.ingredients.any( - (existingIngredient) => - existingIngredient.product_name == ingredient.product_name); + bool ingredientExists = currentState.ingredients.any((existingIngredient) => existingIngredient.product_name == ingredient.product_name); if (ingredientExists) { - emit(RecipeCreateState.error( - errorMessage: 'Ингредиент уже существует.')); + emit(RecipeCreateState.error(errorMessage: 'Ингредиент уже существует.')); return; } - List newIngredients = - List.from(currentState.ingredients); + List newIngredients = List.from(currentState.ingredients); newIngredients.add(ingredient); var updatedState = currentState.copyWith(ingredients: newIngredients); @@ -134,13 +141,9 @@ class RecipeCreateCubit extends Cubit { var currentState = state as RecipeCreateStateLoaded; List newStepImages = List.from(currentState.stepImages); newStepImages.add(null); - List newStepControllers = - List.from(currentState.stepControllers); + List newStepControllers = List.from(currentState.stepControllers); newStepControllers.add(TextEditingController()); - var updatedState = currentState.copyWith( - stepImages: newStepImages, - stepControllers: newStepControllers, - numberOfSteps: currentState.numberOfSteps + 1); + var updatedState = currentState.copyWith(stepImages: newStepImages, stepControllers: newStepControllers, numberOfSteps: currentState.numberOfSteps + 1); emit(updatedState); } } catch (e) { @@ -154,17 +157,58 @@ class RecipeCreateCubit extends Cubit { var currentState = state as RecipeCreateStateLoaded; List newStepImages = List.from(currentState.stepImages); newStepImages.removeLast(); - List newStepControllers = - List.from(currentState.stepControllers); + List newStepControllers = List.from(currentState.stepControllers); newStepControllers.removeLast(); - var updatedState = currentState.copyWith( - stepImages: newStepImages, - stepControllers: newStepControllers, - numberOfSteps: currentState.numberOfSteps - 1); + var updatedState = currentState.copyWith(stepImages: newStepImages, stepControllers: newStepControllers, numberOfSteps: currentState.numberOfSteps - 1); emit(updatedState); } } catch (e) { emit(RecipeCreateState.error(errorMessage: e.toString())); } } + + inputProduct(String input) async { + if (state is RecipeCreateStateLoaded) { + var currentState = state as RecipeCreateStateLoaded; + + final Map> productNutritionalValues = await recipeRepository.inputProduct(input); + print(productNutritionalValues); + var updatedState = currentState.copyWith( + productNameController: TextEditingController(text: input), + autocompleteList: productNutritionalValues['product_nutritional_value'] ?? [], + ); + + emit(updatedState); + } + } + + changeType(SelectionPopupModel type) { + if (state is RecipeCreateStateLoaded) { + var currentState = state as RecipeCreateStateLoaded; + RecipeCreateStateLoaded updatedState = currentState.copyWith( + selectedType: type, + ); + emit(updatedState); + } + } + + changeStatus(SelectionPopupModel status) { + if (state is RecipeCreateStateLoaded) { + var currentState = state as RecipeCreateStateLoaded; + RecipeCreateStateLoaded updatedState = currentState.copyWith( + selectedStatus: status, + ); + emit(updatedState); + } + } + + changeCookingTime(SelectionPopupModel cookingTime) { + if (state is RecipeCreateStateLoaded) { + var currentState = state as RecipeCreateStateLoaded; + RecipeCreateStateLoaded updatedState = currentState.copyWith( + selectedCookingTime: cookingTime, + ); + emit(updatedState); + } + } } diff --git a/lib/features/recipes/presentation/bloc/recipe_create_state.dart b/lib/features/recipes/presentation/bloc/recipe_create_state.dart index 796e27e7..8bb6051a 100644 --- a/lib/features/recipes/presentation/bloc/recipe_create_state.dart +++ b/lib/features/recipes/presentation/bloc/recipe_create_state.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; import 'package:eatvio_app/features/recipes/models/ingredient.dart'; +import 'package:eatvio_app/features/diary/models/product.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:image_picker/image_picker.dart'; @@ -26,11 +27,15 @@ abstract class RecipeCreateState with _$RecipeCreateState { XFile? recipeImage, required List stepImages, required List ingredients, + required List autocompleteList, required int numberOfSteps, required bool ingredientAsItems, required List types, required List cookingTimes, required List statuses, + required SelectionPopupModel selectedType, + required SelectionPopupModel selectedCookingTime, + required SelectionPopupModel selectedStatus, }) = RecipeCreateStateLoaded; const factory RecipeCreateState.error({ diff --git a/lib/features/recipes/presentation/bloc/recipe_create_state.freezed.dart b/lib/features/recipes/presentation/bloc/recipe_create_state.freezed.dart index 783252e9..9acea107 100644 --- a/lib/features/recipes/presentation/bloc/recipe_create_state.freezed.dart +++ b/lib/features/recipes/presentation/bloc/recipe_create_state.freezed.dart @@ -32,11 +32,15 @@ mixin _$RecipeCreateState { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses) + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus) loaded, required TResult Function(String errorMessage) error, }) => @@ -57,11 +61,15 @@ mixin _$RecipeCreateState { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult? Function(String errorMessage)? error, }) => @@ -82,11 +90,15 @@ mixin _$RecipeCreateState { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult Function(String errorMessage)? error, required TResult orElse(), @@ -189,11 +201,15 @@ class _$RecipeCreateStateImpl implements _RecipeCreateState { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses) + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus) loaded, required TResult Function(String errorMessage) error, }) { @@ -217,11 +233,15 @@ class _$RecipeCreateStateImpl implements _RecipeCreateState { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult? Function(String errorMessage)? error, }) { @@ -245,11 +265,15 @@ class _$RecipeCreateStateImpl implements _RecipeCreateState { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult Function(String errorMessage)? error, required TResult orElse(), @@ -358,11 +382,15 @@ class _$RecipeCreateStateLoadingImpl implements RecipeCreateStateLoading { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses) + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus) loaded, required TResult Function(String errorMessage) error, }) { @@ -386,11 +414,15 @@ class _$RecipeCreateStateLoadingImpl implements RecipeCreateStateLoading { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult? Function(String errorMessage)? error, }) { @@ -414,11 +446,15 @@ class _$RecipeCreateStateLoadingImpl implements RecipeCreateStateLoading { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult Function(String errorMessage)? error, required TResult orElse(), @@ -490,13 +526,20 @@ abstract class _$$RecipeCreateStateLoadedImplCopyWith<$Res> { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses}); + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus}); $RecipeModelCopyWith<$Res>? get recipe; + $SelectionPopupModelCopyWith<$Res> get selectedType; + $SelectionPopupModelCopyWith<$Res> get selectedCookingTime; + $SelectionPopupModelCopyWith<$Res> get selectedStatus; } /// @nodoc @@ -522,11 +565,15 @@ class __$$RecipeCreateStateLoadedImplCopyWithImpl<$Res> Object? recipeImage = freezed, Object? stepImages = null, Object? ingredients = null, + Object? autocompleteList = null, Object? numberOfSteps = null, Object? ingredientAsItems = null, Object? types = null, Object? cookingTimes = null, Object? statuses = null, + Object? selectedType = null, + Object? selectedCookingTime = null, + Object? selectedStatus = null, }) { return _then(_$RecipeCreateStateLoadedImpl( recipe: freezed == recipe @@ -573,6 +620,10 @@ class __$$RecipeCreateStateLoadedImplCopyWithImpl<$Res> ? _value._ingredients : ingredients // ignore: cast_nullable_to_non_nullable as List, + autocompleteList: null == autocompleteList + ? _value._autocompleteList + : autocompleteList // ignore: cast_nullable_to_non_nullable + as List, numberOfSteps: null == numberOfSteps ? _value.numberOfSteps : numberOfSteps // ignore: cast_nullable_to_non_nullable @@ -593,6 +644,18 @@ class __$$RecipeCreateStateLoadedImplCopyWithImpl<$Res> ? _value._statuses : statuses // ignore: cast_nullable_to_non_nullable as List, + selectedType: null == selectedType + ? _value.selectedType + : selectedType // ignore: cast_nullable_to_non_nullable + as SelectionPopupModel, + selectedCookingTime: null == selectedCookingTime + ? _value.selectedCookingTime + : selectedCookingTime // ignore: cast_nullable_to_non_nullable + as SelectionPopupModel, + selectedStatus: null == selectedStatus + ? _value.selectedStatus + : selectedStatus // ignore: cast_nullable_to_non_nullable + as SelectionPopupModel, )); } @@ -607,6 +670,31 @@ class __$$RecipeCreateStateLoadedImplCopyWithImpl<$Res> return _then(_value.copyWith(recipe: value)); }); } + + @override + @pragma('vm:prefer-inline') + $SelectionPopupModelCopyWith<$Res> get selectedType { + return $SelectionPopupModelCopyWith<$Res>(_value.selectedType, (value) { + return _then(_value.copyWith(selectedType: value)); + }); + } + + @override + @pragma('vm:prefer-inline') + $SelectionPopupModelCopyWith<$Res> get selectedCookingTime { + return $SelectionPopupModelCopyWith<$Res>(_value.selectedCookingTime, + (value) { + return _then(_value.copyWith(selectedCookingTime: value)); + }); + } + + @override + @pragma('vm:prefer-inline') + $SelectionPopupModelCopyWith<$Res> get selectedStatus { + return $SelectionPopupModelCopyWith<$Res>(_value.selectedStatus, (value) { + return _then(_value.copyWith(selectedStatus: value)); + }); + } } /// @nodoc @@ -624,14 +712,19 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { this.recipeImage, required final List stepImages, required final List ingredients, + required final List autocompleteList, required this.numberOfSteps, required this.ingredientAsItems, required final List types, required final List cookingTimes, - required final List statuses}) + required final List statuses, + required this.selectedType, + required this.selectedCookingTime, + required this.selectedStatus}) : _stepControllers = stepControllers, _stepImages = stepImages, _ingredients = ingredients, + _autocompleteList = autocompleteList, _types = types, _cookingTimes = cookingTimes, _statuses = statuses; @@ -676,6 +769,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { return EqualUnmodifiableListView(_ingredients); } + final List _autocompleteList; + @override + List get autocompleteList { + if (_autocompleteList is EqualUnmodifiableListView) + return _autocompleteList; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_autocompleteList); + } + @override final int numberOfSteps; @override @@ -704,9 +806,16 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { return EqualUnmodifiableListView(_statuses); } + @override + final SelectionPopupModel selectedType; + @override + final SelectionPopupModel selectedCookingTime; + @override + final SelectionPopupModel selectedStatus; + @override String toString() { - return 'RecipeCreateState.loaded(recipe: $recipe, picker: $picker, titleController: $titleController, productNameController: $productNameController, ingredientController: $ingredientController, stepDescriptionController: $stepDescriptionController, youtubeLinkController: $youtubeLinkController, stepControllers: $stepControllers, recipeImage: $recipeImage, stepImages: $stepImages, ingredients: $ingredients, numberOfSteps: $numberOfSteps, ingredientAsItems: $ingredientAsItems, types: $types, cookingTimes: $cookingTimes, statuses: $statuses)'; + return 'RecipeCreateState.loaded(recipe: $recipe, picker: $picker, titleController: $titleController, productNameController: $productNameController, ingredientController: $ingredientController, stepDescriptionController: $stepDescriptionController, youtubeLinkController: $youtubeLinkController, stepControllers: $stepControllers, recipeImage: $recipeImage, stepImages: $stepImages, ingredients: $ingredients, autocompleteList: $autocompleteList, numberOfSteps: $numberOfSteps, ingredientAsItems: $ingredientAsItems, types: $types, cookingTimes: $cookingTimes, statuses: $statuses, selectedType: $selectedType, selectedCookingTime: $selectedCookingTime, selectedStatus: $selectedStatus)'; } @override @@ -735,6 +844,8 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { .equals(other._stepImages, _stepImages) && const DeepCollectionEquality() .equals(other._ingredients, _ingredients) && + const DeepCollectionEquality() + .equals(other._autocompleteList, _autocompleteList) && (identical(other.numberOfSteps, numberOfSteps) || other.numberOfSteps == numberOfSteps) && (identical(other.ingredientAsItems, ingredientAsItems) || @@ -742,28 +853,39 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { const DeepCollectionEquality().equals(other._types, _types) && const DeepCollectionEquality() .equals(other._cookingTimes, _cookingTimes) && - const DeepCollectionEquality().equals(other._statuses, _statuses)); + const DeepCollectionEquality().equals(other._statuses, _statuses) && + (identical(other.selectedType, selectedType) || + other.selectedType == selectedType) && + (identical(other.selectedCookingTime, selectedCookingTime) || + other.selectedCookingTime == selectedCookingTime) && + (identical(other.selectedStatus, selectedStatus) || + other.selectedStatus == selectedStatus)); } @override - int get hashCode => Object.hash( - runtimeType, - recipe, - picker, - titleController, - productNameController, - ingredientController, - stepDescriptionController, - youtubeLinkController, - const DeepCollectionEquality().hash(_stepControllers), - recipeImage, - const DeepCollectionEquality().hash(_stepImages), - const DeepCollectionEquality().hash(_ingredients), - numberOfSteps, - ingredientAsItems, - const DeepCollectionEquality().hash(_types), - const DeepCollectionEquality().hash(_cookingTimes), - const DeepCollectionEquality().hash(_statuses)); + int get hashCode => Object.hashAll([ + runtimeType, + recipe, + picker, + titleController, + productNameController, + ingredientController, + stepDescriptionController, + youtubeLinkController, + const DeepCollectionEquality().hash(_stepControllers), + recipeImage, + const DeepCollectionEquality().hash(_stepImages), + const DeepCollectionEquality().hash(_ingredients), + const DeepCollectionEquality().hash(_autocompleteList), + numberOfSteps, + ingredientAsItems, + const DeepCollectionEquality().hash(_types), + const DeepCollectionEquality().hash(_cookingTimes), + const DeepCollectionEquality().hash(_statuses), + selectedType, + selectedCookingTime, + selectedStatus + ]); @JsonKey(ignore: true) @override @@ -789,11 +911,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses) + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus) loaded, required TResult Function(String errorMessage) error, }) { @@ -809,11 +935,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { recipeImage, stepImages, ingredients, + autocompleteList, numberOfSteps, ingredientAsItems, types, cookingTimes, - statuses); + statuses, + selectedType, + selectedCookingTime, + selectedStatus); } @override @@ -833,11 +963,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult? Function(String errorMessage)? error, }) { @@ -853,11 +987,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { recipeImage, stepImages, ingredients, + autocompleteList, numberOfSteps, ingredientAsItems, types, cookingTimes, - statuses); + statuses, + selectedType, + selectedCookingTime, + selectedStatus); } @override @@ -877,11 +1015,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult Function(String errorMessage)? error, required TResult orElse(), @@ -899,11 +1041,15 @@ class _$RecipeCreateStateLoadedImpl implements RecipeCreateStateLoaded { recipeImage, stepImages, ingredients, + autocompleteList, numberOfSteps, ingredientAsItems, types, cookingTimes, - statuses); + statuses, + selectedType, + selectedCookingTime, + selectedStatus); } return orElse(); } @@ -959,11 +1105,15 @@ abstract class RecipeCreateStateLoaded implements RecipeCreateState { final XFile? recipeImage, required final List stepImages, required final List ingredients, + required final List autocompleteList, required final int numberOfSteps, required final bool ingredientAsItems, required final List types, required final List cookingTimes, - required final List statuses}) = + required final List statuses, + required final SelectionPopupModel selectedType, + required final SelectionPopupModel selectedCookingTime, + required final SelectionPopupModel selectedStatus}) = _$RecipeCreateStateLoadedImpl; RecipeModel? get recipe; @@ -977,11 +1127,15 @@ abstract class RecipeCreateStateLoaded implements RecipeCreateState { XFile? get recipeImage; List get stepImages; List get ingredients; + List get autocompleteList; int get numberOfSteps; bool get ingredientAsItems; List get types; List get cookingTimes; List get statuses; + SelectionPopupModel get selectedType; + SelectionPopupModel get selectedCookingTime; + SelectionPopupModel get selectedStatus; @JsonKey(ignore: true) _$$RecipeCreateStateLoadedImplCopyWith<_$RecipeCreateStateLoadedImpl> get copyWith => throw _privateConstructorUsedError; @@ -1069,11 +1223,15 @@ class _$RecipeCreateStateErrorImpl implements RecipeCreateStateError { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses) + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus) loaded, required TResult Function(String errorMessage) error, }) { @@ -1097,11 +1255,15 @@ class _$RecipeCreateStateErrorImpl implements RecipeCreateStateError { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult? Function(String errorMessage)? error, }) { @@ -1125,11 +1287,15 @@ class _$RecipeCreateStateErrorImpl implements RecipeCreateStateError { XFile? recipeImage, List stepImages, List ingredients, + List autocompleteList, int numberOfSteps, bool ingredientAsItems, List types, List cookingTimes, - List statuses)? + List statuses, + SelectionPopupModel selectedType, + SelectionPopupModel selectedCookingTime, + SelectionPopupModel selectedStatus)? loaded, TResult Function(String errorMessage)? error, required TResult orElse(), diff --git a/lib/features/recipes/presentation/pages/recipe_create_screen.dart b/lib/features/recipes/presentation/pages/recipe_create_screen.dart index 0f5973cb..19d3f689 100644 --- a/lib/features/recipes/presentation/pages/recipe_create_screen.dart +++ b/lib/features/recipes/presentation/pages/recipe_create_screen.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/features/diary/models/product.dart'; 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/image.dart' as image; @@ -49,8 +50,8 @@ class RecipeCreateScreen extends StatelessWidget { ); } - Widget _buildRecipeCreate( - BuildContext context, RecipeCreateStateLoaded state) { + Widget _buildRecipeCreate(BuildContext context, RecipeCreateStateLoaded state) { + List _options = state.autocompleteList.map((Product item) => item.name).toList(); return Scaffold( appBar: AppBar( toolbarHeight: 0, @@ -86,18 +87,16 @@ class RecipeCreateScreen extends StatelessWidget { Text('Тип блюда'), SizedBox(height: 8), CustomDropDown( - value: state.types[0], + value: state.selectedType, items: state.types, + onChanged: changeType(context), ), SizedBox(height: 16), Text('Изображение'), GestureDetector( onTap: () async { - final pickedFile = await state.picker - .pickImage(source: ImageSource.gallery); - context - .read() - .uploadRecipeImage(pickedFile); + final pickedFile = await state.picker.pickImage(source: ImageSource.gallery); + context.read().uploadRecipeImage(pickedFile); }, child: state.recipeImage == null ? Container( @@ -121,31 +120,71 @@ class RecipeCreateScreen extends StatelessWidget { ], ), ) - : Image.file(File(state.recipeImage!.path), - height: 200, fit: BoxFit.cover), + : Image.file(File(state.recipeImage!.path), height: 200, fit: BoxFit.cover), ), SizedBox(height: 16), Text('Время приготовления'), SizedBox(height: 8), CustomDropDown( items: state.cookingTimes, - value: state.cookingTimes[0], + value: state.selectedCookingTime, + onChanged: changeCookingTime(context), ), SizedBox(height: 16), Text('Статус'), SizedBox(height: 8), CustomDropDown( items: state.statuses, - value: state.statuses[0], + value: state.selectedStatus, + onChanged: changeStatus(context), ), SizedBox(height: 24), if (!state.ingredients.isEmpty) - for (int j = 0; j < state.ingredients.length; j++) - _buildIngredient(state.ingredients[j]), + for (int j = 0; j < state.ingredients.length; j++) _buildIngredient(state.ingredients[j]), SizedBox(height: 24), - CustomTextFormField( - hintText: 'Введите название продукта', - controller: state.productNameController, + RawAutocomplete( + optionsBuilder: _buildOptions(_options), + fieldViewBuilder: ( + BuildContext context, + productEditingController, + FocusNode focusNodeName, + VoidCallback onFieldSubmitted, + ) { + return CustomTextFormField( + hintText: 'Введите название продукта', + controller: TextEditingController(text: state.productNameController.text), + focusNode: focusNodeName, + onChanged: getAutocomplete(context, focusNodeName), + ); + }, + optionsViewBuilder: ( + BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options, + ) { + return Align( + alignment: Alignment.topLeft, + child: Material( + elevation: 4.0, + child: SizedBox( + height: 200.0, + child: ListView.builder( + padding: const EdgeInsets.all(8.0), + itemCount: options.length, + itemBuilder: (BuildContext context, int index) { + final String option = options.elementAt(index); + return GestureDetector( + onTap: selectProduct(onSelected, option, state), + child: ListTile( + title: Text(option), + ), + ); + }, + ), + ), + ), + ); + }, ), SizedBox(height: 24), CustomTextFormField( @@ -153,18 +192,14 @@ class RecipeCreateScreen extends StatelessWidget { fillColor: appTheme.gray50, controller: state.ingredientController, keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], + inputFormatters: [FilteringTextInputFormatter.digitsOnly], ), SizedBox(height: 8), Row(children: [ Text('гр'), Switch( value: state.ingredientAsItems, - onChanged: (bool value) => context - .read() - .switchUnits(value), + onChanged: (bool value) => context.read().switchUnits(value), activeColor: appTheme.blueA400, inactiveThumbColor: Colors.grey, inactiveTrackColor: Colors.grey.shade300, @@ -187,16 +222,10 @@ class RecipeCreateScreen extends StatelessWidget { '+', style: theme.textTheme.titleSmall, ), - onPressed: () => context - .read() - .addIngredient(new Ingredient( - product_name: - state.productNameController.text, - portion: int.parse( - state.ingredientController.text), - unit: state.ingredientAsItems - ? 'pcs' - : 'gr'))), + onPressed: () => context.read().addIngredient(new Ingredient( + product_name: state.productNameController.text, + portion: int.parse(state.ingredientController.text), + unit: state.ingredientAsItems ? 'pcs' : 'gr'))), ), SizedBox(height: 24), Text('Описание рецепта'), @@ -207,8 +236,7 @@ class RecipeCreateScreen extends StatelessWidget { controller: state.stepDescriptionController, ), SizedBox(height: 24), - for (int i = 0; i < state.numberOfSteps; i++) - _buildStep(context, i, state), + for (int i = 0; i < state.numberOfSteps; i++) _buildStep(context, i, state), SizedBox(height: 24), Container( height: 48, @@ -221,8 +249,7 @@ class RecipeCreateScreen extends StatelessWidget { borderRadius: BorderRadius.circular(8), ), backgroundColor: appTheme.blueA400, - onPressed: () => - context.read().addStep(), + onPressed: () => context.read().addStep(), label: Text( '+', style: theme.textTheme.titleSmall, @@ -241,8 +268,7 @@ class RecipeCreateScreen extends StatelessWidget { borderRadius: BorderRadius.circular(8), ), backgroundColor: Colors.grey, - onPressed: () => - context.read().removeStep(), + onPressed: () => context.read().removeStep(), label: Text( '-', style: theme.textTheme.titleSmall, @@ -269,7 +295,7 @@ class RecipeCreateScreen extends StatelessWidget { borderRadius: BorderRadius.circular(8), ), backgroundColor: appTheme.blueA400, - onPressed: () {}, + onPressed: () => context.read().publishRecipe(createRecipeFromState(state)), label: Text( 'Опубликовать', style: theme.textTheme.titleSmall, @@ -285,23 +311,15 @@ class RecipeCreateScreen extends StatelessWidget { ); } - _buildStep( - BuildContext context, int stepNumber, RecipeCreateStateLoaded state) { + _buildStep(BuildContext context, int stepNumber, RecipeCreateStateLoaded state) { return Column( children: [ - Row(children: [ - Text('Шаг ${stepNumber + 1}'), - Spacer(), - Text('Всего шагов: ${state.numberOfSteps}') - ]), + Row(children: [Text('Шаг ${stepNumber + 1}'), Spacer(), Text('Всего шагов: ${state.numberOfSteps}')]), SizedBox(height: 8), GestureDetector( onTap: () async { - final pickedFile = - await state.picker.pickImage(source: ImageSource.gallery); - context - .read() - .uploadStepImage(pickedFile, stepNumber); + final pickedFile = await state.picker.pickImage(source: ImageSource.gallery); + context.read().uploadStepImage(pickedFile, stepNumber); }, child: state.stepImages[stepNumber] == null ? Container( @@ -326,8 +344,7 @@ class RecipeCreateScreen extends StatelessWidget { ], ), ) - : Image.file(File(state.stepImages[stepNumber]!.path), - height: 150, fit: BoxFit.cover)), + : Image.file(File(state.stepImages[stepNumber]!.path), height: 150, fit: BoxFit.cover)), SizedBox(height: 16), CustomTextFormField( hintText: 'Введите описание шага', @@ -361,9 +378,7 @@ class RecipeCreateScreen extends StatelessWidget { top: 5.v, ), child: Text( - ingredient.product_name.length > 8 - ? ingredient.product_name.substring(0, 8) - : ingredient.product_name, + ingredient.product_name.length > 8 ? ingredient.product_name.substring(0, 8) : ingredient.product_name, style: CustomTextStyles.titleMedium16, ), ), @@ -374,9 +389,7 @@ class RecipeCreateScreen extends StatelessWidget { bottom: 3.v, ), child: Text( - ingredient.portion.toString() + - ' ' + - ingredient.unit.toString().tr, + ingredient.portion.toString() + ' ' + ingredient.unit.toString().tr, ), ), Spacer(), @@ -395,35 +408,75 @@ class RecipeCreateScreen extends StatelessWidget { ); } + _buildOptions(List _options) { + return (TextEditingValue textEditingValue) { + return _options.where((String option) { + return true; + }); + }; + } + + selectProduct(AutocompleteOnSelected onSelected, String option, RecipeCreateStateLoaded state) { + state.productNameController.text = option; + return () { + onSelected(option); + }; + } + + getAutocomplete(BuildContext context, FocusNode focusNode) { + return (value) { + if (value.length > 2) { + context.read().inputProduct(value); + } + }; + } + + changeType(BuildContext context) { + return (value) { + context.read().changeType(value); + }; + } + + changeStatus(BuildContext context) { + return (value) { + context.read().changeStatus(value); + }; + } + + changeCookingTime(BuildContext context) { + return (value) { + context.read().changeCookingTime(value); + }; + } + RecipeModel createRecipeFromState(RecipeCreateStateLoaded state) { - // Подготовка ингредиентов List ingredients = state.ingredients; - // Подготовка шагов (например, можно создать шаги из stepControllers) List steps = []; for (int i = 0; i < state.stepControllers.length; i++) { + image.Image picStep = image.Image(path: state.stepImages[i]?.path ?? ""); if (state.stepControllers[i].text.isNotEmpty) { - steps.add(step.Step(description: state.stepControllers[i].text)); + steps.add(step.Step( + description: state.stepControllers[i].text, + image: picStep, + )); } } - // Подготовка изображения image.Image pic = image.Image(path: state.recipeImage?.path ?? ''); - - // Создание экземпляра RecipeModel - return RecipeModel( + final RecipeModel recipe = RecipeModel( id: null, ingredients: ingredients, steps: steps, - state: 'draft', - description: state.titleController.text, - type: state.types.first.value, + state: state.selectedStatus.value, + type: state.selectedType.value, + cooking_time: state.selectedCookingTime.value, + description: state.stepDescriptionController.text, + title: state.titleController.text, image: pic, - video_url: state.youtubeLinkController.text.isNotEmpty - ? state.youtubeLinkController.text - : null, - cooking_time: - state.cookingTimes.isNotEmpty ? state.cookingTimes.first.value : null, + video_url: state.youtubeLinkController.text.isNotEmpty ? state.youtubeLinkController.text : null, ); + print(recipe); + return recipe; } } diff --git a/lib/features/recipes/reporitories/recipe_repository.dart b/lib/features/recipes/reporitories/recipe_repository.dart index 621da2fb..16a1723a 100644 --- a/lib/features/recipes/reporitories/recipe_repository.dart +++ b/lib/features/recipes/reporitories/recipe_repository.dart @@ -1,9 +1,27 @@ +import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; import 'package:eatvio_app/features/recipes/services/recipe_service.dart'; +import 'package:eatvio_app/features/diary/models/product.dart'; class RecipeRepository { final RecipeService recipeService; RecipeRepository({required this.recipeService}); + + Future>> inputProduct(String name) async { + try { + return await recipeService.getInputProduct(name); + } catch (e) { + rethrow; + } + } + + Future publishRecipe(RecipeModel recipe) async { + try { + return await recipeService.publishRecipe(recipe); + } catch (e) { + rethrow; + } + } /*Future getRecipe(int recipeID) async { try { return (await recipeService.getRecipe(recipeID)); diff --git a/lib/features/recipes/services/recipe_service.dart b/lib/features/recipes/services/recipe_service.dart index 16b3b890..80f5e35f 100644 --- a/lib/features/recipes/services/recipe_service.dart +++ b/lib/features/recipes/services/recipe_service.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; import 'package:retrofit/retrofit.dart'; +import 'package:eatvio_app/features/diary/models/product.dart'; part 'recipe_service.g.dart'; @@ -10,4 +11,10 @@ abstract class RecipeService { @GET('/api/recipe/{id}/show/') Future> getRecipe(@Path() int id); + + @GET('/api/diary/product_nutritional_values/filter') + Future>> getInputProduct(@Query('name') String name); + + @POST('/api/recipes/create') + Future publishRecipe(RecipeModel recipe); } diff --git a/lib/features/recipes/services/recipe_service.g.dart b/lib/features/recipes/services/recipe_service.g.dart index 04b47319..62c9f9f4 100644 --- a/lib/features/recipes/services/recipe_service.g.dart +++ b/lib/features/recipes/services/recipe_service.g.dart @@ -46,6 +46,64 @@ class _RecipeService implements RecipeService { return _value; } + @override + Future>> getInputProduct(String name) async { + final _extra = {}; + final queryParameters = {r'name': name}; + final _headers = {}; + const Map? _data = null; + final _result = await _dio.fetch>( + _setStreamType>>(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diary/product_nutritional_values/filter', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + var _value = _result.data!.map((k, dynamic v) => MapEntry( + k, + (v as List) + .map((i) => Product.fromJson(i as Map)) + .toList())); + return _value; + } + + @override + Future publishRecipe(RecipeModel recipe) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _result = await _dio + .fetch>(_setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/recipes/create', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final _value = RecipeModel.fromJson(_result.data!); + return _value; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes ||