import 'package:cached_network_image/cached_network_image.dart'; import 'package:dismissible_page/dismissible_page.dart'; 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/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'; import 'package:eatvio_app/features/feed/presentation/widgets/subscribe_button_widget.dart'; 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'; class BlogPostWidget extends StatefulWidget { final int index; const BlogPostWidget({ super.key, required this.index, }); @override State createState() => _BlogPostWidgetState(); } class _BlogPostWidgetState extends State { late bool isLiked; late int totalLikes; bool isMoreOptionsVisible = false; @override Widget build(BuildContext context) { return BlocBuilder( builder: (BuildContext context, FeedState state) { if (state is FeedStateLoaded) { isLiked = state.feedBlogPostList[widget.index].wasLikedByMe; totalLikes = state.feedBlogPostList[widget.index].totalLikes; return InkWell( borderRadius: BorderRadius.circular(12), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', extra: state.feedBlogPostList[widget.index], ), child: Ink( decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.white, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ GestureDetector( onTap: () { GoRouter.of(context).pushNamed( 'profileScreen', extra: Author( authorSlug: state.feedBlogPostList[widget.index].author.authorSlug, authorName: state.feedBlogPostList[widget.index].author.authorName, authorProfileIcon: state.feedBlogPostList[widget.index].author.authorProfileIcon, authorIsMe: state.feedBlogPostList[widget.index].author.authorIsMe, ), ); }, child: Row( children: [ CircleAvatar( radius: 20, backgroundColor: Colors.black12, foregroundImage: NetworkImage( state.feedBlogPostList[widget.index].author.authorProfileIcon, ), ), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( state.feedBlogPostList[widget.index].author.authorName, style: const TextStyle( height: 1, color: Colors.black87, fontWeight: FontWeight.w500, fontSize: 16, ), ), const SizedBox(height: 4), Text( '${calculateDifference(state.feedBlogPostList[widget.index].createdAt)} в ${state.feedBlogPostList[widget.index].createdAt.hour.toString().padLeft(2, '0')}:${state.feedBlogPostList[widget.index].createdAt.minute.toString().padLeft(2, '0')}', style: const TextStyle( height: 1, color: Colors.black45, fontSize: 14, ), ), ], ), ], ), ), const Spacer(), if (!(state.feedBlogPostList[widget.index].author.isEntitySubscribedByMe ?? false) && !state.feedBlogPostList[widget.index].author.authorIsMe) Padding( padding: const EdgeInsets.only(right: 8), child: SubscribeButtonWidget( 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), ), ), ), ), ], ), ), if (state.feedBlogPostList[widget.index].title.isNotEmpty || state.feedBlogPostList[widget.index].description.isNotEmpty) const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Text( state.feedBlogPostList[widget.index].title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.black87, ), ), ), if (state.feedBlogPostList[widget.index].title.isNotEmpty && state.feedBlogPostList[widget.index].description.isNotEmpty) const SizedBox(height: 8), if (state.feedBlogPostList[widget.index].description.isNotEmpty) Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Html( data: state.feedBlogPostList[widget.index].description.length > 200 ? state.feedBlogPostList[widget.index].description.substring(0, 200) : state.feedBlogPostList[widget.index].description, style: { "*": Style( fontSize: FontSize(16), color: Colors.black87, ), }, ), ), if (state.feedBlogPostList[widget.index].description.length > 200) const Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Text( 'Показать ещё', style: TextStyle( height: 1, color: Color(0xFF8B8B8B), fontSize: 14, fontWeight: FontWeight.w600, ), ), ), if (state.feedBlogPostList[widget.index].title.isNotEmpty || state.feedBlogPostList[widget.index].description.isNotEmpty) const SizedBox(height: 8), GestureDetector( onTap: () { context.pushTransparentRoute( rootNavigator: true, PhotoViewScreen( imageUrls: ['$baseUrl/storage/${state.feedBlogPostList[widget.index].image}'], index: 0, ), ); }, child: Hero( tag: '$baseUrl/storage/${state.feedBlogPostList[widget.index].image}', child: CachedNetworkImage( fit: BoxFit.fitWidth, width: MediaQuery.of(context).size.width, imageUrl: '$baseUrl/storage/${state.feedBlogPostList[widget.index].image}', errorWidget: (context, str, object) => const SizedBox.shrink(), ), ), ), const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ FeedIconButton( icon: isLiked ? Icons.favorite_rounded : Icons.favorite_border_rounded, text: totalLikes.toString(), iconColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null, backgroundColor: isLiked ? Colors.red.withValues(alpha: 0.1) : null, textColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null, onTap: () async { setState(() { isLiked = !isLiked; isLiked ? totalLikes++ : totalLikes--; }); isLiked ? await context.read().likePost(state.feedBlogPostList[widget.index].id) : await context.read().unlikedPost(state.feedBlogPostList[widget.index].id); if (!context.mounted) return; await context.read().loadFeedBlogPostList(); }, ), const SizedBox(width: 8), FeedIconButton( svgIcon: SvgPicture.asset( 'assets/icons/comment.svg', height: 20, width: 20, colorFilter: ColorFilter.mode( appTheme.gray600, BlendMode.srcIn, ), ), text: state.feedBlogPostList[widget.index].totalComments.toString(), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', extra: state.feedBlogPostList[widget.index], ), ), const SizedBox(width: 8), FeedIconButton( svgIcon: SvgPicture.asset( height: 20, width: 20, 'assets/icons/share.svg', colorFilter: ColorFilter.mode(appTheme.gray600, BlendMode.srcIn), ), onTap: () { final box = context.findRenderObject() as RenderBox?; Share.shareUri( Uri.parse( '$baseUrl/ru/blog_post/${state.feedBlogPostList[widget.index].id}', ), sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, ); }, ), const Spacer(), Icon( Icons.visibility_rounded, size: 16, color: appTheme.gray500, ), const SizedBox(width: 4), Text( state.feedBlogPostList[widget.index].totalViews.toString(), style: TextStyle( fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400, ), ), ], ), ), const SizedBox(height: 8), ], ), ), ); } return const SizedBox.shrink(); }, ); } }