✨ Better sticker & able embed attachment into markdown
This commit is contained in:
		| @@ -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<AttachmentSelfContainedEntry> createState() => | ||||
|       _AttachmentSelfContainedEntryState(); | ||||
| } | ||||
|  | ||||
| class _AttachmentSelfContainedEntryState | ||||
|     extends State<AttachmentSelfContainedEntry> { | ||||
|   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); | ||||
|           }, | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -295,6 +295,7 @@ class _PostItemState extends State<PostItem> { | ||||
|                     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<PostItem> { | ||||
|                               setState(() => _contentHeight = size.height); | ||||
|                             }, | ||||
|                             child: MarkdownTextContent( | ||||
|                               parentId: 'p${item.id}', | ||||
|                               content: item.body['content'], | ||||
|                               isSelectable: widget.isContentSelectable, | ||||
|                             ).paddingOnly(left: 12, right: 8), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user