import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/widgets.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:solian/models/post.dart'; import 'package:solian/widgets/posts/comment_list.dart'; import 'package:solian/widgets/posts/content/article.dart'; import 'package:solian/widgets/posts/content/attachment.dart'; import 'package:solian/widgets/posts/content/moment.dart'; import 'package:solian/widgets/posts/item_action.dart'; import 'package:solian/widgets/posts/reaction_list.dart'; import 'package:timeago/timeago.dart' as timeago; class PostItem extends StatefulWidget { final Post item; final bool? brief; final Function? onUpdate; final Function? onDelete; const PostItem({ super.key, required this.item, this.brief, this.onUpdate, this.onDelete, }); @override State createState() => _PostItemState(); } class _PostItemState extends State { Map? reactionList; void viewActions(BuildContext context) { showModalBottomSheet( context: context, builder: (context) => PostItemAction( item: widget.item, onUpdate: widget.onUpdate, ), ); } void viewComments(BuildContext context) { final PagingController commentPaging = PagingController(firstPageKey: 0); showModalBottomSheet( context: context, builder: (context) { return Column( children: [ CommentListHeader( related: widget.item, paging: commentPaging, ), Expanded( child: CustomScrollView( slivers: [ CommentList( related: widget.item, dataset: '${widget.item.modelType}s', paging: commentPaging, ), ], ), ), ], ); }, ); } Widget renderContent() { switch (widget.item.modelType) { case 'article': return ArticleContent(item: widget.item, brief: widget.brief ?? true); default: return MomentContent(item: widget.item, brief: widget.brief ?? true); } } Widget renderAttachments() { if (widget.item.modelType == 'article') return Container(); if (widget.item.attachments != null && widget.item.attachments!.isNotEmpty) { return Padding( padding: const EdgeInsets.only(top: 8), child: AttachmentList(items: widget.item.attachments!), ); } else { return Container(); } } Widget renderReactions() { const density = VisualDensity(horizontal: -4, vertical: -2); return Container( height: 48, padding: const EdgeInsets.only(top: 8, left: 4, right: 4), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ActionChip( avatar: const Icon(Icons.comment), label: Text(widget.item.commentCount.toString()), tooltip: 'Comment', onPressed: () => viewComments(context), ), const VerticalDivider(thickness: 0.3, indent: 8, endIndent: 8), Expanded( child: ReactionList( item: widget.item, reactionList: reactionList, onReact: (symbol, changes) { setState(() { if (!reactionList!.containsKey(symbol)) { reactionList![symbol] = 0; } reactionList![symbol] += changes; }); }, ), ), ], ), ); } String getAuthorDescribe() => widget.item.author.description.isNotEmpty ? widget.item.author.description : 'No description yet.'; @override void initState() { reactionList = widget.item.reactionList; super.initState(); } @override Widget build(BuildContext context) { final headingParts = [ Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ Text( widget.item.author.nick, style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(width: 4), Text(timeago.format(widget.item.createdAt)) ], ), ), ]; Widget content; if (widget.brief ?? true) { content = Padding( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ CircleAvatar( backgroundImage: NetworkImage(widget.item.author.avatar), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ...headingParts, Padding( padding: const EdgeInsets.only(left: 12, right: 12, top: 4), child: renderContent(), ), renderAttachments(), renderReactions(), ], ), ), ], ), ], ), ); } else { content = Column( children: [ Padding( padding: const EdgeInsets.only(left: 12, right: 12, top: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ CircleAvatar( backgroundImage: NetworkImage(widget.item.author.avatar), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ...headingParts, Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Text( getAuthorDescribe(), maxLines: 1, ), ), ], ), ), ], ), ), const Padding( padding: EdgeInsets.only(top: 6), child: Divider(thickness: 0.3), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: renderContent(), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: renderAttachments(), ), ClipRRect( borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: renderReactions(), ), ), ], ); } return GestureDetector( child: content, onLongPress: () { viewActions(context); }, ); } }