2019-12-17 06:04:55 +00:00
|
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
|
2022-06-17 07:37:17 +00:00
|
|
|
|
#nullable disable
|
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using System.Collections.Generic;
|
2021-05-26 06:59:29 +00:00
|
|
|
|
using System.Collections.Specialized;
|
2022-12-16 09:16:26 +00:00
|
|
|
|
using System.Diagnostics;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using System.Linq;
|
2021-12-06 22:32:21 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using osu.Framework.Allocation;
|
|
|
|
|
using osu.Framework.Bindables;
|
|
|
|
|
using osu.Framework.Graphics;
|
|
|
|
|
using osu.Framework.Graphics.Sprites;
|
2023-05-17 15:34:39 +00:00
|
|
|
|
using osu.Framework.Platform;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using osu.Game.Configuration;
|
|
|
|
|
using osu.Game.Graphics;
|
2023-06-28 19:11:56 +00:00
|
|
|
|
using osu.Game.Localisation;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using osu.Game.Online.API;
|
2021-11-04 09:02:44 +00:00
|
|
|
|
using osu.Game.Online.API.Requests.Responses;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using osu.Game.Overlays;
|
|
|
|
|
using osu.Game.Overlays.Notifications;
|
|
|
|
|
|
|
|
|
|
namespace osu.Game.Online.Chat
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Component that handles creating and posting notifications for incoming messages.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public partial class MessageNotifier : Component
|
|
|
|
|
{
|
2021-06-11 07:28:53 +00:00
|
|
|
|
[Resolved]
|
2022-04-18 10:59:57 +00:00
|
|
|
|
private INotificationOverlay notifications { get; set; }
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2021-06-11 07:28:53 +00:00
|
|
|
|
[Resolved]
|
2022-05-30 08:54:09 +00:00
|
|
|
|
private ChatOverlay chatOverlay { get; set; }
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2021-06-11 07:28:53 +00:00
|
|
|
|
[Resolved]
|
2019-12-17 05:59:27 +00:00
|
|
|
|
private ChannelManager channelManager { get; set; }
|
|
|
|
|
|
2023-05-17 15:34:39 +00:00
|
|
|
|
[Resolved]
|
|
|
|
|
private GameHost host { get; set; }
|
|
|
|
|
|
2021-06-11 07:37:31 +00:00
|
|
|
|
private Bindable<bool> notifyOnUsername;
|
|
|
|
|
private Bindable<bool> notifyOnPrivateMessage;
|
|
|
|
|
|
2021-11-04 09:02:44 +00:00
|
|
|
|
private readonly IBindable<APIUser> localUser = new Bindable<APIUser>();
|
2021-06-05 09:03:49 +00:00
|
|
|
|
private readonly IBindableList<Channel> joinedChannels = new BindableList<Channel>();
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
2019-12-26 02:32:40 +00:00
|
|
|
|
private void load(OsuConfigManager config, IAPIProvider api)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2021-06-11 07:37:31 +00:00
|
|
|
|
notifyOnUsername = config.GetBindable<bool>(OsuSetting.NotifyOnUsernameMentioned);
|
|
|
|
|
notifyOnPrivateMessage = config.GetBindable<bool>(OsuSetting.NotifyOnPrivateMessage);
|
|
|
|
|
|
2021-06-05 09:03:49 +00:00
|
|
|
|
localUser.BindTo(api.LocalUser);
|
|
|
|
|
joinedChannels.BindTo(channelManager.JoinedChannels);
|
2021-05-26 06:59:29 +00:00
|
|
|
|
}
|
2019-12-26 02:32:40 +00:00
|
|
|
|
|
2021-06-11 08:51:58 +00:00
|
|
|
|
protected override void LoadComplete()
|
|
|
|
|
{
|
|
|
|
|
base.LoadComplete();
|
|
|
|
|
joinedChannels.BindCollectionChanged(channelsChanged, true);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 06:59:29 +00:00
|
|
|
|
private void channelsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
switch (e.Action)
|
2019-12-26 02:32:40 +00:00
|
|
|
|
{
|
2021-05-26 06:59:29 +00:00
|
|
|
|
case NotifyCollectionChangedAction.Add:
|
2022-12-16 09:16:26 +00:00
|
|
|
|
Debug.Assert(e.NewItems != null);
|
|
|
|
|
|
2021-05-26 06:59:29 +00:00
|
|
|
|
foreach (var channel in e.NewItems.Cast<Channel>())
|
2021-06-11 07:37:31 +00:00
|
|
|
|
channel.NewMessagesArrived += checkNewMessages;
|
2021-05-26 06:59:29 +00:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
2022-12-16 09:16:26 +00:00
|
|
|
|
Debug.Assert(e.OldItems != null);
|
|
|
|
|
|
2021-05-26 06:59:29 +00:00
|
|
|
|
foreach (var channel in e.OldItems.Cast<Channel>())
|
2021-06-11 07:37:31 +00:00
|
|
|
|
channel.NewMessagesArrived -= checkNewMessages;
|
2021-05-26 06:59:29 +00:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-12-26 02:32:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-11 07:37:31 +00:00
|
|
|
|
private void checkNewMessages(IEnumerable<Message> messages)
|
2019-12-26 02:32:40 +00:00
|
|
|
|
{
|
2021-06-11 07:27:31 +00:00
|
|
|
|
if (!messages.Any())
|
2019-12-26 02:32:40 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2022-06-08 01:31:31 +00:00
|
|
|
|
var channel = channelManager.JoinedChannels.SingleOrDefault(c => c.Id > 0 && c.Id == messages.First().ChannelId);
|
2019-12-26 02:32:40 +00:00
|
|
|
|
|
|
|
|
|
if (channel == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-05-20 17:29:59 +00:00
|
|
|
|
// Only send notifications if ChatOverlay or the target channel aren't visible, or if the window is unfocused
|
2023-05-17 15:34:39 +00:00
|
|
|
|
if (chatOverlay.IsPresent && channelManager.CurrentChannel.Value == channel && host.IsActive.Value)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-01-21 23:27:46 +00:00
|
|
|
|
foreach (var message in messages.OrderByDescending(m => m.Id))
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2020-01-16 23:00:10 +00:00
|
|
|
|
// ignore messages that already have been read
|
2020-01-21 23:28:08 +00:00
|
|
|
|
if (message.Id <= channel.LastReadId)
|
2020-01-16 23:00:10 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2023-05-20 17:29:59 +00:00
|
|
|
|
// ignore notifications triggered by local user's own chat messages
|
2020-01-21 22:42:15 +00:00
|
|
|
|
if (message.Sender.Id == localUser.Value.Id)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2021-06-11 07:37:31 +00:00
|
|
|
|
// check for private messages first to avoid both posting two notifications about the same message
|
2020-01-19 16:55:17 +00:00
|
|
|
|
if (checkForPMs(channel, message))
|
|
|
|
|
continue;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2022-03-10 19:54:47 +00:00
|
|
|
|
checkForMentions(channel, message);
|
2020-01-19 16:55:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-18 13:17:26 +00:00
|
|
|
|
|
2021-06-05 13:57:14 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks whether the user enabled private message notifications and whether specified <paramref name="message"/> is a direct message.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="channel">The channel associated to the <paramref name="message"/></param>
|
|
|
|
|
/// <param name="message">The message to be checked</param>
|
2021-06-11 07:37:31 +00:00
|
|
|
|
/// <returns>Whether a notification was fired.</returns>
|
2020-01-19 16:55:17 +00:00
|
|
|
|
private bool checkForPMs(Channel channel, Message message)
|
|
|
|
|
{
|
2021-06-11 07:37:31 +00:00
|
|
|
|
if (!notifyOnPrivateMessage.Value || channel.Type != ChannelType.PM)
|
2020-01-19 16:55:17 +00:00
|
|
|
|
return false;
|
2020-01-16 22:15:30 +00:00
|
|
|
|
|
2023-08-21 13:29:41 +00:00
|
|
|
|
host.Window?.Flash();
|
2023-08-19 01:04:09 +00:00
|
|
|
|
|
2022-03-10 19:54:47 +00:00
|
|
|
|
notifications.Post(new PrivateMessageNotification(message, channel));
|
2020-01-19 16:55:17 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 19:54:47 +00:00
|
|
|
|
private void checkForMentions(Channel channel, Message message)
|
2020-01-19 16:55:17 +00:00
|
|
|
|
{
|
2021-12-14 15:23:51 +00:00
|
|
|
|
if (!notifyOnUsername.Value || !CheckContainsUsername(message.Content, localUser.Value.Username)) return;
|
2020-01-19 16:55:17 +00:00
|
|
|
|
|
2023-08-21 13:29:41 +00:00
|
|
|
|
host.Window?.Flash();
|
2023-08-19 01:04:09 +00:00
|
|
|
|
|
2022-03-10 19:54:47 +00:00
|
|
|
|
notifications.Post(new MentionNotification(message, channel));
|
2019-12-17 05:59:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2021-12-06 22:32:21 +00:00
|
|
|
|
/// Checks if <paramref name="message"/> mentions <paramref name="username"/>.
|
2021-06-11 07:37:31 +00:00
|
|
|
|
/// This will match against the case where underscores are used instead of spaces (which is how osu-stable handles usernames with spaces).
|
2019-12-17 05:59:27 +00:00
|
|
|
|
/// </summary>
|
2021-12-14 15:23:51 +00:00
|
|
|
|
public static bool CheckContainsUsername(string message, string username)
|
2021-12-07 00:38:37 +00:00
|
|
|
|
{
|
2021-12-06 22:32:21 +00:00
|
|
|
|
string fullName = Regex.Escape(username);
|
|
|
|
|
string underscoreName = Regex.Escape(username.Replace(' ', '_'));
|
2021-12-07 01:38:10 +00:00
|
|
|
|
return Regex.IsMatch(message, $@"(^|\W)({fullName}|{underscoreName})($|\W)", RegexOptions.IgnoreCase);
|
2021-12-06 22:32:21 +00:00
|
|
|
|
}
|
2021-06-11 07:37:31 +00:00
|
|
|
|
|
2022-03-07 22:22:58 +00:00
|
|
|
|
public partial class PrivateMessageNotification : HighlightMessageNotification
|
2021-06-11 07:37:31 +00:00
|
|
|
|
{
|
2022-03-10 19:54:47 +00:00
|
|
|
|
public PrivateMessageNotification(Message message, Channel channel)
|
|
|
|
|
: base(message, channel)
|
2021-06-11 07:37:31 +00:00
|
|
|
|
{
|
|
|
|
|
Icon = FontAwesome.Solid.Envelope;
|
2023-06-28 19:11:56 +00:00
|
|
|
|
Text = NotificationsStrings.PrivateMessageReceived(message.Sender.Username);
|
2021-06-11 07:37:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 22:22:58 +00:00
|
|
|
|
public partial class MentionNotification : HighlightMessageNotification
|
2021-06-11 07:37:31 +00:00
|
|
|
|
{
|
2022-03-10 19:54:47 +00:00
|
|
|
|
public MentionNotification(Message message, Channel channel)
|
|
|
|
|
: base(message, channel)
|
2021-06-11 07:37:31 +00:00
|
|
|
|
{
|
|
|
|
|
Icon = FontAwesome.Solid.At;
|
2023-06-28 19:11:56 +00:00
|
|
|
|
Text = NotificationsStrings.YourNameWasMentioned(message.Sender.Username);
|
2021-06-11 07:37:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2022-03-07 22:22:58 +00:00
|
|
|
|
public abstract partial class HighlightMessageNotification : SimpleNotification
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2023-07-05 08:25:16 +00:00
|
|
|
|
public override string PopInSampleName => "UI/notification-mention";
|
|
|
|
|
|
2022-03-10 19:54:47 +00:00
|
|
|
|
protected HighlightMessageNotification(Message message, Channel channel)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2022-03-04 20:31:32 +00:00
|
|
|
|
this.message = message;
|
2022-03-10 19:54:47 +00:00
|
|
|
|
this.channel = channel;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-04 20:31:32 +00:00
|
|
|
|
private readonly Message message;
|
2022-03-10 19:54:47 +00:00
|
|
|
|
private readonly Channel channel;
|
2020-01-21 23:28:59 +00:00
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
public override bool IsImportant => false;
|
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
2022-05-30 08:54:09 +00:00
|
|
|
|
private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2022-08-30 08:40:35 +00:00
|
|
|
|
IconContent.Colour = colours.PurpleDark;
|
2020-01-21 23:28:59 +00:00
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
Activated = delegate
|
|
|
|
|
{
|
2020-01-19 16:55:17 +00:00
|
|
|
|
notificationOverlay.Hide();
|
2022-03-10 19:54:47 +00:00
|
|
|
|
chatOverlay.HighlightMessage(message, channel);
|
2019-12-17 05:59:27 +00:00
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|