diff --git a/lib/model/content.dart b/lib/model/content.dart index 8cb3f9a4dc..c98a5ceaaf 100644 --- a/lib/model/content.dart +++ b/lib/model/content.dart @@ -876,8 +876,9 @@ class _ZulipContentParser { final debugHtmlNode = kDebugMode ? element : null; final classes = element.className.split(' ')..sort(); - assert(classes.contains('user-mention') - || classes.contains('user-group-mention')); + assert(classes.contains('topic-mention') + || classes.contains('user-mention') + || classes.contains('user-group-mention')); int i = 0; if (i >= classes.length) return null; @@ -895,12 +896,14 @@ class _ZulipContentParser { } if (i >= classes.length) return null; - if (classes[i] == 'user-mention' + if ((classes[i] == 'topic-mention' && !hasChannelWildcardClass) + || classes[i] == 'user-mention' || (classes[i] == 'user-group-mention' && !hasChannelWildcardClass)) { // The class we already knew we'd find before we called this function. // We ignore the distinction between these; see [UserMentionNode]. // Also, we don't expect "user-group-mention" and "channel-wildcard-mention" - // to be in the list at the same time. + // to be in the list at the same time and neither we expect "topic-mention" + // and "channel-wildcard-mention" to be in the list at the same time. i++; } @@ -931,9 +934,9 @@ class _ZulipContentParser { /// Matches all className values that could be a UserMentionNode, /// and no className values that could be any other type of node. // Specifically, checks for `user-mention` or `user-group-mention` - // as a member of the list. + // or `topic-mention` as a member of the list. static final _userMentionClassNameRegexp = RegExp( - r"(^| )" r"user(?:-group)?-mention" r"( |$)"); + r"(^| )" r"(?:user(?:-group)?|topic)-mention" r"( |$)"); static final _emojiClassNameRegexp = () { const specificEmoji = r"emoji(?:-[0-9a-f]+)+"; diff --git a/test/model/content_test.dart b/test/model/content_test.dart index 8a895b6ab1..361259eb61 100644 --- a/test/model/content_test.dart +++ b/test/model/content_test.dart @@ -181,6 +181,27 @@ class ContentExample { '
all
', const UserMentionNode(nodes: [TextNode('all')])); + static final topicMentionPlain = ContentExample.inline( + 'plain @-topic', + "@**topic**", + expectedText: '@topic', + '@topic
', + const UserMentionNode(nodes: [TextNode('@topic')])); + + static final topicMentionSilent = ContentExample.inline( + 'silent @-topic', + "@_**topic**", + expectedText: 'topic', + 'topic
', + const UserMentionNode(nodes: [TextNode('topic')])); + + static final topicMentionSilentClassOrderReversed = ContentExample.inline( + 'silent @-topic, class order reversed', + "@_**topic**", // (hypothetical server variation) + expectedText: 'topic', + 'topic
', + const UserMentionNode(nodes: [TextNode('topic')])); + static final emojiUnicode = ContentExample.inline( 'Unicode emoji, encoded in span element', ":thumbs_up:", @@ -1262,6 +1283,10 @@ void main() { testParseExample(ContentExample.legacyChannelWildcardMentionPlain); testParseExample(ContentExample.legacyChannelWildcardMentionSilent); testParseExample(ContentExample.legacyChannelWildcardMentionSilentClassOrderReversed); + + testParseExample(ContentExample.topicMentionPlain); + testParseExample(ContentExample.topicMentionSilent); + testParseExample(ContentExample.topicMentionSilentClassOrderReversed); }); testParseExample(ContentExample.emojiUnicode); diff --git a/test/widgets/content_test.dart b/test/widgets/content_test.dart index bee5325714..571034093d 100644 --- a/test/widgets/content_test.dart +++ b/test/widgets/content_test.dart @@ -669,6 +669,9 @@ void main() { testContentSmoke(ContentExample.legacyChannelWildcardMentionPlain); testContentSmoke(ContentExample.legacyChannelWildcardMentionSilent); testContentSmoke(ContentExample.legacyChannelWildcardMentionSilentClassOrderReversed); + testContentSmoke(ContentExample.topicMentionPlain); + testContentSmoke(ContentExample.topicMentionSilent); + testContentSmoke(ContentExample.topicMentionSilentClassOrderReversed); UserMention? findUserMentionInSpan(InlineSpan rootSpan) { UserMention? result;