💄 New media post layout
This commit is contained in:
@@ -9,8 +9,10 @@ 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/screens/posts/compose.dart';
|
import 'package:island/screens/posts/compose.dart';
|
||||||
|
import 'package:island/services/responsive.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_file_collection.dart';
|
||||||
import 'package:island/widgets/extended_refresh_indicator.dart';
|
import 'package:island/widgets/extended_refresh_indicator.dart';
|
||||||
import 'package:island/widgets/post/post_award_sheet.dart';
|
import 'package:island/widgets/post/post_award_sheet.dart';
|
||||||
import 'package:island/widgets/post/post_item.dart';
|
import 'package:island/widgets/post/post_item.dart';
|
||||||
@@ -19,6 +21,7 @@ import 'package:island/widgets/post/post_pin_sheet.dart';
|
|||||||
import 'package:island/widgets/post/post_quick_reply.dart';
|
import 'package:island/widgets/post/post_quick_reply.dart';
|
||||||
import 'package:island/widgets/post/compose_sheet.dart';
|
import 'package:island/widgets/post/compose_sheet.dart';
|
||||||
import 'package:island/widgets/post/post_replies.dart';
|
import 'package:island/widgets/post/post_replies.dart';
|
||||||
|
import 'package:island/widgets/post/post_shared.dart';
|
||||||
import 'package:island/widgets/response.dart';
|
import 'package:island/widgets/response.dart';
|
||||||
import 'package:island/utils/share_utils.dart';
|
import 'package:island/utils/share_utils.dart';
|
||||||
import 'package:island/widgets/safety/abuse_report_helper.dart';
|
import 'package:island/widgets/safety/abuse_report_helper.dart';
|
||||||
@@ -62,6 +65,10 @@ class PostState extends Notifier<AsyncValue<SnPost?>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isMediaPost(SnPost? post) {
|
||||||
|
return post != null && post.type == 0 && post.attachments.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
class PostActionButtons extends HookConsumerWidget {
|
class PostActionButtons extends HookConsumerWidget {
|
||||||
final SnPost post;
|
final SnPost post;
|
||||||
final EdgeInsets renderingPadding;
|
final EdgeInsets renderingPadding;
|
||||||
@@ -435,6 +442,88 @@ class PostActionButtons extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PostDetailLargeScreenLayout extends StatelessWidget {
|
||||||
|
final SnPost post;
|
||||||
|
final WidgetRef ref;
|
||||||
|
final String postId;
|
||||||
|
final Function(SnPost) onUpdate;
|
||||||
|
final VoidCallback onRefresh;
|
||||||
|
|
||||||
|
const _PostDetailLargeScreenLayout({
|
||||||
|
required this.post,
|
||||||
|
required this.ref,
|
||||||
|
required this.postId,
|
||||||
|
required this.onUpdate,
|
||||||
|
required this.onRefresh,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Material(
|
||||||
|
color: Theme.of(context).cardTheme.color,
|
||||||
|
elevation: 8,
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: CloudFileList(
|
||||||
|
files: post.attachments,
|
||||||
|
disableConstraint: true,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
PostHeader(
|
||||||
|
item: post,
|
||||||
|
isFullPost: true,
|
||||||
|
isCompact: false,
|
||||||
|
renderingPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
PostBody(
|
||||||
|
item: post,
|
||||||
|
isFullPost: true,
|
||||||
|
isTextSelectable: true,
|
||||||
|
renderingPadding: EdgeInsets.zero,
|
||||||
|
hideAttachments: true,
|
||||||
|
textScale: post.type == 1 ? 1.2 : 1.1,
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
PostActionButtons(
|
||||||
|
post: post,
|
||||||
|
renderingPadding: EdgeInsets.zero,
|
||||||
|
onRefresh: onRefresh,
|
||||||
|
onUpdate: onUpdate,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PostRepliesList(postId: postId, maxWidth: 800),
|
||||||
|
SliverGap(MediaQuery.of(context).padding.bottom + 80),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PostDetailScreen extends HookConsumerWidget {
|
class PostDetailScreen extends HookConsumerWidget {
|
||||||
final String id;
|
final String id;
|
||||||
const PostDetailScreen({super.key, required this.id});
|
const PostDetailScreen({super.key, required this.id});
|
||||||
@@ -452,6 +541,8 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
body: postState.when(
|
body: postState.when(
|
||||||
data: (post) {
|
data: (post) {
|
||||||
|
final isMediaPostLayout = isWideScreen(context) && _isMediaPost(post);
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -460,7 +551,22 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
ref.invalidate(postProvider(id));
|
ref.invalidate(postProvider(id));
|
||||||
ref.read(postRepliesProvider(id).notifier).refresh();
|
ref.read(postRepliesProvider(id).notifier).refresh();
|
||||||
},
|
},
|
||||||
child: CustomScrollView(
|
child: isMediaPostLayout
|
||||||
|
? _PostDetailLargeScreenLayout(
|
||||||
|
post: post!,
|
||||||
|
ref: ref,
|
||||||
|
postId: id,
|
||||||
|
onUpdate: (newItem) {
|
||||||
|
ref
|
||||||
|
.read(postStateProvider(id).notifier)
|
||||||
|
.updatePost(newItem);
|
||||||
|
},
|
||||||
|
onRefresh: () {
|
||||||
|
ref.invalidate(postProvider(id));
|
||||||
|
ref.read(postRepliesProvider(id).notifier).refresh();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: CustomScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
@@ -473,7 +579,6 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
isEmbedReply: false,
|
isEmbedReply: false,
|
||||||
textScale: post.type == 1 ? 1.2 : 1.1,
|
textScale: post.type == 1 ? 1.2 : 1.1,
|
||||||
onUpdate: (newItem) {
|
onUpdate: (newItem) {
|
||||||
// Update the local state with the new post data
|
|
||||||
ref
|
ref
|
||||||
.read(postStateProvider(id).notifier)
|
.read(postStateProvider(id).notifier)
|
||||||
.updatePost(newItem);
|
.updatePost(newItem);
|
||||||
@@ -511,13 +616,13 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (user.value != null)
|
if (user.value != null && !isMediaPostLayout)
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 16 + MediaQuery.of(context).padding.bottom,
|
bottom: 16 + MediaQuery.of(context).padding.bottom,
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: BoxConstraints(maxWidth: 660),
|
constraints: BoxConstraints(maxWidth: 800),
|
||||||
child: postState.when(
|
child: postState.when(
|
||||||
data: (post) => PostQuickReply(
|
data: (post) => PostQuickReply(
|
||||||
parent: post!,
|
parent: post!,
|
||||||
|
|||||||
@@ -167,7 +167,9 @@ class _PostsSearchTab extends HookConsumerWidget {
|
|||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
),
|
),
|
||||||
child: const PostItemSkeleton(),
|
child: const PostItemSkeleton(
|
||||||
|
maxWidth: double.infinity,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index, post) {
|
itemBuilder: (context, index, post) {
|
||||||
return Card(
|
return Card(
|
||||||
@@ -320,7 +322,7 @@ class _PostsSearchTab extends HookConsumerWidget {
|
|||||||
isRefreshable: false,
|
isRefreshable: false,
|
||||||
footerSkeletonChild: Padding(
|
footerSkeletonChild: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
child: const PostItemSkeleton(),
|
child: const PostItemSkeleton(maxWidth: double.infinity),
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index, post) {
|
itemBuilder: (context, index, post) {
|
||||||
return Center(
|
return Center(
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class PostActionableItem extends HookConsumerWidget {
|
|||||||
final bool isEmbedReply;
|
final bool isEmbedReply;
|
||||||
final bool isEmbedOpenable;
|
final bool isEmbedOpenable;
|
||||||
final bool isCompact;
|
final bool isCompact;
|
||||||
|
final bool hideAttachments;
|
||||||
final double? borderRadius;
|
final double? borderRadius;
|
||||||
final VoidCallback? onRefresh;
|
final VoidCallback? onRefresh;
|
||||||
final Function(SnPost)? onUpdate;
|
final Function(SnPost)? onUpdate;
|
||||||
@@ -80,6 +81,7 @@ class PostActionableItem extends HookConsumerWidget {
|
|||||||
this.isEmbedReply = true,
|
this.isEmbedReply = true,
|
||||||
this.isEmbedOpenable = false,
|
this.isEmbedOpenable = false,
|
||||||
this.isCompact = false,
|
this.isCompact = false,
|
||||||
|
this.hideAttachments = false,
|
||||||
this.borderRadius,
|
this.borderRadius,
|
||||||
this.onRefresh,
|
this.onRefresh,
|
||||||
this.onUpdate,
|
this.onUpdate,
|
||||||
@@ -110,6 +112,7 @@ class PostActionableItem extends HookConsumerWidget {
|
|||||||
isEmbedOpenable: isEmbedOpenable,
|
isEmbedOpenable: isEmbedOpenable,
|
||||||
isTextSelectable: false,
|
isTextSelectable: false,
|
||||||
isCompact: isCompact,
|
isCompact: isCompact,
|
||||||
|
hideAttachments: hideAttachments,
|
||||||
onRefresh: onRefresh,
|
onRefresh: onRefresh,
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
onOpen: onOpen,
|
onOpen: onOpen,
|
||||||
@@ -308,6 +311,7 @@ class PostItem extends HookConsumerWidget {
|
|||||||
final bool isTextSelectable;
|
final bool isTextSelectable;
|
||||||
final bool isTranslatable;
|
final bool isTranslatable;
|
||||||
final bool isCompact;
|
final bool isCompact;
|
||||||
|
final bool hideAttachments;
|
||||||
final double? textScale;
|
final double? textScale;
|
||||||
final VoidCallback? onRefresh;
|
final VoidCallback? onRefresh;
|
||||||
final Function(SnPost)? onUpdate;
|
final Function(SnPost)? onUpdate;
|
||||||
@@ -323,6 +327,7 @@ class PostItem extends HookConsumerWidget {
|
|||||||
this.isTextSelectable = true,
|
this.isTextSelectable = true,
|
||||||
this.isTranslatable = true,
|
this.isTranslatable = true,
|
||||||
this.isCompact = false,
|
this.isCompact = false,
|
||||||
|
this.hideAttachments = false,
|
||||||
this.textScale,
|
this.textScale,
|
||||||
this.onRefresh,
|
this.onRefresh,
|
||||||
this.onUpdate,
|
this.onUpdate,
|
||||||
@@ -569,6 +574,7 @@ class PostItem extends HookConsumerWidget {
|
|||||||
isTextSelectable: isTextSelectable,
|
isTextSelectable: isTextSelectable,
|
||||||
translationSection: translationSection,
|
translationSection: translationSection,
|
||||||
renderingPadding: renderingPadding,
|
renderingPadding: renderingPadding,
|
||||||
|
hideAttachments: hideAttachments,
|
||||||
),
|
),
|
||||||
if (item.embedView != null)
|
if (item.embedView != null)
|
||||||
EmbedViewRenderer(
|
EmbedViewRenderer(
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class SliverPostList extends HookConsumerWidget {
|
|||||||
isSliver: true,
|
isSliver: true,
|
||||||
footerSkeletonChild: Padding(
|
footerSkeletonChild: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
child: const PostItemSkeleton(),
|
child: PostItemSkeleton(maxWidth: maxWidth ?? double.infinity),
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index, post) {
|
itemBuilder: (context, index, post) {
|
||||||
if (maxWidth != null) {
|
if (maxWidth != null) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class PostRepliesList extends HookConsumerWidget {
|
|||||||
|
|
||||||
final skeletonItem = Padding(
|
final skeletonItem = Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
child: const PostItemSkeleton(),
|
child: PostItemSkeleton(maxWidth: maxWidth ?? double.infinity),
|
||||||
);
|
);
|
||||||
|
|
||||||
return PaginationList(
|
return PaginationList(
|
||||||
|
|||||||
@@ -870,6 +870,7 @@ class PostBody extends ConsumerWidget {
|
|||||||
final EdgeInsets renderingPadding;
|
final EdgeInsets renderingPadding;
|
||||||
final bool isRelativeTime;
|
final bool isRelativeTime;
|
||||||
final bool hideOverlay;
|
final bool hideOverlay;
|
||||||
|
final bool hideAttachments;
|
||||||
final double? textScale;
|
final double? textScale;
|
||||||
|
|
||||||
const PostBody({
|
const PostBody({
|
||||||
@@ -882,6 +883,7 @@ class PostBody extends ConsumerWidget {
|
|||||||
this.renderingPadding = EdgeInsets.zero,
|
this.renderingPadding = EdgeInsets.zero,
|
||||||
this.isRelativeTime = true,
|
this.isRelativeTime = true,
|
||||||
this.hideOverlay = false,
|
this.hideOverlay = false,
|
||||||
|
this.hideAttachments = false,
|
||||||
this.textScale,
|
this.textScale,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1140,7 +1142,7 @@ class PostBody extends ConsumerWidget {
|
|||||||
right: renderingPadding.horizontal,
|
right: renderingPadding.horizontal,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (item.attachments.isNotEmpty && item.type != 1)
|
if (item.attachments.isNotEmpty && item.type != 1 && !hideAttachments)
|
||||||
CloudFileList(
|
CloudFileList(
|
||||||
files: item.attachments,
|
files: item.attachments,
|
||||||
isColumn: !isInteractive,
|
isColumn: !isInteractive,
|
||||||
|
|||||||
Reference in New Issue
Block a user