diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart
index 3a47121f..d8c7b9be 100644
--- a/lib/pages/chat/events/html_message.dart
+++ b/lib/pages/chat/events/html_message.dart
@@ -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
+ : '${linkifyElement.text}',
+ )
+ .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 '${element.text}';
- },
- ).join('');
-
final linkColor = textColor.withAlpha(150);
final blockquoteStyle = Style(
@@ -73,87 +88,86 @@ 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,
- style: {
- '*': Style(
- color: textColor,
- margin: Margins.all(0),
- fontSize: FontSize(fontSize),
- ),
- 'a': Style(color: linkColor, textDecorationColor: linkColor),
- 'h1': Style(
- fontSize: FontSize(fontSize * 2),
- lineHeight: LineHeight.number(1.5),
- fontWeight: FontWeight.w600,
- ),
- 'h2': Style(
- fontSize: FontSize(fontSize * 1.75),
- lineHeight: LineHeight.number(1.5),
- fontWeight: FontWeight.w500,
- ),
- 'h3': Style(
- fontSize: FontSize(fontSize * 1.5),
- lineHeight: LineHeight.number(1.5),
- ),
- 'h4': Style(
- fontSize: FontSize(fontSize * 1.25),
- lineHeight: LineHeight.number(1.5),
- ),
- 'h5': Style(
- fontSize: FontSize(fontSize * 1.25),
- lineHeight: LineHeight.number(1.5),
- ),
- 'h6': Style(
- fontSize: FontSize(fontSize),
- lineHeight: LineHeight.number(1.5),
- ),
- 'blockquote': blockquoteStyle,
- 'tg-forward': blockquoteStyle,
- 'hr': Style(
- border: Border.all(color: textColor, width: 0.5),
- ),
- 'table': Style(
- border: Border.all(color: textColor, width: 0.5),
- ),
- 'tr': Style(
- border: Border.all(color: textColor, width: 0.5),
- ),
- 'td': Style(
- border: Border.all(color: textColor, width: 0.5),
- padding: HtmlPaddings.all(2),
- ),
- 'th': Style(
- border: Border.all(color: textColor, width: 0.5),
- ),
- },
- extensions: [
- RoomPillExtension(context, room),
- CodeExtension(fontSize: fontSize),
- MatrixMathExtension(
- style: TextStyle(fontSize: fontSize, color: textColor),
- ),
- const TableHtmlExtension(),
- SpoilerExtension(textColor: textColor),
- const ImageExtension(),
- FontColorExtension(),
- ],
- onLinkTap: (url, _, element) => UrlLauncher(
- context,
- url,
- element?.text,
- ).launchUrl(),
- onlyRenderTheseTags: const {
- ...allowedHtmlTags,
- // Needed to make it work properly
- 'body',
- 'html',
- },
- shrinkWrap: true,
- ),
+ return Html.fromElement(
+ documentElement: element as dom.Element,
+ style: {
+ '*': Style(
+ color: textColor,
+ margin: Margins.all(0),
+ fontSize: FontSize(fontSize),
+ ),
+ 'a': Style(color: linkColor, textDecorationColor: linkColor),
+ 'h1': Style(
+ fontSize: FontSize(fontSize * 2),
+ lineHeight: LineHeight.number(1.5),
+ fontWeight: FontWeight.w600,
+ ),
+ 'h2': Style(
+ fontSize: FontSize(fontSize * 1.75),
+ lineHeight: LineHeight.number(1.5),
+ fontWeight: FontWeight.w500,
+ ),
+ 'h3': Style(
+ fontSize: FontSize(fontSize * 1.5),
+ lineHeight: LineHeight.number(1.5),
+ ),
+ 'h4': Style(
+ fontSize: FontSize(fontSize * 1.25),
+ lineHeight: LineHeight.number(1.5),
+ ),
+ 'h5': Style(
+ fontSize: FontSize(fontSize * 1.25),
+ lineHeight: LineHeight.number(1.5),
+ ),
+ 'h6': Style(
+ fontSize: FontSize(fontSize),
+ lineHeight: LineHeight.number(1.5),
+ ),
+ 'blockquote': blockquoteStyle,
+ 'tg-forward': blockquoteStyle,
+ 'hr': Style(
+ border: Border.all(color: textColor, width: 0.5),
+ ),
+ 'table': Style(
+ border: Border.all(color: textColor, width: 0.5),
+ ),
+ 'tr': Style(
+ border: Border.all(color: textColor, width: 0.5),
+ ),
+ 'td': Style(
+ border: Border.all(color: textColor, width: 0.5),
+ padding: HtmlPaddings.all(2),
+ ),
+ 'th': Style(
+ border: Border.all(color: textColor, width: 0.5),
+ ),
+ },
+ extensions: [
+ RoomPillExtension(context, room),
+ CodeExtension(fontSize: fontSize),
+ MatrixMathExtension(
+ style: TextStyle(fontSize: fontSize, color: textColor),
+ ),
+ const TableHtmlExtension(),
+ SpoilerExtension(textColor: textColor),
+ const ImageExtension(),
+ FontColorExtension(),
+ ],
+ onLinkTap: (url, _, element) => UrlLauncher(
+ context,
+ url,
+ element?.text,
+ ).launchUrl(),
+ onlyRenderTheseTags: const {
+ ...allowedHtmlTags,
+ // Needed to make it work properly
+ 'body',
+ 'html',
+ },
+ shrinkWrap: true,
);
}
diff --git a/pubspec.lock b/pubspec.lock
index ab9190f6..14f70ea6 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -872,7 +872,7 @@ packages:
source: hosted
version: "1.1.0"
html:
- dependency: transitive
+ dependency: "direct main"
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
diff --git a/pubspec.yaml b/pubspec.yaml
index 15001d64..c303f580 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -54,6 +54,7 @@ dependencies:
go_router: ^12.1.1
hive: ^2.2.3
hive_flutter: ^1.1.0
+ html: ^0.15.4
http: ^0.13.6
image_picker: ^1.0.0
intl: any