diff --git a/lib/app_router.dart b/lib/app_router.dart index 03a17959..c8f47968 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -16,6 +16,7 @@ import 'package:eatvio_app/features/feed/presentation/pages/feed_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart'; import 'package:eatvio_app/features/recipes/presentation/pages/about_recipes_screen.dart'; import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart'; +import 'package:eatvio_app/features/recipes/presentation/pages/recipe_edit_screen.dart'; import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart'; import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart'; import 'package:eatvio_app/presentation/diets/diet_create_screen/diet_create_screen.dart'; @@ -102,6 +103,13 @@ class AppRouter { name: 'recipeCreateScreen', builder: (context, state) => RecipeCreateScreen(), ), + GoRoute( + path: '/recipe-edit-screen', + name: 'recipeEditScreen', + builder: (context, state) => RecipeEditScreen( + recipeID: state.extra as String, + ), + ), GoRoute( path: '/profile-screen', name: 'profileScreen', diff --git a/lib/features/recipes/models/image.dart b/lib/features/recipes/models/image.dart deleted file mode 100644 index 4be7357b..00000000 --- a/lib/features/recipes/models/image.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'image.freezed.dart'; - -part 'image.g.dart'; - -@freezed -class Image with _$Image { - const factory Image({ - 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 deleted file mode 100644 index 1b73b841..00000000 --- a/lib/features/recipes/models/image.freezed.dart +++ /dev/null @@ -1,147 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'image.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -Image _$ImageFromJson(Map json) { - return _Image.fromJson(json); -} - -/// @nodoc -mixin _$Image { - String? get path => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $ImageCopyWith get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ImageCopyWith<$Res> { - factory $ImageCopyWith(Image value, $Res Function(Image) then) = - _$ImageCopyWithImpl<$Res, Image>; - @useResult - $Res call({String? path}); -} - -/// @nodoc -class _$ImageCopyWithImpl<$Res, $Val extends Image> - implements $ImageCopyWith<$Res> { - _$ImageCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? path = freezed, - }) { - return _then(_value.copyWith( - path: freezed == path - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ImageImplCopyWith<$Res> implements $ImageCopyWith<$Res> { - factory _$$ImageImplCopyWith( - _$ImageImpl value, $Res Function(_$ImageImpl) then) = - __$$ImageImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({String? path}); -} - -/// @nodoc -class __$$ImageImplCopyWithImpl<$Res> - extends _$ImageCopyWithImpl<$Res, _$ImageImpl> - implements _$$ImageImplCopyWith<$Res> { - __$$ImageImplCopyWithImpl( - _$ImageImpl _value, $Res Function(_$ImageImpl) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? path = freezed, - }) { - return _then(_$ImageImpl( - path: freezed == path - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ImageImpl implements _Image { - const _$ImageImpl({this.path}); - - factory _$ImageImpl.fromJson(Map json) => - _$$ImageImplFromJson(json); - - @override - final String? path; - - @override - String toString() { - return 'Image(path: $path)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ImageImpl && - (identical(other.path, path) || other.path == path)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, path); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$ImageImplCopyWith<_$ImageImpl> get copyWith => - __$$ImageImplCopyWithImpl<_$ImageImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ImageImplToJson( - this, - ); - } -} - -abstract class _Image implements Image { - const factory _Image({final String? path}) = _$ImageImpl; - - factory _Image.fromJson(Map json) = _$ImageImpl.fromJson; - - @override - String? get path; - @override - @JsonKey(ignore: true) - _$$ImageImplCopyWith<_$ImageImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/recipes/models/image.g.dart b/lib/features/recipes/models/image.g.dart deleted file mode 100644 index d311f9c9..00000000 --- a/lib/features/recipes/models/image.g.dart +++ /dev/null @@ -1,16 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'image.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ImageImpl _$$ImageImplFromJson(Map json) => _$ImageImpl( - path: json['path'] as String?, - ); - -Map _$$ImageImplToJson(_$ImageImpl instance) => - { - 'path': instance.path, - }; diff --git a/lib/features/recipes/models/ingredient.dart b/lib/features/recipes/models/ingredient.dart index bf00fe1b..d7a130e4 100644 --- a/lib/features/recipes/models/ingredient.dart +++ b/lib/features/recipes/models/ingredient.dart @@ -7,12 +7,10 @@ part 'ingredient.g.dart'; @freezed class Ingredient with _$Ingredient { const factory Ingredient({ - int? id, - required String product_name, + required String name, required String unit, - required num portion, + required String portion, }) = _Ingredient; - factory Ingredient.fromJson(Map json) => - _$IngredientFromJson(json); + factory Ingredient.fromJson(Map json) => _$IngredientFromJson(json); } diff --git a/lib/features/recipes/models/ingredient.freezed.dart b/lib/features/recipes/models/ingredient.freezed.dart index 2555a744..63f3facb 100644 --- a/lib/features/recipes/models/ingredient.freezed.dart +++ b/lib/features/recipes/models/ingredient.freezed.dart @@ -20,10 +20,9 @@ Ingredient _$IngredientFromJson(Map json) { /// @nodoc mixin _$Ingredient { - int? get id => throw _privateConstructorUsedError; - String get product_name => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; String get unit => throw _privateConstructorUsedError; - num get portion => throw _privateConstructorUsedError; + String get portion => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -37,7 +36,7 @@ abstract class $IngredientCopyWith<$Res> { Ingredient value, $Res Function(Ingredient) then) = _$IngredientCopyWithImpl<$Res, Ingredient>; @useResult - $Res call({int? id, String product_name, String unit, num portion}); + $Res call({String name, String unit, String portion}); } /// @nodoc @@ -53,19 +52,14 @@ class _$IngredientCopyWithImpl<$Res, $Val extends Ingredient> @pragma('vm:prefer-inline') @override $Res call({ - Object? id = freezed, - Object? product_name = null, + Object? name = null, Object? unit = null, Object? portion = null, }) { return _then(_value.copyWith( - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int?, - product_name: null == product_name - ? _value.product_name - : product_name // ignore: cast_nullable_to_non_nullable + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable as String, unit: null == unit ? _value.unit @@ -74,7 +68,7 @@ class _$IngredientCopyWithImpl<$Res, $Val extends Ingredient> portion: null == portion ? _value.portion : portion // ignore: cast_nullable_to_non_nullable - as num, + as String, ) as $Val); } } @@ -87,7 +81,7 @@ abstract class _$$IngredientImplCopyWith<$Res> __$$IngredientImplCopyWithImpl<$Res>; @override @useResult - $Res call({int? id, String product_name, String unit, num portion}); + $Res call({String name, String unit, String portion}); } /// @nodoc @@ -101,19 +95,14 @@ class __$$IngredientImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? id = freezed, - Object? product_name = null, + Object? name = null, Object? unit = null, Object? portion = null, }) { return _then(_$IngredientImpl( - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int?, - product_name: null == product_name - ? _value.product_name - : product_name // ignore: cast_nullable_to_non_nullable + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable as String, unit: null == unit ? _value.unit @@ -122,7 +111,7 @@ class __$$IngredientImplCopyWithImpl<$Res> portion: null == portion ? _value.portion : portion // ignore: cast_nullable_to_non_nullable - as num, + as String, )); } } @@ -131,26 +120,21 @@ class __$$IngredientImplCopyWithImpl<$Res> @JsonSerializable() class _$IngredientImpl implements _Ingredient { const _$IngredientImpl( - {this.id, - required this.product_name, - required this.unit, - required this.portion}); + {required this.name, required this.unit, required this.portion}); factory _$IngredientImpl.fromJson(Map json) => _$$IngredientImplFromJson(json); @override - final int? id; - @override - final String product_name; + final String name; @override final String unit; @override - final num portion; + final String portion; @override String toString() { - return 'Ingredient(id: $id, product_name: $product_name, unit: $unit, portion: $portion)'; + return 'Ingredient(name: $name, unit: $unit, portion: $portion)'; } @override @@ -158,16 +142,14 @@ class _$IngredientImpl implements _Ingredient { return identical(this, other) || (other.runtimeType == runtimeType && other is _$IngredientImpl && - (identical(other.id, id) || other.id == id) && - (identical(other.product_name, product_name) || - other.product_name == product_name) && + (identical(other.name, name) || other.name == name) && (identical(other.unit, unit) || other.unit == unit) && (identical(other.portion, portion) || other.portion == portion)); } @JsonKey(ignore: true) @override - int get hashCode => Object.hash(runtimeType, id, product_name, unit, portion); + int get hashCode => Object.hash(runtimeType, name, unit, portion); @JsonKey(ignore: true) @override @@ -185,22 +167,19 @@ class _$IngredientImpl implements _Ingredient { abstract class _Ingredient implements Ingredient { const factory _Ingredient( - {final int? id, - required final String product_name, + {required final String name, required final String unit, - required final num portion}) = _$IngredientImpl; + required final String portion}) = _$IngredientImpl; factory _Ingredient.fromJson(Map json) = _$IngredientImpl.fromJson; @override - int? get id; - @override - String get product_name; + String get name; @override String get unit; @override - num get portion; + String get portion; @override @JsonKey(ignore: true) _$$IngredientImplCopyWith<_$IngredientImpl> get copyWith => diff --git a/lib/features/recipes/models/ingredient.g.dart b/lib/features/recipes/models/ingredient.g.dart index 7e3c9d91..b3f1844d 100644 --- a/lib/features/recipes/models/ingredient.g.dart +++ b/lib/features/recipes/models/ingredient.g.dart @@ -8,16 +8,14 @@ part of 'ingredient.dart'; _$IngredientImpl _$$IngredientImplFromJson(Map json) => _$IngredientImpl( - id: (json['id'] as num?)?.toInt(), - product_name: json['product_name'] as String, + name: json['name'] as String, unit: json['unit'] as String, - portion: json['portion'] as num, + portion: json['portion'] as String, ); Map _$$IngredientImplToJson(_$IngredientImpl instance) => { - 'id': instance.id, - 'product_name': instance.product_name, + 'name': instance.name, 'unit': instance.unit, 'portion': instance.portion, }; diff --git a/lib/features/recipes/models/recipe_model.dart b/lib/features/recipes/models/recipe_model.dart index 7f52cabf..8b5b8c56 100644 --- a/lib/features/recipes/models/recipe_model.dart +++ b/lib/features/recipes/models/recipe_model.dart @@ -1,4 +1,3 @@ -import 'package:eatvio_app/features/recipes/models/image.dart'; import 'package:eatvio_app/features/recipes/models/ingredient.dart'; import 'package:eatvio_app/features/recipes/models/step.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -9,16 +8,15 @@ part 'recipe_model.g.dart'; @freezed class RecipeModel with _$RecipeModel { const factory RecipeModel({ - int? id, - required List ingredients, - required List steps, - required String title, - required String state, - required String description, - required String type, - required Image image, + List? ingredients, + List? steps, + String? type, + String? state, + String? title, + String? description, String? video_url, - num? cooking_time, + String? image, + int? cooking_time, }) = _RecipeModel; 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 c1cb2cac..fb93e70a 100644 --- a/lib/features/recipes/models/recipe_model.freezed.dart +++ b/lib/features/recipes/models/recipe_model.freezed.dart @@ -20,16 +20,15 @@ RecipeModel _$RecipeModelFromJson(Map json) { /// @nodoc 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; - Image get image => throw _privateConstructorUsedError; + List? get ingredients => throw _privateConstructorUsedError; + List? get steps => throw _privateConstructorUsedError; + String? get type => throw _privateConstructorUsedError; + String? get state => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + String? get description => throw _privateConstructorUsedError; String? get video_url => throw _privateConstructorUsedError; - num? get cooking_time => throw _privateConstructorUsedError; + String? get image => throw _privateConstructorUsedError; + int? get cooking_time => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -44,18 +43,15 @@ abstract class $RecipeModelCopyWith<$Res> { _$RecipeModelCopyWithImpl<$Res, RecipeModel>; @useResult $Res call( - {int? id, - List ingredients, - List steps, - String title, - String state, - String description, - String type, - Image image, + {List? ingredients, + List? steps, + String? type, + String? state, + String? title, + String? description, String? video_url, - num? cooking_time}); - - $ImageCopyWith<$Res> get image; + String? image, + int? cooking_time}); } /// @nodoc @@ -71,68 +67,55 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel> @pragma('vm:prefer-inline') @override $Res call({ - Object? id = freezed, - Object? ingredients = null, - Object? steps = null, - Object? title = null, - Object? state = null, - Object? description = null, - Object? type = null, - Object? image = null, + Object? ingredients = freezed, + Object? steps = freezed, + Object? type = freezed, + Object? state = freezed, + Object? title = freezed, + Object? description = freezed, Object? video_url = freezed, + Object? image = freezed, Object? cooking_time = freezed, }) { return _then(_value.copyWith( - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int?, - ingredients: null == ingredients + ingredients: freezed == ingredients ? _value.ingredients : ingredients // ignore: cast_nullable_to_non_nullable - as List, - steps: null == steps + as List?, + steps: freezed == steps ? _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 - as String, - description: null == description - ? _value.description - : description // ignore: cast_nullable_to_non_nullable - as String, - type: null == type + as List?, + type: freezed == type ? _value.type : type // ignore: cast_nullable_to_non_nullable - as String, - image: null == image - ? _value.image - : image // ignore: cast_nullable_to_non_nullable - as Image, + as String?, + state: freezed == state + ? _value.state + : state // ignore: cast_nullable_to_non_nullable + as String?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + description: freezed == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String?, video_url: freezed == video_url ? _value.video_url : video_url // ignore: cast_nullable_to_non_nullable as String?, + image: freezed == image + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as String?, cooking_time: freezed == cooking_time ? _value.cooking_time : cooking_time // ignore: cast_nullable_to_non_nullable - as num?, + as int?, ) as $Val); } - - @override - @pragma('vm:prefer-inline') - $ImageCopyWith<$Res> get image { - return $ImageCopyWith<$Res>(_value.image, (value) { - return _then(_value.copyWith(image: value) as $Val); - }); - } } /// @nodoc @@ -144,19 +127,15 @@ abstract class _$$RecipeModelImplCopyWith<$Res> @override @useResult $Res call( - {int? id, - List ingredients, - List steps, - String title, - String state, - String description, - String type, - Image image, + {List? ingredients, + List? steps, + String? type, + String? state, + String? title, + String? description, String? video_url, - num? cooking_time}); - - @override - $ImageCopyWith<$Res> get image; + String? image, + int? cooking_time}); } /// @nodoc @@ -170,58 +149,53 @@ class __$$RecipeModelImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? id = freezed, - Object? ingredients = null, - Object? steps = null, - Object? title = null, - Object? state = null, - Object? description = null, - Object? type = null, - Object? image = null, + Object? ingredients = freezed, + Object? steps = freezed, + Object? type = freezed, + Object? state = freezed, + Object? title = freezed, + Object? description = freezed, Object? video_url = freezed, + Object? image = freezed, Object? cooking_time = freezed, }) { return _then(_$RecipeModelImpl( - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int?, - ingredients: null == ingredients + ingredients: freezed == ingredients ? _value._ingredients : ingredients // ignore: cast_nullable_to_non_nullable - as List, - steps: null == steps + as List?, + steps: freezed == steps ? _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 - as String, - description: null == description - ? _value.description - : description // ignore: cast_nullable_to_non_nullable - as String, - type: null == type + as List?, + type: freezed == type ? _value.type : type // ignore: cast_nullable_to_non_nullable - as String, - image: null == image - ? _value.image - : image // ignore: cast_nullable_to_non_nullable - as Image, + as String?, + state: freezed == state + ? _value.state + : state // ignore: cast_nullable_to_non_nullable + as String?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + description: freezed == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String?, video_url: freezed == video_url ? _value.video_url : video_url // ignore: cast_nullable_to_non_nullable as String?, + image: freezed == image + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as String?, cooking_time: freezed == cooking_time ? _value.cooking_time : cooking_time // ignore: cast_nullable_to_non_nullable - as num?, + as int?, )); } } @@ -230,15 +204,14 @@ class __$$RecipeModelImplCopyWithImpl<$Res> @JsonSerializable() class _$RecipeModelImpl implements _RecipeModel { const _$RecipeModelImpl( - {this.id, - required final List ingredients, - required final List steps, - required this.title, - required this.state, - required this.description, - required this.type, - required this.image, + {final List? ingredients, + final List? steps, + this.type, + this.state, + this.title, + this.description, this.video_url, + this.image, this.cooking_time}) : _ingredients = ingredients, _steps = steps; @@ -246,42 +219,44 @@ class _$RecipeModelImpl implements _RecipeModel { factory _$RecipeModelImpl.fromJson(Map json) => _$$RecipeModelImplFromJson(json); + final List? _ingredients; @override - final int? id; - final List _ingredients; - @override - List get ingredients { + List? get ingredients { + final value = _ingredients; + if (value == null) return null; if (_ingredients is EqualUnmodifiableListView) return _ingredients; // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_ingredients); + return EqualUnmodifiableListView(value); } - final List _steps; + final List? _steps; @override - List get steps { + List? get steps { + final value = _steps; + if (value == null) return null; if (_steps is EqualUnmodifiableListView) return _steps; // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_steps); + return EqualUnmodifiableListView(value); } @override - final String title; + final String? type; @override - final String state; + final String? state; @override - final String description; + final String? title; @override - final String type; - @override - final Image image; + final String? description; @override final String? video_url; @override - final num? cooking_time; + final String? image; + @override + final int? cooking_time; @override String toString() { - 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)'; + return 'RecipeModel(ingredients: $ingredients, steps: $steps, type: $type, state: $state, title: $title, description: $description, video_url: $video_url, image: $image, cooking_time: $cooking_time)'; } @override @@ -289,18 +264,17 @@ class _$RecipeModelImpl implements _RecipeModel { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RecipeModelImpl && - (identical(other.id, id) || other.id == id) && const DeepCollectionEquality() .equals(other._ingredients, _ingredients) && const DeepCollectionEquality().equals(other._steps, _steps) && - (identical(other.title, title) || other.title == title) && + (identical(other.type, type) || other.type == type) && (identical(other.state, state) || other.state == state) && + (identical(other.title, title) || other.title == title) && (identical(other.description, description) || other.description == description) && - (identical(other.type, type) || other.type == type) && - (identical(other.image, image) || other.image == image) && (identical(other.video_url, video_url) || other.video_url == video_url) && + (identical(other.image, image) || other.image == image) && (identical(other.cooking_time, cooking_time) || other.cooking_time == cooking_time)); } @@ -309,15 +283,14 @@ class _$RecipeModelImpl implements _RecipeModel { @override int get hashCode => Object.hash( runtimeType, - id, const DeepCollectionEquality().hash(_ingredients), const DeepCollectionEquality().hash(_steps), - title, - state, - description, type, - image, + state, + title, + description, video_url, + image, cooking_time); @JsonKey(ignore: true) @@ -336,40 +309,37 @@ class _$RecipeModelImpl implements _RecipeModel { abstract class _RecipeModel implements RecipeModel { const factory _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, - required final Image image, + {final List? ingredients, + final List? steps, + final String? type, + final String? state, + final String? title, + final String? description, final String? video_url, - final num? cooking_time}) = _$RecipeModelImpl; + final String? image, + final int? cooking_time}) = _$RecipeModelImpl; factory _RecipeModel.fromJson(Map json) = _$RecipeModelImpl.fromJson; @override - int? get id; + List? get ingredients; @override - List get ingredients; + List? get steps; @override - List get steps; + String? get type; @override - String get title; + String? get state; @override - String get state; + String? get title; @override - String get description; - @override - String get type; - @override - Image get image; + String? get description; @override String? get video_url; @override - num? get cooking_time; + String? get image; + @override + int? get cooking_time; @override @JsonKey(ignore: true) _$$RecipeModelImplCopyWith<_$RecipeModelImpl> get copyWith => diff --git a/lib/features/recipes/models/recipe_model.g.dart b/lib/features/recipes/models/recipe_model.g.dart index d058e9c6..d5ab9c96 100644 --- a/lib/features/recipes/models/recipe_model.g.dart +++ b/lib/features/recipes/models/recipe_model.g.dart @@ -8,32 +8,32 @@ part of 'recipe_model.dart'; _$RecipeModelImpl _$$RecipeModelImplFromJson(Map json) => _$RecipeModelImpl( - id: (json['id'] as num?)?.toInt(), - ingredients: (json['ingredients'] as List) - .map((e) => Ingredient.fromJson(e as Map)) + ingredients: (json['ingredients'] as List?) + ?.map((e) => + e == null ? null : Ingredient.fromJson(e as Map)) .toList(), - steps: (json['steps'] as List) - .map((e) => Step.fromJson(e as Map)) + steps: (json['steps'] as List?) + ?.map((e) => + e == null ? null : 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, - image: Image.fromJson(json['image'] as Map), + type: json['type'] as String?, + state: json['state'] as String?, + title: json['title'] as String?, + description: json['description'] as String?, video_url: json['video_url'] as String?, - cooking_time: json['cooking_time'] as num?, + image: json['image'] as String?, + cooking_time: (json['cooking_time'] as num?)?.toInt(), ); 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, - 'image': instance.image, + 'state': instance.state, + 'title': instance.title, + 'description': instance.description, 'video_url': instance.video_url, + 'image': instance.image, 'cooking_time': instance.cooking_time, }; diff --git a/lib/features/recipes/models/step.dart b/lib/features/recipes/models/step.dart index 735f72c2..9c22eb76 100644 --- a/lib/features/recipes/models/step.dart +++ b/lib/features/recipes/models/step.dart @@ -1,4 +1,3 @@ -import 'package:eatvio_app/features/recipes/models/image.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'step.freezed.dart'; @@ -8,8 +7,7 @@ part 'step.g.dart'; @freezed class Step with _$Step { const factory Step({ - int? id, - Image? image, + String? image, String? description, }) = _Step; diff --git a/lib/features/recipes/models/step.freezed.dart b/lib/features/recipes/models/step.freezed.dart index 1ff4d0c1..c2014f4f 100644 --- a/lib/features/recipes/models/step.freezed.dart +++ b/lib/features/recipes/models/step.freezed.dart @@ -20,8 +20,7 @@ Step _$StepFromJson(Map json) { /// @nodoc mixin _$Step { - int? get id => throw _privateConstructorUsedError; - Image? get image => throw _privateConstructorUsedError; + String? get image => throw _privateConstructorUsedError; String? get description => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @@ -34,9 +33,7 @@ abstract class $StepCopyWith<$Res> { factory $StepCopyWith(Step value, $Res Function(Step) then) = _$StepCopyWithImpl<$Res, Step>; @useResult - $Res call({int? id, Image? image, String? description}); - - $ImageCopyWith<$Res>? get image; + $Res call({String? image, String? description}); } /// @nodoc @@ -52,37 +49,20 @@ class _$StepCopyWithImpl<$Res, $Val extends Step> @pragma('vm:prefer-inline') @override $Res call({ - Object? id = freezed, Object? image = freezed, Object? description = freezed, }) { return _then(_value.copyWith( - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int?, image: freezed == image ? _value.image : image // ignore: cast_nullable_to_non_nullable - as Image?, + as String?, description: freezed == description ? _value.description : description // ignore: cast_nullable_to_non_nullable as String?, ) as $Val); } - - @override - @pragma('vm:prefer-inline') - $ImageCopyWith<$Res>? get image { - if (_value.image == null) { - return null; - } - - return $ImageCopyWith<$Res>(_value.image!, (value) { - return _then(_value.copyWith(image: value) as $Val); - }); - } } /// @nodoc @@ -92,10 +72,7 @@ abstract class _$$StepImplCopyWith<$Res> implements $StepCopyWith<$Res> { __$$StepImplCopyWithImpl<$Res>; @override @useResult - $Res call({int? id, Image? image, String? description}); - - @override - $ImageCopyWith<$Res>? get image; + $Res call({String? image, String? description}); } /// @nodoc @@ -108,19 +85,14 @@ class __$$StepImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? id = freezed, Object? image = freezed, Object? description = freezed, }) { return _then(_$StepImpl( - id: freezed == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int?, image: freezed == image ? _value.image : image // ignore: cast_nullable_to_non_nullable - as Image?, + as String?, description: freezed == description ? _value.description : description // ignore: cast_nullable_to_non_nullable @@ -132,21 +104,19 @@ class __$$StepImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() class _$StepImpl implements _Step { - const _$StepImpl({this.id, this.image, this.description}); + const _$StepImpl({this.image, this.description}); factory _$StepImpl.fromJson(Map json) => _$$StepImplFromJson(json); @override - final int? id; - @override - final Image? image; + final String? image; @override final String? description; @override String toString() { - return 'Step(id: $id, image: $image, description: $description)'; + return 'Step(image: $image, description: $description)'; } @override @@ -154,7 +124,6 @@ class _$StepImpl implements _Step { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StepImpl && - (identical(other.id, id) || other.id == id) && (identical(other.image, image) || other.image == image) && (identical(other.description, description) || other.description == description)); @@ -162,7 +131,7 @@ class _$StepImpl implements _Step { @JsonKey(ignore: true) @override - int get hashCode => Object.hash(runtimeType, id, image, description); + int get hashCode => Object.hash(runtimeType, image, description); @JsonKey(ignore: true) @override @@ -179,17 +148,13 @@ class _$StepImpl implements _Step { } abstract class _Step implements Step { - const factory _Step( - {final int? id, - final Image? image, - final String? description}) = _$StepImpl; + const factory _Step({final String? image, final String? description}) = + _$StepImpl; factory _Step.fromJson(Map json) = _$StepImpl.fromJson; @override - int? get id; - @override - Image? get image; + String? get image; @override String? get description; @override diff --git a/lib/features/recipes/models/step.g.dart b/lib/features/recipes/models/step.g.dart index 688769ce..31d97f9f 100644 --- a/lib/features/recipes/models/step.g.dart +++ b/lib/features/recipes/models/step.g.dart @@ -7,16 +7,12 @@ part of 'step.dart'; // ************************************************************************** _$StepImpl _$$StepImplFromJson(Map json) => _$StepImpl( - id: (json['id'] as num?)?.toInt(), - image: json['image'] == null - ? null - : Image.fromJson(json['image'] as Map), + image: json['image'] as String?, description: json['description'] as String?, ); Map _$$StepImplToJson(_$StepImpl instance) => { - 'id': instance.id, 'image': instance.image, 'description': instance.description, }; diff --git a/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart b/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart index f2c9446d..3aef52aa 100644 --- a/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart +++ b/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart @@ -12,6 +12,87 @@ class RecipeCreateCubit extends Cubit { RecipeCreateCubit({required this.recipeRepository}) : super(const RecipeCreateState.loading()); + loadRecipeById(String recipeID) async { + emit(const RecipeCreateStateLoading()); + try { + final RecipeModel recipe = await recipeRepository.getRecipe(recipeID); + 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'), + ]; + final selectedType = types.firstWhere( + (type) => type.value == recipe.type, + orElse: () => types[0], + ); + + final selectedCookingTime = cookingTimes.firstWhere( + (time) => time.value == recipe.cooking_time, + orElse: () => cookingTimes[0], + ); + + final selectedStatus = statuses.firstWhere( + (status) => status.value == recipe.state, + orElse: () => statuses[0], + ); + + final stepControllers = recipe.steps?.map((step) { + return TextEditingController(text: step?.description); + }).toList(); + + final stepImages = recipe.steps?.map((step) { + return step?.image != null ? XFile(step!.image!) : null; + }).toList(); + + final ingredients = recipe.ingredients?.map((ingredient) { + return Ingredient( + name: ingredient!.name, + portion: ingredient.portion, + unit: ingredient.unit, + ); + }).toList(); + + emit(RecipeCreateState.loaded( + picker: ImagePicker(), + recipeImage: XFile(recipe.image!), + titleController: TextEditingController(text: recipe.title), + productNameController: TextEditingController(), + ingredientController: TextEditingController(), + stepDescriptionController: TextEditingController(text: recipe.description), + youtubeLinkController: TextEditingController(text: recipe.video_url), + stepControllers: stepControllers ?? [], + stepImages: stepImages ?? [], + autocompleteList: [], + ingredients: ingredients ?? [], + numberOfSteps: 1, + ingredientAsItems: false, + types: types, + cookingTimes: cookingTimes, + statuses: statuses, + selectedType: selectedType, + selectedCookingTime: selectedCookingTime, + selectedStatus: selectedStatus)); + } catch (e) { + emit(RecipeCreateState.error(errorMessage: e.toString())); + } + } + loadRecipeCreate() { emit(const RecipeCreateStateLoading()); try { @@ -117,7 +198,7 @@ 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.name == ingredient.name); if (ingredientExists) { emit(RecipeCreateState.error(errorMessage: 'Ингредиент уже существует.')); diff --git a/lib/features/recipes/presentation/pages/recipe_create_screen.dart b/lib/features/recipes/presentation/pages/recipe_create_screen.dart index 19d3f689..24e9f277 100644 --- a/lib/features/recipes/presentation/pages/recipe_create_screen.dart +++ b/lib/features/recipes/presentation/pages/recipe_create_screen.dart @@ -3,7 +3,6 @@ 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; import 'package:eatvio_app/features/recipes/models/step.dart' as step; import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_cubit.dart'; import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_state.dart'; @@ -223,8 +222,8 @@ 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), + name: state.productNameController.text, + portion: state.ingredientController.text, unit: state.ingredientAsItems ? 'pcs' : 'gr'))), ), SizedBox(height: 24), @@ -378,7 +377,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.name.length > 8 ? ingredient.name.substring(0, 8) : ingredient.name, style: CustomTextStyles.titleMedium16, ), ), @@ -454,18 +453,15 @@ class RecipeCreateScreen extends StatelessWidget { 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, - image: picStep, + image: state.stepImages[i]?.path, )); } } - image.Image pic = image.Image(path: state.recipeImage?.path ?? ''); final RecipeModel recipe = RecipeModel( - id: null, ingredients: ingredients, steps: steps, state: state.selectedStatus.value, @@ -473,7 +469,7 @@ class RecipeCreateScreen extends StatelessWidget { cooking_time: state.selectedCookingTime.value, description: state.stepDescriptionController.text, title: state.titleController.text, - image: pic, + image: state.recipeImage?.path, video_url: state.youtubeLinkController.text.isNotEmpty ? state.youtubeLinkController.text : null, ); print(recipe); diff --git a/lib/features/recipes/presentation/pages/recipe_edit_screen.dart b/lib/features/recipes/presentation/pages/recipe_edit_screen.dart new file mode 100644 index 00000000..5e5cfeec --- /dev/null +++ b/lib/features/recipes/presentation/pages/recipe_edit_screen.dart @@ -0,0 +1,493 @@ +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/step.dart' as step; +import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_cubit.dart'; +import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_state.dart'; +import 'package:eatvio_app/widgets/custom_drop_down.dart'; +import 'package:eatvio_app/widgets/custom_text_form_field.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker/image_picker.dart'; + +// ignore: must_be_immutable +class RecipeEditScreen extends StatelessWidget { + final String recipeID; + const RecipeEditScreen({ + required this.recipeID, + super.key, + }); + Widget build(BuildContext context) { + if (recipeID != "0") { + context.read().loadRecipeById(recipeID); + } + return BlocBuilder( + builder: (context, state) { + if (state is RecipeCreateStateLoaded) { + return _buildRecipeCreate(context, state); + } else if (state is RecipeCreateStateLoading) { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } else if (state is RecipeCreateStateError) { + 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 _buildRecipeCreate(BuildContext context, RecipeCreateStateLoaded state) { + List _options = state.autocompleteList.map((Product item) => item.name).toList(); + return Scaffold( + appBar: AppBar( + toolbarHeight: 0, + automaticallyImplyLeading: false, + ), + backgroundColor: Color.fromARGB(255, 240, 242, 245), + resizeToAvoidBottomInset: true, + body: SizedBox( + width: double.maxFinite, + child: SingleChildScrollView( + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Заголовок'), + SizedBox(height: 8), + CustomTextFormField( + fillColor: appTheme.gray50, + controller: state.titleController, + hintText: 'Заголовок', + ), + SizedBox(height: 16), + Text('Тип блюда'), + SizedBox(height: 8), + CustomDropDown( + 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); + }, + child: state.recipeImage == null + ? Container( + height: 128, + decoration: AppDecoration.fillGray.copyWith( + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.photo_library, + color: appTheme.blueGray300, + ), + SizedBox(width: 8), + Text( + "Добавить фото", + selectionColor: appTheme.blueA400, + ), + ], + ), + ) + : Image.network( + baseUrl + '/storage/' + state.recipeImage!.path, + height: 150, + fit: BoxFit.cover, + ), + ), + SizedBox(height: 16), + Text('Время приготовления'), + SizedBox(height: 8), + CustomDropDown( + items: state.cookingTimes, + value: state.selectedCookingTime, + onChanged: changeCookingTime(context), + ), + SizedBox(height: 16), + Text('Статус'), + SizedBox(height: 8), + CustomDropDown( + items: state.statuses, + 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]), + SizedBox(height: 24), + 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( + hintText: 'Порция', + fillColor: appTheme.gray50, + controller: state.ingredientController, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + ), + SizedBox(height: 8), + Row(children: [ + Text('гр'), + Switch( + value: state.ingredientAsItems, + onChanged: (bool value) => context.read().switchUnits(value), + activeColor: appTheme.blueA400, + inactiveThumbColor: Colors.grey, + inactiveTrackColor: Colors.grey.shade300, + ), + Text('шт'), + ]), + SizedBox(height: 8), + Container( + height: 48, + width: MediaQuery.of(context).size.width, + child: FloatingActionButton.extended( + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + backgroundColor: appTheme.blueA400, + label: Text( + '+', + style: theme.textTheme.titleSmall, + ), + onPressed: () => context.read().addIngredient(new Ingredient( + name: state.productNameController.text, + portion: state.ingredientController.text, + unit: state.ingredientAsItems ? 'pcs' : 'gr'))), + ), + SizedBox(height: 24), + Text('Описание рецепта'), + SizedBox(height: 8), + CustomTextFormField( + maxLines: 8, + minLines: 8, + controller: state.stepDescriptionController, + ), + SizedBox(height: 24), + for (int i = 0; i < state.numberOfSteps; i++) _buildStep(context, i, state), + SizedBox(height: 24), + Container( + height: 48, + width: MediaQuery.of(context).size.width, + child: FloatingActionButton.extended( + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + backgroundColor: appTheme.blueA400, + onPressed: () => context.read().addStep(), + label: Text( + '+', + style: theme.textTheme.titleSmall, + ), + ), + ), + SizedBox(height: 8), + Container( + height: 48, + width: MediaQuery.of(context).size.width, + child: FloatingActionButton.extended( + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + backgroundColor: Colors.grey, + onPressed: () => context.read().removeStep(), + label: Text( + '-', + style: theme.textTheme.titleSmall, + ), + ), + ), + SizedBox(height: 24), + Text('Ссылка на видео (YouTube)'), + SizedBox(height: 8), + CustomTextFormField( + hintText: 'Введите описание шага', + fillColor: appTheme.gray50, + controller: state.youtubeLinkController, + ), + SizedBox(height: 24), + Container( + height: 48, + width: MediaQuery.of(context).size.width, + child: FloatingActionButton.extended( + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + backgroundColor: appTheme.blueA400, + onPressed: () => context.read().publishRecipe(createRecipeFromState(state)), + label: Text( + 'Опубликовать', + style: theme.textTheme.titleSmall, + ), + ), + ), + ], + )), + ], + ), + ), + ), + ); + } + + _buildStep(BuildContext context, int stepNumber, RecipeCreateStateLoaded state) { + print(baseUrl + '/storage/' + state.stepImages[stepNumber]!.path); + return Column( + children: [ + 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); + }, + child: state.stepImages[stepNumber] == null + ? Container( + height: 128, + decoration: AppDecoration.fillGray.copyWith( + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.photo_library, + color: appTheme.blueGray300, + ), + SizedBox(width: 8), + Text( + "Добавить фото", + selectionColor: appTheme.blueA400, + ), + SizedBox(height: 8), + ], + ), + ) + : Image.network( + baseUrl + '/storage/' + state.stepImages[stepNumber]!.path, + height: 150, + fit: BoxFit.cover, + )), + SizedBox(height: 16), + CustomTextFormField( + hintText: 'Введите описание шага', + maxLines: 8, + minLines: 8, + fillColor: appTheme.gray50, + controller: state.stepControllers[stepNumber], + ), + SizedBox(height: 24), + ], + ); + } + + _buildIngredient(Ingredient ingredient) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 10.h), + padding: EdgeInsets.symmetric( + horizontal: 8.h, + vertical: 6.v, + ), + decoration: AppDecoration.fillGray.copyWith( + borderRadius: BorderRadiusStyle.roundedBorder5, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.only( + left: 7.h, + top: 5.v, + ), + child: Text( + ingredient.name.length > 8 ? ingredient.name.substring(0, 8) : ingredient.name, + style: CustomTextStyles.titleMedium16, + ), + ), + Padding( + padding: EdgeInsets.only( + left: 20.h, + top: 3.v, + bottom: 3.v, + ), + child: Text( + ingredient.portion.toString() + ' ' + ingredient.unit.toString().tr, + ), + ), + Spacer(), + GestureDetector( + // onTap: removeIngredient(context, ingredient), + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Icon( + Icons.close, + color: appTheme.blueGray300, + ), + ), + ), + ], + ), + ); + } + + _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; + + List steps = []; + for (int i = 0; i < state.stepControllers.length; i++) { + if (state.stepControllers[i].text.isNotEmpty) { + steps.add(step.Step( + description: state.stepControllers[i].text, + image: state.stepImages[i]?.path, + )); + } + } + + final RecipeModel recipe = RecipeModel( + ingredients: ingredients, + steps: steps, + state: state.selectedStatus.value, + type: state.selectedType.value, + cooking_time: state.selectedCookingTime.value, + description: state.stepDescriptionController.text, + title: state.titleController.text, + image: state.recipeImage?.path, + video_url: state.youtubeLinkController.text.isNotEmpty ? state.youtubeLinkController.text : null, + ); + print(recipe.toJson()); + return recipe; + } +} diff --git a/lib/features/recipes/reporitories/recipe_repository.dart b/lib/features/recipes/reporitories/recipe_repository.dart index 16a1723a..e6d67b9f 100644 --- a/lib/features/recipes/reporitories/recipe_repository.dart +++ b/lib/features/recipes/reporitories/recipe_repository.dart @@ -22,11 +22,12 @@ class RecipeRepository { rethrow; } } - /*Future getRecipe(int recipeID) async { + + Future getRecipe(String recipeID) async { try { - return (await recipeService.getRecipe(recipeID)); + return await recipeService.getRecipe(recipeID); } catch (e) { rethrow; } - }*/ + } } diff --git a/lib/features/recipes/services/recipe_service.dart b/lib/features/recipes/services/recipe_service.dart index 80f5e35f..6d73ccc6 100644 --- a/lib/features/recipes/services/recipe_service.dart +++ b/lib/features/recipes/services/recipe_service.dart @@ -9,8 +9,8 @@ part 'recipe_service.g.dart'; abstract class RecipeService { factory RecipeService(Dio dio, {String baseUrl}) = _RecipeService; - @GET('/api/recipe/{id}/show/') - Future> getRecipe(@Path() int id); + @GET('/api/recipes/show/{id}') + Future getRecipe(@Path() String id); @GET('/api/diary/product_nutritional_values/filter') Future>> getInputProduct(@Query('name') String name); diff --git a/lib/features/recipes/services/recipe_service.g.dart b/lib/features/recipes/services/recipe_service.g.dart index 62c9f9f4..bb170928 100644 --- a/lib/features/recipes/services/recipe_service.g.dart +++ b/lib/features/recipes/services/recipe_service.g.dart @@ -19,20 +19,20 @@ class _RecipeService implements RecipeService { String? baseUrl; @override - Future> getRecipe(int id) async { + Future getRecipe(String id) async { final _extra = {}; final queryParameters = {}; final _headers = {}; const Map? _data = null; - final _result = await _dio.fetch>( - _setStreamType>(Options( + final _result = await _dio + .fetch>(_setStreamType(Options( method: 'GET', headers: _headers, extra: _extra, ) .compose( _dio.options, - '/api/recipe/${id}/show/', + '/api/recipes/show/${id}', queryParameters: queryParameters, data: _data, ) @@ -41,8 +41,7 @@ class _RecipeService implements RecipeService { _dio.options.baseUrl, baseUrl, )))); - var _value = _result.data!.map((k, dynamic v) => - MapEntry(k, RecipeModel.fromJson(v as Map))); + final _value = RecipeModel.fromJson(_result.data!); return _value; } diff --git a/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart b/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart index 29e47b36..2f7d2cc1 100644 --- a/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart +++ b/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart @@ -91,7 +91,7 @@ class RecipeSelectScreen extends StatelessWidget { Padding( padding: const EdgeInsets.only(right: 20.0), child: GestureDetector( - onTap: () => GoRouter.of(context).goNamed('recipeCreateScreen'), + onTap: () => GoRouter.of(context).pushNamed('recipeCreateScreen'), child: Icon(Icons.create_outlined), ), ), @@ -155,9 +155,9 @@ class RecipeSelectScreen extends StatelessWidget { extra: post?['id'].toString(), ), trailing: GestureDetector( - onTap: () => GoRouter.of(context).goNamed( + onTap: () => GoRouter.of(context).pushNamed( 'recipeEditScreen', - extra: post?['id'].toString(), + extra: post?['slug'].toString(), ), child: Icon(Icons.edit), ),