new linkify handling
Should fix mentions in non-HTML messages
This commit is contained in:
parent
bcb0c57015
commit
40f5d0f459
2 changed files with 123 additions and 42 deletions
45
lib/helpers/extensions/linkify_html.dart
Normal file
45
lib/helpers/extensions/linkify_html.dart
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import "package:html/dom.dart";
|
||||
import "package:html/parser.dart";
|
||||
import "package:linkify/linkify.dart";
|
||||
import "package:nexus/helpers/matrix_linkifier.dart";
|
||||
|
||||
extension LinkifyHtml on String {
|
||||
/// Wraps bare links in this HTML's text in `<a>` tags.
|
||||
///
|
||||
/// Only text nodes are touched, so attribute values are left alone, and
|
||||
/// text already inside a link or code is skipped.
|
||||
String get linkifiedHtml {
|
||||
final fragment = parseFragment(this);
|
||||
_linkifyNode(fragment);
|
||||
return fragment.outerHtml;
|
||||
}
|
||||
}
|
||||
|
||||
void _linkifyNode(Node node) {
|
||||
if (node is Element && const {"a", "code", "pre"}.contains(node.localName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final child in node.nodes.toList()) {
|
||||
if (child is! Text) {
|
||||
_linkifyNode(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
final elements = linkifyText(child.data);
|
||||
if (!elements.any((element) => element is LinkableElement)) continue;
|
||||
|
||||
elements
|
||||
.map(
|
||||
(element) => switch (element) {
|
||||
LinkableElement(:final text, :final url) =>
|
||||
Element.tag("a")
|
||||
..attributes["href"] = url
|
||||
..append(Text(text)),
|
||||
_ => Text(element.text),
|
||||
},
|
||||
)
|
||||
.forEach((replacement) => node.insertBefore(replacement, child));
|
||||
child.remove();
|
||||
}
|
||||
}
|
||||
38
lib/helpers/matrix_linkifier.dart
Normal file
38
lib/helpers/matrix_linkifier.dart
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import "package:linkify/linkify.dart";
|
||||
|
||||
/// Splits [text] into plain text and URL, `matrix:` and email links.
|
||||
List<LinkifyElement> linkifyText(String text) => linkify(
|
||||
text,
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkifiers: const [UrlLinkifier(), MatrixLinkifier(), EmailLinkifier()],
|
||||
);
|
||||
|
||||
/// Linkifies `matrix:` URIs, which [UrlLinkifier] only handles for http(s).
|
||||
final class const MatrixLinkifier() extends Linkifier {
|
||||
static final _regex = RegExp(
|
||||
r"\bmatrix:[^\s<>\x22\x27]*[^\s<>\x22\x27.,;:!?)\]}]",
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
@override
|
||||
List<LinkifyElement> parse(
|
||||
List<LinkifyElement> elements,
|
||||
LinkifyOptions options,
|
||||
) => elements
|
||||
.expand(
|
||||
(element) => element is TextElement ? _split(element.text) : [element],
|
||||
)
|
||||
.toList();
|
||||
|
||||
Iterable<LinkifyElement> _split(String text) sync* {
|
||||
var start = 0;
|
||||
for (final match in _regex.allMatches(text)) {
|
||||
if (match.start > start) {
|
||||
yield TextElement(text.substring(start, match.start));
|
||||
}
|
||||
yield UrlElement(match[0]!);
|
||||
start = match.end;
|
||||
}
|
||||
if (start < text.length) yield TextElement(text.substring(start));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue