diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 7f83f13f..9ff5ebd2 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -80,21 +80,19 @@ class AppRouter { navigatorKey: navigatorKey, redirect: (context, state) { final segments = state.uri.pathSegments; - print(segments); if (segments.length >= 3) { final type = segments[1]; - final slug = segments[2]; + final contentIdentifier = segments[2]; switch (type) { case 'recipe': - print('/recipe-screen?slug=$slug'); - return '/about-recipes-screen?slug=$slug'; + return '/about-recipes-screen?slug=$contentIdentifier'; case 'blog_post': - return '/blog-post?slug=$slug'; + return '/blog-post-screen?slug=$contentIdentifier'; case 'plan': - return '/plan?slug=$slug'; + return '/plan?slug=$contentIdentifier'; case 'diet': - return '/diet?slug=$slug'; + return '/diet?slug=$contentIdentifier'; } } @@ -267,13 +265,13 @@ class AppRouter { name: 'aboutRecipesScreen', builder: (context, state) { final slug = state.uri.queryParameters['slug']; - if (slug != null) { + if (slug != null) { return AboutRecipeScreen( recipeSlug: slug, navigateToComments: false, index: null, ); - } + } final data = state.extra as Map; return AboutRecipeScreen( @@ -309,7 +307,20 @@ class AppRouter { name: 'blogPostCreateScreen', builder: (context, state) => BlogPostCreateScreen(feedBlogPostModel: state.extra != null ? state.extra as FeedBlogPostModel : null), ), - GoRoute(path: '/blog-post-screen', name: 'blogPostScreen', builder: (context, state) => BlogPostScreen(blogPost: state.extra as FeedBlogPostModel)), + GoRoute( + path: '/blog-post-screen', + name: 'blogPostScreen', + builder: (context, state) { + final id = state.uri.queryParameters['slug']; + final parsedId = int.tryParse(id ?? ''); + if (parsedId != null) { + return BlogPostScreen( + id: parsedId, + ); + } + + return BlogPostScreen(id: state.extra as int); + }), GoRoute( path: '/chat-screen', name: 'chatScreen', diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index accb729d..29f820ab 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/core/websockets/ws_service.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart'; import 'package:eatvio_app/features/auth/repositories/auth_repository.dart'; import 'package:eatvio_app/features/auth/services/auth_service.dart'; +import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart'; import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart'; import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart'; import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart'; @@ -158,6 +159,7 @@ Future init() async { sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl())); sl.registerFactory(() => ConsultantFinancesCubit(consultationsRepository: sl())); sl.registerFactory(() => UserPurchasesCubit(consultationsRepository: sl())); + sl.registerFactory(() => BlogPostCubit(sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); diff --git a/lib/features/blog_post/presentation/cubit/blog_post_cubit.dart b/lib/features/blog_post/presentation/cubit/blog_post_cubit.dart new file mode 100644 index 00000000..59665002 --- /dev/null +++ b/lib/features/blog_post/presentation/cubit/blog_post_cubit.dart @@ -0,0 +1,21 @@ +import 'dart:developer'; + +import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.dart'; +import 'package:eatvio_app/features/feed/repositories/feed_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class BlogPostCubit extends Cubit { + final FeedRepository feedRepository; + BlogPostCubit(this.feedRepository) : super(BlogPostState()); + + Future getBlogPost(int id) async { + try { + emit(state.copyWith(isLoading: true)); + final postModel = await feedRepository.getBlogPost(id: id); + emit(state.copyWith(blogPostModel: postModel, isLoading: false)); + } catch (e, st) { + log(e.toString(), stackTrace: st); + emit(state.copyWith(isLoading: false)); + } + } +} diff --git a/lib/features/blog_post/presentation/cubit/blog_post_state.dart b/lib/features/blog_post/presentation/cubit/blog_post_state.dart new file mode 100644 index 00000000..9b5684da --- /dev/null +++ b/lib/features/blog_post/presentation/cubit/blog_post_state.dart @@ -0,0 +1,17 @@ +import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'blog_post_state.freezed.dart'; +part 'blog_post_state.g.dart'; + +@freezed +abstract class BlogPostState with _$BlogPostState { + + factory BlogPostState({ + @Default(null) FeedBlogPostModel? blogPostModel, + @Default(false) bool isLoading, + @Default(null) String? error, + }) = _BlogPostState; + + factory BlogPostState.fromJson(Map json) => _$BlogPostStateFromJson(json); +} \ No newline at end of file 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 f597c244..96a01f2c 100644 --- a/lib/features/blog_post/presentation/pages/blog_post_screen.dart +++ b/lib/features/blog_post/presentation/pages/blog_post_screen.dart @@ -5,7 +5,8 @@ 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/custom_popup.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; -import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; +import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart'; +import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.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'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; @@ -19,9 +20,9 @@ import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; class BlogPostScreen extends StatefulWidget { - final FeedBlogPostModel blogPost; + final int id; - const BlogPostScreen({super.key, required this.blogPost}); + const BlogPostScreen({super.key, required this.id}); @override State createState() => _BlogPostScreenState(); @@ -31,258 +32,269 @@ class _BlogPostScreenState extends State { final CommentsCubit commentsCubit = sl(); final textEditingController = TextEditingController(); int? replyId; - late bool isLiked; - late int totalLikes; + bool isLiked = false; + int totalLikes = 0; @override void initState() { - isLiked = widget.blogPost.wasLikedByMe; - totalLikes = widget.blogPost.totalLikes; - context.read().viewPost(widget.blogPost.id); + context.read().viewPost(widget.id); super.initState(); } @override Widget build(BuildContext context) { - return BlocProvider( - create: (BuildContext context) => commentsCubit..getComments(widget.blogPost.id.toString(), CommentSourceType.blogPost, true), - child: BlocConsumer( - listener: (BuildContext context, CommentsState state) { - if (state is CommentsStateLoaded) { - setState(() { - replyId = state.replyId; - }); - } - }, - builder: (BuildContext context, CommentsState state) { - return Scaffold( - resizeToAvoidBottomInset: true, - bottomSheet: Container( - padding: const EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), - decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: Colors.black.withValues(alpha: 0.1)))), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (replyId != null) - GestureDetector( - onTap: () { - context.read().setReplyId(null); - setState(() { - replyId = null; - textEditingController.clear(); - }); - FocusManager.instance.primaryFocus?.unfocus(); - }, - child: Container( - decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle), - height: 32, - width: 32, - child: const Icon(Icons.close_rounded, size: 18, color: Colors.white), - ), - ), - if (replyId != null) const SizedBox(width: 8), - SizedBox( - width: MediaQuery.of(context).size.width - (replyId == null ? 64 : 104), - child: ConstrainedBox( - constraints: const BoxConstraints(minHeight: 52), - child: TextFormField( - textCapitalization: TextCapitalization.sentences, - onChanged: (_) { - setState(() {}); - }, - controller: textEditingController, - maxLines: 5, - minLines: 1, - keyboardType: TextInputType.multiline, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black, - height: 1.2, - ), - decoration: InputDecoration( - isDense: false, - // убираем true, чтобы padding учитывался корректно - contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), - // вертикальный паддинг увеличен - hintText: 'Комментарий', - hintStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - height: 1.2, - color: Color(0xFFAEAEB2), - ), - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), - ), - ), - ), - ), - ), - const SizedBox(width: 8), - GestureDetector( - onTap: () { - if (textEditingController.text.length > 2) { - commentsCubit.sendComment( - id: widget.blogPost.id.toString(), - commentText: textEditingController.text, - parentCommentId: replyId, - commentSourceType: CommentSourceType.blogPost, - ); - context.read().setReplyId(null); - setState(() { - textEditingController.clear(); - replyId = null; - }); - FocusManager.instance.primaryFocus?.unfocus(); - } - }, - child: Container( - decoration: BoxDecoration(color: textEditingController.text.length > 2 ? appTheme.blueA400 : Colors.grey, shape: BoxShape.circle), - height: 32, - width: 32, - child: const Icon(Icons.send_rounded, size: 18, color: Colors.white), - ), - ), - ], - ), - ), - appBar: AppBar( - title: Text(widget.blogPost.title), - actions: [ - 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( - padding: const EdgeInsets.only(bottom: 80), - physics: const BouncingScrollPhysics(), - child: Column( - children: [ - GestureDetector( - onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: widget.blogPost.author), - child: Ink( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - CircleAvatar( - radius: 16, - foregroundImage: NetworkImage( - baseUrl + widget.blogPost.author.authorProfileIcon, - ), - backgroundImage: const AssetImage('assets/images/no_avatar.png'), - ), - const SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.blogPost.author.authorName, - style: const TextStyle(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400, height: 1), - ), - const SizedBox(height: 2), - Text( - widget.blogPost.createdAt.format('dd MMMM', 'ru'), - style: TextStyle(color: Colors.grey.shade600, fontSize: 12, fontWeight: FontWeight.w400, height: 1), - ), - ], - ), - ], - ), - ), - ), - if (widget.blogPost.description.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Html(data: widget.blogPost.description, style: {"*": Style(color: Colors.black87)}), - ), - const SizedBox(height: 8), - if (widget.blogPost.image.isNotEmpty) - CachedNetworkImage( - imageUrl: '$baseUrl/storage/${widget.blogPost.image}', - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + return BlocProvider( + create: (context) => sl()..getBlogPost(widget.id), + child: BlocProvider( + create: (BuildContext context) => commentsCubit..getComments(widget.id.toString(), CommentSourceType.blogPost, true), + child: BlocBuilder( + builder: (context, blogPostState) { + return BlocConsumer( + listener: (BuildContext context, CommentsState state) { + if (state is CommentsStateLoaded) { + setState(() { + replyId = state.replyId; + }); + } + }, + builder: (BuildContext context, CommentsState state) { + if (blogPostState.isLoading) { + return Scaffold(appBar: AppBar(), body: const Loader()); + } + isLiked = blogPostState.blogPostModel?.wasLikedByMe ?? false; + totalLikes = blogPostState.blogPostModel?.totalLikes ?? 0; + return Scaffold( + resizeToAvoidBottomInset: true, + bottomSheet: Container( + padding: const EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), + decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: Colors.black.withValues(alpha: 0.1)))), child: Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - _iconButton( - icon: 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(widget.blogPost.id) - : await context.read().unlikedPost(widget.blogPost.id); - sl().updateFeedBlogPostList(widget.blogPost, isLiked); - }, + if (replyId != null) + GestureDetector( + onTap: () { + context.read().setReplyId(null); + setState(() { + replyId = null; + textEditingController.clear(); + }); + FocusManager.instance.primaryFocus?.unfocus(); + }, + child: Container( + decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle), + height: 32, + width: 32, + child: const Icon(Icons.close_rounded, size: 18, color: Colors.white), + ), + ), + if (replyId != null) const SizedBox(width: 8), + SizedBox( + width: MediaQuery.of(context).size.width - (replyId == null ? 64 : 104), + child: ConstrainedBox( + constraints: const BoxConstraints(minHeight: 52), + child: TextFormField( + textCapitalization: TextCapitalization.sentences, + onChanged: (_) { + setState(() {}); + }, + controller: textEditingController, + maxLines: 5, + minLines: 1, + keyboardType: TextInputType.multiline, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.black, + height: 1.2, + ), + decoration: InputDecoration( + isDense: false, + // убираем true, чтобы padding учитывался корректно + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + // вертикальный паддинг увеличен + hintText: 'Комментарий', + hintStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1.2, + color: Color(0xFFAEAEB2), + ), + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), + ), + ), + ), + ), ), const SizedBox(width: 8), - _iconButton( - icon: Icons.reply_rounded, + GestureDetector( onTap: () { - final box = context.findRenderObject() as RenderBox?; - SharePlus.instance.share( - ShareParams( - uri: Uri.parse('$baseUrl/ru/blog_post/${widget.blogPost.id}'), - sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, - ), - ); + if (textEditingController.text.length > 2) { + commentsCubit.sendComment( + id: widget.id.toString(), + commentText: textEditingController.text, + parentCommentId: replyId, + commentSourceType: CommentSourceType.blogPost, + ); + context.read().setReplyId(null); + setState(() { + textEditingController.clear(); + replyId = null; + }); + FocusManager.instance.primaryFocus?.unfocus(); + } }, + child: Container( + decoration: BoxDecoration(color: textEditingController.text.length > 2 ? appTheme.blueA400 : Colors.grey, shape: BoxShape.circle), + height: 32, + width: 32, + child: const Icon(Icons.send_rounded, size: 18, color: Colors.white), + ), ), - const Spacer(), - Icon(Icons.visibility_rounded, size: 16, color: appTheme.gray500), - const SizedBox(width: 4), - Text(widget.blogPost.totalViews.toString(), style: TextStyle(fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400)), ], ), ), - Container( - width: MediaQuery.of(context).size.width - 32, - height: 0.4, - decoration: BoxDecoration(color: Colors.black.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(16)), + appBar: AppBar( + title: Text(blogPostState.blogPostModel?.title ?? ''), + actions: [ + CustomPopup( + isAuthor: blogPostState.blogPostModel?.author.authorIsMe ?? false, + complainedContentType: ComplainedContent.posts, + path: 'blog_post/${(widget.id)}', + id: widget.id, + feedBlogPostModel: blogPostState.blogPostModel, + ), + const SizedBox(width: 16), + ], ), - const SizedBox(height: 8), - Container( - padding: const EdgeInsets.symmetric(horizontal: 16), - alignment: Alignment.centerLeft, - child: Text( - '${widget.blogPost.totalComments} КОММЕНТАРИЕВ', - style: TextStyle(fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400), + body: SingleChildScrollView( + padding: const EdgeInsets.only(bottom: 80), + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + GestureDetector( + onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: blogPostState.blogPostModel?.author), + child: Ink( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CircleAvatar( + radius: 16, + foregroundImage: NetworkImage( + baseUrl + (blogPostState.blogPostModel?.author.authorProfileIcon ?? ''), + ), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), + ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + blogPostState.blogPostModel?.author.authorName ?? '', + style: const TextStyle(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400, height: 1), + ), + const SizedBox(height: 2), + Text( + (blogPostState.blogPostModel?.createdAt ?? DateTime.now()).format('dd MMMM', 'ru'), + style: TextStyle(color: Colors.grey.shade600, fontSize: 12, fontWeight: FontWeight.w400, height: 1), + ), + ], + ), + ], + ), + ), + ), + if (blogPostState.blogPostModel?.description.isNotEmpty ?? false) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Html(data: blogPostState.blogPostModel?.description, style: {"*": Style(color: Colors.black87)}), + ), + const SizedBox(height: 8), + if (blogPostState.blogPostModel?.image.isNotEmpty ?? false) + CachedNetworkImage( + imageUrl: '$baseUrl/storage/${blogPostState.blogPostModel?.image ?? ''}', + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + _iconButton( + icon: 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(widget.id) + : await context.read().unlikedPost(widget.id); + sl().updateFeedBlogPostList(blogPostState.blogPostModel, isLiked); + }, + ), + const SizedBox(width: 8), + _iconButton( + icon: Icons.reply_rounded, + onTap: () { + final box = context.findRenderObject() as RenderBox?; + SharePlus.instance.share( + ShareParams( + uri: Uri.parse('$baseUrl/ru/blog_post/${widget.id}'), + sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + ), + ); + }, + ), + const Spacer(), + Icon(Icons.visibility_rounded, size: 16, color: appTheme.gray500), + const SizedBox(width: 4), + Text(blogPostState.blogPostModel?.totalViews.toString() ?? '', style: TextStyle(fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400)), + ], + ), + ), + Container( + width: MediaQuery.of(context).size.width - 32, + height: 0.4, + decoration: BoxDecoration(color: Colors.black.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(16)), + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 16), + alignment: Alignment.centerLeft, + child: Text( + '${blogPostState.blogPostModel?.totalComments} КОММЕНТАРИЕВ', + style: TextStyle(fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400), + ), + ), + const SizedBox(height: 16), + if (state is CommentsStateLoading) const Loader(), + if (state is CommentsStateLoaded) + CommentsWidget( + id: widget.id.toString(), commentSourceType: CommentSourceType.blogPost, comment: state.comment, replyId: replyId), + ], ), ), - const SizedBox(height: 16), - if (state is CommentsStateLoading) const Loader(), - if (state is CommentsStateLoaded) - CommentsWidget(id: widget.blogPost.id.toString(), commentSourceType: CommentSourceType.blogPost, comment: state.comment, replyId: replyId), - ], - ), - ), - ); - }, + ); + }, + ); + }, + ), ), ); } diff --git a/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart b/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart index 17c435cb..670c4df9 100644 --- a/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart +++ b/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart @@ -16,7 +16,8 @@ class FeedCubit extends Cubit { required this.feedRepository, }) : super(const FeedState.loading()); - updateFeedBlogPostList(FeedBlogPostModel feedBlogPostModel, bool wasLikedByMe) { + updateFeedBlogPostList(FeedBlogPostModel? feedBlogPostModel, bool wasLikedByMe) { + if (feedBlogPostModel == null) return; List feedBlogPostList = [...(state as FeedStateLoaded).feedBlogPostList]; final int index = feedBlogPostList.indexOf(feedBlogPostModel); feedBlogPostList[index] = feedBlogPostModel.copyWith( diff --git a/lib/features/feed/presentation/widgets/blog_post_widget.dart b/lib/features/feed/presentation/widgets/blog_post_widget.dart index bc7faec0..a22acf44 100644 --- a/lib/features/feed/presentation/widgets/blog_post_widget.dart +++ b/lib/features/feed/presentation/widgets/blog_post_widget.dart @@ -46,7 +46,7 @@ class _BlogPostWidgetState extends State { borderRadius: BorderRadius.circular(12), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', - extra: state.feedBlogPostList[widget.index], + extra: state.feedBlogPostList[widget.index].id, ), child: Material( borderRadius: BorderRadius.circular(12), @@ -234,7 +234,7 @@ class _BlogPostWidgetState extends State { text: state.feedBlogPostList[widget.index].totalComments.toString(), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', - extra: state.feedBlogPostList[widget.index], + extra: state.feedBlogPostList[widget.index].id, ), ), const SizedBox(width: 8), diff --git a/lib/features/feed/repositories/feed_repository.dart b/lib/features/feed/repositories/feed_repository.dart index c974407c..e6e95969 100644 --- a/lib/features/feed/repositories/feed_repository.dart +++ b/lib/features/feed/repositories/feed_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/feed/models/comment_model.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/feed_blog_post_settings_model.dart'; import 'package:eatvio_app/features/feed/models/feed_consultant_model.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; @@ -27,6 +28,18 @@ class FeedRepository { } } + Future getBlogPost({ + required int id + }) async { + try { + return await feedService.getBlogPost( + postId: id + ); + } catch (e) { + rethrow; + } + } + Future> getRecipes({ String? query, List? tags, diff --git a/lib/features/feed/services/feed_service.dart b/lib/features/feed/services/feed_service.dart index fded5033..cb5de3d0 100644 --- a/lib/features/feed/services/feed_service.dart +++ b/lib/features/feed/services/feed_service.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/feed/models/comment_model.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/feed_blog_post_settings_model.dart'; import 'package:eatvio_app/features/feed/models/feed_consultant_model.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; @@ -19,6 +20,11 @@ abstract class FeedService { @Query('query_search') String? query, }); + @GET('/api/blogPosts/show/{postId}') + Future getBlogPost({ + @Path('postId') required int postId, + }); + @GET('/api/recipes/index') Future getRecipes({ @Query('page') required int page, diff --git a/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart b/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart index 18f2a878..ea667722 100644 --- a/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart +++ b/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart @@ -47,7 +47,7 @@ class _ProfileBlogPostWidgetState extends State { borderRadius: BorderRadius.circular(12), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', - extra: state.blogPosts[widget.index], + extra: state.blogPosts[widget.index].id, ), child: Ink( decoration: BoxDecoration( @@ -209,7 +209,7 @@ class _ProfileBlogPostWidgetState extends State { text: state.blogPosts[widget.index].totalComments.toString(), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', - extra: state.blogPosts[widget.index], + extra: state.blogPosts[widget.index].id, ), ), const SizedBox(width: 8),