@@ -504,6 +504,47 @@ class EmbedVideoNode extends BlockContentNode {
504
504
}
505
505
}
506
506
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
+
507
548
/// A content node that expects an inline layout context from its parent.
508
549
///
509
550
/// When rendered into a Flutter widget tree, an inline content node
@@ -1220,6 +1261,57 @@ class _ZulipContentParser {
1220
1261
return EmbedVideoNode (hrefUrl: href, previewImageSrcUrl: imgSrc, debugHtmlNode: debugHtmlNode);
1221
1262
}
1222
1263
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
+
1223
1315
BlockContentNode parseBlockContent (dom.Node node) {
1224
1316
assert (_debugParserContext == _ParserContext .block);
1225
1317
final debugHtmlNode = kDebugMode ? node : null ;
@@ -1313,6 +1405,10 @@ class _ZulipContentParser {
1313
1405
}
1314
1406
}
1315
1407
1408
+ if (localName == 'div' && className == 'message_embed' ) {
1409
+ return parseLinkPreviewNode (element);
1410
+ }
1411
+
1316
1412
// TODO more types of node
1317
1413
return UnimplementedBlockContentNode (htmlNode: node);
1318
1414
}
0 commit comments