diff --git a/osu.Game.Tests/Chat/TestSceneChannelManager.cs b/osu.Game.Tests/Chat/TestSceneChannelManager.cs index 86be638781..f559b0db73 100644 --- a/osu.Game.Tests/Chat/TestSceneChannelManager.cs +++ b/osu.Game.Tests/Chat/TestSceneChannelManager.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tests.Chat private ChannelManager channelManager; private int currentMessageId; private List sentMessages; + private List silencedUserIds; [SetUp] public void Setup() => Schedule(() => @@ -39,6 +40,7 @@ namespace osu.Game.Tests.Chat { currentMessageId = 0; sentMessages = new List(); + silencedUserIds = new List(); ((DummyAPIAccess)API).HandleRequest = req => { @@ -55,6 +57,11 @@ namespace osu.Game.Tests.Chat case MarkChannelAsReadRequest markRead: handleMarkChannelAsReadRequest(markRead); return true; + + case ChatAckRequest ack: + ack.TriggerSuccess(new ChatAckResponse { Silences = silencedUserIds.Select(u => new ChatSilence { UserId = u }).ToList() }); + silencedUserIds.Clear(); + return true; } return false; @@ -106,6 +113,28 @@ namespace osu.Game.Tests.Chat AddAssert("channel's last read ID is set to the latest message", () => channel.LastReadId == sentMessages.Last().Id); } + [Test] + public void TestSilencedUsersAreRemoved() + { + Channel channel = null; + + AddStep("join channel and select it", () => + { + channelManager.JoinChannel(channel = createChannel(1, ChannelType.Public)); + channelManager.CurrentChannel.Value = channel; + }); + + AddStep("post message", () => channelManager.PostMessage("Definitely something bad")); + + AddStep("mark user as silenced and send ack request", () => + { + silencedUserIds.Add(API.LocalUser.Value.OnlineID); + channelManager.SendAck(); + }); + + AddAssert("channel has no more messages", () => channel.Messages, () => Is.Empty); + } + private void handlePostMessageRequest(PostMessageRequest request) { var message = new Message(++currentMessageId) diff --git a/osu.Game/Online/API/Requests/ChatAckRequest.cs b/osu.Game/Online/API/Requests/ChatAckRequest.cs index f09df4908e..78f51e21c0 100644 --- a/osu.Game/Online/API/Requests/ChatAckRequest.cs +++ b/osu.Game/Online/API/Requests/ChatAckRequest.cs @@ -9,10 +9,16 @@ namespace osu.Game.Online.API.Requests { public class ChatAckRequest : APIRequest { + public long SinceMessageId; + public uint? SinceSilenceId; + protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); req.Method = HttpMethod.Post; + req.AddParameter(@"since", SinceMessageId.ToString()); + if (SinceSilenceId != null) + req.AddParameter(@"history_since", SinceSilenceId.Value.ToString()); return req; } diff --git a/osu.Game/Online/API/Requests/Responses/ChatSilence.cs b/osu.Game/Online/API/Requests/Responses/ChatSilence.cs index 45fd6e1ba3..afb44e385e 100644 --- a/osu.Game/Online/API/Requests/Responses/ChatSilence.cs +++ b/osu.Game/Online/API/Requests/Responses/ChatSilence.cs @@ -12,6 +12,6 @@ namespace osu.Game.Online.API.Requests.Responses public uint Id { get; set; } [JsonProperty("user_id")] - public uint UserId { get; set; } + public int UserId { get; set; } } } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index a901c15bf4..b9a0aacf5a 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -74,6 +74,9 @@ namespace osu.Game.Online.Chat private bool channelsInitialised; private ScheduledDelegate ackDelegate; + private long lastMessageId; + private uint? lastSilenceId; + public ChannelManager(IAPIProvider api, NotificationsClientConnector connector) { this.api = api; @@ -106,8 +109,7 @@ namespace osu.Game.Online.Chat if (status.NewValue == APIState.Online) { - Scheduler.Add(ackDelegate = new ScheduledDelegate(() => api.Queue(new ChatAckRequest()), 0, 60000)); - // Todo: Handle silences. + Scheduler.Add(ackDelegate = new ScheduledDelegate(SendAck, 0, 60000)); } }, true); } @@ -342,6 +344,8 @@ namespace osu.Game.Online.Chat foreach (var group in messages.GroupBy(m => m.ChannelId)) channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); + + lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId; } private void initializeChannels() @@ -391,6 +395,32 @@ namespace osu.Game.Online.Chat api.Queue(fetchInitialMsgReq); } + /// + /// Sends an acknowledgement request to the API. + /// This marks the user as online to receive messages from public channels, while also returning a list of silenced users. + /// It needs to be called at least once every 10 minutes. + /// + public void SendAck() + { + var req = new ChatAckRequest + { + SinceMessageId = lastMessageId, + SinceSilenceId = lastSilenceId + }; + + req.Success += ack => + { + foreach (var silence in ack.Silences) + { + foreach (var channel in JoinedChannels) + channel.RemoveMessagesFromUser(silence.UserId); + lastSilenceId = Math.Max(lastSilenceId ?? 0, silence.Id); + } + }; + + api.Queue(req); + } + /// /// Find an existing channel instance for the provided channel. Lookup is performed basd on ID. /// The provided channel may be used if an existing instance is not found.