diff --git a/lib/widgets/content.dart b/lib/widgets/content.dart index 078fe0a964..9554887451 100644 --- a/lib/widgets/content.dart +++ b/lib/widgets/content.dart @@ -107,13 +107,7 @@ class BlockContentList extends StatelessWidget { ]), style: errorStyle); } else if (node is EmbedVideoNode) { - return Text.rich( - TextSpan(children: [ - const TextSpan(text: "(unimplemented:", style: errorStyle), - TextSpan(text: node.debugHtmlText, style: errorCodeStyle), - const TextSpan(text: ")", style: errorStyle), - ]), - style: errorStyle); + return MessageEmbedVideo(node: node); } else if (node is UnimplementedBlockContentNode) { return Text.rich(_errorUnimplemented(node)); } else { @@ -403,6 +397,33 @@ class MessageImage extends StatelessWidget { } } +class MessageEmbedVideo extends StatelessWidget { + const MessageEmbedVideo({super.key, required this.node}); + + final EmbedVideoNode node; + + @override + Widget build(BuildContext context) { + final store = PerAccountStoreWidget.of(context); + final previewImageSrcUrl = store.tryResolveUrl(node.previewImageSrcUrl); + + return MessageMediaContainer( + onTap: () => _launchUrl(context, node.hrefUrl), + child: Stack( + alignment: Alignment.center, + children: [ + if (previewImageSrcUrl != null) + RealmContentNetworkImage( + previewImageSrcUrl, + filterQuality: FilterQuality.medium), + const Icon( + Icons.play_arrow_rounded, + color: Colors.white, + size: 32), + ])); + } +} + class MessageMediaContainer extends StatelessWidget { const MessageMediaContainer({ super.key, diff --git a/test/widgets/content_test.dart b/test/widgets/content_test.dart index 3809e2b33d..9e061691d8 100644 --- a/test/widgets/content_test.dart +++ b/test/widgets/content_test.dart @@ -270,6 +270,63 @@ void main() { }); }); + group("MessageEmbedVideo", () { + Future prepareContent(WidgetTester tester, String html) async { + addTearDown(testBinding.reset); + await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot()); + prepareBoringImageHttpClient(); + + await tester.pumpWidget(GlobalStoreWidget(child: MaterialApp( + home: PerAccountStoreWidget(accountId: eg.selfAccount.id, + child: MessageContent( + message: eg.streamMessage(content: html), + content: parseContent(html)))))); + await tester.pump(); // global store + await tester.pump(); // per-account store + debugNetworkImageHttpClientProvider = null; + } + + testWidgets('video preview for youtube embed', (tester) async { + const example = ContentExample.videoEmbedYoutube; + await prepareContent(tester, example.html); + + final expectedVideo = example.expectedNodes[1] as EmbedVideoNode; + final expectedResolvedPreviewUrl = eg + .store() + .tryResolveUrl(expectedVideo.previewImageSrcUrl)!; + final image = tester.widget( + find.byType(RealmContentNetworkImage)); + check(image.src) + .equals(expectedResolvedPreviewUrl); + + final expectedLaunchUrl = expectedVideo.hrefUrl; + await tester.tap(find.byIcon(Icons.play_arrow_rounded)); + check(testBinding.takeLaunchUrlCalls()) + .single.equals((url: Uri.parse(expectedLaunchUrl), mode: LaunchMode.platformDefault)); + }); + + testWidgets('video preview for vimeo embed', (tester) async { + const example = ContentExample.videoEmbedVimeo; + await prepareContent(tester, example.html); + + final expectedTitle = (((example.expectedNodes[0] as ParagraphNode).nodes[0] as LinkNode).nodes[0] as TextNode).text; + await tester.ensureVisible(find.text(expectedTitle)); + + final expectedVideo = example.expectedNodes[1] as EmbedVideoNode; + final expectedResolvedUrl = eg.store().tryResolveUrl(expectedVideo.previewImageSrcUrl)!; + final image = tester.widget( + find.byType(RealmContentNetworkImage)); + check(image.src) + .equals(expectedResolvedUrl); + + final expectedLaunchUrl = expectedVideo.hrefUrl; + await tester.tap(find.byIcon(Icons.play_arrow_rounded)); + check(testBinding.takeLaunchUrlCalls()) + .single.equals((url: Uri.parse(expectedLaunchUrl), mode: LaunchMode.platformDefault)); + }); + }); + + group("CodeBlock", () { testContentSmoke(ContentExample.codeBlockPlain); testContentSmoke(ContentExample.codeBlockHighlightedShort);