mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 06:17:23 +08:00
Add back support for new API and private messages
This commit is contained in:
parent
8b9f7f6691
commit
f241fcdba1
@ -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)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,19 @@ namespace osu.Game.Online.Chat
|
||||
public readonly int MaxHistory = 300;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public readonly ObservableCollection<User> JoinedUsers = new ObservableCollection<User>();
|
||||
public readonly ObservableCollection<User> Users = new ObservableCollection<User>();
|
||||
|
||||
[JsonProperty(@"users")]
|
||||
private long[] userIds
|
||||
{
|
||||
set
|
||||
{
|
||||
foreach (var id in value)
|
||||
Users.Add(new User { Id = id });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains all the messages send in the channel.
|
||||
@ -47,11 +57,6 @@ namespace osu.Game.Online.Chat
|
||||
/// </summary>
|
||||
public event Action<Message> MessageRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Signalles whether the channels target is a private channel or public channel.
|
||||
/// </summary>
|
||||
public TargetType Target { get; protected set; }
|
||||
|
||||
public bool ReadOnly => false; //todo not yet used.
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
@ -39,12 +39,12 @@ namespace osu.Game.Online.Chat
|
||||
/// <summary>
|
||||
/// The Channels the player has joined
|
||||
/// </summary>
|
||||
public ObservableCollection<Channel> JoinedChannels { get; } = new ObservableCollection<Channel>();
|
||||
public ObservableCollection<Channel> JoinedChannels { get; } = new ObservableCollection<Channel>(); //todo: should be publicly readonly
|
||||
|
||||
/// <summary>
|
||||
/// The channels available for the player to join
|
||||
/// </summary>
|
||||
public ObservableCollection<Channel> AvailableChannels { get; } = new ObservableCollection<Channel>();
|
||||
public ObservableCollection<Channel> AvailableChannels { get; } = new ObservableCollection<Channel>(); //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);*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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<Message> 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<Message> 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.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel </param>
|
||||
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)
|
||||
{
|
||||
|
@ -1,72 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles tracking and updating of a specific message type, allowing polling and requesting of only new messages on an ongoing basis.
|
||||
/// </summary>
|
||||
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<List<Message>> onNewMessages;
|
||||
|
||||
public bool CanRequestNewMessages => getMessagesRequest == null;
|
||||
|
||||
public IncomingMessagesHandler([NotNull] CreateRequestDelegate createRequest, [NotNull] Action<List<Message>> 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<Message> 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contructs a private channel
|
||||
/// </summary>
|
||||
/// <param name="user">The user</param>
|
||||
public PrivateChannel()
|
||||
{
|
||||
Target = TargetType.User;
|
||||
}
|
||||
}
|
||||
}
|
@ -54,11 +54,11 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
|
||||
protected override TabItem<Channel> 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.");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user