Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
rajveermalviya committed Nov 6, 2024
1 parent ffa41be commit abdec10
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
96 changes: 96 additions & 0 deletions lib/model/content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,47 @@ class EmbedVideoNode extends BlockContentNode {
}
}

class LinkPreviewNode extends BlockContentNode {
const LinkPreviewNode({
super.debugHtmlNode,
required this.imageHref,
required this.imageSrc,
required this.titleHref,
required this.title,
required this.description,
});

final String imageHref;
final String imageSrc;
final String titleHref;
final String title;
final String description;

@override
bool operator ==(Object other) {
return other is LinkPreviewNode
&& other.imageHref == imageHref
&& other.imageSrc == imageSrc
&& other.titleHref == titleHref
&& other.title == title
&& other.description == description;
}

@override
int get hashCode => Object.hash('LinkPreviewNode',
imageHref, imageSrc, titleHref, title, description);

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(StringProperty('imageHref', imageHref));
properties.add(StringProperty('imageSrc', imageSrc));
properties.add(StringProperty('titleHref', titleHref));
properties.add(StringProperty('title', title));
properties.add(StringProperty('description', description));
}
}

/// A content node that expects an inline layout context from its parent.
///
/// When rendered into a Flutter widget tree, an inline content node
Expand Down Expand Up @@ -1220,6 +1261,57 @@ class _ZulipContentParser {
return EmbedVideoNode(hrefUrl: href, previewImageSrcUrl: imgSrc, debugHtmlNode: debugHtmlNode);
}

static final _linkPreviewImageSrcRegexp = RegExp(r'background-image: url\("(.+)"\)');

BlockContentNode parseLinkPreviewNode(dom.Element divElement) {
assert(_debugParserContext == _ParserContext.block);
assert(divElement.localName == 'div'
&& divElement.className == 'message_embed');

assert(divElement.nodes.length == 2);
final imageElm = divElement.nodes.first as dom.Element;
assert(imageElm.localName == 'a'
&& imageElm.className == 'message_embed_image');

final imageHrefUrl = imageElm.attributes['href']!;

final imageElmStyleAttr = imageElm.attributes['style']!;
final match = _linkPreviewImageSrcRegexp.firstMatch(imageElmStyleAttr)!;
final imageSrcUrl = match.group(1)!;

final dataContainer = divElement.nodes.last as dom.Element;
assert(dataContainer.localName == 'div'
&& dataContainer.className == 'data-container');
assert(dataContainer.nodes.length == 2);

final titleElm = dataContainer.nodes.first as dom.Element;
assert(titleElm.localName == 'div'
&& titleElm.className == 'message_embed_title');
assert(titleElm.nodes.length == 1);

final titleLinkElm = titleElm.nodes.single as dom.Element;
assert(titleLinkElm.localName == 'a');
assert(titleLinkElm.nodes.length == 1);

final titleLinkHrefUrl = titleLinkElm.attributes['href']!;
final titleLinkText = titleLinkElm.nodes.single as dom.Text;

final descriptionElm = dataContainer.nodes.last as dom.Element;
assert(descriptionElm.localName == 'div'
&& descriptionElm.className == 'message_embed_description');
assert(descriptionElm.nodes.length == 1);

final descriptionText = descriptionElm.nodes.single as dom.Text;

return LinkPreviewNode(
imageHref: imageHrefUrl,
imageSrc: imageSrcUrl,
titleHref: titleLinkHrefUrl,
title: titleLinkText.text,
description: descriptionText.text,
);
}

BlockContentNode parseBlockContent(dom.Node node) {
assert(_debugParserContext == _ParserContext.block);
final debugHtmlNode = kDebugMode ? node : null;
Expand Down Expand Up @@ -1313,6 +1405,10 @@ class _ZulipContentParser {
}
}

if (localName == 'div' && className == 'message_embed') {
return parseLinkPreviewNode(element);
}

// TODO more types of node
return UnimplementedBlockContentNode(htmlNode: node);
}
Expand Down
37 changes: 37 additions & 0 deletions lib/widgets/content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ class BlockContentList extends StatelessWidget {
EmbedVideoNode() => MessageEmbedVideo(node: node),
UnimplementedBlockContentNode() =>
Text.rich(_errorUnimplemented(node, context: context)),
LinkPreviewNode() => MessageLinkPreview(node: node),
};

}),
Expand Down Expand Up @@ -799,6 +800,42 @@ class MathBlock extends StatelessWidget {
}
}

class MessageLinkPreview extends StatelessWidget {
const MessageLinkPreview({super.key, required this.node});

final LinkPreviewNode node;

@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: const BoxDecoration(
border: Border(left: BorderSide(color: Colors.white, width: 3))
),
child: Padding(
padding: const EdgeInsets.fromLTRB(5 + 3, 5, 5, 5),
child: Row(
children: [
GestureDetector(
onTap: () => _launchUrl(context, node.imageHref),
child: RealmContentNetworkImage(Uri.parse(node.imageSrc), fit: BoxFit.cover, width: 70, height: 70)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => _launchUrl(context, node.titleHref),
child: Text(node.title)),
Text(node.description),
]),
),
]),
),
);
}
}

//
// Inline layout.
//
Expand Down

0 comments on commit abdec10

Please sign in to comment.