From bb67edd227cec0b54579939bc00dbc2f933c2932 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 23 Jun 2024 01:52:05 +0800 Subject: [PATCH] :sparkles: Better post display :bug: Fix realm with post --- ios/Podfile.lock | 30 ++-- lib/models/post.dart | 8 +- lib/router.dart | 8 +- lib/screens/{contact.dart => chat.dart} | 10 +- lib/screens/posts/post_detail.dart | 1 + lib/screens/realms/realm_view.dart | 118 +++++++------ lib/screens/social.dart | 1 - lib/translations.dart | 8 +- lib/widgets/attachments/attachment_list.dart | 171 +++++++++++-------- lib/widgets/navigation/app_navigation.dart | 6 +- lib/widgets/posts/post_item.dart | 116 ++++++++----- lib/widgets/posts/post_list.dart | 4 +- 12 files changed, 271 insertions(+), 210 deletions(-) rename lib/screens/{contact.dart => chat.dart} (95%) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1028e17..cc90d42 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -43,10 +43,10 @@ PODS: - Firebase/Messaging (10.27.0): - Firebase/CoreOnly - FirebaseMessaging (~> 10.27.0) - - firebase_core (3.0.0): + - firebase_core (3.1.0): - Firebase/CoreOnly (= 10.27.0) - Flutter - - firebase_messaging (15.0.0): + - firebase_messaging (15.0.1): - Firebase/Messaging (= 10.27.0) - firebase_core - Flutter @@ -54,9 +54,9 @@ PODS: - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.12) - GoogleUtilities/Logger (~> 7.12) - - FirebaseCoreInternal (10.27.0): + - FirebaseCoreInternal (10.28.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseInstallations (10.27.0): + - FirebaseInstallations (10.28.0): - FirebaseCore (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) @@ -109,7 +109,7 @@ PODS: - GoogleUtilities/Privacy - image_picker_ios (0.0.1): - Flutter - - livekit_client (2.1.6): + - livekit_client (2.2.0): - Flutter - WebRTC-SDK (= 114.5735.10) - nanopb (2.30910.0): @@ -128,11 +128,11 @@ PODS: - SDWebImage (5.19.2): - SDWebImage/Core (= 5.19.2) - SDWebImage/Core (5.19.2) - - Sentry/HybridSDK (8.25.2) - - sentry_flutter (8.2.0): + - Sentry/HybridSDK (8.29.0) + - sentry_flutter (8.3.0): - Flutter - FlutterMacOS - - Sentry/HybridSDK (= 8.25.2) + - Sentry/HybridSDK (= 8.29.0) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -238,11 +238,11 @@ SPEC CHECKSUMS: DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 Firebase: 26b040b20866a55f55eb3611b9fcf3ae64816b86 - firebase_core: 5926464bbb028fef87d2443369b73ada2a8a3608 - firebase_messaging: 17bc029302b3342daa1c5905a1ee4258bcf47572 + firebase_core: 483cfad66d24d8f3c233f31db4263830c625c909 + firebase_messaging: e60c0694699d8a2e56a319e043709583f6544123 FirebaseCore: a2b95ae4ce7c83ceecfbbbe3b6f1cddc7415a808 - FirebaseCoreInternal: 4b297a2d56063dbea2c1d0d04222d44a8d058862 - FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 + FirebaseCoreInternal: 58d07f1362fddeb0feb6a857d1d1d1c5e558e698 + FirebaseInstallations: 60c1d3bc1beef809fd1ad1189a8057a040c59f2e FirebaseMessaging: 585984d0a1df120617eb10b44cad8968b859815e Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 @@ -251,15 +251,15 @@ SPEC CHECKSUMS: GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 - livekit_client: 72da4281bddb8bda3d275f49b46e9a2046d57c58 + livekit_client: 2b3f5185f95d46d62d3570bf981f3d98ad3051e2 nanopb: 438bc412db1928dac798aa6fd75726007be04262 package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a - Sentry: 51b056d96914a741f63eca774d118678b1eb05a1 - sentry_flutter: e8397d13e297a5d4b6be8a752e33140b21c5cc97 + Sentry: 016de45ee5ce5fca2a829996f1bfafeb5e62e8b4 + sentry_flutter: 5fb57c5b7e6427a9dc1fedde4269eb65823982d4 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 diff --git a/lib/models/post.dart b/lib/models/post.dart index a04d69a..69e31df 100755 --- a/lib/models/post.dart +++ b/lib/models/post.dart @@ -75,8 +75,8 @@ class Post { json["reply_to"] != null ? Post.fromJson(json["reply_to"]) : null, repostTo: json["repost_to"] != null ? Post.fromJson(json["repost_to"]) : null, - realm: json["realm"], - publishedAt: json["published_at"], + realm: json["realm"] != null ? Realm.fromJson(json["realm"]) : null, + publishedAt: json["published_at"] != null ? DateTime.parse(json["published_at"]) : null, authorId: json["author_id"], author: Account.fromJson(json["author"]), replyCount: json["reply_count"], @@ -108,8 +108,8 @@ class Post { "realm_id": realmId, "reply_to": replyTo?.toJson(), "repost_to": repostTo?.toJson(), - "realm": realm, - "published_at": publishedAt, + "realm": realm?.toJson(), + "published_at": publishedAt?.toIso8601String(), "author_id": authorId, "author": author.toJson(), "reply_count": replyCount, diff --git a/lib/router.dart b/lib/router.dart index 56f66ef..3f27a00 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -7,7 +7,7 @@ import 'package:solian/screens/account/personalize.dart'; import 'package:solian/screens/channel/channel_chat.dart'; import 'package:solian/screens/channel/channel_detail.dart'; import 'package:solian/screens/channel/channel_organize.dart'; -import 'package:solian/screens/contact.dart'; +import 'package:solian/screens/chat.dart'; import 'package:solian/screens/posts/post_detail.dart'; import 'package:solian/screens/realms.dart'; import 'package:solian/screens/realms/realm_detail.dart'; @@ -31,9 +31,9 @@ abstract class AppRouter { builder: (context, state) => const SocialScreen(), ), GoRoute( - path: '/contact', - name: 'contact', - builder: (context, state) => const ContactScreen(), + path: '/chat', + name: 'chat', + builder: (context, state) => const ChatScreen(), ), GoRoute( path: '/realms', diff --git a/lib/screens/contact.dart b/lib/screens/chat.dart similarity index 95% rename from lib/screens/contact.dart rename to lib/screens/chat.dart index 8bb913e..d212b40 100644 --- a/lib/screens/contact.dart +++ b/lib/screens/chat.dart @@ -15,14 +15,14 @@ import 'package:solian/widgets/channel/channel_list.dart'; import 'package:solian/widgets/chat/call/chat_call_indicator.dart'; import 'package:solian/widgets/current_state_action.dart'; -class ContactScreen extends StatefulWidget { - const ContactScreen({super.key}); +class ChatScreen extends StatefulWidget { + const ChatScreen({super.key}); @override - State createState() => _ContactScreenState(); + State createState() => _ChatScreenState(); } -class _ContactScreenState extends State { +class _ChatScreenState extends State { bool _isBusy = true; int? _accountId; @@ -85,7 +85,7 @@ class _ContactScreenState extends State { child: CustomScrollView( slivers: [ SliverAppBar( - title: AppBarTitle('contact'.tr), + title: AppBarTitle('chat'.tr), centerTitle: false, floating: true, titleSpacing: SolianTheme.titleSpacing(context), diff --git a/lib/screens/posts/post_detail.dart b/lib/screens/posts/post_detail.dart index 2716f64..8a4c5c4 100644 --- a/lib/screens/posts/post_detail.dart +++ b/lib/screens/posts/post_detail.dart @@ -50,6 +50,7 @@ class _PostDetailScreenState extends State { child: PostItem( item: item!, isClickable: true, + isFullDate: true, isShowReply: false, ), ), diff --git a/lib/screens/realms/realm_view.dart b/lib/screens/realms/realm_view.dart index 55ca1a5..f5870e5 100644 --- a/lib/screens/realms/realm_view.dart +++ b/lib/screens/realms/realm_view.dart @@ -83,69 +83,67 @@ class _RealmViewScreenState extends State { color: Theme.of(context).colorScheme.surface, child: DefaultTabController( length: 2, - child: SafeArea( - child: NestedScrollView( - headerSliverBuilder: (context, innerBoxIsScrolled) { - return [ - SliverOverlapAbsorber( - handle: - NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar( - title: Text(_realm?.name ?? 'loading'.tr), - centerTitle: false, - actions: [ - IconButton( - icon: const Icon(Icons.more_vert), - onPressed: () { - AppRouter.instance - .pushNamed( - 'realmDetail', - pathParameters: {'alias': widget.alias}, - extra: _realm, - ) - .then((value) { - if (value == false) AppRouter.instance.pop(); - if (value != null) { - final resp = - Realm.fromJson(value as Map); - getRealm(overrideAlias: resp.alias); - } - }); - }, - ), - SizedBox( - width: SolianTheme.isLargeScreen(context) ? 8 : 16, - ), - ], - bottom: const TabBar( - isScrollable: true, - tabs: [ - Tab(icon: Icon(Icons.feed)), - Tab(icon: Icon(Icons.chat)), - ], + child: NestedScrollView( + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: + NestedScrollView.sliverOverlapAbsorberHandleFor(context), + sliver: SliverAppBar( + title: Text(_realm?.name ?? 'loading'.tr), + centerTitle: false, + actions: [ + IconButton( + icon: const Icon(Icons.more_vert), + onPressed: () { + AppRouter.instance + .pushNamed( + 'realmDetail', + pathParameters: {'alias': widget.alias}, + extra: _realm, + ) + .then((value) { + if (value == false) AppRouter.instance.pop(); + if (value != null) { + final resp = + Realm.fromJson(value as Map); + getRealm(overrideAlias: resp.alias); + } + }); + }, ), - ), - ) - ]; - }, - body: Builder( - builder: (context) { - if (_isBusy) { - return const Center(child: CircularProgressIndicator()); - } - - return TabBarView( - children: [ - RealmPostListWidget(realm: _realm!), - RealmChannelListWidget( - realm: _realm!, - channels: _channels, - onRefresh: () => getChannels(), + SizedBox( + width: SolianTheme.isLargeScreen(context) ? 8 : 16, ), ], - ); - }, - ), + bottom: const TabBar( + isScrollable: true, + tabs: [ + Tab(icon: Icon(Icons.feed)), + Tab(icon: Icon(Icons.chat)), + ], + ), + ), + ) + ]; + }, + body: Builder( + builder: (context) { + if (_isBusy) { + return const Center(child: CircularProgressIndicator()); + } + + return TabBarView( + children: [ + RealmPostListWidget(realm: _realm!), + RealmChannelListWidget( + realm: _realm!, + channels: _channels, + onRefresh: () => getChannels(), + ), + ], + ); + }, ), ), ), diff --git a/lib/screens/social.dart b/lib/screens/social.dart index 0aeae36..511e77d 100644 --- a/lib/screens/social.dart +++ b/lib/screens/social.dart @@ -3,7 +3,6 @@ import 'package:get/get.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:solian/models/pagination.dart'; import 'package:solian/models/post.dart'; -import 'package:solian/platform.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/providers/content/post.dart'; import 'package:solian/router.dart'; diff --git a/lib/translations.dart b/lib/translations.dart index 2494685..cf88721 100644 --- a/lib/translations.dart +++ b/lib/translations.dart @@ -10,7 +10,7 @@ class SolianMessages extends Translations { 'reset': 'Reset', 'page': 'Page', 'social': 'Social', - 'contact': 'Contact', + 'chat': 'Chat', 'apply': 'Apply', 'cancel': 'Cancel', 'confirm': 'Confirm', @@ -72,6 +72,8 @@ class SolianMessages extends Translations { 'postNew': 'Create a new post', 'postNewInRealmHint': 'Add post in realm @realm', 'postAction': 'Post', + 'postEdited': 'Edited at @date', + 'postInRealm': 'In realm @realm', 'postDetail': 'Post', 'postReplies': 'Replies', 'postPublishing': 'Post post', @@ -226,7 +228,7 @@ class SolianMessages extends Translations { 'delete': '删除', 'page': '页面', 'social': '社交', - 'contact': '联系', + 'chat': '聊天', 'apply': '应用', 'search': '搜索', 'reply': '回复', @@ -275,6 +277,8 @@ class SolianMessages extends Translations { 'postNew': '创建新帖子', 'postNewInRealmHint': '在领域 @realm 里发表新帖子', 'postAction': '发表', + 'postEdited': '帖子编辑于 @date', + 'postInRealm': '发表于 @realm', 'postDetail': '帖子详情', 'postReplies': '帖子回复', 'postPublishing': '发表帖子', diff --git a/lib/widgets/attachments/attachment_list.dart b/lib/widgets/attachments/attachment_list.dart index 520533c..c1875eb 100644 --- a/lib/widgets/attachments/attachment_list.dart +++ b/lib/widgets/attachments/attachment_list.dart @@ -11,9 +11,16 @@ import 'package:solian/widgets/attachments/attachment_list_fullscreen.dart'; class AttachmentList extends StatefulWidget { final String parentId; final List attachmentsId; + final bool divided; + final double dividedPadding; - const AttachmentList( - {super.key, required this.parentId, required this.attachmentsId}); + const AttachmentList({ + super.key, + required this.parentId, + required this.attachmentsId, + this.divided = false, + this.dividedPadding = 16, + }); @override State createState() => _AttachmentListState(); @@ -91,6 +98,83 @@ class _AttachmentListState extends State { } } + Widget buildEntry(Attachment element, int idx) { + return GestureDetector( + child: Container( + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + child: Stack( + fit: StackFit.expand, + children: [ + AttachmentItem( + parentId: widget.parentId, + key: Key('a${element.uuid}'), + item: element, + badge: _attachmentsMeta.length > 1 + ? '${idx + 1}/${_attachmentsMeta.length}' + : null, + showHideButton: !element.isMature || _showMature, + onHide: () { + setState(() => _showMature = false); + }, + ), + if (element.isMature && !_showMature) + BackdropFilter( + filter: ImageFilter.blur(sigmaX: 100, sigmaY: 100), + child: Container( + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + ), + ), + ), + if (element.isMature && !_showMature) + Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 280), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.visibility_off, + color: Colors.white, size: 32), + const SizedBox(height: 8), + Text( + 'matureContent'.tr, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + Text( + 'matureContentCaption'.tr, + style: const TextStyle(color: Colors.white), + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + ], + ), + ), + onTap: () { + if (!_showMature && _attachmentsMeta.any((e) => e!.isMature)) { + setState(() => _showMature = true); + } else if (['image'].contains(element.mimetype.split('/').first)) { + Navigator.of(context, rootNavigator: true).push( + MaterialPageRoute( + builder: (context) => AttachmentListFullScreen( + parentId: widget.parentId, + attachment: element, + ), + ), + ); + } + }, + ); + } + @override void initState() { super.initState(); @@ -145,80 +229,21 @@ class _AttachmentListState extends State { ); } - return GestureDetector( - child: Container( - width: MediaQuery.of(context).size.width, + if (widget.divided) { + const radius = BorderRadius.all(Radius.circular(16)); + return Container( decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHigh, + border: Border.all(color: Theme.of(context).dividerColor, width: 1), + borderRadius: radius, ), - child: Stack( - fit: StackFit.expand, - children: [ - AttachmentItem( - parentId: widget.parentId, - key: Key('a${element.uuid}'), - item: element, - badge: _attachmentsMeta.length > 1 - ? '${idx + 1}/${_attachmentsMeta.length}' - : null, - showHideButton: !element.isMature || _showMature, - onHide: () { - setState(() => _showMature = false); - }, - ), - if (element.isMature && !_showMature) - BackdropFilter( - filter: ImageFilter.blur(sigmaX: 100, sigmaY: 100), - child: Container( - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), - ), - ), - ), - if (element.isMature && !_showMature) - Center( - child: Container( - constraints: const BoxConstraints(maxWidth: 280), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.visibility_off, - color: Colors.white, size: 32), - const SizedBox(height: 8), - Text( - 'matureContent'.tr, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16), - ), - Text( - 'matureContentCaption'.tr, - style: const TextStyle(color: Colors.white), - textAlign: TextAlign.center, - ), - ], - ), - ), - ), - ], + child: ClipRRect( + borderRadius: radius, + child: buildEntry(element, idx), ), - ), - onTap: () { - if (!_showMature && _attachmentsMeta.any((e) => e!.isMature)) { - setState(() => _showMature = true); - } else if (['image'].contains(element.mimetype.split('/').first)) { - Navigator.of(context, rootNavigator: true).push( - MaterialPageRoute( - builder: (context) => AttachmentListFullScreen( - parentId: widget.parentId, - attachment: element, - ), - ), - ); - } - }, - ); + ).paddingSymmetric(horizontal: widget.dividedPadding); + } else { + return buildEntry(element, idx); + } }, ); } diff --git a/lib/widgets/navigation/app_navigation.dart b/lib/widgets/navigation/app_navigation.dart index 51ad43b..3883c81 100644 --- a/lib/widgets/navigation/app_navigation.dart +++ b/lib/widgets/navigation/app_navigation.dart @@ -9,9 +9,9 @@ abstract class AppNavigation { page: 'social', ), AppNavigationDestination( - icon: const Icon(Icons.contacts), - label: 'contact'.tr, - page: 'contact', + icon: const Icon(Icons.forum), + label: 'chat'.tr, + page: 'chat', ), AppNavigationDestination( icon: const Icon(Icons.workspaces), diff --git a/lib/widgets/posts/post_item.dart b/lib/widgets/posts/post_item.dart index f8e727e..520fed7 100644 --- a/lib/widgets/posts/post_item.dart +++ b/lib/widgets/posts/post_item.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get_utils/get_utils.dart'; +import 'package:intl/intl.dart'; import 'package:solian/models/post.dart'; import 'package:solian/router.dart'; import 'package:solian/widgets/account/account_avatar.dart'; @@ -18,6 +19,7 @@ class PostItem extends StatefulWidget { final bool isReactable; final bool isShowReply; final bool isShowEmbed; + final bool isFullDate; final String? overrideAttachmentParent; const PostItem({ @@ -28,6 +30,7 @@ class PostItem extends StatefulWidget { this.isReactable = true, this.isShowReply = true, this.isShowEmbed = true, + this.isFullDate = false, this.overrideAttachmentParent, }); @@ -44,6 +47,69 @@ class _PostItemState extends State { super.initState(); } + Widget buildDate() { + if (widget.isFullDate) { + return Text(DateFormat('y/M/d H:m').format(item.createdAt)); + } else { + return Text(format(item.createdAt, locale: 'en_short')); + } + } + + Widget buildHeader() { + return Row( + children: [ + Text( + item.author.nick, + style: const TextStyle(fontWeight: FontWeight.bold), + ).paddingOnly(left: 12), + buildDate().paddingOnly(left: 4), + ], + ); + } + + Widget buildBody() { + return Markdown( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + data: item.content, + padding: const EdgeInsets.all(0), + onTapLink: (text, href, title) async { + if (href == null) return; + await launchUrlString( + href, + mode: LaunchMode.externalApplication, + ); + }, + ); + } + + Widget buildFooter() { + List labels = List.empty(growable: true); + if (widget.item.createdAt != widget.item.updatedAt) { + labels.add('postEdited'.trParams({ + 'date': DateFormat('yy/M/d H:m').format(item.updatedAt), + })); + } + if (widget.item.realm != null) { + labels.add('postInRealm'.trParams({ + 'realm': '#${widget.item.realm!.alias}', + })); + } + + if (labels.isNotEmpty) { + return Text( + labels.join(" · "), + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75), + ), + ).paddingOnly(top: 8); + } else { + return const SizedBox(); + } + } + Widget buildReply(BuildContext context) { return Column( children: [ @@ -116,39 +182,20 @@ class _PostItemState extends State { if (widget.isCompact) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - AccountAvatar(content: item.author.avatar.toString(), radius: 10), - Text( - item.author.nick, - style: const TextStyle(fontWeight: FontWeight.bold), - ).paddingOnly(left: 6), - Text(format(item.createdAt, locale: 'en_short')) - .paddingOnly(left: 4), - ], - ).paddingSymmetric(horizontal: 12), - Markdown( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - data: item.content, - padding: const EdgeInsets.all(0), - onTapLink: (text, href, title) async { - if (href == null) return; - await launchUrlString( - href, - mode: LaunchMode.externalApplication, - ); - }, - ).paddingOnly( + buildHeader().paddingSymmetric(horizontal: 12), + buildBody().paddingOnly( left: 16, right: 12, top: 2, bottom: hasAttachment ? 4 : 0, ), + buildFooter().paddingOnly(left: 16), AttachmentList( parentId: widget.overrideAttachmentParent ?? widget.item.alias, attachmentsId: item.attachments ?? List.empty(), + divided: true, ), ], ); @@ -175,23 +222,10 @@ class _PostItemState extends State { ), Expanded( child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Text( - item.author.nick, - style: const TextStyle(fontWeight: FontWeight.bold), - ).paddingOnly(left: 12), - Text(format(item.createdAt, locale: 'en_short')) - .paddingOnly(left: 4), - ], - ), - Markdown( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - data: item.content, - padding: const EdgeInsets.all(0), - ).paddingOnly(left: 12, right: 8), + buildHeader(), + buildBody().paddingOnly(left: 12, right: 8), if (widget.item.replyTo != null && widget.isShowEmbed) GestureDetector( child: buildReply(context).paddingOnly(top: 4), @@ -218,6 +252,7 @@ class _PostItemState extends State { ); }, ), + buildFooter().paddingOnly(left: 12), ], ), ) @@ -231,6 +266,7 @@ class _PostItemState extends State { AttachmentList( parentId: widget.item.alias, attachmentsId: item.attachments ?? List.empty(), + divided: true, ), PostQuickAction( isShowReply: widget.isShowReply, diff --git a/lib/widgets/posts/post_list.dart b/lib/widgets/posts/post_list.dart index 2292783..04dd1f7 100644 --- a/lib/widgets/posts/post_list.dart +++ b/lib/widgets/posts/post_list.dart @@ -32,9 +32,7 @@ class PostListWidget extends StatelessWidget { item: item, isShowEmbed: isShowEmbed, isClickable: isNestedClickable, - ).paddingSymmetric( - vertical: (item.attachments?.isEmpty ?? false) ? 8 : 0, - ), + ).paddingSymmetric(vertical: 8), onTap: () { if (!isClickable) return; AppRouter.instance.pushNamed(