diff --git a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs
index 1c1675a67c..1d39cba81d 100644
--- a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs
@@ -87,15 +87,18 @@ namespace osu.Game.Tests.Visual
private void addRandomUser()
{
- channelTabControl.AddChannel(new PrivateChannel
+ channelTabControl.AddChannel(new Channel
{
- User = users?.Count > 0
+ Users =
+ {
+ users?.Count > 0
? users[RNG.Next(0, users.Count - 1)]
: new User
{
Id = RNG.Next(),
Username = "testuser" + RNG.Next(1000)
}
+ }
});
}
diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs
index e6ad72a2bc..c49490ea19 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -17,9 +17,19 @@ namespace osu.Game.Online.Chat
public readonly int MaxHistory = 300;
///
- /// Contains every joined user except the current logged in user.
+ /// Contains every joined user except the current logged in user. Currently only returned for PM channels.
///
- public readonly ObservableCollection JoinedUsers = new ObservableCollection();
+ public readonly ObservableCollection Users = new ObservableCollection();
+
+ [JsonProperty(@"users")]
+ private long[] userIds
+ {
+ set
+ {
+ foreach (var id in value)
+ Users.Add(new User { Id = id });
+ }
+ }
///
/// Contains all the messages send in the channel.
@@ -47,11 +57,6 @@ namespace osu.Game.Online.Chat
///
public event Action MessageRemoved;
- ///
- /// Signalles whether the channels target is a private channel or public channel.
- ///
- public TargetType Target { get; protected set; }
-
public bool ReadOnly => false; //todo not yet used.
public override string ToString() => Name;
diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 377e9ee7bb..8099f97999 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -39,12 +39,12 @@ namespace osu.Game.Online.Chat
///
/// The Channels the player has joined
///
- public ObservableCollection JoinedChannels { get; } = new ObservableCollection();
+ public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); //todo: should be publicly readonly
///
/// The channels available for the player to join
///
- public ObservableCollection AvailableChannels { get; } = new ObservableCollection();
+ public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); //todo: should be publicly readonly
/*private readonly IncomingMessagesHandler privateMessagesHandler;*/
@@ -54,12 +54,6 @@ namespace osu.Game.Online.Chat
public ChannelManager()
{
CurrentChannel.ValueChanged += currentChannelChanged;
-
- /*channelMessagesHandler = new IncomingMessagesHandler(
- lastId => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel)), handleChannelMessages);
-
- privateMessagesHandler = new IncomingMessagesHandler(
- lastId => new GetPrivateMessagesRequest(lastId),handleUserMessages);*/
}
///
@@ -85,14 +79,13 @@ namespace osu.Game.Online.Chat
if (user == null)
throw new ArgumentNullException(nameof(user));
- CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Target == TargetType.User && c.Id == user.Id)
- ?? new PrivateChannel { User = user };
+ CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id))
+ ?? new Channel { Name = user.Username, Users = { user } };
}
private void currentChannelChanged(Channel channel)
{
- if (!JoinedChannels.Contains(channel))
- JoinedChannels.Add(channel);
+ JoinChannel(channel);
}
///
@@ -169,71 +162,6 @@ namespace osu.Game.Online.Chat
}
}
- private void fetchNewMessages()
- {
- /*if (channelMessagesHandler.CanRequestNewMessages)
- channelMessagesHandler.RequestNewMessages(api);
-
- if (privateMessagesHandler.CanRequestNewMessages)
- privateMessagesHandler.RequestNewMessages(api);*/
- }
-
- private void handleUserMessages(IEnumerable messages)
- {
- var joinedPrivateChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList();
-
- Channel getChannelForUser(User user)
- {
- var channel = joinedPrivateChannels.FirstOrDefault(c => c.Id == user.Id);
-
- if (channel == null)
- {
- channel = new PrivateChannel { User = user };
- JoinedChannels.Add(channel);
- joinedPrivateChannels.Add(channel);
- }
-
- return channel;
- }
-
- long localUserId = api.LocalUser.Value.Id;
-
- var outgoingGroups = messages.Where(m => m.Sender.Id == localUserId).GroupBy(m => m.ChannelId);
- var incomingGroups = messages.Where(m => m.Sender.Id != localUserId).GroupBy(m => m.UserId);
-
- foreach (var group in incomingGroups)
- {
- var targetUser = group.First().Sender;
-
- var channel = getChannelForUser(targetUser);
-
- channel.AddNewMessages(group.ToArray());
-
- var outgoingTargetMessages = outgoingGroups.FirstOrDefault(g => g.Key == targetUser.Id);
- if (outgoingTargetMessages != null)
- channel.AddNewMessages(outgoingTargetMessages.ToArray());
- }
-
- // Because of the way the API provides data right now, outgoing messages do not contain required
- // user (or in the future, target channel) metadata. As such we need to do a second request
- // to find out the specifics of the user.
- var withoutReplyGroups = outgoingGroups.Where(g => joinedPrivateChannels.All(m => m.Id != g.Key));
-
- foreach (var withoutReplyGroup in withoutReplyGroups)
- {
- var userReq = new GetUserRequest(withoutReplyGroup.First().ChannelId);
-
- userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations.");
- userReq.Success += user =>
- {
- var channel = getChannelForUser(user);
- channel.AddNewMessages(withoutReplyGroup.ToArray());
- };
-
- api.Queue(userReq);
- }
- }
-
private void handleChannelMessages(IEnumerable messages)
{
var channels = JoinedChannels.ToList();
@@ -246,32 +174,24 @@ namespace osu.Game.Online.Chat
{
var req = new ListChannelsRequest();
+ //var joinDefaults = JoinedChannels.Count == 0;
+
req.Success += channels =>
{
foreach (var channel in channels)
{
- if (JoinedChannels.Any(c => c.Id == channel.Id))
- continue;
-
// add as available if not already
if (AvailableChannels.All(c => c.Id != channel.Id))
AvailableChannels.Add(channel);
// join any channels classified as "defaults"
- if (defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase)))
- {
- JoinedChannels.Add(channel);
-
- FetchInitalMessages(channel);
- }
+ /*if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase)))
+ JoinChannel(channel);*/
}
-
- fetchNewMessages();
};
req.Failure += error =>
{
Logger.Error(error, "Fetching channel list failed");
-
initializeDefaultChannels();
};
@@ -285,7 +205,7 @@ namespace osu.Game.Online.Chat
/// right now it caps out at 50 messages and therefore only returns one channel's worth of content.
///
/// The channel
- public void FetchInitalMessages(Channel channel)
+ private void fetchInitalMessages(Channel channel)
{
var fetchInitialMsgReq = new GetMessagesRequest(channel);
fetchInitialMsgReq.Success += handleChannelMessages;
@@ -293,6 +213,62 @@ namespace osu.Game.Online.Chat
api.Queue(fetchInitialMsgReq);
}
+ public void JoinChannel(Channel channel)
+ {
+ if (channel == null) return;
+
+ // ReSharper disable once AccessToModifiedClosure
+ var existing = JoinedChannels.FirstOrDefault(c => c.Id == channel.Id);
+
+ if (existing != null)
+ {
+ // if we already have this channel loaded, we don't want to make a second one.
+ channel = existing;
+ }
+ else
+ {
+ var foundSelf = channel.Users.FirstOrDefault(u => u.Id == api.LocalUser.Value.Id);
+ if (foundSelf != null)
+ channel.Users.Remove(foundSelf);
+
+ JoinedChannels.Add(channel);
+
+ if (channel.Type == ChannelType.Public && !channel.Joined)
+ {
+ var req = new JoinChannelRequest(channel, api.LocalUser);
+ req.Success += () => JoinChannel(channel);
+ req.Failure += ex => LeaveChannel(channel);
+ api.Queue(req);
+ return;
+ }
+ }
+
+ if (CurrentChannel.Value == null)
+ CurrentChannel.Value = channel;
+
+ if (!channel.Joined.Value)
+ {
+ // let's fetch a small number of messages to bring us up-to-date with the backlog.
+ fetchInitalMessages(channel);
+ channel.Joined.Value = true;
+ }
+ }
+
+ public void LeaveChannel(Channel channel)
+ {
+ if (channel == null) return;
+
+ if (channel == CurrentChannel.Value) CurrentChannel.Value = null;
+
+ JoinedChannels.Remove(channel);
+
+ if (channel.Joined.Value)
+ {
+ api.Queue(new LeaveChannelRequest(channel, api.LocalUser));
+ channel.Joined.Value = false;
+ }
+ }
+
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
@@ -301,18 +277,53 @@ namespace osu.Game.Online.Chat
if (JoinedChannels.Count == 0)
initializeDefaultChannels();
- fetchMessagesScheduleder = Scheduler.AddDelayed(fetchNewMessages, 1000, true);
+ fetchUpdates();
break;
default:
- /*channelMessagesHandler.CancelOngoingRequests();
- privateMessagesHandler.CancelOngoingRequests();*/
-
fetchMessagesScheduleder?.Cancel();
fetchMessagesScheduleder = null;
break;
}
}
+ private long lastMessageId;
+ private const int update_poll_interval = 1000;
+
+ private void fetchUpdates()
+ {
+ fetchMessagesScheduleder?.Cancel();
+ fetchMessagesScheduleder = Scheduler.AddDelayed(() =>
+ {
+ var fetchReq = new GetUpdatesRequest(lastMessageId);
+
+ fetchReq.Success += updates =>
+ {
+ if (updates?.Presence != null)
+ {
+ foreach (var channel in updates.Presence)
+ {
+ JoinChannel(AvailableChannels.FirstOrDefault(c => c.Id == channel.Id) ?? channel);
+ }
+
+ //todo: handle left channels
+
+ handleChannelMessages(updates.Messages);
+
+ foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
+ JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
+
+ lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
+ }
+
+ fetchUpdates();
+ };
+
+ fetchReq.Failure += delegate { fetchUpdates(); };
+
+ api.Queue(fetchReq);
+ }, update_poll_interval);
+ }
+
[BackgroundDependencyLoader]
private void load(IAPIProvider api)
{
diff --git a/osu.Game/Online/Chat/IncomingMessagesHandler.cs b/osu.Game/Online/Chat/IncomingMessagesHandler.cs
deleted file mode 100644
index 46f2b805b3..0000000000
--- a/osu.Game/Online/Chat/IncomingMessagesHandler.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using JetBrains.Annotations;
-using osu.Framework.Logging;
-using osu.Game.Online.API;
-
-namespace osu.Game.Online.Chat
-{
- ///
- /// Handles tracking and updating of a specific message type, allowing polling and requesting of only new messages on an ongoing basis.
- ///
- public class IncomingMessagesHandler
- {
- public delegate APIMessagesRequest CreateRequestDelegate(long? lastMessageId);
-
- public long? LastMessageId { get; private set; }
-
- private APIMessagesRequest getMessagesRequest;
-
- private readonly CreateRequestDelegate createRequest;
- private readonly Action> onNewMessages;
-
- public bool CanRequestNewMessages => getMessagesRequest == null;
-
- public IncomingMessagesHandler([NotNull] CreateRequestDelegate createRequest, [NotNull] Action> onNewMessages)
- {
- this.createRequest = createRequest ?? throw new ArgumentNullException(nameof(createRequest));
- this.onNewMessages = onNewMessages ?? throw new ArgumentNullException(nameof(onNewMessages));
- }
-
- public void RequestNewMessages(IAPIProvider api)
- {
- if (!CanRequestNewMessages)
- throw new InvalidOperationException("Requesting new messages is not possible yet, because the old request is still ongoing.");
-
- getMessagesRequest = createRequest.Invoke(LastMessageId);
- getMessagesRequest.Success += handleNewMessages;
- getMessagesRequest.Failure += exception =>
- {
- Logger.Error(exception, "Fetching messages failed.");
-
- // allowing new messages to be requested even after the fail.
- getMessagesRequest = null;
- };
-
- api.Queue(getMessagesRequest);
- }
-
- private void handleNewMessages(List messages)
- {
- // allowing new messages to be requested.
- getMessagesRequest = null;
-
- // in case of no new messages we simply do nothing.
- if (messages == null || messages.Count == 0)
- return;
-
- onNewMessages.Invoke(messages);
-
- LastMessageId = messages.Max(m => m.Id) ?? LastMessageId;
- }
-
- public void CancelOngoingRequests()
- {
- getMessagesRequest?.Cancel();
- }
- }
-}
diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs
index b4a7b6faa7..3f0f352ac7 100644
--- a/osu.Game/Online/Chat/Message.cs
+++ b/osu.Game/Online/Chat/Message.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using Newtonsoft.Json;
using osu.Game.Users;
@@ -69,12 +68,4 @@ namespace osu.Game.Online.Chat
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public override int GetHashCode() => Id.GetHashCode();
}
-
- public enum TargetType
- {
- [Description(@"channel")]
- Channel,
- [Description(@"user")]
- User
- }
}
diff --git a/osu.Game/Online/Chat/PrivateChannel.cs b/osu.Game/Online/Chat/PrivateChannel.cs
deleted file mode 100644
index aac88ecb27..0000000000
--- a/osu.Game/Online/Chat/PrivateChannel.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Game.Users;
-
-namespace osu.Game.Online.Chat
-{
- public class PrivateChannel : Channel
- {
- public User User
- {
- set
- {
- Name = value.Username;
- Id = value.Id;
- JoinedUsers.Add(value);
- }
- }
-
- ///
- /// Contructs a private channel
- ///
- /// The user
- public PrivateChannel()
- {
- Target = TargetType.User;
- }
- }
-}
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
index 6470963b4f..08d4e40a64 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
@@ -54,11 +54,11 @@ namespace osu.Game.Overlays.Chat.Tabs
protected override TabItem CreateTabItem(Channel value)
{
- switch (value.Target)
+ switch (value.Type)
{
- case TargetType.Channel:
+ case ChannelType.Public:
return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
- case TargetType.User:
+ case ChannelType.PM:
return new PrivateChannelTabItem(value) { OnRequestClose = tabCloseRequested };
default:
throw new InvalidOperationException("Only TargetType User and Channel are supported.");
diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs
index 7492de0123..c7ca1cb073 100644
--- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs
public PrivateChannelTabItem(Channel value)
: base(value)
{
- if (value.Target != TargetType.User)
+ if (value.Type != ChannelType.PM)
throw new ArgumentException("Argument value needs to have the targettype user!");
AddRange(new Drawable[]
@@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Chat.Tabs
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
- Child = new DelayedLoadWrapper(new Avatar(value.JoinedUsers.First())
+ Child = new DelayedLoadWrapper(new Avatar(value.Users.First())
{
RelativeSizeAxes = Axes.Both,
OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
@@ -88,7 +88,7 @@ namespace osu.Game.Overlays.Chat.Tabs
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- var user = Value.JoinedUsers.First();
+ var user = Value.Users.First();
BackgroundActive = user.Colour != null ? OsuColour.FromHex(user.Colour) : colours.BlueDark;
BackgroundInactive = BackgroundActive.Darken(0.5f);
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 9fc4c15849..e45373c36f 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -153,7 +153,7 @@ namespace osu.Game.Overlays
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
- OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel)
+ OnRequestLeave = channel => channelManager.LeaveChannel(channel)
},
}
},
@@ -176,15 +176,9 @@ namespace osu.Game.Overlays
else
textbox.HoldFocus = true;
};
- channelSelection.OnRequestJoin = channel =>
- {
- if (!channelManager.JoinedChannels.Contains(channel))
- {
- channelManager.JoinedChannels.Add(channel);
- channelManager.FetchInitalMessages(channel);
- }
- };
- channelSelection.OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel);
+
+ channelSelection.OnRequestJoin = channel => channelManager.JoinChannel(channel);
+ channelSelection.OnRequestLeave = channel => channelManager.LeaveChannel(channel);
}
private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args)
@@ -195,18 +189,16 @@ namespace osu.Game.Overlays
foreach (Channel newChannel in args.NewItems)
{
channelTabControl.AddChannel(newChannel);
-
- newChannel.Joined.Value = true;
}
+
break;
case NotifyCollectionChangedAction.Remove:
foreach (Channel removedChannel in args.OldItems)
{
channelTabControl.RemoveChannel(removedChannel);
-
- loadedChannels.Remove(loadedChannels.Find(c => c.Channel == removedChannel ));
- removedChannel.Joined.Value = false;
+ loadedChannels.Remove(loadedChannels.Find(c => c.Channel == removedChannel));
}
+
break;
}
}