diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index cb4feb360c..93d9068a2e 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -53,7 +53,6 @@ namespace osu.Game.Configuration
Set(OsuSetting.ChatMessageNotification, true);
Set(OsuSetting.HighlightWords, string.Empty);
- Set(OsuSetting.IgnoreList, string.Empty);
// Audio
Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01);
@@ -198,7 +197,6 @@ namespace osu.Game.Configuration
ChatHighlightName,
ChatMessageNotification,
HighlightWords,
- IgnoreList,
UIHoldActivationDelay,
HitLighting,
MenuBackgroundSource
diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 1d8c5609d9..937acf2128 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -50,6 +50,9 @@ namespace osu.Game.Online.Chat
private IAPIProvider api;
+ [Resolved]
+ private MessageNotifier messageNotifier { get; set; }
+
public readonly BindableBool HighPollRate = new BindableBool();
public ChannelManager()
@@ -247,7 +250,16 @@ namespace osu.Game.Online.Chat
var channels = JoinedChannels.ToList();
foreach (var group in messages.GroupBy(m => m.ChannelId))
- channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
+ {
+ var channel = channels.Find(c => c.Id == group.Key);
+
+ if (channel == null)
+ continue;
+
+ var groupArray = group.ToArray();
+ channel.AddNewMessages(groupArray);
+ messageNotifier.HandleMessages(channel, groupArray);
+ }
}
private void initializeChannels()
diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs
new file mode 100644
index 0000000000..61ec7351c4
--- /dev/null
+++ b/osu.Game/Online/Chat/MessageNotifier.cs
@@ -0,0 +1,231 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
+using osu.Game.Graphics;
+using osu.Game.Online.API;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
+using osu.Game.Users;
+
+namespace osu.Game.Online.Chat
+{
+ ///
+ /// Component that handles creating and posting notifications for incoming messages.
+ ///
+ public class MessageNotifier : Component
+ {
+ [Resolved(CanBeNull = true)]
+ private NotificationOverlay notificationOverlay { get; set; }
+
+ [Resolved(CanBeNull = true)]
+ private ChatOverlay chatOverlay { get; set; }
+
+ [Resolved(CanBeNull = true)]
+ private ChannelManager channelManager { get; set; }
+
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ private Bindable notifyOnMention;
+ private Bindable notifyOnChat;
+ private Bindable highlightWords;
+ private Bindable localUser;
+
+ ///
+ /// Determines if the user is able to see incoming messages.
+ ///
+ public bool IsActive => chatOverlay?.IsPresent == true;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, OsuConfigManager config, IAPIProvider api)
+ {
+ notifyOnMention = config.GetBindable(OsuSetting.ChatHighlightName);
+ notifyOnChat = config.GetBindable(OsuSetting.ChatMessageNotification);
+ highlightWords = config.GetBindable(OsuSetting.HighlightWords);
+ localUser = api.LocalUser;
+ }
+
+ public void HandleMessages(Channel channel, IEnumerable messages)
+ {
+ // don't show if visible or not visible
+ if (IsActive && channelManager.CurrentChannel.Value == channel)
+ return;
+
+ var channelDrawable = chatOverlay.GetChannelDrawable(channel);
+ if (channelDrawable == null)
+ return;
+
+ foreach (var message in messages)
+ {
+ var words = getWords(message.Content);
+ var localUsername = localUser.Value.Username;
+
+ if (message.Sender.Username == localUsername)
+ continue;
+
+ void onClick()
+ {
+ if (channelManager != null)
+ channelManager.CurrentChannel.Value = channel;
+
+ channelDrawable.ScrollToAndHighlightMessage(message);
+ }
+
+ if (notifyOnChat.Value && channel.Type == ChannelType.PM)
+ {
+ var username = message.Sender.Username;
+ var existingNotification = notificationOverlay.Notifications.OfType().FirstOrDefault(n => n.Username == username);
+
+ if (existingNotification == null)
+ {
+ var notification = new PrivateMessageNotification(username, onClick);
+ notificationOverlay?.Post(notification);
+ }
+ else
+ {
+ existingNotification.MessageCount++;
+ }
+
+ continue;
+ }
+ if (notifyOnMention.Value && anyCaseInsensitive(words, localUsername))
+ {
+ var notification = new MentionNotification(message.Sender.Username, onClick);
+ notificationOverlay?.Post(notification);
+
+ continue;
+ }
+ if (!string.IsNullOrWhiteSpace(highlightWords.Value))
+ {
+ var matchedWord = hasCaseInsensitive(words, getWords(highlightWords.Value));
+
+ if (matchedWord != null)
+ {
+ var notification = new HighlightNotification(message.Sender.Username, matchedWord, onClick);
+ notificationOverlay?.Post(notification);
+ }
+ }
+ }
+ }
+
+ private static string[] getWords(string input) => input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+
+ ///
+ /// Finds the first matching string/word in both and (case-insensitive)
+ ///
+ private static string hasCaseInsensitive(IEnumerable x, IEnumerable y) => x.FirstOrDefault(x2 => anyCaseInsensitive(y, x2));
+
+ private static bool anyCaseInsensitive(IEnumerable x, string y) => x.Any(x2 => x2.Equals(y, StringComparison.InvariantCultureIgnoreCase));
+
+ private class HighlightNotification : SimpleNotification
+ {
+ public HighlightNotification(string highlighter, string word, Action onClick)
+ {
+ Icon = FontAwesome.Solid.Highlighter;
+ Text = $"'{word}' was mentioned in chat by '{highlighter}'. Click to find out why!";
+ this.onClick = onClick;
+ }
+
+ private readonly Action onClick;
+
+ public override bool IsImportant => false;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, NotificationOverlay notificationOverlay, ChatOverlay chatOverlay)
+ {
+ IconBackgound.Colour = colours.PurpleDark;
+ Activated = delegate
+ {
+ notificationOverlay.Hide();
+ chatOverlay.Show();
+ onClick?.Invoke();
+
+ return true;
+ };
+ }
+ }
+
+ private class PrivateMessageNotification : SimpleNotification
+ {
+ public PrivateMessageNotification(string username, Action onClick)
+ {
+ Icon = FontAwesome.Solid.Envelope;
+ Username = username;
+ MessageCount = 1;
+ this.onClick = onClick;
+ }
+
+ private int messageCount = 0;
+
+ public int MessageCount
+ {
+ get => messageCount;
+ set
+ {
+ messageCount = value;
+ if (messageCount > 1)
+ {
+ Text = $"You received {messageCount} private messages from '{Username}'. Click to read it!";
+ }
+ else
+ {
+ Text = $"You received a private message from '{Username}'. Click to read it!";
+ }
+ }
+ }
+
+ public string Username { get; set; }
+
+ private readonly Action onClick;
+
+ public override bool IsImportant => false;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, NotificationOverlay notificationOverlay, ChatOverlay chatOverlay)
+ {
+ IconBackgound.Colour = colours.PurpleDark;
+ Activated = delegate
+ {
+ notificationOverlay.Hide();
+ chatOverlay.Show();
+ onClick?.Invoke();
+
+ return true;
+ };
+ }
+ }
+
+ private class MentionNotification : SimpleNotification
+ {
+ public MentionNotification(string username, Action onClick)
+ {
+ Icon = FontAwesome.Solid.At;
+ Text = $"Your name was mentioned in chat by '{username}'. Click to find out why!";
+ this.onClick = onClick;
+ }
+
+ private readonly Action onClick;
+
+ public override bool IsImportant => false;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, NotificationOverlay notificationOverlay, ChatOverlay chatOverlay)
+ {
+ IconBackgound.Colour = colours.PurpleDark;
+ Activated = delegate
+ {
+ notificationOverlay.Hide();
+ chatOverlay.Show();
+ onClick?.Invoke();
+
+ return true;
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index c7c746bed3..d89109e9b9 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -58,6 +58,8 @@ namespace osu.Game
private ChannelManager channelManager;
+ private MessageNotifier messageNotifier;
+
private NotificationOverlay notifications;
private DirectOverlay direct;
@@ -589,6 +591,7 @@ namespace osu.Game
loadComponentSingleFile(direct = new DirectOverlay(), overlayContent.Add, true);
loadComponentSingleFile(social = new SocialOverlay(), overlayContent.Add, true);
loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true);
+ loadComponentSingleFile(messageNotifier = new MessageNotifier(), AddInternal, true);
loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true);
loadComponentSingleFile(Settings = new SettingsOverlay { GetToolbarHeight = () => ToolbarOffset }, leftFloatingOverlayContent.Add, true);
var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true);
diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index 1ca65a1da7..74aac2a7cf 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -33,21 +33,6 @@ namespace osu.Game.Overlays.Chat
private OsuScrollContainer scroll;
public ColourInfo HighlightColour { get; set; }
- [Resolved(CanBeNull = true)]
- private NotificationOverlay notificationOverlay { get; set; }
-
- [Resolved(CanBeNull = true)]
- private ChatOverlay chatOverlay { get; set; }
-
- [Resolved(CanBeNull = true)]
- private ChannelManager channelManager { get; set; }
-
- private Bindable notifyOnMention;
- private Bindable notifyOnChat;
- private Bindable highlightWords;
- private Bindable ignoreList;
- private Bindable localUser;
-
[Resolved]
private OsuColour colours { get; set; }
@@ -58,13 +43,8 @@ namespace osu.Game.Overlays.Chat
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, OsuConfigManager config, IAPIProvider api)
+ private void load(OsuColour colours)
{
- notifyOnMention = config.GetBindable(OsuSetting.ChatHighlightName);
- notifyOnChat = config.GetBindable(OsuSetting.ChatMessageNotification);
- highlightWords = config.GetBindable(OsuSetting.HighlightWords);
- ignoreList = config.GetBindable(OsuSetting.IgnoreList);
- localUser = api.LocalUser;
HighlightColour = colours.Blue;
Child = new OsuContextMenuContainer
@@ -122,14 +102,10 @@ namespace osu.Game.Overlays.Chat
bool shouldScrollToEnd = scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage);
// Add up to last Channel.MAX_HISTORY messages
- var ignoredWords = getWords(ignoreList.Value);
- var displayMessages = newMessages.Where(m => hasCaseInsensitive(getWords(m.Content), ignoredWords) == null);
- displayMessages = displayMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY));
+ var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY));
Message lastMessage = chatLines.LastOrDefault()?.Message;
- checkForMentions(displayMessages);
-
foreach (var message in displayMessages)
{
if (lastMessage == null || lastMessage.Timestamp.ToLocalTime().Date != message.Timestamp.ToLocalTime().Date)
@@ -167,62 +143,6 @@ namespace osu.Game.Overlays.Chat
scrollToEnd();
}
- private void checkForMentions(IEnumerable messages)
- {
- // only send notifications when the chat overlay is **closed** and the channel is not visible.
- if (chatOverlay?.IsPresent == true && channelManager?.CurrentChannel.Value == Channel)
- return;
-
- foreach (var message in messages)
- {
- var words = getWords(message.Content);
- var username = localUser.Value.Username;
-
- if (message.Sender.Username == username)
- continue;
-
- if (notifyOnChat.Value && Channel.Type == ChannelType.PM)
- {
- var notification = new PrivateMessageNotification(message.Sender.Username, () =>
- {
- channelManager.CurrentChannel.Value = Channel;
- ScrollToAndHighlightMessage(message);
- });
-
- notificationOverlay?.Post(notification);
- continue;
- }
-
- if (notifyOnMention.Value && anyCaseInsensitive(words, username))
- {
- var notification = new MentionNotification(message.Sender.Username, () =>
- {
- channelManager.CurrentChannel.Value = Channel;
- ScrollToAndHighlightMessage(message);
- });
-
- notificationOverlay?.Post(notification);
- continue;
- }
-
- if (!string.IsNullOrWhiteSpace(highlightWords.Value))
- {
- var matchedWord = hasCaseInsensitive(words, getWords(highlightWords.Value));
-
- if (matchedWord != null)
- {
- var notification = new HighlightNotification(message.Sender.Username, matchedWord, () =>
- {
- channelManager.CurrentChannel.Value = Channel;
- ScrollToAndHighlightMessage(message);
- });
-
- notificationOverlay?.Post(notification);
- }
- }
- }
- }
-
private void pendingMessageResolved(Message existing, Message updated)
{
var found = chatLines.LastOrDefault(c => c.Message == existing);
@@ -256,15 +176,6 @@ namespace osu.Game.Overlays.Chat
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
- private string[] getWords(string input) => input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
-
- ///
- /// Finds the first matching string/word in both and (case-insensitive)
- ///
- private string hasCaseInsensitive(IEnumerable x, IEnumerable y) => x.FirstOrDefault(x2 => anyCaseInsensitive(y, x2));
-
- private bool anyCaseInsensitive(IEnumerable x, string y) => x.Any(x2 => x2.Equals(y, StringComparison.InvariantCultureIgnoreCase));
-
private ChatLine findChatLine(Message message) => chatLines.FirstOrDefault(c => c.Message == message);
public class DaySeparator : Container
@@ -330,89 +241,5 @@ namespace osu.Game.Overlays.Chat
};
}
}
-
- private class HighlightNotification : SimpleNotification
- {
- public HighlightNotification(string highlighter, string word, Action onClick)
- {
- Icon = FontAwesome.Solid.Highlighter;
- Text = $"'{word}' was mentioned in chat by '{highlighter}'. Click to find out why!";
- this.onClick = onClick;
- }
-
- private readonly Action onClick;
-
- public override bool IsImportant => false;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, NotificationOverlay notificationOverlay, ChatOverlay chatOverlay)
- {
- IconBackgound.Colour = colours.PurpleDark;
- Activated = delegate
- {
- notificationOverlay.Hide();
- chatOverlay.Show();
- onClick?.Invoke();
-
- return true;
- };
- }
- }
-
- private class PrivateMessageNotification : SimpleNotification
- {
- public PrivateMessageNotification(string username, Action onClick)
- {
- Icon = FontAwesome.Solid.Envelope;
- Text = $"You received a private message from '{username}'. Click to read it!";
- this.onClick = onClick;
- }
-
- private readonly Action onClick;
-
- public override bool IsImportant => false;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, NotificationOverlay notificationOverlay, ChatOverlay chatOverlay)
- {
- IconBackgound.Colour = colours.PurpleDark;
- Activated = delegate
- {
- notificationOverlay.Hide();
- chatOverlay.Show();
- onClick?.Invoke();
-
- return true;
- };
- }
- }
-
- private class MentionNotification : SimpleNotification
- {
- public MentionNotification(string username, Action onClick)
- {
- Icon = FontAwesome.Solid.At;
- Text = $"Your name was mentioned in chat by '{username}'. Click to find out why!";
- this.onClick = onClick;
- }
-
- private readonly Action onClick;
-
- public override bool IsImportant => false;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, NotificationOverlay notificationOverlay, ChatOverlay chatOverlay)
- {
- IconBackgound.Colour = colours.PurpleDark;
- Activated = delegate
- {
- notificationOverlay.Hide();
- chatOverlay.Show();
- onClick?.Invoke();
-
- return true;
- };
- }
- }
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Online/InGameChatSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/InGameChatSettings.cs
index 4d8d06e557..781aa10618 100644
--- a/osu.Game/Overlays/Settings/Sections/Online/InGameChatSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Online/InGameChatSettings.cs
@@ -16,16 +16,11 @@ namespace osu.Game.Overlays.Settings.Sections.Online
{
Children = new Drawable[]
{
- new SettingsTextBox
- {
- LabelText = "Chat ignore list (space-separated list)",
- Bindable = config.GetBindable(OsuSetting.IgnoreList)
- },
new SettingsTextBox
{
LabelText = "Chat highlight words (space-separated list)",
Bindable = config.GetBindable(OsuSetting.HighlightWords)
- },
+ }
};
}
}