From 72ae22b0c4ab417b47c8a86e2d9bca508c9c3b3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Nov 2018 17:24:11 +0900 Subject: [PATCH] Add support for creating new PM conversations --- .../CreateNewPrivateMessageRequest.cs | 34 +++++ .../CreateNewPrivateMessageResponse.cs | 16 +++ osu.Game/Online/Chat/Channel.cs | 3 +- osu.Game/Online/Chat/ChannelManager.cs | 132 +++++++++++++----- 4 files changed, 150 insertions(+), 35 deletions(-) create mode 100644 osu.Game/Online/API/Requests/CreateNewPrivateMessageRequest.cs create mode 100644 osu.Game/Online/API/Requests/CreateNewPrivateMessageResponse.cs diff --git a/osu.Game/Online/API/Requests/CreateNewPrivateMessageRequest.cs b/osu.Game/Online/API/Requests/CreateNewPrivateMessageRequest.cs new file mode 100644 index 0000000000..00dab10c75 --- /dev/null +++ b/osu.Game/Online/API/Requests/CreateNewPrivateMessageRequest.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class CreateNewPrivateMessageRequest : APIRequest + { + private readonly User user; + private readonly Message message; + + public CreateNewPrivateMessageRequest(User user, Message message) + { + this.user = user; + this.message = message; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Post; + req.AddParameter(@"target_id", user.Id.ToString()); + req.AddParameter(@"message", message.Content); + req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant()); + return req; + } + + protected override string Target => @"chat/new"; + } +} diff --git a/osu.Game/Online/API/Requests/CreateNewPrivateMessageResponse.cs b/osu.Game/Online/API/Requests/CreateNewPrivateMessageResponse.cs new file mode 100644 index 0000000000..84f1593c31 --- /dev/null +++ b/osu.Game/Online/API/Requests/CreateNewPrivateMessageResponse.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class CreateNewPrivateMessageResponse + { + [JsonProperty("new_channel_id")] + public int ChannelID; + + public Message Message; + } +} diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index c49490ea19..9d3b7b5cc9 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -100,7 +100,7 @@ namespace osu.Game.Online.Chat NewMessagesArrived?.Invoke(new[] { message }); } - public bool MessagesLoaded { get; private set; } + public bool MessagesLoaded; /// /// Adds new messages to the channel and purges old messages. Triggers the event. @@ -113,7 +113,6 @@ namespace osu.Game.Online.Chat if (messages.Length == 0) return; Messages.AddRange(messages); - MessagesLoaded = true; var maxMessageId = messages.Max(m => m.Id); if (maxMessageId > LastMessageId) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 8099f97999..71b1d8a80a 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -88,6 +88,12 @@ namespace osu.Game.Online.Chat JoinChannel(channel); } + + /// + /// Ensure we run post actions in sequence, once at a time. + /// + private readonly Queue postQueue = new Queue(); + /// /// Posts a message to the currently opened channel. /// @@ -100,31 +106,70 @@ namespace osu.Game.Online.Chat var currentChannel = CurrentChannel.Value; - if (!api.IsLoggedIn) + void dequeueAndRun() { - currentChannel.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); - return; + if (postQueue.Count > 0) + postQueue.Dequeue().Invoke(); } - var message = new LocalEchoMessage + postQueue.Enqueue(() => { - Sender = api.LocalUser.Value, - Timestamp = DateTimeOffset.Now, - ChannelId = CurrentChannel.Value.Id, - IsAction = isAction, - Content = text - }; + if (!api.IsLoggedIn) + { + currentChannel.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + return; + } - currentChannel.AddLocalEcho(message); + var message = new LocalEchoMessage + { + Sender = api.LocalUser.Value, + Timestamp = DateTimeOffset.Now, + ChannelId = CurrentChannel.Value.Id, + IsAction = isAction, + Content = text + }; - var req = new PostMessageRequest(message); - req.Failure += exception => - { - Logger.Error(exception, "Posting message failed."); - currentChannel.ReplaceMessage(message, null); - }; - req.Success += m => currentChannel.ReplaceMessage(message, m); - api.Queue(req); + currentChannel.AddLocalEcho(message); + + // if this is a PM and the first message, we need to do a special request to create the PM channel + if (currentChannel.Type == ChannelType.PM && !currentChannel.Joined) + { + var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(currentChannel.Users.First(), message); + createNewPrivateMessageRequest.Success += createRes => + { + currentChannel.Id = createRes.ChannelID; + currentChannel.ReplaceMessage(message, createRes.Message); + dequeueAndRun(); + }; + createNewPrivateMessageRequest.Failure += exception => + { + Logger.Error(exception, "Posting message failed."); + currentChannel.ReplaceMessage(message, null); + dequeueAndRun(); + }; + + api.Queue(createNewPrivateMessageRequest); + return; + } + + var req = new PostMessageRequest(message); + req.Success += m => + { + currentChannel.ReplaceMessage(message, m); + dequeueAndRun(); + }; + req.Failure += exception => + { + Logger.Error(exception, "Posting message failed."); + currentChannel.ReplaceMessage(message, null); + dequeueAndRun(); + }; + api.Queue(req); + }); + + // always run if the queue is empty + if (postQueue.Count == 1) + dequeueAndRun(); } /// @@ -170,11 +215,11 @@ namespace osu.Game.Online.Chat channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); } - private void initializeDefaultChannels() + private void initializeChannels() { var req = new ListChannelsRequest(); - //var joinDefaults = JoinedChannels.Count == 0; + var joinDefaults = JoinedChannels.Count == 0; req.Success += channels => { @@ -185,14 +230,14 @@ namespace osu.Game.Online.Chat AvailableChannels.Add(channel); // join any channels classified as "defaults" - /*if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase))) - JoinChannel(channel);*/ + if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase))) + JoinChannel(channel); } }; req.Failure += error => { Logger.Error(error, "Fetching channel list failed"); - initializeDefaultChannels(); + initializeChannels(); }; api.Queue(req); @@ -207,9 +252,15 @@ namespace osu.Game.Online.Chat /// The channel private void fetchInitalMessages(Channel channel) { + if (channel.Id <= 0) return; + var fetchInitialMsgReq = new GetMessagesRequest(channel); - fetchInitialMsgReq.Success += handleChannelMessages; - fetchInitialMsgReq.Failure += exception => Logger.Error(exception, $"Failed to fetch inital messages for the channel {channel.Name}"); + fetchInitialMsgReq.Success += messages => + { + handleChannelMessages(messages); + channel.MessagesLoaded = true; // this will mark the channel as having received messages even if tehre were none. + }; + api.Queue(fetchInitialMsgReq); } @@ -236,7 +287,11 @@ namespace osu.Game.Online.Chat if (channel.Type == ChannelType.Public && !channel.Joined) { var req = new JoinChannelRequest(channel, api.LocalUser); - req.Success += () => JoinChannel(channel); + req.Success += () => + { + channel.Joined.Value = true; + JoinChannel(channel); + }; req.Failure += ex => LeaveChannel(channel); api.Queue(req); return; @@ -246,11 +301,10 @@ namespace osu.Game.Online.Chat if (CurrentChannel.Value == null) CurrentChannel.Value = channel; - if (!channel.Joined.Value) + if (!channel.MessagesLoaded) { // let's fetch a small number of messages to bring us up-to-date with the backlog. fetchInitalMessages(channel); - channel.Joined.Value = true; } } @@ -274,9 +328,6 @@ namespace osu.Game.Online.Chat switch (state) { case APIState.Online: - if (JoinedChannels.Count == 0) - initializeDefaultChannels(); - fetchUpdates(); break; default: @@ -289,6 +340,8 @@ namespace osu.Game.Online.Chat private long lastMessageId; private const int update_poll_interval = 1000; + private bool channelsInitialised; + private void fetchUpdates() { fetchMessagesScheduleder?.Cancel(); @@ -302,7 +355,20 @@ namespace osu.Game.Online.Chat { foreach (var channel in updates.Presence) { - JoinChannel(AvailableChannels.FirstOrDefault(c => c.Id == channel.Id) ?? channel); + if (!channel.Joined.Value) + { + // we received this from the server so should mark the channel already joined. + channel.Joined.Value = true; + + JoinChannel(channel); + } + } + + if (!channelsInitialised) + { + channelsInitialised = true; + // we want this to run after the first presence so we can see if the user is in any channels already. + initializeChannels(); } //todo: handle left channels