Merge pull request 'Контекстное меню (три точки)' (#403) from mso7nueh/eatvio_app:main into main

Reviewed-on: eatvio/eatvio_app#403
This commit is contained in:
mso7nueh
2025-04-30 09:18:32 +00:00
7 changed files with 215 additions and 62 deletions

View File

@@ -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<PopupMenuEntry> _buildMenuItems(s, context) {
final entries = <PopupMenuEntry>[];
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<BlogPostCreateCubit>().deleteBlogPost(id!);
if (!context.mounted) return;
if (!result) {
showErrorDialog(context, title: S.current.could_not_delete_blog_post);
return;
}
context.read<FeedCubit>().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,
),
);
}
}