diff --git a/osu.Game.Tests/Online/Chat/MessageNotifierTest.cs b/osu.Game.Tests/Online/Chat/MessageNotifierTest.cs index e4118a23b4..a391ec4066 100644 --- a/osu.Game.Tests/Online/Chat/MessageNotifierTest.cs +++ b/osu.Game.Tests/Online/Chat/MessageNotifierTest.cs @@ -12,79 +12,79 @@ namespace osu.Game.Tests.Online.Chat [Test] public void TestContainsUsernameMidlinePositive() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("This is a test message", "Test")); + Assert.IsTrue(MessageNotifier.MatchUsername("This is a test message", "Test").Success); } [Test] public void TestContainsUsernameStartOfLinePositive() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("Test message", "Test")); + Assert.IsTrue(MessageNotifier.MatchUsername("Test message", "Test").Success); } [Test] public void TestContainsUsernameEndOfLinePositive() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("This is a test", "Test")); + Assert.IsTrue(MessageNotifier.MatchUsername("This is a test", "Test").Success); } [Test] public void TestContainsUsernameMidlineNegative() { - Assert.IsFalse(MessageNotifier.CheckContainsUsername("This is a testmessage for notifications", "Test")); + Assert.IsFalse(MessageNotifier.MatchUsername("This is a testmessage for notifications", "Test").Success); } [Test] public void TestContainsUsernameStartOfLineNegative() { - Assert.IsFalse(MessageNotifier.CheckContainsUsername("Testmessage", "Test")); + Assert.IsFalse(MessageNotifier.MatchUsername("Testmessage", "Test").Success); } [Test] public void TestContainsUsernameEndOfLineNegative() { - Assert.IsFalse(MessageNotifier.CheckContainsUsername("This is a notificationtest", "Test")); + Assert.IsFalse(MessageNotifier.MatchUsername("This is a notificationtest", "Test").Success); } [Test] public void TestContainsUsernameBetweenPunctuation() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("Hello 'test'-message", "Test")); + Assert.IsTrue(MessageNotifier.MatchUsername("Hello 'test'-message", "Test").Success); } [Test] public void TestContainsUsernameUnicode() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("Test \u0460\u0460 message", "\u0460\u0460")); + Assert.IsTrue(MessageNotifier.MatchUsername("Test \u0460\u0460 message", "\u0460\u0460").Success); } [Test] public void TestContainsUsernameUnicodeNegative() { - Assert.IsFalse(MessageNotifier.CheckContainsUsername("Test ha\u0460\u0460o message", "\u0460\u0460")); + Assert.IsFalse(MessageNotifier.MatchUsername("Test ha\u0460\u0460o message", "\u0460\u0460").Success); } [Test] public void TestContainsUsernameSpecialCharactersPositive() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("Test [#^-^#] message", "[#^-^#]")); + Assert.IsTrue(MessageNotifier.MatchUsername("Test [#^-^#] message", "[#^-^#]").Success); } [Test] public void TestContainsUsernameSpecialCharactersNegative() { - Assert.IsFalse(MessageNotifier.CheckContainsUsername("Test pad[#^-^#]oru message", "[#^-^#]")); + Assert.IsFalse(MessageNotifier.MatchUsername("Test pad[#^-^#]oru message", "[#^-^#]").Success); } [Test] public void TestContainsUsernameAtSign() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("@username hi", "username")); + Assert.IsTrue(MessageNotifier.MatchUsername("@username hi", "username").Success); } [Test] public void TestContainsUsernameColon() { - Assert.IsTrue(MessageNotifier.CheckContainsUsername("username: hi", "username")); + Assert.IsTrue(MessageNotifier.MatchUsername("username: hi", "username").Success); } } } diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs index 3614ed9133..d72bb195ab 100644 --- a/osu.Game/Localisation/NotificationsStrings.cs +++ b/osu.Game/Localisation/NotificationsStrings.cs @@ -83,16 +83,6 @@ Please try changing your audio device to a working setting."); /// public static LocalisableString LinkTypeNotSupported => new TranslatableString(getKey(@"unsupported_link_type"), @"This link type is not yet supported!"); - /// - /// "You received a private message from '{0}'. Click to read it!" - /// - public static LocalisableString PrivateMessageReceived(string username) => new TranslatableString(getKey(@"private_message_received"), @"You received a private message from '{0}'. Click to read it!", username); - - /// - /// "Your name was mentioned in chat by '{0}'. Click to find out why!" - /// - public static LocalisableString YourNameWasMentioned(string username) => new TranslatableString(getKey(@"your_name_was_mentioned"), @"Your name was mentioned in chat by '{0}'. Click to find out why!", username); - /// /// "{0} invited you to the multiplayer match "{1}"! Click to join." /// diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index 49304c93a3..a8d6746b10 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -11,11 +11,9 @@ using System.Text.RegularExpressions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; @@ -136,59 +134,104 @@ namespace osu.Game.Online.Chat private void checkForMentions(Channel channel, Message message) { - if (!notifyOnUsername.Value || !CheckContainsUsername(message.Content, localUser.Value.Username)) return; + if (!notifyOnUsername.Value) + return; - notifications.Post(new MentionNotification(message, channel)); + var match = MatchUsername(message.Content, localUser.Value.Username); + if (!match.Success) + return; + + notifications.Post(new MentionNotification(message, channel, match)); } /// /// Checks if mentions . /// This will match against the case where underscores are used instead of spaces (which is how osu-stable handles usernames with spaces). /// - public static bool CheckContainsUsername(string message, string username) + public static Match MatchUsername(string message, string username) { string fullName = Regex.Escape(username); string underscoreName = Regex.Escape(username.Replace(' ', '_')); - return Regex.IsMatch(message, $@"(^|\W)({fullName}|{underscoreName})($|\W)", RegexOptions.IgnoreCase); + return Regex.Match(message, $@"(^|\W)({fullName}|{underscoreName})($|\W)", RegexOptions.IgnoreCase); } - public partial class PrivateMessageNotification : HighlightMessageNotification + public partial class PrivateMessageNotification : UserAvatarNotification { + private readonly Message message; + private readonly Channel channel; + public PrivateMessageNotification(Message message, Channel channel) - : base(message, channel) - { - Icon = FontAwesome.Solid.Envelope; - Text = NotificationsStrings.PrivateMessageReceived(message.Sender.Username); - } - } - - public partial class MentionNotification : HighlightMessageNotification - { - public MentionNotification(Message message, Channel channel) - : base(message, channel) - { - Icon = FontAwesome.Solid.At; - Text = NotificationsStrings.YourNameWasMentioned(message.Sender.Username); - } - } - - public abstract partial class HighlightMessageNotification : SimpleNotification - { - public override string PopInSampleName => "UI/notification-mention"; - - protected HighlightMessageNotification(Message message, Channel channel) + : base(message.Sender) { this.message = message; this.channel = channel; } + [BackgroundDependencyLoader] + private void load(ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) + { + // Sane maximum height to avoid the notification becoming too tall on long messages. + // The height is ballparked to display two lines. + TextFlow.AutoSizeAxes = Axes.None; + TextFlow.Height = 45; + + TextFlow.ParagraphSpacing = 0.25f; + TextFlow.AddParagraph(message.Sender.Username, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); + TextFlow.AddParagraph(message.Content); + + Activated = delegate + { + notificationOverlay.Hide(); + chatOverlay.HighlightMessage(message, channel); + return true; + }; + } + } + + public partial class MentionNotification : UserAvatarNotification + { + public override string PopInSampleName => "UI/notification-mention"; + private readonly Message message; private readonly Channel channel; + private readonly Match match; + + public MentionNotification(Message message, Channel channel, Match match) + : base(message.Sender) + { + this.message = message; + this.channel = channel; + this.match = match; + } [BackgroundDependencyLoader] - private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) + private void load(ChatOverlay chatOverlay, INotificationOverlay notificationOverlay, OverlayColourProvider colourProvider) { - IconContent.Colour = colours.PurpleDark; + // Sane maximum height to avoid the notification becoming too tall on long messages. + // The height is ballparked to display two lines. + TextFlow.AutoSizeAxes = Axes.None; + TextFlow.Height = 45; + + TextFlow.ParagraphSpacing = 0.25f; + TextFlow.AddText(message.Sender.Username, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); + TextFlow.AddText($" in {channel.Name}", s => + { + s.Font = s.Font.With(weight: FontWeight.SemiBold); + s.Colour = colourProvider.Content2; + }); + + TextFlow.NewParagraph(); + + int start = match.Index; + int end = match.Index + match.Length; + + TextFlow.AddText(message.Content[..start]); + TextFlow.AddText(message.Content[start..end], s => + { + s.Font = s.Font.With(weight: FontWeight.SemiBold); + s.Colour = colourProvider.Colour0; + }); + TextFlow.AddText(message.Content[end..]); Activated = delegate { diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 20c3b26b8b..427d874f12 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -292,7 +292,7 @@ namespace osu.Game.Overlays.Chat // remove non-existent channels from the link list message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chatManager?.AvailableChannels.Any(c => c.Name == link.Argument.ToString()) != true); - isMention = MessageNotifier.CheckContainsUsername(message.DisplayContent, api.LocalUser.Value.Username); + isMention = MessageNotifier.MatchUsername(message.DisplayContent, api.LocalUser.Value.Username).Success; drawableContentFlow.Clear(); drawableContentFlow.AddLinks(message.DisplayContent, message.Links);