diff --git a/.gitea/workflows/test-flight-cd.yaml b/.gitea/workflows/test-flight-cd.yaml
index 2a4f5aec..1bb78c94 100644
--- a/.gitea/workflows/test-flight-cd.yaml
+++ b/.gitea/workflows/test-flight-cd.yaml
@@ -11,6 +11,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
+ - name: Remove cache
+ run: rm -rf /Users/gitea_runner_ci/Library/Developer/Xcode/DerivedData/*
+
- name: Download provisioning
uses: mso7nueh/apple-provisioning-profiles-downloader@v1.0.8
with:
diff --git a/assets/icons/edit_paint.svg b/assets/icons/edit_paint.svg
new file mode 100644
index 00000000..e4a9eeef
--- /dev/null
+++ b/assets/icons/edit_paint.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/icons/red_trash.svg b/assets/icons/red_trash.svg
new file mode 100644
index 00000000..3864fb63
--- /dev/null
+++ b/assets/icons/red_trash.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/remove_from_public.svg b/assets/icons/remove_from_public.svg
new file mode 100644
index 00000000..36213c53
--- /dev/null
+++ b/assets/icons/remove_from_public.svg
@@ -0,0 +1,5 @@
+
diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart
new file mode 100644
index 00000000..967f6fee
--- /dev/null
+++ b/lib/core/widgets/custom_popup.dart
@@ -0,0 +1,175 @@
+import 'package:eatvio_app/core/const.dart';
+import 'package:eatvio_app/core/enums/complained_content.dart';
+import 'package:eatvio_app/core/locator_service.dart';
+import 'package:eatvio_app/core/theme/theme_helper.dart';
+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/feed/models/feed_blog_post_model.dart';
+import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
+import 'package:eatvio_app/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:go_router/go_router.dart';
+import 'package:share_plus/share_plus.dart';
+
+class CustomPopup extends StatelessWidget {
+ final bool isAuthor;
+ final ComplainedContent? complainedContentType;
+ final String path;
+ final String? slug;
+ final int? id;
+ final FeedBlogPostModel? feedBlogPostModel;
+
+ const CustomPopup({
+ super.key,
+ required this.isAuthor,
+ this.complainedContentType,
+ required this.path,
+ this.id,
+ this.slug,
+ this.feedBlogPostModel,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final s = S.of(context);
+
+ return GestureDetector(
+ onTap: () async {
+ final RenderBox button = context.findRenderObject() as RenderBox;
+ final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
+ final Offset position = button.localToGlobal(Offset.zero, ancestor: overlay);
+
+ await showMenu(
+ context: context,
+ position: RelativeRect.fromLTRB(
+ position.dx,
+ position.dy + button.size.height,
+ position.dx + button.size.width,
+ position.dy,
+ ),
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
+ color: Colors.white,
+ constraints: const BoxConstraints(minWidth: 260),
+ surfaceTintColor: Colors.transparent,
+ elevation: 2,
+ items: _buildMenuItems(s, context),
+ );
+ },
+ child: const Icon(
+ Icons.more_horiz_rounded,
+ ),
+ );
+ }
+
+ List _buildMenuItems(s, context) {
+ final entries = [];
+
+ void addItem(String icon, String text, Function() onTap) {
+ entries.add(_buildCompactItem(icon: icon, text: text, onTap: onTap));
+ entries.add(_buildDivider());
+ }
+
+ if (isAuthor) {
+ addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () {
+ Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
+ showInfoToast(s.link_was_copied);
+ });
+ addItem('assets/icons/share2.svg', 'Поделиться', () {
+ final box = context.findRenderObject() as RenderBox?;
+ Share.shareUri(
+ Uri.parse(
+ '$baseUrl/ru/$path',
+ ),
+ sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
+ );
+ });
+ addItem('assets/icons/edit_paint.svg', 'Редактировать', () {
+ if (complainedContentType == ComplainedContent.posts) {
+ GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedBlogPostModel);
+ }
+ });
+ if (complainedContentType == ComplainedContent.plan || complainedContentType == ComplainedContent.diet) {
+ addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () {});
+ }
+ addItem('assets/icons/red_trash.svg', 'Удалить', () async {
+ if (complainedContentType == ComplainedContent.posts) {
+ final result = await sl().deleteBlogPost(id!);
+ if (!context.mounted) return;
+ if (!result) {
+ showErrorDialog(context, title: S.current.could_not_delete_blog_post);
+ return;
+ }
+ context.read().loadFeedBlogPostList();
+ }
+ });
+ } else {
+ addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () {
+ Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
+ showInfoToast(s.link_was_copied);
+ });
+ addItem('assets/icons/share2.svg', 'Поделиться', () {
+ final box = context.findRenderObject() as RenderBox?;
+ Share.shareUri(
+ Uri.parse(
+ '$baseUrl/ru/$path',
+ ),
+ sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
+ );
+ });
+ addItem('assets/icons/complain.svg', 'Пожаловаться', () {
+ if (complainedContentType != null) {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ builder: (context) => ComplainingBottomSheet(
+ complainedContentType: complainedContentType!,
+ id: id,
+ slug: slug,
+ ),
+ );
+ }
+ });
+ }
+
+ if (entries.isNotEmpty && entries.last is PopupMenuItem) {
+ entries.removeLast();
+ }
+
+ return entries;
+ }
+
+ PopupMenuItem _buildCompactItem({required String icon, required String text, required Function() onTap}) {
+ return PopupMenuItem(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
+ height: 36,
+ onTap: onTap,
+ child: SizedBox(
+ width: double.infinity,
+ child: Row(
+ children: [
+ Text(text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
+ const Spacer(),
+ SvgPicture.asset(icon),
+ ],
+ ),
+ ),
+ );
+ }
+
+ PopupMenuItem _buildDivider() {
+ return PopupMenuItem(
+ enabled: false,
+ height: 1,
+ padding: EdgeInsets.zero,
+ child: Container(
+ margin: const EdgeInsets.symmetric(horizontal: 12),
+ height: 1,
+ color: appTheme.dividerColor,
+ ),
+ );
+ }
+}
diff --git a/lib/features/blog_post/presentation/pages/blog_post_screen.dart b/lib/features/blog_post/presentation/pages/blog_post_screen.dart
index 63f87233..e963a812 100644
--- a/lib/features/blog_post/presentation/pages/blog_post_screen.dart
+++ b/lib/features/blog_post/presentation/pages/blog_post_screen.dart
@@ -3,10 +3,8 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
-import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
-import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart';
+import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
-import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
import 'package:eatvio_app/features/feed/models/feed_blog_post_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';
@@ -14,7 +12,6 @@ import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
-import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
@@ -163,39 +160,14 @@ class _BlogPostScreenState extends State {
appBar: AppBar(
title: Text(widget.blogPost.title),
actions: [
- PopupMenuButton(
- onSelected: (value) async {
- if (value == 'delete') {
- final result = await sl().deleteBlogPost(widget.blogPost.id);
- if (!context.mounted) return;
- if (!result) {
- showErrorDialog(context, title: S.current.could_not_delete_blog_post);
- return;
- }
- context.read().loadFeedBlogPostList();
- Navigator.of(context).pop();
- }
- if (value == 'edit') {
- GoRouter.of(context).replaceNamed('blogPostCreateScreen', extra: widget.blogPost);
- }
- },
- itemBuilder: (context) => [
- if (widget.blogPost.author.authorIsMe) const PopupMenuItem(value: 'edit', child: Text('Редактировать')),
- if (widget.blogPost.author.authorIsMe) const PopupMenuItem(value: 'delete', child: Text('Удалить')),
- if (!widget.blogPost.author.authorIsMe)
- PopupMenuItem(
- value: 'report',
- child: const Text('Пожаловаться'),
- onTap: () {
- showModalBottomSheet(
- context: context,
- isScrollControlled: true,
- builder: (context) => ComplainingBottomSheet(complainedContentType: ComplainedContent.posts, id: widget.blogPost.id),
- );
- },
- ),
- ],
+ CustomPopup(
+ isAuthor: widget.blogPost.author.authorIsMe,
+ complainedContentType: ComplainedContent.posts,
+ path: 'blog_post/${(widget.blogPost.id)}',
+ id: widget.blogPost.id,
+ feedBlogPostModel: widget.blogPost,
),
+ const SizedBox(width: 16),
],
),
body: SingleChildScrollView(
diff --git a/lib/features/feed/presentation/widgets/blog_post_widget.dart b/lib/features/feed/presentation/widgets/blog_post_widget.dart
index 605df679..f5b8dd2d 100644
--- a/lib/features/feed/presentation/widgets/blog_post_widget.dart
+++ b/lib/features/feed/presentation/widgets/blog_post_widget.dart
@@ -4,9 +4,9 @@ import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
+import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.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/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart';
@@ -14,7 +14,6 @@ import 'package:eatvio_app/features/feed/presentation/widgets/subscribe_button_w
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
-import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart';
@@ -119,30 +118,12 @@ class _BlogPostWidgetState extends State {
slug: state.feedBlogPostList[widget.index].author.authorSlug,
),
),
- PortalTarget(
- visible: isMoreOptionsVisible,
- portalFollower: MoreOptions(
- complainedContentType: ComplainedContent.posts,
- path: 'blog_post/${(state.feedBlogPostList[widget.index].id)}',
- id: state.feedBlogPostList[widget.index].id,
- ),
- anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight),
- child: InkWell(
- onTap: () {
- setState(() {
- isMoreOptionsVisible = !isMoreOptionsVisible;
- });
- },
- child: SizedBox(
- width: 32,
- height: 24,
- child: Material(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- child: const Icon(Icons.more_horiz),
- ),
- ),
- ),
+ CustomPopup(
+ isAuthor: state.feedBlogPostList[widget.index].author.authorIsMe,
+ complainedContentType: ComplainedContent.posts,
+ path: 'blog_post/${(state.feedBlogPostList[widget.index].id)}',
+ id: state.feedBlogPostList[widget.index].id,
+ feedBlogPostModel: state.feedBlogPostList[widget.index],
),
],
),