new linkify handling

Should fix mentions in non-HTML messages
This commit is contained in:
Henry Hiles 2026-11-01 16:42:23 -05:00
commit 40f5d0f459
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
3 changed files with 123 additions and 42 deletions

View file

@ -1,26 +1,46 @@
import "package:flutter/gestures.dart";
import "package:material_ui/material_ui.dart";
import "package:flutter_linkify/flutter_linkify.dart";
import "package:linkify/linkify.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/helpers/extensions/get_link_info.dart";
import "package:nexus/helpers/launch_helper.dart";
import "package:nexus/helpers/matrix_linkifier.dart";
import "package:nexus/widgets/html/mention_chip.dart";
final class const LinkifiedText(
final String text, {
final String? roomId,
final int? maxLines,
final TextStyle? style,
super.key,
}) extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) =>
MaterialUiCompatibilityBridge(
child: Linkify(
text: text,
maxLines: maxLines,
style: style,
options: .new(humanize: false),
onOpen: (link) =>
ref.read(LaunchHelper.provider).launchUrl(.parse(link.url)),
linkStyle: .new(color: Theme.of(context).colorScheme.primary),
overflow: maxLines == null ? null : .ellipsis,
),
);
Widget build(BuildContext context, WidgetRef ref) => Text.rich(
TextSpan(
style: style,
children: linkifyText(text)
.map<InlineSpan>(
(element) => switch (element) {
LinkableElement(:final url) when url.linkInfo != null =>
WidgetSpan(
alignment: .baseline,
baseline: .alphabetic,
child: MentionChip(url, roomId),
),
LinkableElement(:final text, :final url) => TextSpan(
text: text,
style: .new(color: Theme.of(context).colorScheme.primary),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () =>
ref.read(LaunchHelper.provider).launchUrl(.parse(url)),
),
_ => TextSpan(text: element.text),
},
)
.toList(),
),
maxLines: maxLines,
overflow: maxLines == null ? null : .ellipsis,
);
}

View file

@ -5,6 +5,7 @@ import "package:linkify/linkify.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/controllers/event.dart";
import "package:nexus/helpers/extensions/get_filename.dart";
import "package:nexus/helpers/extensions/linkify_html.dart";
import "package:nexus/models/content/encrypted.dart";
import "package:nexus/models/content/message.dart";
import "package:nexus/models/content/sticker.dart";
@ -77,26 +78,11 @@ class const MessageRenderer(
? Html(
roomId: event.roomId,
textStyle: textStyle,
formattedBody.replaceAllMapped(
RegExp(
r"(<a\b[^>]*>.*?<\/a>)|(\bhttps?:\/\/[^\s<]+)",
caseSensitive: false,
dotAll: true,
),
(m) {
// If it's already an <a> tag, leave it unchanged
if (m.group(1) != null) {
return m.group(1)!;
}
// Otherwise, wrap the bare URL
final url = m.group(2)!;
return "<a href=\"$url\">$url</a>";
},
),
formattedBody.linkifiedHtml,
)
: LinkifiedText(
body.isEmpty ? content.filename ?? "" : body,
roomId: event.roomId,
style: textStyle,
maxLines: maxLines,
),

View file

@ -174,7 +174,7 @@ class const RoomSummarySheet(
if (summary.topic != null)
ListTile(
title: Text("Topic"),
subtitle: LinkifiedText(summary.topic!),
subtitle: LinkifiedText(summary.topic!, roomId: summary.roomId),
leading: Icon(Icons.info),
),
if (isKnocking)