💄 Optimize post

This commit is contained in:
2025-08-01 11:30:38 +08:00
parent d0ff14659f
commit 8e3583f57a
6 changed files with 117 additions and 47 deletions

View File

@@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/post/post_quick_reply.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 postState = ref.watch(postStateProvider(id));
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
final isWide = isWideScreen(context);
return AppScaffold( return AppScaffold(
noBackground: false, noBackground: false,
appBar: AppBar(title: const Text('Post')), appBar: AppBar(title: const Text('Post')),
@@ -67,19 +64,24 @@ class PostDetailScreen extends HookConsumerWidget {
CustomScrollView( CustomScrollView(
slivers: [ slivers: [
SliverToBoxAdapter( SliverToBoxAdapter(
child: PostItem( child: Center(
item: post!, child: ConstrainedBox(
isFullPost: true, constraints: BoxConstraints(maxWidth: 600),
backgroundColor: isWide ? Colors.transparent : null, child: PostItem(
onUpdate: (newItem) { item: post!,
// Update the local state with the new post data isFullPost: true,
ref isEmbedReply: false,
.read(postStateProvider(id).notifier) onUpdate: (newItem) {
.updatePost(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), SliverGap(MediaQuery.of(context).padding.bottom + 80),
], ],
), ),

View File

@@ -153,9 +153,17 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
} }
final post = data.items[index]; final post = data.items[index];
return Card( return Center(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: ConstrainedBox(
child: PostActionableItem(item: post, borderRadius: 8), constraints: BoxConstraints(maxWidth: 600),
child: Card(
margin: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: PostActionableItem(item: post, borderRadius: 8),
),
),
); );
}, },
); );

View File

@@ -67,13 +67,7 @@ class CloudFileList extends HookConsumerWidget {
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: disableConstraint ? double.infinity : maxHeight, maxHeight: disableConstraint ? double.infinity : maxHeight,
minWidth: minWidth ?? 0, minWidth: minWidth ?? 0,
maxWidth: maxWidth: files.length == 1 ? maxWidth : double.infinity,
files.length == 1
? math.max(
math.min(520, MediaQuery.of(context).size.width * 0.85),
minWidth ?? 0,
)
: double.infinity,
), ),
child: AspectRatio( child: AspectRatio(
aspectRatio: calculateAspectRatio(), aspectRatio: calculateAspectRatio(),

View File

@@ -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/cloud_files.dart';
import 'package:island/widgets/content/embed/link.dart'; import 'package:island/widgets/content/embed/link.dart';
import 'package:island/widgets/content/markdown.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/safety/abuse_report_helper.dart';
import 'package:island/widgets/share/share_sheet.dart'; import 'package:island/widgets/share/share_sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
@@ -44,7 +45,6 @@ Future<SnPost?> postFeaturedReply(Ref ref, String id) async {
} }
class PostActionableItem extends HookConsumerWidget { class PostActionableItem extends HookConsumerWidget {
final Color? backgroundColor;
final SnPost item; final SnPost item;
final EdgeInsets? padding; final EdgeInsets? padding;
final bool isFullPost; final bool isFullPost;
@@ -55,7 +55,6 @@ class PostActionableItem extends HookConsumerWidget {
const PostActionableItem({ const PostActionableItem({
super.key, super.key,
required this.item, required this.item,
this.backgroundColor,
this.padding, this.padding,
this.isFullPost = false, this.isFullPost = false,
this.isShowReference = true, this.isShowReference = true,
@@ -80,7 +79,6 @@ class PostActionableItem extends HookConsumerWidget {
child: PostItem( child: PostItem(
key: key, key: key,
item: item, item: item,
backgroundColor: backgroundColor,
padding: padding, padding: padding,
isFullPost: isFullPost, isFullPost: isFullPost,
isShowReference: isShowReference, isShowReference: isShowReference,
@@ -205,20 +203,20 @@ class PostActionableItem extends HookConsumerWidget {
class PostItem extends HookConsumerWidget { class PostItem extends HookConsumerWidget {
final SnPost item; final SnPost item;
final Color? backgroundColor;
final EdgeInsets? padding; final EdgeInsets? padding;
final bool isFullPost; final bool isFullPost;
final bool isShowReference; final bool isShowReference;
final bool isEmbedReply;
final bool isTextSelectable; final bool isTextSelectable;
final Function? onRefresh; final Function? onRefresh;
final Function(SnPost)? onUpdate; final Function(SnPost)? onUpdate;
const PostItem({ const PostItem({
super.key, super.key,
required this.item, required this.item,
this.backgroundColor,
this.padding, this.padding,
this.isFullPost = false, this.isFullPost = false,
this.isShowReference = true, this.isShowReference = true,
this.isEmbedReply = true,
this.isTextSelectable = true, this.isTextSelectable = true,
this.onRefresh, this.onRefresh,
this.onUpdate, this.onUpdate,
@@ -531,6 +529,10 @@ class PostItem extends HookConsumerWidget {
)), )),
if (isShowReference) if (isShowReference)
_buildReferencePost(context, item, renderingPadding), _buildReferencePost(context, item, renderingPadding),
if (item.repliesCount > 0 && isEmbedReply)
PostReplyPreview(
item,
).padding(horizontal: renderingPadding.horizontal, top: 8),
Gap(renderingPadding.vertical), 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 { class PostReactionList extends HookConsumerWidget {
final String parentId; final String parentId;
final Map<String, int> reactions; final Map<String, int> reactions;

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
@@ -56,17 +55,11 @@ class PostRepliesNotifier extends _$PostRepliesNotifier
class PostRepliesList extends HookConsumerWidget { class PostRepliesList extends HookConsumerWidget {
final String postId; final String postId;
final Color? backgroundColor; final double? maxWidth;
const PostRepliesList({ const PostRepliesList({super.key, required this.postId, this.maxWidth});
super.key,
required this.postId,
this.backgroundColor,
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isWide = isWideScreen(context);
return PagingHelperSliverView( return PagingHelperSliverView(
provider: postRepliesNotifierProvider(postId), provider: postRepliesNotifierProvider(postId),
futureRefreshable: postRepliesNotifierProvider(postId).future, futureRefreshable: postRepliesNotifierProvider(postId).future,
@@ -93,15 +86,23 @@ class PostRepliesList extends HookConsumerWidget {
return endItemView; return endItemView;
} }
return Card( final contentWidget = Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: PostActionableItem( child: PostActionableItem(
borderRadius: 8,
item: data.items[index], item: data.items[index],
backgroundColor:
backgroundColor ?? (isWide ? Colors.transparent : null),
isShowReference: false, isShowReference: false,
), ),
); );
if (maxWidth == null) return contentWidget;
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth!),
child: contentWidget,
),
);
}, },
); );
}, },

View File

@@ -24,12 +24,7 @@ class PostRepliesSheet extends HookConsumerWidget {
// Replies list // Replies list
Expanded( Expanded(
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [PostRepliesList(postId: post.id.toString())],
PostRepliesList(
postId: post.id.toString(),
backgroundColor: Colors.transparent,
),
],
), ),
), ),
// Quick reply section // Quick reply section