1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-25 15:00:44 +08:00

Display user avatar and content in DM / mention notifications

This commit is contained in:
Salman Alshamrani
2025-07-03 11:19:48 +03:00
committed by Dean Herbert
Unverified
parent c2ace36348
commit d796dee6fc
4 changed files with 88 additions and 55 deletions
@@ -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);
}
}
}
@@ -83,16 +83,6 @@ Please try changing your audio device to a working setting.");
/// </summary>
public static LocalisableString LinkTypeNotSupported => new TranslatableString(getKey(@"unsupported_link_type"), @"This link type is not yet supported!");
/// <summary>
/// "You received a private message from &#39;{0}&#39;. Click to read it!"
/// </summary>
public static LocalisableString PrivateMessageReceived(string username) => new TranslatableString(getKey(@"private_message_received"), @"You received a private message from '{0}'. Click to read it!", username);
/// <summary>
/// "Your name was mentioned in chat by &#39;{0}&#39;. Click to find out why!"
/// </summary>
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);
/// <summary>
/// "{0} invited you to the multiplayer match &quot;{1}&quot;! Click to join."
/// </summary>
+74 -31
View File
@@ -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));
}
/// <summary>
/// Checks if <paramref name="message"/> mentions <paramref name="username"/>.
/// This will match against the case where underscores are used instead of spaces (which is how osu-stable handles usernames with spaces).
/// </summary>
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
{
+1 -1
View File
@@ -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);