From 56bbf73b5e4b9acb553094c6f12c42e7938100cd Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 6 Aug 2024 16:24:47 +0800 Subject: [PATCH] :sparkles: Better sticker & able embed attachment into markdown --- lib/widgets/attachments/attachment_list.dart | 47 ++++++++++++++++++++ lib/widgets/chat/chat_event_message.dart | 6 ++- lib/widgets/markdown_text_content.dart | 40 ++++++++++++++++- lib/widgets/posts/post_item.dart | 2 + 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/lib/widgets/attachments/attachment_list.dart b/lib/widgets/attachments/attachment_list.dart index 51afe3a..e831ba1 100644 --- a/lib/widgets/attachments/attachment_list.dart +++ b/lib/widgets/attachments/attachment_list.dart @@ -322,3 +322,50 @@ class AttachmentListEntry extends StatelessWidget { ); } } + +class AttachmentSelfContainedEntry extends StatefulWidget { + final int id; + final String parentId; + final bool isDense; + + const AttachmentSelfContainedEntry({ + super.key, + required this.id, + required this.parentId, + this.isDense = false, + }); + + @override + State createState() => + _AttachmentSelfContainedEntryState(); +} + +class _AttachmentSelfContainedEntryState + extends State { + bool _showMature = false; + + @override + Widget build(BuildContext context) { + final AttachmentProvider attachments = Get.find(); + + return FutureBuilder( + future: attachments.getMetadata(widget.id), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Text('Loading...'); + } + + return AttachmentListEntry( + item: snapshot.data, + isDense: widget.isDense, + parentId: widget.parentId, + showMature: _showMature, + showBorder: true, + onReveal: (value) { + setState(() => _showMature = value); + }, + ); + }, + ); + } +} diff --git a/lib/widgets/chat/chat_event_message.dart b/lib/widgets/chat/chat_event_message.dart index 146dccd..5e0764d 100644 --- a/lib/widgets/chat/chat_event_message.dart +++ b/lib/widgets/chat/chat_event_message.dart @@ -43,7 +43,11 @@ class ChatEventMessage extends StatelessWidget { ); } - return MarkdownTextContent(content: body.text); + return MarkdownTextContent( + parentId: 'm${item.id}', + isSelectable: true, + content: body.text, + ); } Widget _buildBody(BuildContext context) { diff --git a/lib/widgets/markdown_text_content.dart b/lib/widgets/markdown_text_content.dart index 3aee67e..755be8c 100644 --- a/lib/widgets/markdown_text_content.dart +++ b/lib/widgets/markdown_text_content.dart @@ -1,20 +1,25 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown_selectionarea/flutter_markdown.dart'; import 'package:get/get.dart'; import 'package:markdown/markdown.dart' as markdown; import 'package:markdown/markdown.dart'; +import 'package:solian/platform.dart'; import 'package:solian/providers/stickers.dart'; +import 'package:solian/widgets/attachments/attachment_list.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'account/account_profile_popup.dart'; class MarkdownTextContent extends StatelessWidget { final String content; + final String parentId; final bool isSelectable; const MarkdownTextContent({ super.key, required this.content, + required this.parentId, this.isSelectable = false, }); @@ -42,6 +47,7 @@ class MarkdownTextContent extends StatelessWidget { _UserNameCardInlineSyntax(), _CustomEmoteInlineSyntax(), markdown.EmojiSyntax(), + markdown.AutolinkSyntax(), markdown.AutolinkExtensionSyntax(), ...markdown.ExtensionSet.gitHubFlavored.inlineSyntaxes ], @@ -70,6 +76,38 @@ class MarkdownTextContent extends StatelessWidget { mode: LaunchMode.externalApplication, ); }, + imageBuilder: (uri, title, alt) { + var url = uri.toString(); + double? width, height; + if (url.startsWith('solink://')) { + final segments = url.replaceFirst('solink://', '').split('/'); + switch (segments[0]) { + case 'stickers': + final StickerProvider sticker = Get.find(); + url = sticker.aliasImageMapping[segments[1]]!; + width = 28; + height = 28; + break; + case 'attachments': + const radius = BorderRadius.all(Radius.circular(8)); + return LimitedBox( + maxHeight: 360, + child: ClipRRect( + borderRadius: radius, + child: AttachmentSelfContainedEntry( + isDense: true, + parentId: parentId, + id: int.parse(segments[1]), + ), + ), + ).paddingSymmetric(vertical: 4); + } + } + + return PlatformInfo.canCacheImage + ? CachedNetworkImage(imageUrl: url, width: width, height: height) + : Image.network(url, width: width, height: height); + }, ); } @@ -111,7 +149,7 @@ class _CustomEmoteInlineSyntax extends InlineSyntax { } final element = markdown.Element.empty('img'); - element.attributes['src'] = sticker.aliasImageMapping[alias]!; + element.attributes['src'] = 'solink://stickers/$alias'; parser.addNode(element); return true; diff --git a/lib/widgets/posts/post_item.dart b/lib/widgets/posts/post_item.dart index dc4261a..b49e390 100644 --- a/lib/widgets/posts/post_item.dart +++ b/lib/widgets/posts/post_item.dart @@ -295,6 +295,7 @@ class _PostItemState extends State { setState(() => _contentHeight = size.height); }, child: MarkdownTextContent( + parentId: 'p${item.id}', content: item.body['content'], isSelectable: widget.isContentSelectable, ).paddingOnly( @@ -389,6 +390,7 @@ class _PostItemState extends State { setState(() => _contentHeight = size.height); }, child: MarkdownTextContent( + parentId: 'p${item.id}', content: item.body['content'], isSelectable: widget.isContentSelectable, ).paddingOnly(left: 12, right: 8),