diff --git a/lib/providers/content/posts.dart b/lib/providers/content/posts.dart index 16f2578..8248687 100644 --- a/lib/providers/content/posts.dart +++ b/lib/providers/content/posts.dart @@ -1,6 +1,7 @@ import 'package:get/get.dart'; import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; +import 'package:solian/models/post.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/services.dart'; @@ -96,6 +97,15 @@ class PostProvider extends GetConnect { return resp; } + Future> listPostFeaturedReply(String alias) async { + final resp = await get('/posts/$alias/replies/featured'); + if (resp.statusCode != 200) { + throw RequestException(resp); + } + + return List.from(resp.body.map((x) => Post.fromJson(x))); + } + Future getPost(String alias) async { final resp = await get('/posts/$alias'); if (resp.statusCode != 200) { diff --git a/lib/screens/account/profile_page.dart b/lib/screens/account/profile_page.dart index 9621f9c..09c854a 100644 --- a/lib/screens/account/profile_page.dart +++ b/lib/screens/account/profile_page.dart @@ -302,6 +302,7 @@ class _AccountProfilePageState extends State { isClickable: true, isNestedClickable: true, isShowEmbed: true, + showFeaturedReply: true, onUpdate: () { _postController.reloadAllOver(); }, diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 8c755e8..282b102 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -379,6 +379,7 @@ class _DashboardScreenState extends State { isClickable: true, isShowEmbed: true, isNestedClickable: true, + showFeaturedReply: true, onUpdate: (_) { _pullPosts(); }, diff --git a/lib/widgets/posts/post_item.dart b/lib/widgets/posts/post_item.dart index c8fc041..2c1fa7f 100644 --- a/lib/widgets/posts/post_item.dart +++ b/lib/widgets/posts/post_item.dart @@ -3,9 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:gap/gap.dart'; -import 'package:get/get_utils/get_utils.dart'; +import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:solian/models/post.dart'; +import 'package:solian/providers/content/posts.dart'; import 'package:solian/screens/posts/post_detail.dart'; import 'package:solian/shells/title_shell.dart'; import 'package:solian/theme.dart'; @@ -30,6 +31,7 @@ class PostItem extends StatefulWidget { final bool isFullDate; final bool isFullContent; final bool isContentSelectable; + final bool showFeaturedReply; final String? attachmentParent; final Color? backgroundColor; @@ -45,6 +47,7 @@ class PostItem extends StatefulWidget { this.isFullDate = false, this.isFullContent = false, this.isContentSelectable = false, + this.showFeaturedReply = false, this.attachmentParent, this.backgroundColor, }); @@ -103,7 +106,7 @@ class _PostItemState extends State { children: [ if (widget.isCompact) AccountAvatar( - content: item.author.avatar.toString(), + content: item.author.avatar, radius: 10, ).paddingOnly(left: 2, top: 1), Expanded( @@ -320,6 +323,95 @@ class _PostItemState extends State { } } + Widget _buildFeaturedReply() { + if ((widget.item.metric?.replyCount ?? 0) == 0) { + return const SizedBox.shrink(); + } + final List attachments = item.body['attachments'] is List + ? List.from(item.body['attachments']?.whereType()) + : List.empty(); + final unFocusColor = + Theme.of(context).colorScheme.onSurface.withOpacity(0.75); + return FutureBuilder( + future: Get.find().listPostFeaturedReply( + widget.item.id.toString(), + ), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data!.isEmpty) { + return const SizedBox.shrink(); + } + return Card( + margin: EdgeInsets.zero, + child: Column( + children: snapshot.data! + .map( + (x) => Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AccountAvatar(content: x.author.avatar, radius: 10), + const Gap(6), + Text( + x.author.nick, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + const Gap(6), + Text( + format( + x.publishedAt?.toLocal() ?? DateTime.now(), + locale: 'en_short', + ), + ).paddingOnly(top: 0.5), + const Gap(16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MarkdownTextContent( + content: x.body['content'], + parentId: 'p${item.id}-featured-reply${x.id}', + ), + if (x.body['attachments'] is List && + x.body['attachments'].length > 0) + Row( + children: [ + Icon( + Icons.file_copy, + size: 15, + color: unFocusColor, + ).paddingOnly(right: 5), + Text( + 'attachmentHint'.trParams( + { + 'count': x.body['attachments'].length + .toString() + }, + ), + style: TextStyle(color: unFocusColor), + ) + ], + ), + ], + ), + ), + ], + ).paddingSymmetric(horizontal: 12, vertical: 8), + ) + .toList(), + ), + ).paddingOnly( + top: (attachments.length == 1 && !AppTheme.isLargeScreen(context)) + ? 10 + : 6, + left: (attachments.length == 1 && !AppTheme.isLargeScreen(context)) + ? 24 + : 60, + right: 16, + ); + }, + ); + } + double _contentHeight = 0; @override @@ -417,7 +509,7 @@ class _PostItemState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( - child: AccountAvatar(content: item.author.avatar.toString()), + child: AccountAvatar(content: item.author.avatar), onTap: () { showModalBottomSheet( useRootNavigator: true, @@ -506,6 +598,7 @@ class _PostItemState extends State { left: 16, ), _buildAttachments(), + if (widget.showFeaturedReply) _buildFeaturedReply(), if (widget.isShowReply || widget.isReactable) PostQuickAction( isShowReply: widget.isShowReply, diff --git a/lib/widgets/posts/post_list.dart b/lib/widgets/posts/post_list.dart index 05a8eaa..121156c 100644 --- a/lib/widgets/posts/post_list.dart +++ b/lib/widgets/posts/post_list.dart @@ -33,6 +33,7 @@ class PostListWidget extends StatelessWidget { isShowEmbed: isShowEmbed, isNestedClickable: isNestedClickable, isClickable: isClickable, + showFeaturedReply: true, item: item, backgroundColor: backgroundColor, onUpdate: () { @@ -51,6 +52,7 @@ class PostListEntryWidget extends StatelessWidget { final bool isShowEmbed; final bool isNestedClickable; final bool isClickable; + final bool showFeaturedReply; final Post item; final Function onUpdate; final Color? backgroundColor; @@ -61,6 +63,7 @@ class PostListEntryWidget extends StatelessWidget { required this.isShowEmbed, required this.isNestedClickable, required this.isClickable, + required this.showFeaturedReply, required this.item, required this.onUpdate, this.backgroundColor, @@ -74,6 +77,7 @@ class PostListEntryWidget extends StatelessWidget { item: item, isShowEmbed: isShowEmbed, isClickable: isNestedClickable, + showFeaturedReply: showFeaturedReply, backgroundColor: backgroundColor, ).paddingSymmetric(vertical: 8), onLongPress: () { diff --git a/lib/widgets/posts/post_single_display.dart b/lib/widgets/posts/post_single_display.dart index 32cb9a1..fdfc428 100644 --- a/lib/widgets/posts/post_single_display.dart +++ b/lib/widgets/posts/post_single_display.dart @@ -23,6 +23,7 @@ class PostSingleDisplay extends StatelessWidget { isClickable: true, isShowEmbed: true, isNestedClickable: true, + showFeaturedReply: true, onUpdate: onUpdate, backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow, ), diff --git a/lib/widgets/posts/post_warped_list.dart b/lib/widgets/posts/post_warped_list.dart index 1e4c317..bc447ad 100644 --- a/lib/widgets/posts/post_warped_list.dart +++ b/lib/widgets/posts/post_warped_list.dart @@ -36,6 +36,7 @@ class PostWarpedListWidget extends StatelessWidget { isShowEmbed: isShowEmbed, isNestedClickable: isNestedClickable, isClickable: isClickable, + showFeaturedReply: true, item: item, onUpdate: onUpdate ?? () {}, ); diff --git a/pubspec.lock b/pubspec.lock index 29c054b..cac38bf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1894,10 +1894,10 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "7b41b6c3507854a159e24ae90a8e3e9cc01eb26a477c118d6dca065b5f55453e" + sha256: "4058172e418eb7e7f2058dcb7657d451a8fc264afa0dea4dbd0f304a57131611" url: "https://pub.dev" source: hosted - version: "2.5.4+2" + version: "2.5.4+3" sqlite3: dependency: transitive description: