diff --git a/lib/screens/articles/article_detail.dart b/lib/screens/articles/article_detail.dart index f6e9061..0430bff 100644 --- a/lib/screens/articles/article_detail.dart +++ b/lib/screens/articles/article_detail.dart @@ -56,10 +56,6 @@ class _ArticleDetailScreenState extends State { ), ), ), - SliverToBoxAdapter( - child: const Divider(thickness: 0.3, height: 1) - .paddingOnly(top: 4), - ), SliverToBoxAdapter( child: SizedBox(height: MediaQuery.of(context).padding.bottom), ), diff --git a/lib/widgets/articles/article_item.dart b/lib/widgets/articles/article_item.dart index d97579a..2d00a35 100644 --- a/lib/widgets/articles/article_item.dart +++ b/lib/widgets/articles/article_item.dart @@ -5,7 +5,7 @@ import 'package:solian/models/articles.dart'; import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_profile_popup.dart'; import 'package:solian/widgets/articles/article_quick_action.dart'; -import 'package:solian/widgets/feed/feed_content.dart'; +import 'package:solian/widgets/markdown_text_content.dart'; import 'package:solian/widgets/feed/feed_tags.dart'; import 'package:timeago/timeago.dart' show format; @@ -179,15 +179,11 @@ class _ArticleItemState extends State { right: 16, left: 16, ), - const Divider(thickness: 0.3, height: 0.3).paddingSymmetric( - vertical: 10, - ), - FeedContent(content: item.content).paddingSymmetric( - horizontal: 20, - ), - const Divider(thickness: 0.3, height: 0.3).paddingOnly( + MarkdownTextContent(content: item.content).paddingOnly( + left: 20, + right: 20, top: 10, - bottom: 6, + bottom: 8, ), buildFooter().paddingOnly(left: 20), if (widget.isReactable) diff --git a/lib/widgets/articles/article_list.dart b/lib/widgets/articles/article_list.dart index b23e96f..bab8a33 100644 --- a/lib/widgets/articles/article_list.dart +++ b/lib/widgets/articles/article_list.dart @@ -24,18 +24,17 @@ class ArticleListWidget extends StatelessWidget { @override Widget build(BuildContext context) { return PagedSliverList.separated( + addRepaintBoundaries: true, pagingController: controller, builderDelegate: PagedChildBuilderDelegate
( itemBuilder: (context, item, index) { - return RepaintBoundary( - child: CenteredContainer( - child: ArticleListEntryWidget( - isClickable: isClickable, - item: item, - onUpdate: () { - controller.refresh(); - }, - ), + return CenteredContainer( + child: ArticleListEntryWidget( + isClickable: isClickable, + item: item, + onUpdate: () { + controller.refresh(); + }, ), ); }, diff --git a/lib/widgets/attachments/attachment_list.dart b/lib/widgets/attachments/attachment_list.dart index 5a48439..fb8b543 100644 --- a/lib/widgets/attachments/attachment_list.dart +++ b/lib/widgets/attachments/attachment_list.dart @@ -102,81 +102,79 @@ class _AttachmentListState extends State { } Widget buildEntry(Attachment element, int idx) { - return RepaintBoundary( - child: GestureDetector( - child: Container( - width: widget.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, - ), - ], - ), - ), - ), - ], - ), + return GestureDetector( + child: Container( + width: widget.width ?? MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, ), - 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, + 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, + ), + ), + ); + } + }, ); } diff --git a/lib/widgets/chat/chat_event_list.dart b/lib/widgets/chat/chat_event_list.dart index 310ce5e..a88e9f6 100644 --- a/lib/widgets/chat/chat_event_list.dart +++ b/lib/widgets/chat/chat_event_list.dart @@ -56,16 +56,14 @@ class ChatEventList extends StatelessWidget { final item = chatController.currentEvents[index].data; return InkWell( - child: RepaintBoundary( - child: ChatEvent( - key: Key('m${item.uuid}'), - item: item, - isMerged: isMerged, - chatController: chatController, - ).paddingOnly( - top: !isMerged ? 8 : 0, - bottom: !hasMerged ? 8 : 0, - ), + child: ChatEvent( + key: Key('m${item.uuid}'), + item: item, + isMerged: isMerged, + chatController: chatController, + ).paddingOnly( + top: !isMerged ? 8 : 0, + bottom: !hasMerged ? 8 : 0, ), onLongPress: () { showModalBottomSheet( diff --git a/lib/widgets/chat/chat_event_message.dart b/lib/widgets/chat/chat_event_message.dart index d955d31..777d00f 100644 --- a/lib/widgets/chat/chat_event_message.dart +++ b/lib/widgets/chat/chat_event_message.dart @@ -1,12 +1,10 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:get/get.dart'; import 'package:solian/models/event.dart'; import 'package:solian/widgets/attachments/attachment_list.dart'; -import 'package:url_launcher/url_launcher_string.dart'; -import 'package:markdown/markdown.dart' as markdown; +import 'package:solian/widgets/markdown_text_content.dart'; class ChatEventMessage extends StatelessWidget { final Event item; @@ -44,28 +42,7 @@ class ChatEventMessage extends StatelessWidget { final body = EventMessageBody.fromJson(item.body); final hasAttachment = body.attachments?.isNotEmpty ?? false; - return Markdown( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - data: body.text, - selectable: true, - padding: const EdgeInsets.all(0), - extensionSet: markdown.ExtensionSet( - markdown.ExtensionSet.gitHubFlavored.blockSyntaxes, - [ - markdown.EmojiSyntax(), - markdown.AutolinkExtensionSyntax(), - ...markdown.ExtensionSet.gitHubFlavored.inlineSyntaxes - ], - ), - onTapLink: (text, href, title) async { - if (href == null) return; - await launchUrlString( - href, - mode: LaunchMode.externalApplication, - ); - }, - ).paddingOnly( + return MarkdownTextContent(content: body.text).paddingOnly( left: isQuote ? 0 : 12, right: isQuote ? 0 : 12, top: body.quoteEvent == null ? 2 : 0, diff --git a/lib/widgets/feed/feed_list.dart b/lib/widgets/feed/feed_list.dart index c4e65af..3fd5b5f 100644 --- a/lib/widgets/feed/feed_list.dart +++ b/lib/widgets/feed/feed_list.dart @@ -24,39 +24,38 @@ class FeedListWidget extends StatelessWidget { @override Widget build(BuildContext context) { return PagedSliverList.separated( + addRepaintBoundaries: true, pagingController: controller, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) { - return RepaintBoundary( - child: CenteredContainer( - child: Builder( - builder: (context) { - switch (item.type) { - case 'post': - final data = Post.fromJson(item.data); - return PostListEntryWidget( - isShowEmbed: isShowEmbed, - isNestedClickable: isNestedClickable, - isClickable: isClickable, - item: data, - onUpdate: () { - controller.refresh(); - }, - ); - case 'article': - final data = Article.fromJson(item.data); - return ArticleListEntryWidget( - isClickable: isClickable, - item: data, - onUpdate: () { - controller.refresh(); - }, - ); - default: - return const SizedBox(); - } - }, - ), + return CenteredContainer( + child: Builder( + builder: (context) { + switch (item.type) { + case 'post': + final data = Post.fromJson(item.data); + return PostListEntryWidget( + isShowEmbed: isShowEmbed, + isNestedClickable: isNestedClickable, + isClickable: isClickable, + item: data, + onUpdate: () { + controller.refresh(); + }, + ); + case 'article': + final data = Article.fromJson(item.data); + return ArticleListEntryWidget( + isClickable: isClickable, + item: data, + onUpdate: () { + controller.refresh(); + }, + ); + default: + return const SizedBox(); + } + }, ), ); }, diff --git a/lib/widgets/feed/feed_content.dart b/lib/widgets/markdown_text_content.dart similarity index 68% rename from lib/widgets/feed/feed_content.dart rename to lib/widgets/markdown_text_content.dart index aef1889..df5e005 100644 --- a/lib/widgets/feed/feed_content.dart +++ b/lib/widgets/markdown_text_content.dart @@ -3,10 +3,10 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:markdown/markdown.dart' as markdown; import 'package:url_launcher/url_launcher_string.dart'; -class FeedContent extends StatelessWidget { +class MarkdownTextContent extends StatelessWidget { final String content; - const FeedContent({super.key, required this.content}); + const MarkdownTextContent({super.key, required this.content}); @override Widget build(BuildContext context) { @@ -15,6 +15,16 @@ class FeedContent extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), data: content, padding: EdgeInsets.zero, + styleSheet: MarkdownStyleSheet.fromTheme(Theme.of(context)).copyWith( + horizontalRuleDecoration: BoxDecoration( + border: Border( + top: BorderSide( + width: 1.0, + color: Theme.of(context).dividerColor, + ), + ), + ), + ), extensionSet: markdown.ExtensionSet( markdown.ExtensionSet.gitHubFlavored.blockSyntaxes, [ diff --git a/lib/widgets/posts/post_item.dart b/lib/widgets/posts/post_item.dart index 50f87c8..76afe9e 100644 --- a/lib/widgets/posts/post_item.dart +++ b/lib/widgets/posts/post_item.dart @@ -7,7 +7,7 @@ import 'package:solian/router.dart'; import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_profile_popup.dart'; import 'package:solian/widgets/attachments/attachment_list.dart'; -import 'package:solian/widgets/feed/feed_content.dart'; +import 'package:solian/widgets/markdown_text_content.dart'; import 'package:solian/widgets/feed/feed_tags.dart'; import 'package:solian/widgets/posts/post_quick_action.dart'; import 'package:timeago/timeago.dart' show format; @@ -190,7 +190,7 @@ class _PostItemState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ buildHeader().paddingSymmetric(horizontal: 12), - FeedContent(content: item.content).paddingOnly( + MarkdownTextContent(content: item.content).paddingOnly( left: 16, right: 12, top: 2, @@ -231,7 +231,7 @@ class _PostItemState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ buildHeader(), - FeedContent(content: item.content) + MarkdownTextContent(content: item.content) .paddingOnly(left: 12, right: 8), if (widget.item.replyTo != null && widget.isShowEmbed) GestureDetector( @@ -275,7 +275,7 @@ class _PostItemState extends State { attachmentsId: item.attachments ?? List.empty(), divided: true, ), - if (!widget.isShowReply && widget.isReactable) + if (widget.isShowReply && widget.isReactable) PostQuickAction( isShowReply: widget.isShowReply, isReactable: widget.isReactable, diff --git a/lib/widgets/posts/post_list.dart b/lib/widgets/posts/post_list.dart index 5c9753e..7a718bf 100644 --- a/lib/widgets/posts/post_list.dart +++ b/lib/widgets/posts/post_list.dart @@ -24,20 +24,19 @@ class PostListWidget extends StatelessWidget { @override Widget build(BuildContext context) { return PagedSliverList.separated( + addRepaintBoundaries: true, pagingController: controller, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) { - return RepaintBoundary( - child: CenteredContainer( - child: PostListEntryWidget( - isShowEmbed: isShowEmbed, - isNestedClickable: isNestedClickable, - isClickable: isClickable, - item: item, - onUpdate: () { - controller.refresh(); - }, - ), + return CenteredContainer( + child: PostListEntryWidget( + isShowEmbed: isShowEmbed, + isNestedClickable: isNestedClickable, + isClickable: isClickable, + item: item, + onUpdate: () { + controller.refresh(); + }, ), ); },