♻️ Refactor markdown sticker rendering

This commit is contained in:
2025-10-26 03:04:02 +08:00
parent 1e71ad33a6
commit 01fa228e45

View File

@@ -47,7 +47,6 @@ class MarkdownTextContent extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final baseUrl = ref.watch(serverUrlProvider);
final doesEnlargeSticker = useMemoized(() { final doesEnlargeSticker = useMemoized(() {
// Check if content only contains one sticker by matching the sticker pattern // Check if content only contains one sticker by matching the sticker pattern
final stickerPattern = RegExp(stickerRegex); final stickerPattern = RegExp(stickerRegex);
@@ -88,6 +87,14 @@ class MarkdownTextContent extends HookConsumerWidget {
onToggle: () => spoilerRevealed.value = !spoilerRevealed.value, onToggle: () => spoilerRevealed.value = !spoilerRevealed.value,
); );
final baseUrl = ref.watch(serverUrlProvider);
final stickerGenerator = StickerGenerator(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
isEnlarged: doesEnlargeSticker,
baseUrl: baseUrl,
);
return MarkdownBlock( return MarkdownBlock(
data: content, data: content,
selectable: isSelectable, selectable: isSelectable,
@@ -186,28 +193,6 @@ class MarkdownTextContent extends HookConsumerWidget {
).clipRRect(all: 8), ).clipRRect(all: 8),
), ),
); );
case 'stickers':
final size = doesEnlargeSticker ? 96.0 : 24.0;
final stickerUri =
'$baseUrl/sphere/stickers/lookup/${uri.pathSegments[0]}/open';
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: const BorderRadius.all(
Radius.circular(8),
),
),
child: UniversalImage(
uri: stickerUri,
width: size,
height: size,
fit: BoxFit.contain,
noCacheOptimization: true,
),
),
);
} }
} }
final content = ClipRRect( final content = ClipRRect(
@@ -228,7 +213,12 @@ class MarkdownTextContent extends HookConsumerWidget {
generator: MarkdownTextContent.buildGenerator( generator: MarkdownTextContent.buildGenerator(
isDark: isDark, isDark: isDark,
linesMargin: linesMargin, linesMargin: linesMargin,
generators: [mentionGenerator, highlightGenerator, spoilerGenerator], generators: [
mentionGenerator,
highlightGenerator,
spoilerGenerator,
stickerGenerator,
],
), ),
); );
} }
@@ -284,9 +274,8 @@ class _StickerInlineSyntax extends markdown.InlineSyntax {
@override @override
bool onMatch(markdown.InlineParser parser, Match match) { bool onMatch(markdown.InlineParser parser, Match match) {
final placeholder = match[1]!; final placeholder = match[1]!;
final image = markdown.Element.text('img', '') final element = markdown.Element('sticker', [markdown.Text(placeholder)]);
..attributes['src'] = Uri.encodeFull('solian://stickers/$placeholder'); parser.addNode(element);
parser.addNode(image);
return true; return true;
} }
@@ -559,3 +548,68 @@ class SpoilerSpanNode extends SpanNode {
); );
} }
} }
class StickerGenerator extends SpanNodeGeneratorWithTag {
StickerGenerator({
required Color backgroundColor,
required Color foregroundColor,
required bool isEnlarged,
required String baseUrl,
}) : super(
tag: 'sticker',
generator: (
markdown.Element element,
MarkdownConfig config,
WidgetVisitor visitor,
) {
return StickerSpanNode(
placeholder: element.textContent,
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
isEnlarged: isEnlarged,
baseUrl: baseUrl,
);
},
);
}
class StickerSpanNode extends SpanNode {
final String placeholder;
final Color backgroundColor;
final Color foregroundColor;
final bool isEnlarged;
final String baseUrl;
StickerSpanNode({
required this.placeholder,
required this.backgroundColor,
required this.foregroundColor,
required this.isEnlarged,
required this.baseUrl,
});
@override
InlineSpan build() {
final size = isEnlarged ? 96.0 : 24.0;
final stickerUri = '$baseUrl/sphere/stickers/lookup/$placeholder/open';
return WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container(
decoration: BoxDecoration(
color: backgroundColor.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
child: UniversalImage(
uri: stickerUri,
width: size,
height: size,
fit: BoxFit.contain,
noCacheOptimization: true,
),
),
),
);
}
}