Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
The one with the braid
ef2dbe8fbe chore: refactor linkifyer
- do not parse HTML Strings
- use HTML Element constructors everywhere
- removes need for applying <p> patch to new Anchor Elements

Signed-off-by: The one with the braid <info@braid.business>
2024-01-13 14:50:32 +01:00
The one with the braid
defe39bc88 fix: HTML renderer edge-case without top-level element
Signed-off-by: The one with the braid <info@braid.business>
2024-01-13 14:49:38 +01:00

View file

@ -28,30 +28,36 @@ class HtmlMessage extends StatelessWidget {
});
dom.Node _linkifyHtml(dom.Node element) {
for (final node in element.nodes) {
if (node is! dom.Text) {
node.replaceWith(_linkifyHtml(node));
continue;
final nodes = element.nodes;
if (!element.hasChildNodes()) {
return element;
}
for (final dom.Node node in nodes) {
if (node is Element) {
final newNode = _linkifyHtml(node);
node.replaceWith(newNode);
} else if (node is dom.Text) {
final linkified = linkify(
node.text,
options: const LinkifyOptions(
humanize: false,
looseUrl: true,
defaultToHttps: true,
),
);
final newNode = dom.Element.tag('span');
for (final element in linkified) {
if (element is TextElement) {
newNode.nodes.add(dom.Text(element.text));
} else if (element is LinkableElement) {
final anchor = dom.Element.tag('a');
anchor.attributes['href'] = element.url;
anchor.nodes.add(dom.Text(element.originText));
newNode.nodes.add(anchor);
}
}
node.replaceWith(newNode);
}
final parts = linkify(
node.text,
options: const LinkifyOptions(humanize: false),
);
if (!parts.any((part) => part is UrlElement)) {
continue;
}
final newHtml = parts
.map(
(linkifyElement) => linkifyElement is! UrlElement
? linkifyElement.text
: '<a href="${linkifyElement.text}">${linkifyElement.text}</a>',
)
.join(' ');
node.replaceWith(dom.Element.html('<p>$newHtml</p>'));
}
return element;
}
@ -88,7 +94,13 @@ class HtmlMessage extends StatelessWidget {
padding: HtmlPaddings.only(left: 6, bottom: 0),
);
final element = _linkifyHtml(HtmlParser.parseHTML(renderHtml));
// I encountered messages containing only a String with several HTML elements - without a common parent containing
// them - in this case, we'd end up with *several* top level elements - and the HTML parser fails
//
// We hence add an inline-block `<div>` around the entire message in order to ensure we're dealing with a single
// top-level element.
final paddedHtml = '<div style="display: inline-block;">$renderHtml</div>';
final element = _linkifyHtml(HtmlParser.parseHTML(paddedHtml));
// there is no need to pre-validate the html, as we validate it while rendering
return Html.fromElement(
@ -344,6 +356,7 @@ class MatrixMathExtension extends HtmlExtension {
final TextStyle? style;
MatrixMathExtension({this.style});
@override
Set<String> get supportedTags => {'div'};
@ -377,6 +390,7 @@ class CodeExtension extends HtmlExtension {
final double fontSize;
CodeExtension({required this.fontSize});
@override
Set<String> get supportedTags => {'code'};
@ -414,6 +428,7 @@ class RoomPillExtension extends HtmlExtension {
final BuildContext context;
RoomPillExtension(this.context, this.room);
@override
Set<String> get supportedTags => {'a'};