From 8e3583f57aa65f6242c8a2a06f7ce657235c0a59 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 1 Aug 2025 11:30:38 +0800 Subject: [PATCH] :lipstick: Optimize post --- lib/screens/posts/post_detail.dart | 30 +++---- lib/screens/posts/post_search.dart | 14 +++- .../content/cloud_file_collection.dart | 8 +- lib/widgets/post/post_item.dart | 80 +++++++++++++++++-- lib/widgets/post/post_replies.dart | 25 +++--- lib/widgets/post/post_replies_sheet.dart | 7 +- 6 files changed, 117 insertions(+), 47 deletions(-) diff --git a/lib/screens/posts/post_detail.dart b/lib/screens/posts/post_detail.dart index 1f14583..e5f953e 100644 --- a/lib/screens/posts/post_detail.dart +++ b/lib/screens/posts/post_detail.dart @@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/post.dart'; import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; -import 'package:island/services/responsive.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_quick_reply.dart'; @@ -54,8 +53,6 @@ class PostDetailScreen extends HookConsumerWidget { final postState = ref.watch(postStateProvider(id)); final user = ref.watch(userInfoProvider); - final isWide = isWideScreen(context); - return AppScaffold( noBackground: false, appBar: AppBar(title: const Text('Post')), @@ -67,19 +64,24 @@ class PostDetailScreen extends HookConsumerWidget { CustomScrollView( slivers: [ SliverToBoxAdapter( - child: PostItem( - item: post!, - isFullPost: true, - backgroundColor: isWide ? Colors.transparent : null, - onUpdate: (newItem) { - // Update the local state with the new post data - ref - .read(postStateProvider(id).notifier) - .updatePost(newItem); - }, + child: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 600), + child: PostItem( + item: post!, + isFullPost: true, + isEmbedReply: false, + onUpdate: (newItem) { + // Update the local state with the new post data + ref + .read(postStateProvider(id).notifier) + .updatePost(newItem); + }, + ), + ), ), ), - PostRepliesList(postId: id), + PostRepliesList(postId: id, maxWidth: 600), SliverGap(MediaQuery.of(context).padding.bottom + 80), ], ), diff --git a/lib/screens/posts/post_search.dart b/lib/screens/posts/post_search.dart index a7abbba..43f9098 100644 --- a/lib/screens/posts/post_search.dart +++ b/lib/screens/posts/post_search.dart @@ -153,9 +153,17 @@ class _PostSearchScreenState extends ConsumerState { } final post = data.items[index]; - return Card( - margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: PostActionableItem(item: post, borderRadius: 8), + return Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 600), + child: Card( + margin: EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + child: PostActionableItem(item: post, borderRadius: 8), + ), + ), ); }, ); diff --git a/lib/widgets/content/cloud_file_collection.dart b/lib/widgets/content/cloud_file_collection.dart index 03f0e77..1db23f8 100644 --- a/lib/widgets/content/cloud_file_collection.dart +++ b/lib/widgets/content/cloud_file_collection.dart @@ -67,13 +67,7 @@ class CloudFileList extends HookConsumerWidget { constraints: BoxConstraints( maxHeight: disableConstraint ? double.infinity : maxHeight, minWidth: minWidth ?? 0, - maxWidth: - files.length == 1 - ? math.max( - math.min(520, MediaQuery.of(context).size.width * 0.85), - minWidth ?? 0, - ) - : double.infinity, + maxWidth: files.length == 1 ? maxWidth : double.infinity, ), child: AspectRatio( aspectRatio: calculateAspectRatio(), diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index 06ee9f1..cc2bbbe 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -23,6 +23,7 @@ import 'package:island/widgets/content/cloud_file_collection.dart'; import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/embed/link.dart'; import 'package:island/widgets/content/markdown.dart'; +import 'package:island/widgets/post/post_replies_sheet.dart'; import 'package:island/widgets/safety/abuse_report_helper.dart'; import 'package:island/widgets/share/share_sheet.dart'; import 'package:material_symbols_icons/symbols.dart'; @@ -44,7 +45,6 @@ Future postFeaturedReply(Ref ref, String id) async { } class PostActionableItem extends HookConsumerWidget { - final Color? backgroundColor; final SnPost item; final EdgeInsets? padding; final bool isFullPost; @@ -55,7 +55,6 @@ class PostActionableItem extends HookConsumerWidget { const PostActionableItem({ super.key, required this.item, - this.backgroundColor, this.padding, this.isFullPost = false, this.isShowReference = true, @@ -80,7 +79,6 @@ class PostActionableItem extends HookConsumerWidget { child: PostItem( key: key, item: item, - backgroundColor: backgroundColor, padding: padding, isFullPost: isFullPost, isShowReference: isShowReference, @@ -205,20 +203,20 @@ class PostActionableItem extends HookConsumerWidget { class PostItem extends HookConsumerWidget { final SnPost item; - final Color? backgroundColor; final EdgeInsets? padding; final bool isFullPost; final bool isShowReference; + final bool isEmbedReply; final bool isTextSelectable; final Function? onRefresh; final Function(SnPost)? onUpdate; const PostItem({ super.key, required this.item, - this.backgroundColor, this.padding, this.isFullPost = false, this.isShowReference = true, + this.isEmbedReply = true, this.isTextSelectable = true, this.onRefresh, this.onUpdate, @@ -531,6 +529,10 @@ class PostItem extends HookConsumerWidget { )), if (isShowReference) _buildReferencePost(context, item, renderingPadding), + if (item.repliesCount > 0 && isEmbedReply) + PostReplyPreview( + item, + ).padding(horizontal: renderingPadding.horizontal, top: 8), Gap(renderingPadding.vertical), ], ); @@ -699,6 +701,74 @@ Widget _buildReferencePost( ); } +class PostReplyPreview extends HookConsumerWidget { + final SnPost parent; + const PostReplyPreview(this.parent, {super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final featuredReply = ref.watch(PostFeaturedReplyProvider(parent.id)); + final contentWidget = Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerLow, + border: Border.all(color: Theme.of(context).dividerColor), + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 4, + children: [ + Text('repliesCount') + .plural(parent.repliesCount) + .tr() + .fontSize(15) + .bold() + .padding(horizontal: 5), + featuredReply.when( + data: + (value) => Row( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8, + children: [ + ProfilePictureWidget( + file: value!.publisher.picture, + radius: 12, + ).padding(top: 4), + if (value.content?.isNotEmpty ?? false) + Expanded( + child: MarkdownTextContent(content: value.content!), + ) + else + Expanded( + child: Text( + 'postHasAttachments', + ).plural(value.attachments.length), + ), + ], + ), + error: (error, _) => Row(children: [const Icon(Symbols.close)]), + loading: () => Row(children: [CircularProgressIndicator()]), + ), + ], + ), + ); + + return InkWell( + borderRadius: const BorderRadius.all(Radius.circular(8)), + onTap: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + builder: (context) => PostRepliesSheet(post: parent), + ); + }, + child: contentWidget, + ); + } +} + class PostReactionList extends HookConsumerWidget { final String parentId; final Map reactions; diff --git a/lib/widgets/post/post_replies.dart b/lib/widgets/post/post_replies.dart index ce490e9..6cfe67c 100644 --- a/lib/widgets/post/post_replies.dart +++ b/lib/widgets/post/post_replies.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/post.dart'; import 'package:island/pods/network.dart'; -import 'package:island/services/responsive.dart'; import 'package:island/widgets/post/post_item.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; @@ -56,17 +55,11 @@ class PostRepliesNotifier extends _$PostRepliesNotifier class PostRepliesList extends HookConsumerWidget { final String postId; - final Color? backgroundColor; - const PostRepliesList({ - super.key, - required this.postId, - this.backgroundColor, - }); + final double? maxWidth; + const PostRepliesList({super.key, required this.postId, this.maxWidth}); @override Widget build(BuildContext context, WidgetRef ref) { - final isWide = isWideScreen(context); - return PagingHelperSliverView( provider: postRepliesNotifierProvider(postId), futureRefreshable: postRepliesNotifierProvider(postId).future, @@ -93,15 +86,23 @@ class PostRepliesList extends HookConsumerWidget { return endItemView; } - return Card( + final contentWidget = Card( margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: PostActionableItem( + borderRadius: 8, item: data.items[index], - backgroundColor: - backgroundColor ?? (isWide ? Colors.transparent : null), isShowReference: false, ), ); + + if (maxWidth == null) return contentWidget; + + return Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: maxWidth!), + child: contentWidget, + ), + ); }, ); }, diff --git a/lib/widgets/post/post_replies_sheet.dart b/lib/widgets/post/post_replies_sheet.dart index d8e8860..a9e1510 100644 --- a/lib/widgets/post/post_replies_sheet.dart +++ b/lib/widgets/post/post_replies_sheet.dart @@ -24,12 +24,7 @@ class PostRepliesSheet extends HookConsumerWidget { // Replies list Expanded( child: CustomScrollView( - slivers: [ - PostRepliesList( - postId: post.id.toString(), - backgroundColor: Colors.transparent, - ), - ], + slivers: [PostRepliesList(postId: post.id.toString())], ), ), // Quick reply section