diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart index 711995f2..a4167f11 100644 --- a/lib/core/widgets/custom_popup.dart +++ b/lib/core/widgets/custom_popup.dart @@ -7,7 +7,11 @@ 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/feed/models/feed_blog_post_model.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; +import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_cubit.dart'; +import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -96,23 +100,65 @@ class CustomPopup extends StatelessWidget { } }); if (complainedContentType == ComplainedContent.plan || complainedContentType == ComplainedContent.diet) { - addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () { - showInfoToast('В разработке'); - return; + addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () async { + if (complainedContentType == ComplainedContent.diet) { + final result = await sl().unpublishDiet(id!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('Диета перенесена в архив, обновите ленту'); + } else if (complainedContentType == ComplainedContent.plan) { + final result = await sl().unpublish(slug!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('План перенесен в архив, обновите ленту'); + } }); } addItem('assets/icons/red_trash.svg', 'Удалить', () async { if (complainedContentType == ComplainedContent.posts) { final result = await sl().deleteBlogPost(id!); if (!context.mounted) return; + context.read().loadFeedBlogPostList(); if (!result) { showErrorDialog(context, title: S.current.could_not_delete_blog_post); return; } + showSuccessToast('Пост удален, обновите ленту'); + } else if (complainedContentType == ComplainedContent.recipe) { + final result = await sl().deleteRecipe(slug!); + if (!context.mounted) return; context.read().loadFeedBlogPostList(); - } else { - showInfoToast('В разработке'); - return; + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('Рецепт удален, обновите ленту'); + } else if (complainedContentType == ComplainedContent.diet) { + final result = await sl().deleteDiet(id!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('Диета удалена, обновите ленту'); + } else if (complainedContentType == ComplainedContent.plan) { + final result = await sl().deletePlan(slug!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('План удален, обновите ленту'); } }); } else { diff --git a/lib/features/create_diet/presentation/diet_preview_screen.dart b/lib/features/create_diet/presentation/diet_preview_screen.dart index 759a9ad3..884c283d 100644 --- a/lib/features/create_diet/presentation/diet_preview_screen.dart +++ b/lib/features/create_diet/presentation/diet_preview_screen.dart @@ -1,24 +1,24 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'; import 'package:eatvio_app/core/widgets/cpfc_container.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; class DietPreviewScreen extends StatefulWidget { @@ -190,30 +190,12 @@ class _DietPreviewScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - disableActions: true, - path: 'diet/${widget.diet.id}', - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index d4a07038..8a21395a 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -19,7 +19,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/cancel_diet_botto import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; @@ -32,7 +31,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; class AboutDietScreen extends StatefulWidget { @@ -498,31 +496,13 @@ class _AboutDietScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'diet/${widget.diet.id}', - complainedContentType: ComplainedContent.diet, - id: widget.diet.id, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, + id: widget.diet.id, ), + const SizedBox(width: 16), ], ), body: DataErrorWidget( @@ -542,31 +522,13 @@ class _AboutDietScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'diet/${widget.diet.id}', - complainedContentType: ComplainedContent.diet, - id: widget.diet.id, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, + id: widget.diet.id, ), + const SizedBox(width: 16), ], ), body: const Loader(), diff --git a/lib/features/diets/repositories/diet_repository.dart b/lib/features/diets/repositories/diet_repository.dart index cefc0c26..a3bbbbb3 100644 --- a/lib/features/diets/repositories/diet_repository.dart +++ b/lib/features/diets/repositories/diet_repository.dart @@ -65,6 +65,14 @@ class DietRepository { } } + Future deleteDiet(int id) async { + try { + return await dietsService.deleteDiet(id: id); + } catch (e) { + rethrow; + } + } + void emitLikesStateToFeed(LikesState state) => _dietLikeStateStreamController.add(state); void emitFavoriteStateToFeed(FavoriteState state) => _dietFavoriteStateStreamController.add(state); void emitCommentsStateToFeed(CommentsState state) => _dietCommentsStateStreamController.add(state); diff --git a/lib/features/diets/services/diet_service.dart b/lib/features/diets/services/diet_service.dart index c93e3296..59c2c977 100644 --- a/lib/features/diets/services/diet_service.dart +++ b/lib/features/diets/services/diet_service.dart @@ -39,4 +39,9 @@ abstract class DietService { Future cancelDiet({ @Path() required int id, }); + + @DELETE('/api/diets/delete/{id}') + Future deleteDiet({ + @Path() required int id, + }); } diff --git a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart index 23c7ae37..3f1ea0b0 100644 --- a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart +++ b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart @@ -29,6 +29,7 @@ class DietsCubit extends Cubit { late final StreamSubscription likesStateSubscription; late final StreamSubscription favoriteStateSubscription; late final StreamSubscription commentsStateSubscription; + DietsCubit({ required this.feedRepository, required this.dietsRepository, @@ -342,4 +343,13 @@ class DietsCubit extends Cubit { }, ); } + + Future deleteDiet(int dietId) async { + try { + await dietsRepository.deleteDiet(dietId); + return true; + } catch (e) { + return false; + } + } } diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart index 18c7ebb8..866d4abb 100644 --- a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart @@ -49,7 +49,7 @@ class RecipesCubit extends Cubit { tags: _selectedTags.toList(), query: query, ); - + final recipesTags = (await recipeRepository.getRecipesTags()); emit( state.copyWith( @@ -129,7 +129,7 @@ class RecipesCubit extends Cubit { EasyDebounce.debounce( 'add_or_remove_plan_from_favorite', const Duration(milliseconds: 200), - () async { + () async { try { await aboutRecipeRepository.addToFavorite(slug); } catch (e) { @@ -169,7 +169,7 @@ class RecipesCubit extends Cubit { EasyDebounce.debounce( 'add_or_remove_plan_from_favorite', const Duration(milliseconds: 200), - () async { + () async { try { await aboutRecipeRepository.removeFromFavorite(slug); } catch (e) { @@ -225,7 +225,7 @@ class RecipesCubit extends Cubit { EasyDebounce.debounce( 'like_plan', const Duration(milliseconds: 200), - () async { + () async { try { await aboutRecipeRepository.likeRecipe(slug); } catch (e) { @@ -300,4 +300,13 @@ class RecipesCubit extends Cubit { ), ); } + + Future deleteRecipe(String slug) async { + try { + await aboutRecipeRepository.deleteRecipe(slug); + return true; + } catch (e) { + return false; + } + } } diff --git a/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart b/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart index 6c14003f..33462563 100644 --- a/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart +++ b/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart @@ -204,4 +204,13 @@ class MyPlansCubit extends Cubit { return false; } } + + Future unpublish(String slug) async { + try { + await myPlansRepository.unpublish(slug); + return true; + } catch (e) { + return false; + } + } } diff --git a/lib/features/my_plans/repositories/my_diets_repository.dart b/lib/features/my_plans/repositories/my_diets_repository.dart index a28c88cd..1ee9afb1 100644 --- a/lib/features/my_plans/repositories/my_diets_repository.dart +++ b/lib/features/my_plans/repositories/my_diets_repository.dart @@ -35,5 +35,8 @@ class MyPlansRepository { rethrow; } } - + + Future unpublish(String slug) async { + return await myPlansService.unpublish(slug: slug); + } } diff --git a/lib/features/my_plans/services/my_plans_service.dart b/lib/features/my_plans/services/my_plans_service.dart index ee81e55e..9c5cbf0c 100644 --- a/lib/features/my_plans/services/my_plans_service.dart +++ b/lib/features/my_plans/services/my_plans_service.dart @@ -6,8 +6,7 @@ part 'my_plans_service.g.dart'; @RestApi() abstract class MyPlansService { - - factory MyPlansService(Dio dio, {String baseUrl}) = _MyPlansService; + factory MyPlansService(Dio dio, {String baseUrl}) = _MyPlansService; @GET('/api/plans/index') Future getPlans({ @@ -24,5 +23,8 @@ abstract class MyPlansService { @Path() required String slug, }); - -} \ No newline at end of file + @POST('/api/plans/draft/{slug}') + Future unpublish({ + @Path() required String slug, + }); +} diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index 3d35fc51..72cb71bd 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -15,7 +15,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart' import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; @@ -30,7 +29,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; @@ -143,31 +141,13 @@ class _AboutPlanScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.plan, - path: 'plan/${widget.plan.id}', - slug: widget.plan.slug, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.plan.author.authorIsMe, + path: 'plan/${widget.plan.id}', + complainedContentType: ComplainedContent.plan, + slug: widget.plan.slug, ), + const SizedBox(width: 16), ], ), body: DataErrorWidget(onReload: () => context.read().getPlan(widget.plan.slug)), @@ -380,31 +360,13 @@ class _AboutPlanScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.plan, - path: 'plan/${widget.plan.id}', - slug: widget.plan.slug, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.plan.author.authorIsMe, + path: 'plan/${widget.plan.id}', + complainedContentType: ComplainedContent.plan, + slug: widget.plan.slug, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( diff --git a/lib/features/recipe/presentation/about_recipe_screen.dart b/lib/features/recipe/presentation/about_recipe_screen.dart index cc65a0c2..2436ba0b 100644 --- a/lib/features/recipe/presentation/about_recipe_screen.dart +++ b/lib/features/recipe/presentation/about_recipe_screen.dart @@ -16,7 +16,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart' import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; @@ -32,7 +31,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -398,23 +396,13 @@ class _AboutRecipeScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.recipe, - path: 'recipe/${widget.recipe.slug}', - slug: widget.recipe.slug, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset('assets/icons/more_button.svg', width: 20, height: 20), - ), + CustomPopup( + isAuthor: widget.recipe.author.authorIsMe, + path: 'recipe/${widget.recipe.slug}', + complainedContentType: ComplainedContent.recipe, + slug: widget.recipe.slug, ), + const SizedBox(width: 16), ], ), body: DataErrorWidget(onReload: () => context.read().getRecipe(widget.recipe.slug, index: widget.index)), @@ -432,23 +420,13 @@ class _AboutRecipeScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.recipe, - path: 'recipe/${widget.recipe.slug}', - slug: widget.recipe.slug, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset('assets/icons/more_button.svg', width: 20, height: 20), - ), + CustomPopup( + isAuthor: widget.recipe.author.authorIsMe, + path: 'recipe/${widget.recipe.slug}', + complainedContentType: ComplainedContent.recipe, + slug: widget.recipe.slug, ), + const SizedBox(width: 16), ], ), body: const Loader(), diff --git a/lib/features/recipe/repositories/about_recipe_repository.dart b/lib/features/recipe/repositories/about_recipe_repository.dart index a9178a65..df433f5e 100644 --- a/lib/features/recipe/repositories/about_recipe_repository.dart +++ b/lib/features/recipe/repositories/about_recipe_repository.dart @@ -47,6 +47,14 @@ class AboutRecipeRepository { } } + Future deleteRecipe(String slug) async { + try { + await recipeService.deleteRecipe(slug: slug); + } catch (e) { + rethrow; + } + } + void emitLikesStateToFeed(LikesState state) => _recipeLikeStateStreamController.add(state); void emitFavoriteStateToFeed(FavoriteState state) => _recipeFavoriteStateStreamController.add(state); void emitCommentsStateToFeed(CommentsState state) => _recipeCommentsStateStreamController.add(state); diff --git a/lib/features/recipe/services/about_recipe_service.dart b/lib/features/recipe/services/about_recipe_service.dart index b326f970..21170ed4 100644 --- a/lib/features/recipe/services/about_recipe_service.dart +++ b/lib/features/recipe/services/about_recipe_service.dart @@ -25,4 +25,9 @@ abstract class AboutRecipeService { @GET('/api/recipes/show/{id}') Future getRecipe(@Path() String id); + + @DELETE('/api/recipes/delete/{slug}') + Future deleteRecipe({ + @Path() required String slug, + }); }