From 941cfb04cc2e4dc12f7c4a1a69540d2b5595070e Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Tue, 20 May 2025 18:28:32 +0300 Subject: [PATCH] feat(diet): add diet editing --- lib/core/widgets/custom_popup.dart | 10 ++++++- lib/core/widgets/show_error_dialog.dart | 4 +-- .../presentation/create_diet_screen.dart | 28 ++++++++----------- .../presentation/cubit/create_diet_cubit.dart | 26 +++++++++++++++-- .../widgets/status_drop_down_widget.dart | 14 ++++++++-- .../presentation/pages/about_diet_screen.dart | 3 ++ .../feed/models/diets_pagination_model.dart | 4 +-- .../presentation/widgets/diets_widget.dart | 1 + .../presentation/widgets/my_diet_widget.dart | 2 ++ 9 files changed, 66 insertions(+), 26 deletions(-) diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart index 837c165b..011de1cd 100644 --- a/lib/core/widgets/custom_popup.dart +++ b/lib/core/widgets/custom_popup.dart @@ -6,7 +6,9 @@ import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart'; +import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; @@ -28,6 +30,7 @@ class CustomPopup extends StatelessWidget { final int? id; final FeedBlogPostModel? feedBlogPostModel; final Recipe? recipe; + final Diet? diet; const CustomPopup({ super.key, @@ -38,6 +41,7 @@ class CustomPopup extends StatelessWidget { this.slug, this.feedBlogPostModel, this.recipe, + this.diet, }); @override @@ -94,11 +98,15 @@ class CustomPopup extends StatelessWidget { sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, ); }); - addItem('assets/icons/edit_paint.svg', 'Редактировать', () { + addItem('assets/icons/edit_paint.svg', 'Редактировать', () async { if (complainedContentType == ComplainedContent.posts) { GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedBlogPostModel); } else if (complainedContentType == ComplainedContent.recipe) { GoRouter.of(context).pushNamed(RecipeCreateScreen.name, extra: recipe); + } else if (complainedContentType == ComplainedContent.diet) { + await showErrorDialog(context, title: 'Предупреждение', description: 'Диета будет перенесена в архив. Вы создаете новую диету на основе существующей.'); + await sl().unpublishDiet(id!); + GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: diet); } else { showInfoToast('В разработке'); return; diff --git a/lib/core/widgets/show_error_dialog.dart b/lib/core/widgets/show_error_dialog.dart index a064538c..9685ad14 100644 --- a/lib/core/widgets/show_error_dialog.dart +++ b/lib/core/widgets/show_error_dialog.dart @@ -5,8 +5,8 @@ import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -void showErrorDialog(BuildContext context, {String? title, String? description}) { - showDialog( +Future showErrorDialog(BuildContext context, {String? title, String? description}) async { + return await showDialog( context: context, barrierColor: Colors.transparent, builder: (BuildContext context) { diff --git a/lib/features/create_diet/presentation/create_diet_screen.dart b/lib/features/create_diet/presentation/create_diet_screen.dart index 48d1d27e..f0f729d4 100644 --- a/lib/features/create_diet/presentation/create_diet_screen.dart +++ b/lib/features/create_diet/presentation/create_diet_screen.dart @@ -27,8 +27,11 @@ import 'package:go_router/go_router.dart'; class CreateDietScreen extends StatelessWidget { static const name = 'createDietScreen'; static const path = '/create-diet-screen'; + const CreateDietScreen({super.key, this.diet}); + final Diet? diet; + @override Widget build(BuildContext context) { return BlocProvider( @@ -40,6 +43,7 @@ class CreateDietScreen extends StatelessWidget { class CreateDietScreenContent extends StatefulWidget { const CreateDietScreenContent({super.key, this.diet}); + final Diet? diet; @override @@ -95,7 +99,7 @@ class _CreateDietScreenContentState extends State { appBar: AppBar( centerTitle: true, title: Text( - widget.diet != null ? 'Редактирование диеты' : s.create_diet, + s.create_diet, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, softWrap: true, @@ -151,7 +155,7 @@ class _CreateDietScreenContentState extends State { const SizedBox(height: 12), const DietDurationDropDownWidget(), const SizedBox(height: 12), - const StatusDropDownWidget(), + StatusDropDownWidget(dietState: widget.diet?.state), const SizedBox(height: 16), const DietTypeChoiceWidget(), const SizedBox(height: 16), @@ -247,23 +251,13 @@ class _CreateDietScreenContentState extends State { else CustomOutlinedButton( onPressed: () async { - if (widget.diet != null) { - final result = await context.read().updateDiet( - widget.diet!.id, - ); - if (result == 'error') { - if (!context.mounted) return; - showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}'); - } - } else { - final result = await context.read().createDiet(); - if (result == 'error') { - if (!context.mounted) return; - showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}'); - } + final result = await context.read().createDiet(); + if (result == 'error') { + if (!context.mounted) return; + showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}'); } }, - text: widget.diet != null ? s.save : s.publish, + text: s.publish, height: 52, buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), buttonStyle: ButtonStyle( diff --git a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart index 820d36b7..8e0d4a8b 100644 --- a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart +++ b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart @@ -12,6 +12,8 @@ import 'package:eatvio_app/features/glossary/repositories/glossary_repository.da import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; class CreateDietCubit extends Cubit { final CreateDietRepository createDietRepository; @@ -319,18 +321,38 @@ class CreateDietCubit extends Cubit { return diet; } + Future downloadImageAsFile(String imageUrl) async { + final response = await http.get(Uri.parse(imageUrl)); + + if (response.statusCode == 200) { + final tempDir = await getTemporaryDirectory(); + final filePath = '${tempDir.path}/${imageUrl.split('/').last}'; + final file = File(filePath); + return await file.writeAsBytes(response.bodyBytes); + } else { + throw Exception('Не удалось загрузить изображение'); + } + } + Future createDiet() async { emit(state.copyWith(isDietPublishing: true)); if (!_isFromValid()) return 'validation'; try { - final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path)); + File? file; + if (state.dietData.imageUrl != null) { + file = await downloadImageAsFile(state.dietData.imageUrl!); + } + File? image; + if (state.dietData.image?.path != null) { + image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path)); + } final dietState = state.dietData.state!; final dailySchedule = state.dietData.dailySchedule; await createDietRepository.createDiet( name: state.dietData.name ?? '', duration: state.dietData.duration ?? 7, state: dietState, - image: image, + image: image ?? file!, meatType: state.dietData.type['meat'] == true ? '1' : '0', carbType: state.dietData.type['carb'] == true ? '1' : '0', vegType: state.dietData.type['veg'] == true ? '1' : '0', diff --git a/lib/features/create_diet/presentation/widgets/status_drop_down_widget.dart b/lib/features/create_diet/presentation/widgets/status_drop_down_widget.dart index 75b30b59..249e897a 100644 --- a/lib/features/create_diet/presentation/widgets/status_drop_down_widget.dart +++ b/lib/features/create_diet/presentation/widgets/status_drop_down_widget.dart @@ -11,7 +11,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:collection/collection.dart'; class StatusDropDownWidget extends StatefulWidget { - const StatusDropDownWidget({super.key}); + final DietState? dietState; + + const StatusDropDownWidget({super.key, this.dietState}); @override State createState() => _StatusDropDownWidgetState(); @@ -19,6 +21,7 @@ class StatusDropDownWidget extends StatefulWidget { class _StatusDropDownWidgetState extends State { final List dietStateTitles = []; + String? currentState; // late final List dietStates = DietState.values; @override @@ -39,6 +42,13 @@ class _StatusDropDownWidgetState extends State { // } // } } + if (widget.dietState != null) { + if (widget.dietState == DietState.active) { + currentState = dietStateTitles[0]; + } else if (widget.dietState == DietState.personal) { + currentState = dietStateTitles[1]; + } + } } @override @@ -77,7 +87,7 @@ class _StatusDropDownWidgetState extends State { ), ) .toList(), - value: state.dietState == DietState.active ? dietStateTitles[0] : state.dietState == DietState.personal ? dietStateTitles[1] : null, + value: currentState, ), if (state.dietStateError != null) ...[ const SizedBox( diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index 8a21395a..4dd3878d 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -321,6 +321,7 @@ class _AboutDietScreenState extends State { path: 'diet/${widget.diet.id}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, + diet: widget.diet, ), const SizedBox(width: 16), ], @@ -501,6 +502,7 @@ class _AboutDietScreenState extends State { path: 'diet/${widget.diet.id}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, + diet: widget.diet, ), const SizedBox(width: 16), ], @@ -527,6 +529,7 @@ class _AboutDietScreenState extends State { path: 'diet/${widget.diet.id}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, + diet: widget.diet, ), const SizedBox(width: 16), ], diff --git a/lib/features/feed/models/diets_pagination_model.dart b/lib/features/feed/models/diets_pagination_model.dart index 14538a30..4c1f41fd 100644 --- a/lib/features/feed/models/diets_pagination_model.dart +++ b/lib/features/feed/models/diets_pagination_model.dart @@ -42,7 +42,7 @@ class Diet with _$Diet { enum DietState { @JsonKey(name: 'active') active, - @JsonKey(name: 'draft') + @JsonKey(name: 'drafted') draft, @JsonKey(name: 'personal') personal; @@ -53,7 +53,7 @@ enum DietState { case DietState.active: return 'active'; case DietState.draft: - return 'draft'; + return 'drafted'; case DietState.personal: return 'personal'; } diff --git a/lib/features/feed/presentation/widgets/diets_widget.dart b/lib/features/feed/presentation/widgets/diets_widget.dart index 87091ad8..bcf1056a 100644 --- a/lib/features/feed/presentation/widgets/diets_widget.dart +++ b/lib/features/feed/presentation/widgets/diets_widget.dart @@ -93,6 +93,7 @@ class _DietsWidgetState extends State { path: 'diet/${widget.diet.id}', id: widget.diet.id, complainedContentType: ComplainedContent.diet, + diet: widget.diet, ), ), ), diff --git a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart index cfbd39d5..99489f9d 100644 --- a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart +++ b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; +import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/core/widgets/success_published_diet_bottom_sheet.dart'; import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; @@ -194,6 +195,7 @@ class _MyDietWidgetState extends State { showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); } } else { + await showErrorDialog(context, title: 'Предупреждение', description: 'Вы можете создать новую диету на основе существующей.'); GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: widget.diet); } },