|
|
|
@ -6,6 +6,7 @@ import 'package:flutter_highlighter/themes/shades-of-purple.dart';
|
|
|
|
|
import 'package:flutter_html/flutter_html.dart';
|
|
|
|
|
import 'package:flutter_html_table/flutter_html_table.dart';
|
|
|
|
|
import 'package:flutter_math_fork/flutter_math.dart';
|
|
|
|
|
import 'package:html/dom.dart' as dom;
|
|
|
|
|
import 'package:linkify/linkify.dart';
|
|
|
|
|
import 'package:matrix/matrix.dart';
|
|
|
|
|
|
|
|
|
@ -26,6 +27,35 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
this.textColor = Colors.black,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
dom.Node _linkifyHtml(dom.Node element) {
|
|
|
|
|
for (final node in element.nodes) {
|
|
|
|
|
if (node is! dom.Text) {
|
|
|
|
|
node.replaceWith(_linkifyHtml(node));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(newHtml));
|
|
|
|
|
}
|
|
|
|
|
return element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
// riot-web is notorious for creating bad reply fallback events from invalid messages which, if
|
|
|
|
@ -46,21 +76,6 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
|
|
|
|
|
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
|
|
|
|
|
|
|
|
|
final linkifiedRenderHtml = linkify(
|
|
|
|
|
renderHtml,
|
|
|
|
|
options: const LinkifyOptions(humanize: false),
|
|
|
|
|
).map(
|
|
|
|
|
(element) {
|
|
|
|
|
if (element is! UrlElement ||
|
|
|
|
|
element.text.contains('<') ||
|
|
|
|
|
element.text.contains('>') ||
|
|
|
|
|
element.text.contains('"')) {
|
|
|
|
|
return element.text;
|
|
|
|
|
}
|
|
|
|
|
return '<a href="${element.url}">${element.text}</a>';
|
|
|
|
|
},
|
|
|
|
|
).join('');
|
|
|
|
|
|
|
|
|
|
final linkColor = textColor.withAlpha(150);
|
|
|
|
|
|
|
|
|
|
final blockquoteStyle = Style(
|
|
|
|
@ -73,11 +88,11 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
padding: HtmlPaddings.only(left: 6, bottom: 0),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
final element = _linkifyHtml(HtmlParser.parseHTML(renderHtml));
|
|
|
|
|
|
|
|
|
|
// there is no need to pre-validate the html, as we validate it while rendering
|
|
|
|
|
return MouseRegion(
|
|
|
|
|
cursor: SystemMouseCursors.text,
|
|
|
|
|
child: Html(
|
|
|
|
|
data: linkifiedRenderHtml,
|
|
|
|
|
return Html.fromElement(
|
|
|
|
|
documentElement: element as dom.Element,
|
|
|
|
|
style: {
|
|
|
|
|
'*': Style(
|
|
|
|
|
color: textColor,
|
|
|
|
@ -153,7 +168,6 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
'html',
|
|
|
|
|
},
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|