From 972ee605be6db19a0e5f4d48c87cbe1e41b162b6 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 1 Nov 2024 05:24:08 +0300 Subject: [PATCH] feat(diary): add gallery --- .gitea/workflows/test.yml | 2 +- .../diary/models/diary_day_model.dart | 15 +- .../diary/models/diary_day_model.freezed.dart | 242 ++++++++++++++++-- .../diary/models/diary_day_model.g.dart | 18 +- .../diary/presentation/bloc/diary_cubit.dart | 31 +++ .../presentation/pages/diary_screen.dart | 172 +++++++++++-- .../diary/repositories/diary_repository.dart | 36 +++ .../diary/services/diary_service.dart | 30 ++- .../diary/services/diary_service.freezed.dart | 220 ---------------- .../diary/services/diary_service.g.dart | 88 +++++-- 10 files changed, 555 insertions(+), 299 deletions(-) delete mode 100644 lib/features/diary/services/diary_service.freezed.dart diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 883dee6a..ed756ee9 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -24,7 +24,7 @@ jobs: - name: Install Android SDK run: | apt-get update - apt-get install -y openjdk-21-jdk wget + apt install -y openjdk-21-jdk wget wget https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip -O commandlinetools.zip unzip commandlinetools.zip mkdir -p $HOME/Android/Sdk/cmdline-tools/latest diff --git a/lib/features/diary/models/diary_day_model.dart b/lib/features/diary/models/diary_day_model.dart index 773920dc..e4d8a26f 100644 --- a/lib/features/diary/models/diary_day_model.dart +++ b/lib/features/diary/models/diary_day_model.dart @@ -47,10 +47,10 @@ class MealModel with _$MealModel { @freezed class DiaryPhotoModel with _$DiaryPhotoModel { const factory DiaryPhotoModel({ - @JsonKey(name: "id") required String id, + @JsonKey(name: "id") required int id, @JsonKey(name: "original_name") required String originalName, @JsonKey(name: "path") required String path, - @JsonKey(name: "size") required String size, + @JsonKey(name: "size") required int size, }) = _DiaryPhotoModel; factory DiaryPhotoModel.fromJson(Map json) => _$DiaryPhotoModelFromJson(json); @@ -78,3 +78,14 @@ class TypeOfDiaryModel with _$TypeOfDiaryModel { factory TypeOfDiaryModel.fromJson(Map json) => _$TypeOfDiaryModelFromJson(json); } + +@freezed +class ProductInMeal with _$ProductInMeal { + const factory ProductInMeal({ + @JsonKey(name: 'product_name') required String productName, + @JsonKey(name: 'portion') required String portion, + @JsonKey(name: 'quantity') required int quantity, + }) = _ProductInMeal; + + factory ProductInMeal.fromJson(Map json) => _$ProductInMealFromJson(json); +} diff --git a/lib/features/diary/models/diary_day_model.freezed.dart b/lib/features/diary/models/diary_day_model.freezed.dart index dc780145..72b0d70b 100644 --- a/lib/features/diary/models/diary_day_model.freezed.dart +++ b/lib/features/diary/models/diary_day_model.freezed.dart @@ -850,13 +850,13 @@ DiaryPhotoModel _$DiaryPhotoModelFromJson(Map json) { /// @nodoc mixin _$DiaryPhotoModel { @JsonKey(name: "id") - String get id => throw _privateConstructorUsedError; + int get id => throw _privateConstructorUsedError; @JsonKey(name: "original_name") String get originalName => throw _privateConstructorUsedError; @JsonKey(name: "path") String get path => throw _privateConstructorUsedError; @JsonKey(name: "size") - String get size => throw _privateConstructorUsedError; + int get size => throw _privateConstructorUsedError; /// Serializes this DiaryPhotoModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -875,10 +875,10 @@ abstract class $DiaryPhotoModelCopyWith<$Res> { _$DiaryPhotoModelCopyWithImpl<$Res, DiaryPhotoModel>; @useResult $Res call( - {@JsonKey(name: "id") String id, + {@JsonKey(name: "id") int id, @JsonKey(name: "original_name") String originalName, @JsonKey(name: "path") String path, - @JsonKey(name: "size") String size}); + @JsonKey(name: "size") int size}); } /// @nodoc @@ -905,7 +905,7 @@ class _$DiaryPhotoModelCopyWithImpl<$Res, $Val extends DiaryPhotoModel> id: null == id ? _value.id : id // ignore: cast_nullable_to_non_nullable - as String, + as int, originalName: null == originalName ? _value.originalName : originalName // ignore: cast_nullable_to_non_nullable @@ -917,7 +917,7 @@ class _$DiaryPhotoModelCopyWithImpl<$Res, $Val extends DiaryPhotoModel> size: null == size ? _value.size : size // ignore: cast_nullable_to_non_nullable - as String, + as int, ) as $Val); } } @@ -931,10 +931,10 @@ abstract class _$$DiaryPhotoModelImplCopyWith<$Res> @override @useResult $Res call( - {@JsonKey(name: "id") String id, + {@JsonKey(name: "id") int id, @JsonKey(name: "original_name") String originalName, @JsonKey(name: "path") String path, - @JsonKey(name: "size") String size}); + @JsonKey(name: "size") int size}); } /// @nodoc @@ -959,7 +959,7 @@ class __$$DiaryPhotoModelImplCopyWithImpl<$Res> id: null == id ? _value.id : id // ignore: cast_nullable_to_non_nullable - as String, + as int, originalName: null == originalName ? _value.originalName : originalName // ignore: cast_nullable_to_non_nullable @@ -971,7 +971,7 @@ class __$$DiaryPhotoModelImplCopyWithImpl<$Res> size: null == size ? _value.size : size // ignore: cast_nullable_to_non_nullable - as String, + as int, )); } } @@ -990,7 +990,7 @@ class _$DiaryPhotoModelImpl implements _DiaryPhotoModel { @override @JsonKey(name: "id") - final String id; + final int id; @override @JsonKey(name: "original_name") final String originalName; @@ -999,7 +999,7 @@ class _$DiaryPhotoModelImpl implements _DiaryPhotoModel { final String path; @override @JsonKey(name: "size") - final String size; + final int size; @override String toString() { @@ -1041,18 +1041,17 @@ class _$DiaryPhotoModelImpl implements _DiaryPhotoModel { abstract class _DiaryPhotoModel implements DiaryPhotoModel { const factory _DiaryPhotoModel( - {@JsonKey(name: "id") required final String id, - @JsonKey(name: "original_name") required final String originalName, - @JsonKey(name: "path") required final String path, - @JsonKey(name: "size") required final String size}) = - _$DiaryPhotoModelImpl; + {@JsonKey(name: "id") required final int id, + @JsonKey(name: "original_name") required final String originalName, + @JsonKey(name: "path") required final String path, + @JsonKey(name: "size") required final int size}) = _$DiaryPhotoModelImpl; factory _DiaryPhotoModel.fromJson(Map json) = _$DiaryPhotoModelImpl.fromJson; @override @JsonKey(name: "id") - String get id; + int get id; @override @JsonKey(name: "original_name") String get originalName; @@ -1061,7 +1060,7 @@ abstract class _DiaryPhotoModel implements DiaryPhotoModel { String get path; @override @JsonKey(name: "size") - String get size; + int get size; /// Create a copy of DiaryPhotoModel /// with the given fields replaced by the non-null parameter values. @@ -1502,3 +1501,208 @@ abstract class _TypeOfDiaryModel implements TypeOfDiaryModel { _$$TypeOfDiaryModelImplCopyWith<_$TypeOfDiaryModelImpl> get copyWith => throw _privateConstructorUsedError; } + +ProductInMeal _$ProductInMealFromJson(Map json) { + return _ProductInMeal.fromJson(json); +} + +/// @nodoc +mixin _$ProductInMeal { + @JsonKey(name: 'product_name') + String get productName => throw _privateConstructorUsedError; + @JsonKey(name: 'portion') + String get portion => throw _privateConstructorUsedError; + @JsonKey(name: 'quantity') + int get quantity => throw _privateConstructorUsedError; + + /// Serializes this ProductInMeal to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ProductInMeal + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ProductInMealCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ProductInMealCopyWith<$Res> { + factory $ProductInMealCopyWith( + ProductInMeal value, $Res Function(ProductInMeal) then) = + _$ProductInMealCopyWithImpl<$Res, ProductInMeal>; + @useResult + $Res call( + {@JsonKey(name: 'product_name') String productName, + @JsonKey(name: 'portion') String portion, + @JsonKey(name: 'quantity') int quantity}); +} + +/// @nodoc +class _$ProductInMealCopyWithImpl<$Res, $Val extends ProductInMeal> + implements $ProductInMealCopyWith<$Res> { + _$ProductInMealCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ProductInMeal + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? productName = null, + Object? portion = null, + Object? quantity = null, + }) { + return _then(_value.copyWith( + productName: null == productName + ? _value.productName + : productName // ignore: cast_nullable_to_non_nullable + as String, + portion: null == portion + ? _value.portion + : portion // ignore: cast_nullable_to_non_nullable + as String, + quantity: null == quantity + ? _value.quantity + : quantity // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ProductInMealImplCopyWith<$Res> + implements $ProductInMealCopyWith<$Res> { + factory _$$ProductInMealImplCopyWith( + _$ProductInMealImpl value, $Res Function(_$ProductInMealImpl) then) = + __$$ProductInMealImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@JsonKey(name: 'product_name') String productName, + @JsonKey(name: 'portion') String portion, + @JsonKey(name: 'quantity') int quantity}); +} + +/// @nodoc +class __$$ProductInMealImplCopyWithImpl<$Res> + extends _$ProductInMealCopyWithImpl<$Res, _$ProductInMealImpl> + implements _$$ProductInMealImplCopyWith<$Res> { + __$$ProductInMealImplCopyWithImpl( + _$ProductInMealImpl _value, $Res Function(_$ProductInMealImpl) _then) + : super(_value, _then); + + /// Create a copy of ProductInMeal + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? productName = null, + Object? portion = null, + Object? quantity = null, + }) { + return _then(_$ProductInMealImpl( + productName: null == productName + ? _value.productName + : productName // ignore: cast_nullable_to_non_nullable + as String, + portion: null == portion + ? _value.portion + : portion // ignore: cast_nullable_to_non_nullable + as String, + quantity: null == quantity + ? _value.quantity + : quantity // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ProductInMealImpl implements _ProductInMeal { + const _$ProductInMealImpl( + {@JsonKey(name: 'product_name') required this.productName, + @JsonKey(name: 'portion') required this.portion, + @JsonKey(name: 'quantity') required this.quantity}); + + factory _$ProductInMealImpl.fromJson(Map json) => + _$$ProductInMealImplFromJson(json); + + @override + @JsonKey(name: 'product_name') + final String productName; + @override + @JsonKey(name: 'portion') + final String portion; + @override + @JsonKey(name: 'quantity') + final int quantity; + + @override + String toString() { + return 'ProductInMeal(productName: $productName, portion: $portion, quantity: $quantity)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ProductInMealImpl && + (identical(other.productName, productName) || + other.productName == productName) && + (identical(other.portion, portion) || other.portion == portion) && + (identical(other.quantity, quantity) || + other.quantity == quantity)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, productName, portion, quantity); + + /// Create a copy of ProductInMeal + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ProductInMealImplCopyWith<_$ProductInMealImpl> get copyWith => + __$$ProductInMealImplCopyWithImpl<_$ProductInMealImpl>(this, _$identity); + + @override + Map toJson() { + return _$$ProductInMealImplToJson( + this, + ); + } +} + +abstract class _ProductInMeal implements ProductInMeal { + const factory _ProductInMeal( + {@JsonKey(name: 'product_name') required final String productName, + @JsonKey(name: 'portion') required final String portion, + @JsonKey(name: 'quantity') required final int quantity}) = + _$ProductInMealImpl; + + factory _ProductInMeal.fromJson(Map json) = + _$ProductInMealImpl.fromJson; + + @override + @JsonKey(name: 'product_name') + String get productName; + @override + @JsonKey(name: 'portion') + String get portion; + @override + @JsonKey(name: 'quantity') + int get quantity; + + /// Create a copy of ProductInMeal + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ProductInMealImplCopyWith<_$ProductInMealImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/diary/models/diary_day_model.g.dart b/lib/features/diary/models/diary_day_model.g.dart index 05d36efa..93ece876 100644 --- a/lib/features/diary/models/diary_day_model.g.dart +++ b/lib/features/diary/models/diary_day_model.g.dart @@ -74,10 +74,10 @@ Map _$$MealModelImplToJson(_$MealModelImpl instance) => _$DiaryPhotoModelImpl _$$DiaryPhotoModelImplFromJson( Map json) => _$DiaryPhotoModelImpl( - id: json['id'] as String, + id: (json['id'] as num).toInt(), originalName: json['original_name'] as String, path: json['path'] as String, - size: json['size'] as String, + size: (json['size'] as num).toInt(), ); Map _$$DiaryPhotoModelImplToJson( @@ -120,3 +120,17 @@ Map _$$TypeOfDiaryModelImplToJson( 'has_chosen_diet': instance.hasChosenDiet, 'has_chosen_plan': instance.hasChosenPlan, }; + +_$ProductInMealImpl _$$ProductInMealImplFromJson(Map json) => + _$ProductInMealImpl( + productName: json['product_name'] as String, + portion: json['portion'] as String, + quantity: (json['quantity'] as num).toInt(), + ); + +Map _$$ProductInMealImplToJson(_$ProductInMealImpl instance) => + { + 'product_name': instance.productName, + 'portion': instance.portion, + 'quantity': instance.quantity, + }; diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 8d48da04..d7f31da6 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -1,3 +1,6 @@ +import 'dart:io'; + +import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; @@ -78,4 +81,32 @@ class DiaryCubit extends Cubit { Future> getGlossaryListByQuery(String query) async { return await glossaryRepository.getGlossaryListByQuery(query); } + + uploadPhotos({ + required List files, + required int diaryId, + required int mealId, + required String date, + }) async { + await diaryRepository.uploadPhotos( + files: files, + diaryId: diaryId, + mealId: mealId, + ); + await getDiaryDay(date, false); + } + + deletePhoto({ + required DiaryPhotoModel photo, + required int diaryId, + required int mealId, + required String date, + }) async { + await diaryRepository.deletePhoto( + photo: photo, + diaryId: diaryId, + mealId: mealId, + ); + await getDiaryDay(date, false); + } } diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index 5b329b80..bb9e530a 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -1,3 +1,6 @@ +import 'dart:io'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; @@ -10,7 +13,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; -// import 'package:image_picker/image_picker.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:rive/rive.dart'; import 'package:table_calendar/table_calendar.dart'; @@ -30,6 +33,7 @@ class _DiaryScreenState extends State { GlossaryModel? selectedGlossary; final productNameController = TextEditingController(); final quantityController = TextEditingController(); + int currentSelectedPhotoIndex = 0; @override Widget build(BuildContext context) { @@ -172,47 +176,69 @@ class _DiaryScreenState extends State { const SizedBox(height: 16), _addProductButton(diaryDay), const SizedBox(height: 24), - GestureDetector( - onTap: () => showCupertinoActionSheet(), - child: SvgPicture.asset( - width: MediaQuery.of(context).size.width - 32, - 'assets/no_one_image_was_added.svg', + if (diaryDay.meals[currentSelectedMealNumber - 1].photos.isEmpty) + GestureDetector( + onTap: () => _showImagePicker(diaryDay), + child: SvgPicture.asset( + width: MediaQuery.of(context).size.width - 32, + 'assets/no_one_image_was_added.svg', + ), + ), + if (diaryDay.meals[currentSelectedMealNumber - 1].photos.isNotEmpty) + _photoGallery( + diaryDay.meals[currentSelectedMealNumber - 1].photos, + diaryDay, ), - ), ], ), ), ); } - showCupertinoActionSheet() { - showCupertinoModalPopup( + _showImagePicker(DiaryDayModel diaryDay) { + showCupertinoModalPopup( context: context, - builder: (_) => CupertinoActionSheet( + builder: (context) => CupertinoActionSheet( actions: [ CupertinoActionSheetAction( onPressed: () async { - // final picker = ImagePicker(); - // List? images = await picker.pickMultiImage(); + final picker = ImagePicker(); + XFile? image = await picker.pickImage(source: ImageSource.camera); + if (image != null) + diaryCubit.uploadPhotos( + files: [File(image.path)], + diaryId: diaryDay.id, + mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, + date: _selectedDay.toString(), + ); + Navigator.of(context).pop(); }, - child: Row( - children: [ - const Icon(Icons.photo), - const SizedBox(width: 8), - const Text('Галерея'), + child: const Row( + children: const [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), ], ), ), CupertinoActionSheetAction( onPressed: () async { - // final picker = ImagePicker(); - // XFile? image = await picker.pickImage(source: ImageSource.camera); + final picker = ImagePicker(); + List? images = await picker.pickMultiImage(); + if (images.isNotEmpty) + diaryCubit.uploadPhotos( + files: images.map((f) => File(f.path)).toList(), + diaryId: diaryDay.id, + mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, + date: _selectedDay.toString(), + ); + Navigator.of(context).pop(); }, - child: Row( - children: [ - const Icon(Icons.photo_camera), - const SizedBox(width: 8), - const Text('Фото'), + child: const Row( + children: const [ + Icon(Icons.photo), + SizedBox(width: 8), + Text('Галерея'), ], ), ), @@ -226,6 +252,104 @@ class _DiaryScreenState extends State { ); } + _photoGallery(List photos, DiaryDayModel diaryDay) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.topRight, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12), + child: CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + photos[currentSelectedPhotoIndex].path, + ), + ), + Padding( + padding: const EdgeInsets.all(12), + child: IconButton.filled( + style: IconButton.styleFrom( + backgroundColor: Colors.white, + ), + onPressed: () { + setState(() { + if (currentSelectedPhotoIndex == photos.length - 1 && currentSelectedPhotoIndex != 0) { + currentSelectedPhotoIndex = photos.length - 2; + } + }); + diaryCubit.deletePhoto( + photo: photos[currentSelectedPhotoIndex], + diaryId: diaryDay.id, + mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, + date: _selectedDay.toString(), + ); + }, + icon: Icon( + Icons.close_rounded, + color: Colors.grey, + ), + ), + ), + ], + ), + const SizedBox(height: 8), + SingleChildScrollView( + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + for (int i = 0; i < photos.length + 1; i++) ...[ + if (i == photos.length) + Padding( + padding: const EdgeInsets.only(left: 8), + child: InkWell( + onTap: () => _showImagePicker(diaryDay), + borderRadius: BorderRadius.circular(8), + child: Ink( + height: 52, + width: 52, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Color(0xFFF1F2F6), + ), + child: Icon( + Icons.add_rounded, + color: appTheme.blueA400, + ), + ), + ), + ), + if (i < photos.length) + GestureDetector( + onTap: () { + setState(() { + currentSelectedPhotoIndex = i; + }); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + height: 52, + width: 52, + child: CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + photos[i].path, + fit: BoxFit.fitWidth, + ), + ), + ), + ), + if (i < photos.length - 1) const SizedBox(width: 8), + ], + ], + ), + ), + ], + ); + } + _mealButton(int mealNumber) { bool isActive = currentSelectedMealNumber == mealNumber; diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart index d76219b5..b9b8243c 100644 --- a/lib/features/diary/repositories/diary_repository.dart +++ b/lib/features/diary/repositories/diary_repository.dart @@ -1,3 +1,4 @@ +import 'dart:io'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/services/diary_service.dart'; @@ -66,4 +67,39 @@ class DiaryRepository { portionId: portionId, ); } + + Future uploadPhotos({ + required int diaryId, + required int mealId, + required List files, + }) async { + try { + for (int i = 0; i < files.length; i++) { + await diaryService.uploadPhotoOfMeal( + diaryId: diaryId, + mealId: mealId, + photo: files[i], + ); + } + } catch (e) { + rethrow; + } + } + + Future deletePhoto({ + required int diaryId, + required int mealId, + required DiaryPhotoModel photo, + }) async { + try { + await diaryService.deletePhotoOfMeal( + diaryId: diaryId, + mealId: mealId, + id: photo.id, + path: photo.path, + ); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/diary/services/diary_service.dart b/lib/features/diary/services/diary_service.dart index 0b16782c..16990b1b 100644 --- a/lib/features/diary/services/diary_service.dart +++ b/lib/features/diary/services/diary_service.dart @@ -1,23 +1,11 @@ +import 'dart:io'; import 'package:dio/dio.dart'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/models/diary_index_model.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:retrofit/retrofit.dart'; -part 'diary_service.freezed.dart'; part 'diary_service.g.dart'; -@freezed -class ProductInMeal with _$ProductInMeal { - const factory ProductInMeal({ - @JsonKey(name: 'product_name') required String productName, - @JsonKey(name: 'portion') required String portion, - @JsonKey(name: 'quantity') required int quantity, - }) = _ProductInMeal; - - factory ProductInMeal.fromJson(Map json) => _$ProductInMealFromJson(json); -} - @RestApi() abstract class DiaryService { factory DiaryService(Dio dio, {String baseUrl}) = _DiaryService; @@ -32,6 +20,22 @@ abstract class DiaryService { @Path('diary_id') required int index, }); + @POST('/api/diary/{diary_id}/meal/{meal_id}/photo/upload') + @MultiPart() + Future uploadPhotoOfMeal({ + @Path('diary_id') required int diaryId, + @Path('meal_id') required int mealId, + @Part(name: 'photo') required File photo, + }); + + @DELETE('/api/diary/{diary_id}/meal/{meal_id}/photo/delete') + Future deletePhotoOfMeal({ + @Path('diary_id') required int diaryId, + @Path('meal_id') required int mealId, + @Query('id') required int id, + @Query('path') required String path, + }); + @POST('/api/diary/{diary_id}/meal/{meal_id}/portions/create') Future addProductToMeal({ @Path('diary_id') required int diaryId, diff --git a/lib/features/diary/services/diary_service.freezed.dart b/lib/features/diary/services/diary_service.freezed.dart deleted file mode 100644 index 258040d1..00000000 --- a/lib/features/diary/services/diary_service.freezed.dart +++ /dev/null @@ -1,220 +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 'diary_service.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'); - -ProductInMeal _$ProductInMealFromJson(Map json) { - return _ProductInMeal.fromJson(json); -} - -/// @nodoc -mixin _$ProductInMeal { - @JsonKey(name: 'product_name') - String get productName => throw _privateConstructorUsedError; - @JsonKey(name: 'portion') - String get portion => throw _privateConstructorUsedError; - @JsonKey(name: 'quantity') - int get quantity => throw _privateConstructorUsedError; - - /// Serializes this ProductInMeal to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ProductInMeal - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ProductInMealCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ProductInMealCopyWith<$Res> { - factory $ProductInMealCopyWith( - ProductInMeal value, $Res Function(ProductInMeal) then) = - _$ProductInMealCopyWithImpl<$Res, ProductInMeal>; - @useResult - $Res call( - {@JsonKey(name: 'product_name') String productName, - @JsonKey(name: 'portion') String portion, - @JsonKey(name: 'quantity') int quantity}); -} - -/// @nodoc -class _$ProductInMealCopyWithImpl<$Res, $Val extends ProductInMeal> - implements $ProductInMealCopyWith<$Res> { - _$ProductInMealCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ProductInMeal - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? productName = null, - Object? portion = null, - Object? quantity = null, - }) { - return _then(_value.copyWith( - productName: null == productName - ? _value.productName - : productName // ignore: cast_nullable_to_non_nullable - as String, - portion: null == portion - ? _value.portion - : portion // ignore: cast_nullable_to_non_nullable - as String, - quantity: null == quantity - ? _value.quantity - : quantity // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ProductInMealImplCopyWith<$Res> - implements $ProductInMealCopyWith<$Res> { - factory _$$ProductInMealImplCopyWith( - _$ProductInMealImpl value, $Res Function(_$ProductInMealImpl) then) = - __$$ProductInMealImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {@JsonKey(name: 'product_name') String productName, - @JsonKey(name: 'portion') String portion, - @JsonKey(name: 'quantity') int quantity}); -} - -/// @nodoc -class __$$ProductInMealImplCopyWithImpl<$Res> - extends _$ProductInMealCopyWithImpl<$Res, _$ProductInMealImpl> - implements _$$ProductInMealImplCopyWith<$Res> { - __$$ProductInMealImplCopyWithImpl( - _$ProductInMealImpl _value, $Res Function(_$ProductInMealImpl) _then) - : super(_value, _then); - - /// Create a copy of ProductInMeal - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? productName = null, - Object? portion = null, - Object? quantity = null, - }) { - return _then(_$ProductInMealImpl( - productName: null == productName - ? _value.productName - : productName // ignore: cast_nullable_to_non_nullable - as String, - portion: null == portion - ? _value.portion - : portion // ignore: cast_nullable_to_non_nullable - as String, - quantity: null == quantity - ? _value.quantity - : quantity // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ProductInMealImpl implements _ProductInMeal { - const _$ProductInMealImpl( - {@JsonKey(name: 'product_name') required this.productName, - @JsonKey(name: 'portion') required this.portion, - @JsonKey(name: 'quantity') required this.quantity}); - - factory _$ProductInMealImpl.fromJson(Map json) => - _$$ProductInMealImplFromJson(json); - - @override - @JsonKey(name: 'product_name') - final String productName; - @override - @JsonKey(name: 'portion') - final String portion; - @override - @JsonKey(name: 'quantity') - final int quantity; - - @override - String toString() { - return 'ProductInMeal(productName: $productName, portion: $portion, quantity: $quantity)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ProductInMealImpl && - (identical(other.productName, productName) || - other.productName == productName) && - (identical(other.portion, portion) || other.portion == portion) && - (identical(other.quantity, quantity) || - other.quantity == quantity)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, productName, portion, quantity); - - /// Create a copy of ProductInMeal - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ProductInMealImplCopyWith<_$ProductInMealImpl> get copyWith => - __$$ProductInMealImplCopyWithImpl<_$ProductInMealImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ProductInMealImplToJson( - this, - ); - } -} - -abstract class _ProductInMeal implements ProductInMeal { - const factory _ProductInMeal( - {@JsonKey(name: 'product_name') required final String productName, - @JsonKey(name: 'portion') required final String portion, - @JsonKey(name: 'quantity') required final int quantity}) = - _$ProductInMealImpl; - - factory _ProductInMeal.fromJson(Map json) = - _$ProductInMealImpl.fromJson; - - @override - @JsonKey(name: 'product_name') - String get productName; - @override - @JsonKey(name: 'portion') - String get portion; - @override - @JsonKey(name: 'quantity') - int get quantity; - - /// Create a copy of ProductInMeal - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ProductInMealImplCopyWith<_$ProductInMealImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/diary/services/diary_service.g.dart b/lib/features/diary/services/diary_service.g.dart index 177dd20f..74de16af 100644 --- a/lib/features/diary/services/diary_service.g.dart +++ b/lib/features/diary/services/diary_service.g.dart @@ -2,24 +2,6 @@ part of 'diary_service.dart'; -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ProductInMealImpl _$$ProductInMealImplFromJson(Map json) => - _$ProductInMealImpl( - productName: json['product_name'] as String, - portion: json['portion'] as String, - quantity: (json['quantity'] as num).toInt(), - ); - -Map _$$ProductInMealImplToJson(_$ProductInMealImpl instance) => - { - 'product_name': instance.productName, - 'portion': instance.portion, - 'quantity': instance.quantity, - }; - // ************************************************************************** // RetrofitGenerator // ************************************************************************** @@ -105,6 +87,76 @@ class _DiaryService implements DiaryService { return _value; } + @override + Future uploadPhotoOfMeal({ + required int diaryId, + required int mealId, + required File photo, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = FormData(); + _data.files.add(MapEntry( + 'photo', + MultipartFile.fromFileSync( + photo.path, + filename: photo.path.split(Platform.pathSeparator).last, + ), + )); + final _options = _setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + contentType: 'multipart/form-data', + ) + .compose( + _dio.options, + '/api/diary/${diaryId}/meal/${mealId}/photo/upload', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + + @override + Future deletePhotoOfMeal({ + required int diaryId, + required int mealId, + required int id, + required String path, + }) async { + final _extra = {}; + final queryParameters = { + r'id': id, + r'path': path, + }; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'DELETE', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diary/${diaryId}/meal/${mealId}/photo/delete', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + @override Future addProductToMeal({ required int diaryId,