diff --git a/mobile/lib/features/channels/message_content.dart b/mobile/lib/features/channels/message_content.dart index 143e6b9f36..4ac6b4339d 100644 --- a/mobile/lib/features/channels/message_content.dart +++ b/mobile/lib/features/channels/message_content.dart @@ -112,10 +112,10 @@ class MessageContent extends HookConsumerWidget { var segment = mentionParts[i]; for (final name in mentionNames.values) { if (name.contains(' ')) { - final nbspName = name.replaceAll(' ', '\u00A0'); + final normalizedName = _markdownMentionName(name); segment = segment.replaceAllMapped( RegExp('@${RegExp.escape(name)}', caseSensitive: false), - (m) => '@$nbspName', + (m) => '@$normalizedName', ); } } @@ -716,11 +716,13 @@ RegExp _buildPrefixPattern({ ); } +String _markdownMentionName(String name) => name.replaceAll(' ', '\u00A0'); + Iterable _mentionAliases(Iterable mentionNames) sync* { for (final name in mentionNames) { final trimmed = name.trim(); if (trimmed.isEmpty) continue; - yield trimmed; + yield _markdownMentionName(trimmed); final firstName = trimmed.split(RegExp(r'\s+')).first; if (firstName.isNotEmpty) { yield firstName; diff --git a/mobile/test/features/channels/message_content_test.dart b/mobile/test/features/channels/message_content_test.dart index 947c57e1ed..e22225476b 100644 --- a/mobile/test/features/channels/message_content_test.dart +++ b/mobile/test/features/channels/message_content_test.dart @@ -732,6 +732,23 @@ void main() { expect(find.text('@Alice'), findsOneWidget); }); + testWidgets('highlights an entire multi-word display name', ( + tester, + ) async { + await tester.pumpWidget( + _testable( + const MessageContent( + content: 'Hey @Kenny Lopez can you review this?', + mentionNames: {'pk1': 'Kenny Lopez'}, + ), + ), + ); + + expect(find.text('@Kenny Lopez'), findsOneWidget); + expect(find.text('@Kenny'), findsNothing); + expect(_allRichText(tester), isNot(contains('Lopez Lopez'))); + }); + testWidgets('renders unknown @mention as-is', (tester) async { await tester.pumpWidget( _testable(