diff --git a/lib/model/content.dart b/lib/model/content.dart index 7186400ae9..8cb3f9a4dc 100644 --- a/lib/model/content.dart +++ b/lib/model/content.dart @@ -880,6 +880,14 @@ class _ZulipContentParser { || classes.contains('user-group-mention')); int i = 0; + if (i >= classes.length) return null; + bool hasChannelWildcardClass = false; + if (classes[i] == 'channel-wildcard-mention') { + // Newer channel wildcard mentions have this class; older ones don't. + i++; + hasChannelWildcardClass = true; + } + if (i >= classes.length) return null; if (classes[i] == 'silent') { // A silent @-mention. We ignore this flag; see [UserMentionNode]. @@ -887,9 +895,12 @@ class _ZulipContentParser { } if (i >= classes.length) return null; - if (classes[i] == 'user-mention' || classes[i] == 'user-group-mention') { + if (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. i++; } diff --git a/test/model/content_test.dart b/test/model/content_test.dart index 98cf4d28e6..8a895b6ab1 100644 --- a/test/model/content_test.dart +++ b/test/model/content_test.dart @@ -139,6 +139,48 @@ class ContentExample { '

test-empty

', const UserMentionNode(nodes: [TextNode('test-empty')])); + static final channelWildcardMentionPlain = ContentExample.inline( + 'plain channel wildcard @-mention', + "@**all**", + expectedText: '@all', + '

@all

', + const UserMentionNode(nodes: [TextNode('@all')])); + + static final channelWildcardMentionSilent = ContentExample.inline( + 'silent channel wildcard @-mention', + "@_**everyone**", + expectedText: 'everyone', + '

everyone

', + const UserMentionNode(nodes: [TextNode('everyone')])); + + static final channelWildcardMentionSilentClassOrderReversed = ContentExample.inline( + 'silent channel wildcard @-mention, class order reversed', + "@_**channel**", // (hypothetical server variation) + expectedText: 'channel', + '

channel

', + const UserMentionNode(nodes: [TextNode('channel')])); + + static final legacyChannelWildcardMentionPlain = ContentExample.inline( + 'legacy plain channel wildcard @-mention', + "@**channel**", + expectedText: '@channel', + '

@channel

', + const UserMentionNode(nodes: [TextNode('@channel')])); + + static final legacyChannelWildcardMentionSilent = ContentExample.inline( + 'legacy silent channel wildcard @-mention', + "@_**stream**", + expectedText: 'stream', + '

stream

', + const UserMentionNode(nodes: [TextNode('stream')])); + + static final legacyChannelWildcardMentionSilentClassOrderReversed = ContentExample.inline( + 'legacy silent channel wildcard @-mention, class order reversed', + "@_**all**", // (hypothetical server variation) + expectedText: 'all', + '

all

', + const UserMentionNode(nodes: [TextNode('all')])); + static final emojiUnicode = ContentExample.inline( 'Unicode emoji, encoded in span element', ":thumbs_up:", @@ -1213,7 +1255,13 @@ void main() { testParseExample(ContentExample.groupMentionSilent); testParseExample(ContentExample.groupMentionSilentClassOrderReversed); - // TODO test wildcard mentions + testParseExample(ContentExample.channelWildcardMentionPlain); + testParseExample(ContentExample.channelWildcardMentionSilent); + testParseExample(ContentExample.channelWildcardMentionSilentClassOrderReversed); + + testParseExample(ContentExample.legacyChannelWildcardMentionPlain); + testParseExample(ContentExample.legacyChannelWildcardMentionSilent); + testParseExample(ContentExample.legacyChannelWildcardMentionSilentClassOrderReversed); }); testParseExample(ContentExample.emojiUnicode); diff --git a/test/widgets/content_test.dart b/test/widgets/content_test.dart index fa7febaa17..acb3b3bce1 100644 --- a/test/widgets/content_test.dart +++ b/test/widgets/content_test.dart @@ -650,6 +650,12 @@ void main() { testContentSmoke(ContentExample.userMentionSilent); testContentSmoke(ContentExample.groupMentionPlain); testContentSmoke(ContentExample.groupMentionSilent); + testContentSmoke(ContentExample.channelWildcardMentionPlain); + testContentSmoke(ContentExample.channelWildcardMentionSilent); + testContentSmoke(ContentExample.channelWildcardMentionSilentClassOrderReversed); + testContentSmoke(ContentExample.legacyChannelWildcardMentionPlain); + testContentSmoke(ContentExample.legacyChannelWildcardMentionSilent); + testContentSmoke(ContentExample.legacyChannelWildcardMentionSilentClassOrderReversed); UserMention? findUserMentionInSpan(InlineSpan rootSpan) { UserMention? result;