Better sticker & able embed attachment into markdown

This commit is contained in:
LittleSheep 2024-08-06 16:24:47 +08:00
parent 4f6c5aa053
commit 56bbf73b5e
4 changed files with 93 additions and 2 deletions

View File

@ -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);
},
);
},
);
}
}

View File

@ -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) { Widget _buildBody(BuildContext context) {

View File

@ -1,20 +1,25 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_markdown_selectionarea/flutter_markdown.dart'; import 'package:flutter_markdown_selectionarea/flutter_markdown.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:markdown/markdown.dart' as markdown; import 'package:markdown/markdown.dart' as markdown;
import 'package:markdown/markdown.dart'; import 'package:markdown/markdown.dart';
import 'package:solian/platform.dart';
import 'package:solian/providers/stickers.dart'; import 'package:solian/providers/stickers.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'account/account_profile_popup.dart'; import 'account/account_profile_popup.dart';
class MarkdownTextContent extends StatelessWidget { class MarkdownTextContent extends StatelessWidget {
final String content; final String content;
final String parentId;
final bool isSelectable; final bool isSelectable;
const MarkdownTextContent({ const MarkdownTextContent({
super.key, super.key,
required this.content, required this.content,
required this.parentId,
this.isSelectable = false, this.isSelectable = false,
}); });
@ -42,6 +47,7 @@ class MarkdownTextContent extends StatelessWidget {
_UserNameCardInlineSyntax(), _UserNameCardInlineSyntax(),
_CustomEmoteInlineSyntax(), _CustomEmoteInlineSyntax(),
markdown.EmojiSyntax(), markdown.EmojiSyntax(),
markdown.AutolinkSyntax(),
markdown.AutolinkExtensionSyntax(), markdown.AutolinkExtensionSyntax(),
...markdown.ExtensionSet.gitHubFlavored.inlineSyntaxes ...markdown.ExtensionSet.gitHubFlavored.inlineSyntaxes
], ],
@ -70,6 +76,38 @@ class MarkdownTextContent extends StatelessWidget {
mode: LaunchMode.externalApplication, 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'); final element = markdown.Element.empty('img');
element.attributes['src'] = sticker.aliasImageMapping[alias]!; element.attributes['src'] = 'solink://stickers/$alias';
parser.addNode(element); parser.addNode(element);
return true; return true;

View File

@ -295,6 +295,7 @@ class _PostItemState extends State<PostItem> {
setState(() => _contentHeight = size.height); setState(() => _contentHeight = size.height);
}, },
child: MarkdownTextContent( child: MarkdownTextContent(
parentId: 'p${item.id}',
content: item.body['content'], content: item.body['content'],
isSelectable: widget.isContentSelectable, isSelectable: widget.isContentSelectable,
).paddingOnly( ).paddingOnly(
@ -389,6 +390,7 @@ class _PostItemState extends State<PostItem> {
setState(() => _contentHeight = size.height); setState(() => _contentHeight = size.height);
}, },
child: MarkdownTextContent( child: MarkdownTextContent(
parentId: 'p${item.id}',
content: item.body['content'], content: item.body['content'],
isSelectable: widget.isContentSelectable, isSelectable: widget.isContentSelectable,
).paddingOnly(left: 12, right: 8), ).paddingOnly(left: 12, right: 8),