@@ -504,6 +504,47 @@ class EmbedVideoNode extends BlockContentNode {
504504 }
505505}
506506
507+ class LinkPreviewNode extends BlockContentNode {
508+ const LinkPreviewNode ({
509+ super .debugHtmlNode,
510+ required this .imageHref,
511+ required this .imageSrc,
512+ required this .titleHref,
513+ required this .title,
514+ required this .description,
515+ });
516+
517+ final String imageHref;
518+ final String imageSrc;
519+ final String titleHref;
520+ final String title;
521+ final String description;
522+
523+ @override
524+ bool operator == (Object other) {
525+ return other is LinkPreviewNode
526+ && other.imageHref == imageHref
527+ && other.imageSrc == imageSrc
528+ && other.titleHref == titleHref
529+ && other.title == title
530+ && other.description == description;
531+ }
532+
533+ @override
534+ int get hashCode => Object .hash ('LinkPreviewNode' ,
535+ imageHref, imageSrc, titleHref, title, description);
536+
537+ @override
538+ void debugFillProperties (DiagnosticPropertiesBuilder properties) {
539+ super .debugFillProperties (properties);
540+ properties.add (StringProperty ('imageHref' , imageHref));
541+ properties.add (StringProperty ('imageSrc' , imageSrc));
542+ properties.add (StringProperty ('titleHref' , titleHref));
543+ properties.add (StringProperty ('title' , title));
544+ properties.add (StringProperty ('description' , description));
545+ }
546+ }
547+
507548/// A content node that expects an inline layout context from its parent.
508549///
509550/// When rendered into a Flutter widget tree, an inline content node
@@ -1220,6 +1261,57 @@ class _ZulipContentParser {
12201261 return EmbedVideoNode (hrefUrl: href, previewImageSrcUrl: imgSrc, debugHtmlNode: debugHtmlNode);
12211262 }
12221263
1264+ static final _linkPreviewImageSrcRegexp = RegExp (r'background-image: url\("(.+)"\)' );
1265+
1266+ BlockContentNode parseLinkPreviewNode (dom.Element divElement) {
1267+ assert (_debugParserContext == _ParserContext .block);
1268+ assert (divElement.localName == 'div'
1269+ && divElement.className == 'message_embed' );
1270+
1271+ assert (divElement.nodes.length == 2 );
1272+ final imageElm = divElement.nodes.first as dom.Element ;
1273+ assert (imageElm.localName == 'a'
1274+ && imageElm.className == 'message_embed_image' );
1275+
1276+ final imageHrefUrl = imageElm.attributes['href' ]! ;
1277+
1278+ final imageElmStyleAttr = imageElm.attributes['style' ]! ;
1279+ final match = _linkPreviewImageSrcRegexp.firstMatch (imageElmStyleAttr)! ;
1280+ final imageSrcUrl = match.group (1 )! ;
1281+
1282+ final dataContainer = divElement.nodes.last as dom.Element ;
1283+ assert (dataContainer.localName == 'div'
1284+ && dataContainer.className == 'data-container' );
1285+ assert (dataContainer.nodes.length == 2 );
1286+
1287+ final titleElm = dataContainer.nodes.first as dom.Element ;
1288+ assert (titleElm.localName == 'div'
1289+ && titleElm.className == 'message_embed_title' );
1290+ assert (titleElm.nodes.length == 1 );
1291+
1292+ final titleLinkElm = titleElm.nodes.single as dom.Element ;
1293+ assert (titleLinkElm.localName == 'a' );
1294+ assert (titleLinkElm.nodes.length == 1 );
1295+
1296+ final titleLinkHrefUrl = titleLinkElm.attributes['href' ]! ;
1297+ final titleLinkText = titleLinkElm.nodes.single as dom.Text ;
1298+
1299+ final descriptionElm = dataContainer.nodes.last as dom.Element ;
1300+ assert (descriptionElm.localName == 'div'
1301+ && descriptionElm.className == 'message_embed_description' );
1302+ assert (descriptionElm.nodes.length == 1 );
1303+
1304+ final descriptionText = descriptionElm.nodes.single as dom.Text ;
1305+
1306+ return LinkPreviewNode (
1307+ imageHref: imageHrefUrl,
1308+ imageSrc: imageSrcUrl,
1309+ titleHref: titleLinkHrefUrl,
1310+ title: titleLinkText.text,
1311+ description: descriptionText.text,
1312+ );
1313+ }
1314+
12231315 BlockContentNode parseBlockContent (dom.Node node) {
12241316 assert (_debugParserContext == _ParserContext .block);
12251317 final debugHtmlNode = kDebugMode ? node : null ;
@@ -1313,6 +1405,10 @@ class _ZulipContentParser {
13131405 }
13141406 }
13151407
1408+ if (localName == 'div' && className == 'message_embed' ) {
1409+ return parseLinkPreviewNode (element);
1410+ }
1411+
13161412 // TODO more types of node
13171413 return UnimplementedBlockContentNode (htmlNode: node);
13181414 }
0 commit comments