💄 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/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,10 +64,13 @@ class PostDetailScreen extends HookConsumerWidget {
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 600),
child: PostItem(
item: post!,
isFullPost: true,
backgroundColor: isWide ? Colors.transparent : null,
isEmbedReply: false,
onUpdate: (newItem) {
// Update the local state with the new post data
ref
@@ -79,7 +79,9 @@ class PostDetailScreen extends HookConsumerWidget {
},
),
),
PostRepliesList(postId: id),
),
),
PostRepliesList(postId: id, maxWidth: 600),
SliverGap(MediaQuery.of(context).padding.bottom + 80),
],
),

View File

@@ -153,9 +153,17 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
}
final post = data.items[index];
return Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
return Center(
child: ConstrainedBox(
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(
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(),

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/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<SnPost?> 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<String, int> reactions;

View File

@@ -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,
),
);
},
);
},

View File

@@ -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