From 56de6c10670199f6d0633e3bd59720610bd1376d Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 28 Mar 2018 21:11:06 +0200 Subject: [PATCH 01/88] Rename Channel to ChannelChat --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 4 ++-- .../Online/API/Requests/GetMessagesRequest.cs | 4 ++-- .../API/Requests/ListChannelsRequest.cs | 2 +- .../Chat/{Channel.cs => ChannelChat.cs} | 4 ++-- osu.Game/Overlays/Chat/ChannelListItem.cs | 8 ++++---- osu.Game/Overlays/Chat/ChannelSection.cs | 2 +- .../Overlays/Chat/ChannelSelectionOverlay.cs | 4 ++-- osu.Game/Overlays/Chat/ChatTabControl.cs | 20 +++++++++---------- osu.Game/Overlays/Chat/DrawableChannel.cs | 8 ++++---- osu.Game/Overlays/ChatOverlay.cs | 20 +++++++++---------- 10 files changed, 38 insertions(+), 38 deletions(-) rename osu.Game/Online/Chat/{Channel.cs => ChannelChat.cs} (95%) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 786fcb64ab..d638019b24 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual { AvailableChannels = { - new Channel { Name = "#english" }, - new Channel { Name = "#japanese" } + new ChannelChat { Name = "#english" }, + new ChannelChat { Name = "#japanese" } } }); diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index a8f63887a1..b7546ce7f4 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -10,10 +10,10 @@ namespace osu.Game.Online.API.Requests { public class GetMessagesRequest : APIRequest> { - private readonly List channels; + private readonly List channels; private long? since; - public GetMessagesRequest(List channels, long? sinceId) + public GetMessagesRequest(List channels, long? sinceId) { this.channels = channels; since = sinceId; diff --git a/osu.Game/Online/API/Requests/ListChannelsRequest.cs b/osu.Game/Online/API/Requests/ListChannelsRequest.cs index b387af9694..97ed3d3cbc 100644 --- a/osu.Game/Online/API/Requests/ListChannelsRequest.cs +++ b/osu.Game/Online/API/Requests/ListChannelsRequest.cs @@ -6,7 +6,7 @@ using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { - public class ListChannelsRequest : APIRequest> + public class ListChannelsRequest : APIRequest> { protected override string Target => @"chat/channels"; } diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/ChannelChat.cs similarity index 95% rename from osu.Game/Online/Chat/Channel.cs rename to osu.Game/Online/Chat/ChannelChat.cs index 35952fbc6e..c39d5cf4b1 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/ChannelChat.cs @@ -10,7 +10,7 @@ using osu.Framework.Lists; namespace osu.Game.Online.Chat { - public class Channel + public class ChannelChat { [JsonProperty(@"name")] public string Name; @@ -35,7 +35,7 @@ namespace osu.Game.Online.Chat public const int MAX_HISTORY = 300; [JsonConstructor] - public Channel() + public ChannelChat() { } diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs index 19418c63a8..9625c715d2 100644 --- a/osu.Game/Overlays/Chat/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Chat private const float text_size = 15; private const float transition_duration = 100; - private readonly Channel channel; + private readonly ChannelChat channel; private readonly Bindable joinedBind = new Bindable(); private readonly OsuSpriteText name; @@ -44,10 +44,10 @@ namespace osu.Game.Overlays.Chat } } - public Action OnRequestJoin; - public Action OnRequestLeave; + public Action OnRequestJoin; + public Action OnRequestLeave; - public ChannelListItem(Channel channel) + public ChannelListItem(ChannelChat channel) { this.channel = channel; diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs index 132891bcc0..6bec82f505 100644 --- a/osu.Game/Overlays/Chat/ChannelSection.cs +++ b/osu.Game/Overlays/Chat/ChannelSection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Chat set { header.Text = value.ToUpper(); } } - public IEnumerable Channels + public IEnumerable Channels { set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); } } diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs index 3684c47e40..598e1fe527 100644 --- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs @@ -32,8 +32,8 @@ namespace osu.Game.Overlays.Chat private readonly SearchTextBox search; private readonly SearchContainer sectionsFlow; - public Action OnRequestJoin; - public Action OnRequestLeave; + public Action OnRequestJoin; + public Action OnRequestLeave; public IEnumerable Sections { diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 1d3dab249d..e495faf944 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -21,11 +21,11 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Chat { - public class ChatTabControl : OsuTabControl + public class ChatTabControl : OsuTabControl { private const float shear_width = 10; - public Action OnRequestLeave; + public Action OnRequestLeave; public readonly Bindable ChannelSelectorActive = new Bindable(); @@ -46,12 +46,12 @@ namespace osu.Game.Overlays.Chat Margin = new MarginPadding(10), }); - AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); + AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new ChannelChat { Name = "+" })); ChannelSelectorActive.BindTo(selectorTab.Active); } - protected override void AddTabItem(TabItem item, bool addToDropdown = true) + protected override void AddTabItem(TabItem item, bool addToDropdown = true) { if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) // performTabSort might've made selectorTab's position wonky, fix it @@ -63,9 +63,9 @@ namespace osu.Game.Overlays.Chat SelectTab(item); } - protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + protected override TabItem CreateTabItem(ChannelChat value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; - protected override void SelectTab(TabItem tab) + protected override void SelectTab(TabItem tab) { if (tab is ChannelTabItem.ChannelSelectorTabItem) { @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Chat base.SelectTab(tab); } - private void tabCloseRequested(TabItem tab) + private void tabCloseRequested(TabItem tab) { int totalTabs = TabContainer.Count - 1; // account for selectorTab int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Chat OnRequestLeave?.Invoke(tab.Value); } - private class ChannelTabItem : TabItem + private class ChannelTabItem : TabItem { private Color4 backgroundInactive; private Color4 backgroundHover; @@ -175,7 +175,7 @@ namespace osu.Game.Overlays.Chat updateState(); } - public ChannelTabItem(Channel value) : base(value) + public ChannelTabItem(ChannelChat value) : base(value) { Width = 150; @@ -307,7 +307,7 @@ namespace osu.Game.Overlays.Chat { public override bool IsRemovable => false; - public ChannelSelectorTabItem(Channel value) : base(value) + public ChannelSelectorTabItem(ChannelChat value) : base(value) { Depth = float.MaxValue; Width = 45; diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index d12df70b74..ac41b2f157 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -17,11 +17,11 @@ namespace osu.Game.Overlays.Chat { public class DrawableChannel : Container { - public readonly Channel Channel; + public readonly ChannelChat Channel; private readonly ChatLineContainer flow; private readonly ScrollContainer scroll; - public DrawableChannel(Channel channel) + public DrawableChannel(ChannelChat channel) { Channel = channel; @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { // Add up to last Channel.MAX_HISTORY messages - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); + var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - ChannelChat.MAX_HISTORY)); flow.AddRange(displayMessages.Select(m => new ChatLine(m))); @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Chat scrollToEnd(); var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); - int count = staleMessages.Length - Channel.MAX_HISTORY; + int count = staleMessages.Length - ChannelChat.MAX_HISTORY; for (int i = 0; i < count; i++) { diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 210f5ce01e..315f8d2c41 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays public Bindable ChatHeight { get; set; } - public List AvailableChannels { get; private set; } = new List(); + public List AvailableChannels { get; private set; } = new List(); private readonly Container channelSelectionContainer; private readonly ChannelSelectionOverlay channelSelection; @@ -191,7 +191,7 @@ namespace osu.Game.Overlays private double startDragChatHeight; private bool isDragging; - public void OpenChannel(Channel channel) => addChannel(channel); + public void OpenChannel(ChannelChat channel) => addChannel(channel); protected override bool OnDragStart(InputState state) { @@ -289,7 +289,7 @@ namespace osu.Game.Overlays private long? lastMessageId; - private readonly List careChannels = new List(); + private readonly List careChannels = new List(); private readonly List loadedChannels = new List(); @@ -300,7 +300,7 @@ namespace osu.Game.Overlays messageRequest?.Cancel(); ListChannelsRequest req = new ListChannelsRequest(); - req.Success += delegate (List channels) + req.Success += delegate (List channels) { AvailableChannels = channels; @@ -328,9 +328,9 @@ namespace osu.Game.Overlays api.Queue(req); } - private Channel currentChannel; + private ChannelChat currentChannel; - protected Channel CurrentChannel + protected ChannelChat CurrentChannel { get { @@ -380,7 +380,7 @@ namespace osu.Game.Overlays } } - private void addChannel(Channel channel) + private void addChannel(ChannelChat channel) { if (channel == null) return; @@ -407,7 +407,7 @@ namespace osu.Game.Overlays channel.Joined.Value = true; } - private void removeChannel(Channel channel) + private void removeChannel(ChannelChat channel) { if (channel == null) return; @@ -420,9 +420,9 @@ namespace osu.Game.Overlays channel.Joined.Value = false; } - private void fetchInitialMessages(Channel channel) + private void fetchInitialMessages(ChannelChat channel) { - var req = new GetMessagesRequest(new List { channel }, null); + var req = new GetMessagesRequest(new List { channel }, null); req.Success += delegate (List messages) { From c9377896848861a0bcd035e42b726bdbed334efe Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 28 Mar 2018 21:33:50 +0200 Subject: [PATCH 02/88] Rename GetMessagesRequest to GetChannelMessagesRequest --- .../{GetMessagesRequest.cs => GetChannelMessagesRequest.cs} | 4 ++-- osu.Game/Overlays/ChatOverlay.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game/Online/API/Requests/{GetMessagesRequest.cs => GetChannelMessagesRequest.cs} (83%) diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs similarity index 83% rename from osu.Game/Online/API/Requests/GetMessagesRequest.cs rename to osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs index b7546ce7f4..45541c202e 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs @@ -8,12 +8,12 @@ using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { - public class GetMessagesRequest : APIRequest> + public class GetChannelMessagesRequest : APIRequest> { private readonly List channels; private long? since; - public GetMessagesRequest(List channels, long? sinceId) + public GetChannelMessagesRequest(List channels, long? sinceId) { this.channels = channels; since = sinceId; diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 315f8d2c41..8b3031b9e7 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private GetMessagesRequest fetchReq; + private GetChannelMessagesRequest fetchReq; private readonly ChatTabControl channelTabs; @@ -422,7 +422,7 @@ namespace osu.Game.Overlays private void fetchInitialMessages(ChannelChat channel) { - var req = new GetMessagesRequest(new List { channel }, null); + var req = new GetChannelMessagesRequest(new List { channel }, null); req.Success += delegate (List messages) { @@ -442,7 +442,7 @@ namespace osu.Game.Overlays { if (fetchReq != null) return; - fetchReq = new GetMessagesRequest(careChannels, lastMessageId); + fetchReq = new GetChannelMessagesRequest(careChannels, lastMessageId); fetchReq.Success += delegate (List messages) { From f1696eae922d812ad41337d1e79ffa431530f78d Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 15:01:14 +0200 Subject: [PATCH 03/88] Use IEnumable instead of List --- osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs b/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs index 45541c202e..d463af6c25 100644 --- a/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs @@ -10,10 +10,10 @@ namespace osu.Game.Online.API.Requests { public class GetChannelMessagesRequest : APIRequest> { - private readonly List channels; + private readonly IEnumerable channels; private long? since; - public GetChannelMessagesRequest(List channels, long? sinceId) + public GetChannelMessagesRequest(IEnumerable channels, long? sinceId) { this.channels = channels; since = sinceId; From a70b329155e69f27113bc66af9840cbceed4d040 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 18:21:48 +0200 Subject: [PATCH 04/88] Split drawing and business logic of ChatOverlay --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 16 +- .../Graphics/Containers/LinkFlowContainer.cs | 10 +- osu.Game/Online/Chat/ChannelChat.cs | 80 +--- osu.Game/Online/Chat/ChatBase.cs | 85 ++++ osu.Game/Online/Chat/ChatManager.cs | 193 +++++++++ osu.Game/Online/Chat/Message.cs | 2 +- osu.Game/OsuGame.cs | 7 +- osu.Game/OsuGameBase.cs | 6 + osu.Game/Overlays/Chat/ChatLine.cs | 10 +- .../{DrawableChannel.cs => DrawableChat.cs} | 28 +- osu.Game/Overlays/ChatOverlay.cs | 382 +++++------------- 11 files changed, 431 insertions(+), 388 deletions(-) create mode 100644 osu.Game/Online/Chat/ChatBase.cs create mode 100644 osu.Game/Online/Chat/ChatManager.cs rename osu.Game/Overlays/Chat/{DrawableChannel.cs => DrawableChat.cs} (80%) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index d638019b24..4f85779bce 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -15,6 +15,7 @@ using System.Linq; using NUnit.Framework; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; using osu.Game.Overlays; namespace osu.Game.Tests.Visual @@ -50,17 +51,14 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, IAPIProvider api) { linkColour = colours.Blue; - dependencies.Cache(new ChatOverlay - { - AvailableChannels = - { - new ChannelChat { Name = "#english" }, - new ChannelChat { Name = "#japanese" } - } - }); + dependencies.Cache(new ChatOverlay()); + + var chatManager = new ChatManager(Scheduler); + api.Register(chatManager); + dependencies.Cache(chatManager); testLinksGeneral(); testEcho(); diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 1d231ada23..627efbda76 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -23,15 +23,15 @@ namespace osu.Game.Graphics.Containers public override bool HandleMouseInput => true; private OsuGame game; - + private ChatManager chatManager; private Action showNotImplementedError; [BackgroundDependencyLoader(true)] - private void load(OsuGame game, NotificationOverlay notifications) + private void load(OsuGame game, NotificationOverlay notifications, ChatManager chatManager) { // will be null in tests this.game = game; - + this.chatManager = chatManager; showNotImplementedError = () => notifications?.Post(new SimpleNotification { Text = @"This link type is not yet supported!", @@ -80,7 +80,9 @@ namespace osu.Game.Graphics.Containers game?.ShowBeatmapSet(setId); break; case LinkAction.OpenChannel: - game?.OpenChannel(linkArgument); + var channel = chatManager.AvailableChannels.FirstOrDefault(c => c.Name == linkArgument); + if (channel != null) + chatManager.CurrentChat.Value = channel; break; case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: diff --git a/osu.Game/Online/Chat/ChannelChat.cs b/osu.Game/Online/Chat/ChannelChat.cs index c39d5cf4b1..fb24806294 100644 --- a/osu.Game/Online/Chat/ChannelChat.cs +++ b/osu.Game/Online/Chat/ChannelChat.cs @@ -1,16 +1,11 @@ // 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 Newtonsoft.Json; -using osu.Framework.Configuration; -using osu.Framework.Lists; namespace osu.Game.Online.Chat { - public class ChannelChat + public class ChannelChat : ChatBase { [JsonProperty(@"name")] public string Name; @@ -24,82 +19,13 @@ namespace osu.Game.Online.Chat [JsonProperty(@"channel_id")] public int Id; - public readonly SortedList Messages = new SortedList(Comparer.Default); - - private readonly List pendingMessages = new List(); - - public Bindable Joined = new Bindable(); - - public bool ReadOnly => false; - - public const int MAX_HISTORY = 300; - [JsonConstructor] public ChannelChat() { } - public event Action> NewMessagesArrived; - public event Action PendingMessageResolved; - public event Action MessageRemoved; - - public void AddLocalEcho(LocalEchoMessage message) - { - pendingMessages.Add(message); - Messages.Add(message); - - NewMessagesArrived?.Invoke(new[] { message }); - } - - public void AddNewMessages(params Message[] messages) - { - messages = messages.Except(Messages).ToArray(); - - Messages.AddRange(messages); - - purgeOldMessages(); - - NewMessagesArrived?.Invoke(messages); - } - - private void purgeOldMessages() - { - // never purge local echos - int messageCount = Messages.Count - pendingMessages.Count; - if (messageCount > MAX_HISTORY) - Messages.RemoveRange(0, messageCount - MAX_HISTORY); - } - - /// - /// Replace or remove a message from the channel. - /// - /// The local echo message (client-side). - /// The response message, or null if the message became invalid. - public void ReplaceMessage(LocalEchoMessage echo, Message final) - { - if (!pendingMessages.Remove(echo)) - throw new InvalidOperationException("Attempted to remove echo that wasn't present"); - - Messages.Remove(echo); - - if (final == null) - { - MessageRemoved?.Invoke(echo); - return; - } - - if (Messages.Contains(final)) - { - // message already inserted, so let's throw away this update. - // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed. - MessageRemoved?.Invoke(echo); - return; - } - - Messages.Add(final); - PendingMessageResolved?.Invoke(echo, final); - } - public override string ToString() => Name; + public override long ChatID => Id; + public override TargetType Target => TargetType.Channel; } } diff --git a/osu.Game/Online/Chat/ChatBase.cs b/osu.Game/Online/Chat/ChatBase.cs new file mode 100644 index 0000000000..969d2c0f1f --- /dev/null +++ b/osu.Game/Online/Chat/ChatBase.cs @@ -0,0 +1,85 @@ +// 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 osu.Framework.Configuration; +using osu.Framework.Lists; + +namespace osu.Game.Online.Chat +{ + public abstract class ChatBase + { + public const int MAX_HISTORY = 300; + public bool ReadOnly { get; } = false; + public abstract TargetType Target { get; } + public abstract long ChatID { get; } + public Bindable Joined = new Bindable(); + + public readonly SortedList Messages = new SortedList(Comparer.Default); + private readonly List pendingMessages = new List(); + + public event Action> NewMessagesArrived; + public event Action PendingMessageResolved; + public event Action MessageRemoved; + + public void AddLocalEcho(LocalEchoMessage message) + { + pendingMessages.Add(message); + Messages.Add(message); + + NewMessagesArrived?.Invoke(new[] { message }); + } + + public void AddNewMessages(params Message[] messages) + { + messages = messages.Except(Messages).ToArray(); + + Messages.AddRange(messages); + + purgeOldMessages(); + + NewMessagesArrived?.Invoke(messages); + } + + private void purgeOldMessages() + { + // never purge local echos + int messageCount = Messages.Count - pendingMessages.Count; + if (messageCount > MAX_HISTORY) + Messages.RemoveRange(0, messageCount - MAX_HISTORY); + } + + /// + /// Replace or remove a message from the chat. + /// + /// The local echo message (client-side). + /// The response message, or null if the message became invalid. + public void ReplaceMessage(LocalEchoMessage echo, Message final) + { + if (!pendingMessages.Remove(echo)) + throw new InvalidOperationException("Attempted to remove echo that wasn't present"); + + Messages.Remove(echo); + + if (final == null) + { + MessageRemoved?.Invoke(echo); + return; + } + + if (Messages.Contains(final)) + { + // message already inserted, so let's throw away this update. + // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed. + MessageRemoved?.Invoke(echo); + return; + } + + Messages.Add(final); + PendingMessageResolved?.Invoke(echo, final); + } + + } +} diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs new file mode 100644 index 0000000000..69620c8f53 --- /dev/null +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -0,0 +1,193 @@ +// 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.Collections.ObjectModel; +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Logging; +using osu.Framework.Threading; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; + +namespace osu.Game.Online.Chat +{ + /// + /// Manages everything chat related + /// + public sealed class ChatManager : IOnlineComponent + { + /// + /// The channels the player joins on startup + /// + private readonly string[] defaultChannels = + { + @"#lazer", @"#osu", @"#lobby" + }; + + /// + /// The currently opened chat + /// + public Bindable CurrentChat { get; } = new Bindable(); + /// + /// The Channels the player has joined + /// + public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); + /// + /// The channels available for the player to join + /// + public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); + + private APIAccess api; + private readonly Scheduler scheduler; + private ScheduledDelegate fetchMessagesScheduleder; + private GetChannelMessagesRequest fetchChannelMsgReq; + private long? lastChannelMsgId; + + public ChatManager(Scheduler scheduler) + { + this.scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); + CurrentChat.ValueChanged += currentChatChanged; + } + + private void currentChatChanged(ChatBase chatBase) + { + if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) + JoinedChannels.Add(channel); + + } + + /// + /// Posts a message to the currently opened chat. + /// + /// The message text that is going to be posted + /// Is true if the message is an action, e.g.: user is currently eating + public void PostMessage(string text, bool isAction = false) + { + if (CurrentChat.Value == null) + return; + + if (!api.IsLoggedIn) + { + CurrentChat.Value.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + return; + } + + var message = new LocalEchoMessage + { + Sender = api.LocalUser.Value, + Timestamp = DateTimeOffset.Now, + TargetType = CurrentChat.Value.Target, + TargetId = CurrentChat.Value.ChatID, + IsAction = isAction, + Content = text + }; + + CurrentChat.Value.AddLocalEcho(message); + + var req = new PostMessageRequest(message); + req.Failure += e => CurrentChat.Value?.ReplaceMessage(message, null); + req.Success += m => CurrentChat.Value?.ReplaceMessage(message, m); + api.Queue(req); + } + + public void PostCommand(string text) + { + if (CurrentChat.Value == null) + return; + + var parameters = text.Split(new[] { ' ' }, 2); + string command = parameters[0]; + string content = parameters.Length == 2 ? parameters[1] : string.Empty; + + switch (command) + { + case "me": + if (string.IsNullOrWhiteSpace(content)) + { + CurrentChat.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); + break; + } + PostMessage(content, true); + break; + + case "help": + CurrentChat.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); + break; + + default: + CurrentChat.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); + break; + } + } + + private void fetchNewMessages() + { + if (fetchChannelMsgReq == null) + fetchNewChannelMessages(); + } + + private void fetchNewChannelMessages() + { + fetchChannelMsgReq = new GetChannelMessagesRequest(JoinedChannels, lastChannelMsgId); + + fetchChannelMsgReq.Success += messages => + { + handleChannelMessages(messages); + lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; + fetchChannelMsgReq = null; + }; + fetchChannelMsgReq.Failure += exception => Logger.Error(exception, "Fetching channel messages failed."); + + api.Queue(fetchChannelMsgReq); + } + + private void handleChannelMessages(IEnumerable messages) + { + var channels = JoinedChannels.ToList(); + + foreach (var group in messages.GroupBy(m => m.TargetId)) + channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); + } + + private void initializeDefaultChannels() + { + var req = new ListChannelsRequest(); + + req.Success += channels => + { + channels.Where(channel => AvailableChannels.All(c => c.ChatID != channel.ChatID)) + .ForEach(channel => AvailableChannels.Add(channel)); + + channels.Where(channel => defaultChannels.Contains(channel.Name)) + .Where(channel => JoinedChannels.All(c => c.ChatID != channel.ChatID)) + .ForEach(channel => JoinedChannels.Add(channel)); + + fetchNewMessages(); + }; + req.Failure += error => Logger.Error(error, "Fetching channels failed"); + + api.Queue(req); + } + + public void APIStateChanged(APIAccess api, APIState state) + { + this.api = api ?? throw new ArgumentNullException(nameof(api)); + + switch (state) + { + case APIState.Online: + if (JoinedChannels.Count == 0) + initializeDefaultChannels(); + fetchMessagesScheduleder = scheduler.AddDelayed(fetchNewMessages, 1000, true); + break; + default: + fetchChannelMsgReq?.Cancel(); + fetchMessagesScheduleder?.Cancel(); + break; + } + } + } +} diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index df3753da6a..d1d1f1b55b 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -22,7 +22,7 @@ namespace osu.Game.Online.Chat public TargetType TargetType; [JsonProperty(@"target_id")] - public int TargetId; + public long TargetId; [JsonProperty(@"is_action")] public bool IsAction; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 89447b8ed6..1b55418c7b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -29,6 +29,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; using osu.Game.Input.Bindings; +using osu.Game.Online.Chat; using osu.Game.Rulesets.Mods; using osu.Game.Skinning; using OpenTK.Graphics; @@ -142,12 +143,6 @@ namespace osu.Game private ScheduledDelegate scoreLoad; - /// - /// Open chat to a channel matching the provided name, if present. - /// - /// The name of the channel. - public void OpenChannel(string channelName) => chat.OpenChannel(chat.AvailableChannels.Find(c => c.Name == channelName)); - /// /// Show a beatmap set as an overlay. /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 54a279e977..d247bc74ff 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -28,6 +28,7 @@ using osu.Game.Graphics.Textures; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; +using osu.Game.Online.Chat; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -112,6 +113,11 @@ namespace osu.Game dependencies.Cache(api); dependencies.CacheAs(api); + var chatManager = new ChatManager(Scheduler); + api.Register(chatManager); + + dependencies.Cache(chatManager); + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index dd41dd5428..eb1ab9ef26 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -81,6 +81,8 @@ namespace osu.Game.Overlays.Chat Padding = new MarginPadding { Left = padding, Right = padding }; } + private ChatManager chatManager; + private Message message; private OsuSpriteText username; private LinkFlowContainer contentFlow; @@ -104,9 +106,9 @@ namespace osu.Game.Overlays.Chat } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, ChatOverlay chat) + private void load(OsuColour colours, ChatManager chatManager) { - this.chat = chat; + this.chatManager = chatManager; customUsernameColour = colours.ChatBlue; } @@ -215,8 +217,6 @@ namespace osu.Game.Overlays.Chat FinishTransforms(true); } - private ChatOverlay chat; - private void updateMessageContent() { this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint); @@ -226,7 +226,7 @@ namespace osu.Game.Overlays.Chat username.Text = $@"{message.Sender.Username}" + (senderHasBackground || message.IsAction ? "" : ":"); // remove non-existent channels from the link list - message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chat?.AvailableChannels.Any(c => c.Name == link.Argument) != true); + message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chatManager?.AvailableChannels.Any(c => c.Name == link.Argument) != true); contentFlow.Clear(); contentFlow.AddLinks(message.DisplayContent, message.Links); diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChat.cs similarity index 80% rename from osu.Game/Overlays/Chat/DrawableChannel.cs rename to osu.Game/Overlays/Chat/DrawableChat.cs index ac41b2f157..0efcf1ac00 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChat.cs @@ -15,15 +15,15 @@ using osu.Game.Online.Chat; namespace osu.Game.Overlays.Chat { - public class DrawableChannel : Container + public class DrawableChat : Container { - public readonly ChannelChat Channel; + public readonly ChatBase Chat; private readonly ChatLineContainer flow; private readonly ScrollContainer scroll; - public DrawableChannel(ChannelChat channel) + public DrawableChat(ChatBase chat) { - Channel = channel; + Chat = chat; RelativeSizeAxes = Axes.Both; @@ -50,15 +50,15 @@ namespace osu.Game.Overlays.Chat } }; - Channel.NewMessagesArrived += newMessagesArrived; - Channel.MessageRemoved += messageRemoved; - Channel.PendingMessageResolved += pendingMessageResolved; + Chat.NewMessagesArrived += newMessagesArrived; + Chat.MessageRemoved += messageRemoved; + Chat.PendingMessageResolved += pendingMessageResolved; } [BackgroundDependencyLoader] private void load() { - newMessagesArrived(Channel.Messages); + newMessagesArrived(Chat.Messages); } protected override void LoadComplete() @@ -71,15 +71,15 @@ namespace osu.Game.Overlays.Chat { base.Dispose(isDisposing); - Channel.NewMessagesArrived -= newMessagesArrived; - Channel.MessageRemoved -= messageRemoved; - Channel.PendingMessageResolved -= pendingMessageResolved; + Chat.NewMessagesArrived -= newMessagesArrived; + Chat.MessageRemoved -= messageRemoved; + Chat.PendingMessageResolved -= pendingMessageResolved; } private void newMessagesArrived(IEnumerable newMessages) { - // Add up to last Channel.MAX_HISTORY messages - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - ChannelChat.MAX_HISTORY)); + // Add up to last ChatBase.MAX_HISTORY messages + var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - ChatBase.MAX_HISTORY)); flow.AddRange(displayMessages.Select(m => new ChatLine(m))); @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Chat scrollToEnd(); var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); - int count = staleMessages.Length - ChannelChat.MAX_HISTORY; + int count = staleMessages.Length - ChatBase.MAX_HISTORY; for (int i = 0; i < count; i++) { diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8b3031b9e7..855a631f6b 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -1,10 +1,9 @@ // 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.Collections.Specialized; using System.Diagnostics; -using System.Linq; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; @@ -16,41 +15,36 @@ using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.MathUtils; -using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; namespace osu.Game.Overlays { - public class ChatOverlay : OsuFocusedOverlayContainer, IOnlineComponent + public class ChatOverlay : OsuFocusedOverlayContainer { private const float textbox_height = 60; private const float channel_selection_min_height = 0.3f; - private ScheduledDelegate messageRequest; + private ChatManager chatManager; - private readonly Container currentChannelContainer; + private readonly Container currentChannelContainer; + private readonly List loadedChannels = new List(); private readonly LoadingAnimation loading; private readonly FocusedTextBox textbox; - private APIAccess api; - private const int transition_length = 500; public const float DEFAULT_HEIGHT = 0.4f; public const float TAB_AREA_HEIGHT = 50; - private GetChannelMessagesRequest fetchReq; - private readonly ChatTabControl channelTabs; private readonly Container chatContainer; @@ -60,10 +54,10 @@ namespace osu.Game.Overlays public Bindable ChatHeight { get; set; } - public List AvailableChannels { get; private set; } = new List(); private readonly Container channelSelectionContainer; private readonly ChannelSelectionOverlay channelSelection; + public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceiveMouseInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceiveMouseInputAt(screenSpacePos); public ChatOverlay() @@ -110,7 +104,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, }, - currentChannelContainer = new Container + currentChannelContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -163,7 +157,7 @@ namespace osu.Game.Overlays channelTabs = new ChatTabControl { RelativeSizeAxes = Axes.Both, - OnRequestLeave = removeChannel, + OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel), }, } }, @@ -171,7 +165,7 @@ namespace osu.Game.Overlays }, }; - channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel; + channelTabs.Current.ValueChanged += newChannel => chatManager.CurrentChat.Value = newChannel; channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; channelSelection.StateChanged += state => { @@ -186,13 +180,97 @@ namespace osu.Game.Overlays else textbox.HoldFocus = true; }; + channelSelection.OnRequestJoin = channel => + { + if (!chatManager.JoinedChannels.Contains(channel)) + chatManager.JoinedChannels.Add(channel); + }; + channelSelection.OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel); + } + + private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) + { + channelSelection.Sections = new[] + { + new ChannelSection + { + Header = "All Channels", + Channels = chatManager.AvailableChannels, + }, + }; + } + + private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) + { + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (ChannelChat newChannel in args.NewItems) + { + channelTabs.AddItem(newChannel); + newChannel.Joined.Value = true; + if (chatManager.CurrentChat.Value == null) + { + chatManager.CurrentChat.Value = newChannel; + } + + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (ChannelChat removedChannel in args.OldItems) + { + channelTabs.RemoveItem(removedChannel); + loadedChannels.Remove(loadedChannels.Find(c => c.Chat == removedChannel )); + removedChannel.Joined.Value = false; + if (chatManager.CurrentChat.Value == removedChannel) + chatManager.CurrentChat.Value = null; + } + break; + } + } + + private void currentChatChanged(ChatBase chat) + { + if (chat == null) + { + textbox.Current.Disabled = true; + currentChannelContainer.Clear(false); + return; + } + + textbox.Current.Disabled = chat.ReadOnly; + + if (chat is ChannelChat channelChat) + channelTabs.Current.Value = channelChat; + + var loaded = loadedChannels.Find(d => d.Chat == chat); + if (loaded == null) + { + currentChannelContainer.FadeOut(500, Easing.OutQuint); + loading.Show(); + + loaded = new DrawableChat(chat); + loadedChannels.Add(loaded); + LoadComponentAsync(loaded, l => + { + loading.Hide(); + + + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + currentChannelContainer.FadeIn(500, Easing.OutQuint); + }); + } + else + { + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + } } private double startDragChatHeight; private bool isDragging; - public void OpenChannel(ChannelChat channel) => addChannel(channel); - protected override bool OnDragStart(InputState state) { isDragging = tabsArea.IsHovered; @@ -229,19 +307,6 @@ namespace osu.Game.Overlays return base.OnDragEnd(state); } - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - case APIState.Online: - initializeChannels(); - break; - default: - messageRequest?.Cancel(); - break; - } - } - public override bool AcceptsFocus => true; protected override void OnFocus(InputState state) @@ -270,10 +335,9 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, OsuConfigManager config, OsuColour colours) + private void load(APIAccess api, OsuConfigManager config, OsuColour colours, ChatManager chatManager) { - this.api = api; - api.Register(this); + api.Register(chatManager); ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); ChatHeight.ValueChanged += h => @@ -285,253 +349,27 @@ namespace osu.Game.Overlays ChatHeight.TriggerChange(); chatBackground.Colour = colours.ChatBlue; - } - - private long? lastMessageId; - - private readonly List careChannels = new List(); - - private readonly List loadedChannels = new List(); - - private void initializeChannels() - { loading.Show(); - messageRequest?.Cancel(); - - ListChannelsRequest req = new ListChannelsRequest(); - req.Success += delegate (List channels) - { - AvailableChannels = channels; - - Scheduler.Add(delegate - { - addChannel(channels.Find(c => c.Name == @"#lazer")); - addChannel(channels.Find(c => c.Name == @"#osu")); - addChannel(channels.Find(c => c.Name == @"#lobby")); - - channelSelection.OnRequestJoin = addChannel; - channelSelection.OnRequestLeave = removeChannel; - channelSelection.Sections = new[] - { - new ChannelSection - { - Header = "All Channels", - Channels = channels, - }, - }; - }); - - messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); - }; - - api.Queue(req); - } - - private ChannelChat currentChannel; - - protected ChannelChat CurrentChannel - { - get - { - return currentChannel; - } - - set - { - if (currentChannel == value) return; - - if (value == null) - { - currentChannel = null; - textbox.Current.Disabled = true; - currentChannelContainer.Clear(false); - return; - } - - currentChannel = value; - - textbox.Current.Disabled = currentChannel.ReadOnly; - channelTabs.Current.Value = value; - - var loaded = loadedChannels.Find(d => d.Channel == value); - if (loaded == null) - { - currentChannelContainer.FadeOut(500, Easing.OutQuint); - loading.Show(); - - loaded = new DrawableChannel(currentChannel); - loadedChannels.Add(loaded); - LoadComponentAsync(loaded, l => - { - if (currentChannel.Messages.Any()) - loading.Hide(); - - currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); - currentChannelContainer.FadeIn(500, Easing.OutQuint); - }); - } - else - { - currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); - } - } - } - - private void addChannel(ChannelChat channel) - { - if (channel == null) return; - - // ReSharper disable once AccessToModifiedClosure - var existing = careChannels.Find(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 - { - careChannels.Add(channel); - channelTabs.AddItem(channel); - } - - // let's fetch a small number of messages to bring us up-to-date with the backlog. - fetchInitialMessages(channel); - - if (CurrentChannel == null) - CurrentChannel = channel; - - channel.Joined.Value = true; - } - - private void removeChannel(ChannelChat channel) - { - if (channel == null) return; - - if (channel == CurrentChannel) CurrentChannel = null; - - careChannels.Remove(channel); - loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); - channelTabs.RemoveItem(channel); - - channel.Joined.Value = false; - } - - private void fetchInitialMessages(ChannelChat channel) - { - var req = new GetChannelMessagesRequest(new List { channel }, null); - - req.Success += delegate (List messages) - { - loading.Hide(); - channel.AddNewMessages(messages.ToArray()); - Debug.Write("success!"); - }; - req.Failure += delegate - { - Debug.Write("failure!"); - }; - - api.Queue(req); - } - - private void fetchNewMessages() - { - if (fetchReq != null) return; - - fetchReq = new GetChannelMessagesRequest(careChannels, lastMessageId); - - fetchReq.Success += delegate (List messages) - { - foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId)) - careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); - - lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId; - - Debug.Write("success!"); - fetchReq = null; - }; - - fetchReq.Failure += delegate - { - Debug.Write("failure!"); - fetchReq = null; - }; - - api.Queue(fetchReq); + this.chatManager = chatManager; + chatManager.CurrentChat.ValueChanged += currentChatChanged; + chatManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; + chatManager.AvailableChannels.CollectionChanged += availableChannelsChanged; } private void postMessage(TextBox textbox, bool newText) { - var postText = textbox.Text; + var text = textbox.Text.Trim(); + + if (string.IsNullOrWhiteSpace(text)) + return; + + if (text[0] == '/') + chatManager.PostCommand(text.Substring(1)); + else + chatManager.PostMessage(text); textbox.Text = string.Empty; - - if (string.IsNullOrWhiteSpace(postText)) - return; - - var target = currentChannel; - - if (target == null) return; - - if (!api.IsLoggedIn) - { - target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); - return; - } - - bool isAction = false; - - if (postText[0] == '/') - { - string[] parameters = postText.Substring(1).Split(new[] { ' ' }, 2); - string command = parameters[0]; - string content = parameters.Length == 2 ? parameters[1] : string.Empty; - - switch (command) - { - case "me": - - if (string.IsNullOrWhiteSpace(content)) - { - currentChannel.AddNewMessages(new ErrorMessage("Usage: /me [action]")); - return; - } - - isAction = true; - postText = content; - break; - - case "help": - currentChannel.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); - return; - - default: - currentChannel.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); - return; - } - } - - var message = new LocalEchoMessage - { - Sender = api.LocalUser.Value, - Timestamp = DateTimeOffset.Now, - TargetType = TargetType.Channel, //TODO: read this from channel - TargetId = target.Id, - IsAction = isAction, - Content = postText - }; - - var req = new PostMessageRequest(message); - - target.AddLocalEcho(message); - req.Failure += e => target.ReplaceMessage(message, null); - req.Success += m => target.ReplaceMessage(message, m); - - api.Queue(req); } private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None) From a48ccb56038b0daf80fa76abd634b6b1915b10e9 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 22:12:57 +0200 Subject: [PATCH 05/88] Implement Private chat --- .../API/Requests/GetUserMessagesRequest.cs | 30 +++ osu.Game/Online/Chat/ChatManager.cs | 79 ++++++- osu.Game/Online/Chat/UserChat.cs | 23 ++ osu.Game/OsuGame.cs | 1 - ...ChatTabControl.cs => ChannelTabControl.cs} | 11 +- .../Overlays/Chat/ChatTabItemCloseButton.cs | 55 +++++ osu.Game/Overlays/Chat/UserChatTabControl.cs | 51 +++++ osu.Game/Overlays/Chat/UserChatTabItem.cs | 201 ++++++++++++++++++ osu.Game/Overlays/ChatOverlay.cs | 37 +++- 9 files changed, 480 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetUserMessagesRequest.cs create mode 100644 osu.Game/Online/Chat/UserChat.cs rename osu.Game/Overlays/Chat/{ChatTabControl.cs => ChannelTabControl.cs} (95%) create mode 100644 osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs create mode 100644 osu.Game/Overlays/Chat/UserChatTabControl.cs create mode 100644 osu.Game/Overlays/Chat/UserChatTabItem.cs diff --git a/osu.Game/Online/API/Requests/GetUserMessagesRequest.cs b/osu.Game/Online/API/Requests/GetUserMessagesRequest.cs new file mode 100644 index 0000000000..ef9871c5d2 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserMessagesRequest.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserMessagesRequest : APIRequest> + { + private long? since; + + public GetUserMessagesRequest(long? sinceId = null) + { + since = sinceId; + } + + protected override WebRequest CreateWebRequest() + { + var request = base.CreateWebRequest(); + if (since.HasValue) + request.AddParameter(@"since", since.Value.ToString()); + + return request; + } + + protected override string Target => @"chat/messages/private"; + } +} diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index 69620c8f53..f8c1e53ad8 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -11,6 +11,7 @@ using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Users; namespace osu.Game.Online.Chat { @@ -39,12 +40,18 @@ namespace osu.Game.Online.Chat /// The channels available for the player to join /// public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); + /// + /// The user chats opened. + /// + public ObservableCollection OpenedUserChats { get; } = new ObservableCollection(); private APIAccess api; private readonly Scheduler scheduler; private ScheduledDelegate fetchMessagesScheduleder; private GetChannelMessagesRequest fetchChannelMsgReq; + private GetUserMessagesRequest fetchUserMsgReq; private long? lastChannelMsgId; + private long? lastUserMsgId; public ChatManager(Scheduler scheduler) { @@ -55,8 +62,7 @@ namespace osu.Game.Online.Chat private void currentChatChanged(ChatBase chatBase) { if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) - JoinedChannels.Add(channel); - + JoinedChannels.Add(channel); } /// @@ -127,6 +133,63 @@ namespace osu.Game.Online.Chat { if (fetchChannelMsgReq == null) fetchNewChannelMessages(); + + if (fetchUserMsgReq == null) + fetchNewUserMessages(); + } + + private void fetchNewUserMessages() + { + fetchUserMsgReq = new GetUserMessagesRequest(lastUserMsgId); + + fetchUserMsgReq.Success += messages => + { + handleUserMessages(messages); + lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; + fetchUserMsgReq = null; + }; + fetchUserMsgReq.Failure += exception => Logger.Error(exception, "Fetching user messages failed."); + + api.Queue(fetchUserMsgReq); + } + + private void handleUserMessages(IEnumerable messages) + { + var outgoingMessages = messages.Where(m => m.Sender.Id == api.LocalUser.Value.Id); + var outgoingMessagesGroups = outgoingMessages.GroupBy(m => m.TargetId); + var incomingMessagesGroups = messages.Except(outgoingMessages).GroupBy(m => m.UserId); + + foreach (var messageGroup in incomingMessagesGroups) + { + var targetUser = messageGroup.First().Sender; + var chat = OpenedUserChats.FirstOrDefault(c => c.User.Id == targetUser.Id); + + if (chat == null) + { + chat = new UserChat(targetUser); + OpenedUserChats.Add(chat); + } + + chat.AddNewMessages(messageGroup.ToArray()); + var outgoingTargetMessages = outgoingMessagesGroups.FirstOrDefault(g => g.Key == targetUser.Id); + chat.AddNewMessages(outgoingTargetMessages.ToArray()); + } + + var withoutReplyGroups = outgoingMessagesGroups.Where(g => OpenedUserChats.All(m => m.ChatID != g.Key)); + + foreach (var withoutReplyGroup in withoutReplyGroups) + { + var getUserRequest = new GetUserRequest(withoutReplyGroup.First().TargetId); + getUserRequest.Success += user => + { + var chat = new UserChat(user); + + chat.AddNewMessages(withoutReplyGroup.ToArray()); + OpenedUserChats.Add(chat); + }; + + api.Queue(getUserRequest); + } } private void fetchNewChannelMessages() @@ -135,6 +198,8 @@ namespace osu.Game.Online.Chat fetchChannelMsgReq.Success += messages => { + if (messages == null) + return; handleChannelMessages(messages); lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; fetchChannelMsgReq = null; @@ -163,7 +228,13 @@ namespace osu.Game.Online.Chat channels.Where(channel => defaultChannels.Contains(channel.Name)) .Where(channel => JoinedChannels.All(c => c.ChatID != channel.ChatID)) - .ForEach(channel => JoinedChannels.Add(channel)); + .ForEach(channel => + { + JoinedChannels.Add(channel); + var fetchInitialMsgReq = new GetChannelMessagesRequest(new[] {channel}, null); + fetchInitialMsgReq.Success += handleChannelMessages; + api.Queue(fetchInitialMsgReq); + }); fetchNewMessages(); }; @@ -185,7 +256,9 @@ namespace osu.Game.Online.Chat break; default: fetchChannelMsgReq?.Cancel(); + fetchChannelMsgReq = null; fetchMessagesScheduleder?.Cancel(); + break; } } diff --git a/osu.Game/Online/Chat/UserChat.cs b/osu.Game/Online/Chat/UserChat.cs new file mode 100644 index 0000000000..2cbb38dad8 --- /dev/null +++ b/osu.Game/Online/Chat/UserChat.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Users; + +namespace osu.Game.Online.Chat +{ + public class UserChat : ChatBase + { + public User User { get; } + + public UserChat(User user, Message[] messages = null) + { + User = user ?? throw new ArgumentNullException(nameof(user)); + + if (messages != null) AddNewMessages(messages); + } + + public override TargetType Target => TargetType.User; + public override long ChatID => User.Id; + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1b55418c7b..cc942a1e32 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -29,7 +29,6 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; using osu.Game.Input.Bindings; -using osu.Game.Online.Chat; using osu.Game.Rulesets.Mods; using osu.Game.Skinning; using OpenTK.Graphics; diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChannelTabControl.cs similarity index 95% rename from osu.Game/Overlays/Chat/ChatTabControl.cs rename to osu.Game/Overlays/Chat/ChannelTabControl.cs index e495faf944..bf15aa51e9 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChannelTabControl.cs @@ -21,7 +21,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Chat { - public class ChatTabControl : OsuTabControl + public class ChannelTabControl : OsuTabControl { private const float shear_width = 10; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Chat private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; - public ChatTabControl() + public ChannelTabControl() { TabContainer.Margin = new MarginPadding { Left = 50 }; TabContainer.Spacing = new Vector2(-shear_width, 0); @@ -51,6 +51,13 @@ namespace osu.Game.Overlays.Chat ChannelSelectorActive.BindTo(selectorTab.Active); } + public void DeselectAll() + { + if (SelectedTab != null) + SelectedTab.Active.Value = false; + SelectedTab = null; + } + protected override void AddTabItem(TabItem item, bool addToDropdown = true) { if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) diff --git a/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs b/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs new file mode 100644 index 0000000000..e87396356a --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTabItemCloseButton : OsuClickableContainer + { + private readonly SpriteIcon icon; + + public ChatTabItemCloseButton() + { + Size = new Vector2(20); + + Child = icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.75f), + Icon = FontAwesome.fa_close, + RelativeSizeAxes = Axes.Both, + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.5f, 1000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(0.75f, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnHover(InputState state) + { + icon.FadeColour(Color4.Red, 200, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + icon.FadeColour(Color4.White, 200, Easing.OutQuint); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Chat/UserChatTabControl.cs b/osu.Game/Overlays/Chat/UserChatTabControl.cs new file mode 100644 index 0000000000..73dee8f714 --- /dev/null +++ b/osu.Game/Overlays/Chat/UserChatTabControl.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; +using OpenTK; + +namespace osu.Game.Overlays.Chat +{ + public class UserChatTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(UserChat value) => new UserChatTabItem(value) { OnRequestClose = tabCloseRequested }; + + public Action OnRequestLeave; + + public UserChatTabControl() + { + TabContainer.Spacing = new Vector2(-10, 0); + TabContainer.Masking = false; + } + + protected override void AddTabItem(TabItem item, bool addToDropdown = true) + { + base.AddTabItem(item, addToDropdown); + + if (SelectedTab == null) + SelectTab(item); + } + + private void tabCloseRequested(TabItem priv) + { + int totalTabs = TabContainer.Count -1; // account for selectorTab + int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(priv), 1, totalTabs); + + if (priv == SelectedTab && totalTabs > 1) + // Select the tab after tab-to-be-removed's index, or the tab before if current == last + SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); + + OnRequestLeave?.Invoke(priv.Value); + } + + public void DeselectAll() + { + if (SelectedTab != null) + SelectedTab.Active.Value = false; + SelectedTab = null; + } + } +} diff --git a/osu.Game/Overlays/Chat/UserChatTabItem.cs b/osu.Game/Overlays/Chat/UserChatTabItem.cs new file mode 100644 index 0000000000..1426a1ac32 --- /dev/null +++ b/osu.Game/Overlays/Chat/UserChatTabItem.cs @@ -0,0 +1,201 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osu.Game.Screens.Menu; +using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Chat +{ + public class UserChatTabItem : TabItem + { + private static readonly Vector2 shear = new Vector2(1f / 5f, 0); + + public override bool IsRemovable => true; + + private readonly Box highlightBox; + private readonly Container backgroundContainer; + private readonly Box backgroundBox; + private readonly OsuSpriteText username; + private readonly ChatTabItemCloseButton closeButton; + + public UserChatTabItem(UserChat value) + : base(value) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + Origin = Anchor.BottomRight; + Anchor = Anchor.BottomRight; + EdgeEffect = deactivateEdgeEffect; + Masking = false; + Shear = shear; + + Children = new Drawable[] + { + new Container() + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + backgroundBox = new Box + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + EdgeSmoothness = new Vector2(1, 0), + }, + } + }, + highlightBox = new Box + { + Width = 5, + BypassAutoSizeAxes = Axes.X, + Alpha = 0, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Y, + Colour = new OsuColour().Yellow + }, + new Container + { + Masking = true, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Child = new FlowContainerWithOrigin + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + X = -5, + Direction = FillDirection.Horizontal, + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Shear = -shear, + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding + { + Horizontal = 5 + }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Children = new Drawable[] + { + + new SpriteIcon + { + Icon = FontAwesome.fa_eercast, + Origin = Anchor.Centre, + Scale = new Vector2(1.2f), + X = -5, + Y = 5, + Anchor = Anchor.Centre, + Colour = new OsuColour().BlueDarker, + RelativeSizeAxes = Axes.Both, + }, + new CircularContainer + { + RelativeSizeAxes = Axes.Y, + Scale = new Vector2(0.95f), + AutoSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Child = new Avatar(value.User) + { + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + } + }, + } + }, + username = new OsuSpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.User.Username, + Margin = new MarginPadding(1), + TextSize = 18, + }, + closeButton = new ChatTabItemCloseButton + { + Height = 1, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Y, + + Action = delegate + { + if (IsRemovable) OnRequestClose?.Invoke(this); + }, + }, + } + } + } + }; + } + + public Action OnRequestClose; + + private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 30, + Colour = Color4.Black.Opacity(0.3f), + }; + + protected override void OnActivated() + { + const int activate_length = 1000; + + backgroundBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); + highlightBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); + highlightBox.FadeIn(activate_length, Easing.OutQuint); + username.FadeIn(activate_length, Easing.OutQuint); + username.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); + closeButton.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); + closeButton.FadeIn(activate_length, Easing.OutQuint); + TweenEdgeEffectTo(activateEdgeEffect, activate_length); + } + + private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.0f), + }; + + protected override void OnDeactivated() + { + const int deactivate_length = 500; + + backgroundBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); + highlightBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); + highlightBox.FadeOut(deactivate_length, Easing.OutQuint); + username.FadeOut(deactivate_length, Easing.OutQuint); + username.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); + closeButton.FadeOut(deactivate_length, Easing.OutQuint); + closeButton.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); + TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + backgroundBox.Colour = Value.User.Colour != null ? OsuColour.FromHex(Value.User.Colour) : colours.BlueDark; + } + } +} diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 855a631f6b..251e4a2be0 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -45,7 +45,8 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private readonly ChatTabControl channelTabs; + private readonly ChannelTabControl channelTabs; + private readonly UserChatTabControl userTabs; private readonly Container chatContainer; private readonly Container tabsArea; @@ -154,17 +155,23 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - channelTabs = new ChatTabControl + channelTabs = new ChannelTabControl { RelativeSizeAxes = Axes.Both, OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel), }, + userTabs = new UserChatTabControl + { + RelativeSizeAxes = Axes.Both, + OnRequestLeave = privateChat => chatManager.OpenedUserChats.Remove(privateChat), + } } }, }, }, }; + userTabs.Current.ValueChanged += user => chatManager.CurrentChat.Value = user; channelTabs.Current.ValueChanged += newChannel => chatManager.CurrentChat.Value = newChannel; channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; channelSelection.StateChanged += state => @@ -241,7 +248,16 @@ namespace osu.Game.Overlays textbox.Current.Disabled = chat.ReadOnly; if (chat is ChannelChat channelChat) + { channelTabs.Current.Value = channelChat; + userTabs.DeselectAll(); + } + + if (chat is UserChat userChat) + { + userTabs.Current.Value = userChat; + channelTabs.DeselectAll(); + } var loaded = loadedChannels.Find(d => d.Chat == chat); if (loaded == null) @@ -355,6 +371,23 @@ namespace osu.Game.Overlays chatManager.CurrentChat.ValueChanged += currentChatChanged; chatManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; chatManager.AvailableChannels.CollectionChanged += availableChannelsChanged; + chatManager.OpenedUserChats.CollectionChanged += openedUserChatsChanged; + } + + private void openedUserChatsChanged(object sender, NotifyCollectionChangedEventArgs args) + { + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + userTabs.AddItem(args.NewItems[0] as UserChat); + break; + case NotifyCollectionChangedAction.Remove: + userTabs.RemoveItem(args.OldItems[0] as UserChat); + break; + case NotifyCollectionChangedAction.Reset: + userTabs.Clear(); + break; + } } private void postMessage(TextBox textbox, bool newText) From 4b7ffd09d92b1829add2ba32e35ac49de43b7374 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 22:18:29 +0200 Subject: [PATCH 06/88] Trim whitespace --- osu.Game/Online/Chat/ChatManager.cs | 2 +- osu.Game/Overlays/Chat/UserChatTabItem.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index f8c1e53ad8..4001bbcf6f 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Chat private void currentChatChanged(ChatBase chatBase) { - if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) + if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) JoinedChannels.Add(channel); } diff --git a/osu.Game/Overlays/Chat/UserChatTabItem.cs b/osu.Game/Overlays/Chat/UserChatTabItem.cs index 1426a1ac32..4169f5a77e 100644 --- a/osu.Game/Overlays/Chat/UserChatTabItem.cs +++ b/osu.Game/Overlays/Chat/UserChatTabItem.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Chat private readonly Box backgroundBox; private readonly OsuSpriteText username; private readonly ChatTabItemCloseButton closeButton; - + public UserChatTabItem(UserChat value) : base(value) { diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 251e4a2be0..273a2ea926 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -220,7 +220,7 @@ namespace osu.Game.Overlays { chatManager.CurrentChat.Value = newChannel; } - + } break; case NotifyCollectionChangedAction.Remove: From 0a207e00d52f1068cd841d37f100152e0b047295 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 22:24:55 +0200 Subject: [PATCH 07/88] Trim whilespace --- osu.Game/Online/Chat/ChatManager.cs | 2 +- osu.Game/Overlays/Chat/UserChatTabItem.cs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index 4001bbcf6f..9657ca3d96 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -62,7 +62,7 @@ namespace osu.Game.Online.Chat private void currentChatChanged(ChatBase chatBase) { if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) - JoinedChannels.Add(channel); + JoinedChannels.Add(channel); } /// diff --git a/osu.Game/Overlays/Chat/UserChatTabItem.cs b/osu.Game/Overlays/Chat/UserChatTabItem.cs index 4169f5a77e..8e6d06db46 100644 --- a/osu.Game/Overlays/Chat/UserChatTabItem.cs +++ b/osu.Game/Overlays/Chat/UserChatTabItem.cs @@ -40,7 +40,6 @@ namespace osu.Game.Overlays.Chat EdgeEffect = deactivateEdgeEffect; Masking = false; Shear = shear; - Children = new Drawable[] { new Container() @@ -82,7 +81,6 @@ namespace osu.Game.Overlays.Chat Origin = Anchor.TopLeft, Anchor = Anchor.TopLeft, Shear = -shear, - Children = new Drawable[] { new Container @@ -97,7 +95,6 @@ namespace osu.Game.Overlays.Chat Anchor = Anchor.BottomLeft, Children = new Drawable[] { - new SpriteIcon { Icon = FontAwesome.fa_eercast, @@ -138,13 +135,12 @@ namespace osu.Game.Overlays.Chat Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, RelativeSizeAxes = Axes.Y, - Action = delegate { if (IsRemovable) OnRequestClose?.Invoke(this); }, }, - } + } } } }; From 2a314f052a15410e5c3f8665eb2767d7d5764314 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 22:28:50 +0200 Subject: [PATCH 08/88] Trim more whitespace --- osu.Game/Online/Chat/ChatManager.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index 9657ca3d96..210a6cd43b 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -222,7 +222,7 @@ namespace osu.Game.Online.Chat var req = new ListChannelsRequest(); req.Success += channels => - { + { channels.Where(channel => AvailableChannels.All(c => c.ChatID != channel.ChatID)) .ForEach(channel => AvailableChannels.Add(channel)); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 273a2ea926..bc6ec72e14 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -225,7 +225,7 @@ namespace osu.Game.Overlays break; case NotifyCollectionChangedAction.Remove: foreach (ChannelChat removedChannel in args.OldItems) - { + { channelTabs.RemoveItem(removedChannel); loadedChannels.Remove(loadedChannels.Find(c => c.Chat == removedChannel )); removedChannel.Joined.Value = false; @@ -371,7 +371,7 @@ namespace osu.Game.Overlays chatManager.CurrentChat.ValueChanged += currentChatChanged; chatManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; chatManager.AvailableChannels.CollectionChanged += availableChannelsChanged; - chatManager.OpenedUserChats.CollectionChanged += openedUserChatsChanged; + chatManager.OpenedUserChats.CollectionChanged += openedUserChatsChanged; } private void openedUserChatsChanged(object sender, NotifyCollectionChangedEventArgs args) From aa26ea483d557287c2c6d9eddc4bbbf9fd25d801 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 8 Apr 2018 22:43:01 +0200 Subject: [PATCH 09/88] remove using, remove empty agrument list --- osu.Game/Online/Chat/ChatManager.cs | 1 - osu.Game/Overlays/Chat/UserChatTabItem.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index 210a6cd43b..dc7b8d129a 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -11,7 +11,6 @@ using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Users; namespace osu.Game.Online.Chat { diff --git a/osu.Game/Overlays/Chat/UserChatTabItem.cs b/osu.Game/Overlays/Chat/UserChatTabItem.cs index 8e6d06db46..9886a2ba3d 100644 --- a/osu.Game/Overlays/Chat/UserChatTabItem.cs +++ b/osu.Game/Overlays/Chat/UserChatTabItem.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Chat Shear = shear; Children = new Drawable[] { - new Container() + new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] From 5512d58c1d33b9b9dcb3e89a6eb3f0772330df51 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Apr 2018 21:06:05 +0200 Subject: [PATCH 10/88] Remove the sealed modifier --- osu.Game/Online/Chat/ChatManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index dc7b8d129a..50895287a8 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat /// /// Manages everything chat related /// - public sealed class ChatManager : IOnlineComponent + public class ChatManager : IOnlineComponent { /// /// The channels the player joins on startup From 96bacaf13fae358d4d28b7702407faf89060c001 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Apr 2018 21:06:28 +0200 Subject: [PATCH 11/88] Allow the ChatLinkTestcase to work again --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 4f85779bce..6f57312d84 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -54,12 +54,14 @@ namespace osu.Game.Tests.Visual private void load(OsuColour colours, IAPIProvider api) { linkColour = colours.Blue; - dependencies.Cache(new ChatOverlay()); var chatManager = new ChatManager(Scheduler); - api.Register(chatManager); + chatManager.AvailableChannels.Add(new ChannelChat { Name = "#english"}); + chatManager.AvailableChannels.Add(new ChannelChat { Name = "#japanese" }); dependencies.Cache(chatManager); + dependencies.Cache(new ChatOverlay()); + testLinksGeneral(); testEcho(); } From 762b4412e5ed98f1eacfd26b49540cea51a711b1 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Apr 2018 22:29:48 +0200 Subject: [PATCH 12/88] Convert if to switch --- osu.Game/Overlays/ChatOverlay.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index bc6ec72e14..0935293058 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -247,16 +247,16 @@ namespace osu.Game.Overlays textbox.Current.Disabled = chat.ReadOnly; - if (chat is ChannelChat channelChat) + switch (chat) { - channelTabs.Current.Value = channelChat; - userTabs.DeselectAll(); - } - - if (chat is UserChat userChat) - { - userTabs.Current.Value = userChat; - channelTabs.DeselectAll(); + case ChannelChat channelChat: + channelTabs.Current.Value = channelChat; + userTabs.DeselectAll(); + break; + case UserChat userChat: + userTabs.Current.Value = userChat; + channelTabs.DeselectAll(); + break; } var loaded = loadedChannels.Find(d => d.Chat == chat); From 85f736ae893ab434d7c11ce224c2aa5b97bf19a5 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Apr 2018 23:14:39 +0200 Subject: [PATCH 13/88] Allow opening a new chat with right click on User Allow faster viewing of the usertab using lasy loading --- osu.Game/Online/Chat/ChatManager.cs | 56 ++++++++++++++++++----- osu.Game/Online/Chat/UserChat.cs | 25 ++++++++-- osu.Game/Overlays/Chat/ChatLine.cs | 7 ++- osu.Game/Overlays/Chat/UserChatTabItem.cs | 12 ++++- osu.Game/Overlays/ChatOverlay.cs | 29 ++++++------ 5 files changed, 96 insertions(+), 33 deletions(-) diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index 50895287a8..ba11ff9019 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -11,6 +11,7 @@ using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Users; namespace osu.Game.Online.Chat { @@ -52,6 +53,40 @@ namespace osu.Game.Online.Chat private long? lastChannelMsgId; private long? lastUserMsgId; + public void OpenChannelChat(string name) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + CurrentChat.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) + ?? throw new ArgumentException($"Channel {name} was not found."); + } + + public void OpenUserChat(long userId) + { + var chat = OpenedUserChats.FirstOrDefault(c => c.ChatID == userId); + + if (chat == null) + { + chat = new UserChat(new User + { + Id = userId + }); + chat.RequestDetails(api); + } + + CurrentChat.Value = chat; + } + + public void OpenUserChat(User user) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + + CurrentChat.Value = OpenedUserChats.FirstOrDefault(c => c.ChatID == user.Id) + ?? new UserChat(user); + } + public ChatManager(Scheduler scheduler) { this.scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); @@ -62,6 +97,9 @@ namespace osu.Game.Online.Chat { if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) JoinedChannels.Add(channel); + + if (chatBase is UserChat userChat && !OpenedUserChats.Contains(userChat)) + OpenedUserChats.Add(userChat); } /// @@ -171,23 +209,19 @@ namespace osu.Game.Online.Chat chat.AddNewMessages(messageGroup.ToArray()); var outgoingTargetMessages = outgoingMessagesGroups.FirstOrDefault(g => g.Key == targetUser.Id); - chat.AddNewMessages(outgoingTargetMessages.ToArray()); + if (outgoingTargetMessages != null) + chat.AddNewMessages(outgoingTargetMessages.ToArray()); } var withoutReplyGroups = outgoingMessagesGroups.Where(g => OpenedUserChats.All(m => m.ChatID != g.Key)); foreach (var withoutReplyGroup in withoutReplyGroups) - { - var getUserRequest = new GetUserRequest(withoutReplyGroup.First().TargetId); - getUserRequest.Success += user => - { - var chat = new UserChat(user); + { + var chat = new UserChat(new User {Id = withoutReplyGroup.First().TargetId }); - chat.AddNewMessages(withoutReplyGroup.ToArray()); - OpenedUserChats.Add(chat); - }; - - api.Queue(getUserRequest); + chat.AddNewMessages(withoutReplyGroup.ToArray()); + OpenedUserChats.Add(chat); + chat.RequestDetails(api); } } diff --git a/osu.Game/Online/Chat/UserChat.cs b/osu.Game/Online/Chat/UserChat.cs index 2cbb38dad8..77d8b53214 100644 --- a/osu.Game/Online/Chat/UserChat.cs +++ b/osu.Game/Online/Chat/UserChat.cs @@ -2,13 +2,20 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Logging; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Users; namespace osu.Game.Online.Chat { public class UserChat : ChatBase { - public User User { get; } + public User User { get; private set; } + public override TargetType Target => TargetType.User; + public override long ChatID => User.Id; + + public Action DetailsArrived; public UserChat(User user, Message[] messages = null) { @@ -17,7 +24,19 @@ namespace osu.Game.Online.Chat if (messages != null) AddNewMessages(messages); } - public override TargetType Target => TargetType.User; - public override long ChatID => User.Id; + public void RequestDetails(IAPIProvider api) + { + if (api == null) + throw new ArgumentNullException(nameof(api)); + + var req = new GetUserRequest(User.Id); + req.Success += user => + { + User = user; + DetailsArrived?.Invoke(user); + }; + req.Failure += exception => Logger.Error(exception, $"Requesting details for user with Id:{User.Id} failed."); + api.Queue(req); + } } } diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index eb1ab9ef26..19ad452943 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Linq; using OpenTK; using OpenTK.Graphics; @@ -236,20 +237,24 @@ namespace osu.Game.Overlays.Chat { private readonly User sender; + private Action startChatAction; + public MessageSender(User sender) { this.sender = sender; } [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profile) + private void load(UserProfileOverlay profile, ChatManager chatManager) { Action = () => profile?.ShowUser(sender); + startChatAction = () => chatManager?.OpenUserChat(sender); } public MenuItem[] ContextMenuItems => new MenuItem[] { new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action), + new OsuMenuItem("Start Chat", MenuItemType.Highlighted, startChatAction), }; } } diff --git a/osu.Game/Overlays/Chat/UserChatTabItem.cs b/osu.Game/Overlays/Chat/UserChatTabItem.cs index 9886a2ba3d..24a4c11784 100644 --- a/osu.Game/Overlays/Chat/UserChatTabItem.cs +++ b/osu.Game/Overlays/Chat/UserChatTabItem.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Screens.Menu; using osu.Game.Users; @@ -21,7 +22,7 @@ namespace osu.Game.Overlays.Chat public class UserChatTabItem : TabItem { private static readonly Vector2 shear = new Vector2(1f / 5f, 0); - + private readonly UserChat chat; public override bool IsRemovable => true; private readonly Box highlightBox; @@ -33,6 +34,7 @@ namespace osu.Game.Overlays.Chat public UserChatTabItem(UserChat value) : base(value) { + chat = value; AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; Origin = Anchor.BottomRight; @@ -189,9 +191,15 @@ namespace osu.Game.Overlays.Chat } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, IAPIProvider api) { backgroundBox.Colour = Value.User.Colour != null ? OsuColour.FromHex(Value.User.Colour) : colours.BlueDark; + + if (chat.User.Username == null || chat.User.Id < 1) + { + chat.DetailsArrived += arrivedUser => { Scheduler.Add(() => { username.Text = arrivedUser.Username; }); }; + chat.RequestDetails(api); + } } } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 0935293058..bcda27e022 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -217,10 +217,10 @@ namespace osu.Game.Overlays channelTabs.AddItem(newChannel); newChannel.Joined.Value = true; if (chatManager.CurrentChat.Value == null) - { chatManager.CurrentChat.Value = newChannel; - } + if (chatManager.CurrentChat.Value == newChannel) + channelTabs.Current.Value = newChannel; } break; case NotifyCollectionChangedAction.Remove: @@ -247,17 +247,8 @@ namespace osu.Game.Overlays textbox.Current.Disabled = chat.ReadOnly; - switch (chat) - { - case ChannelChat channelChat: - channelTabs.Current.Value = channelChat; - userTabs.DeselectAll(); - break; - case UserChat userChat: - userTabs.Current.Value = userChat; - channelTabs.DeselectAll(); - break; - } + userTabs.DeselectAll(); + channelTabs.DeselectAll(); var loaded = loadedChannels.Find(d => d.Chat == chat); if (loaded == null) @@ -271,7 +262,6 @@ namespace osu.Game.Overlays { loading.Hide(); - currentChannelContainer.Clear(false); currentChannelContainer.Add(loaded); currentChannelContainer.FadeIn(500, Easing.OutQuint); @@ -379,10 +369,17 @@ namespace osu.Game.Overlays switch (args.Action) { case NotifyCollectionChangedAction.Add: - userTabs.AddItem(args.NewItems[0] as UserChat); + foreach (UserChat chat in args.NewItems) + { + userTabs.AddItem(args.NewItems[0] as UserChat); + + if (chatManager.CurrentChat.Value == chat) + userTabs.Current.Value = chat; + } break; case NotifyCollectionChangedAction.Remove: - userTabs.RemoveItem(args.OldItems[0] as UserChat); + foreach (UserChat chat in args.OldItems) + userTabs.RemoveItem(chat); break; case NotifyCollectionChangedAction.Reset: userTabs.Clear(); From 39ecc3d31d007cce178a86eb4ffa5c4b9979b1a3 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 18:23:09 +0200 Subject: [PATCH 14/88] Add Test Case, improve displaying the avatar, use a chatTabControl instead of putting both in ChatOverlay, readd shadow. Requires osu-framework for a fix --- .../Visual/TestCaseChatTabControl.cs | 96 ++++++++++++++++++ osu.Game/Online/Chat/ChatManager.cs | 5 +- osu.Game/Online/Chat/UserChat.cs | 2 + osu.Game/Overlays/Chat/ChatTabControl.cs | 99 +++++++++++++++++++ osu.Game/Overlays/Chat/DrawableChat.cs | 2 +- ...serChatTabControl.cs => UserTabControl.cs} | 14 ++- .../{UserChatTabItem.cs => UserTabItem.cs} | 31 +++--- osu.Game/Overlays/ChatOverlay.cs | 69 ++++++------- 8 files changed, 263 insertions(+), 55 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseChatTabControl.cs create mode 100644 osu.Game/Overlays/Chat/ChatTabControl.cs rename osu.Game/Overlays/Chat/{UserChatTabControl.cs => UserTabControl.cs} (78%) rename osu.Game/Overlays/Chat/{UserChatTabItem.cs => UserTabItem.cs} (86%) diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs new file mode 100644 index 0000000000..0b7a66ccf2 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -0,0 +1,96 @@ + +using System; +using System.Collections.Generic; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.MathUtils; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; +using osu.Game.Users; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseChatTabControl : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ChatTabControl), + typeof(ChannelTabControl), + typeof(UserTabControl), + + }; + + private readonly ChatTabControl chatTabControl; + private readonly SpriteText currentText; + + public TestCaseChatTabControl() + { + Add(new Container + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Children = new Drawable[] + { + chatTabControl = new ChatTabControl + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Height = 50 + }, + new Box + { + Colour = Color4.Black.Opacity(0.1f), + RelativeSizeAxes = Axes.X, + Height = 50, + Depth = -1, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + } + }); + + Add(new Container() + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Children = new Drawable[] + { + currentText = new SpriteText + { + Text = "Currently selected chat: " + } + } + }); + + chatTabControl.OnRequestLeave += chat => chatTabControl.RemoveItem(chat); + chatTabControl.Current.ValueChanged += chat => currentText.Text = "Currently selected chat: " + chat.ToString(); + + AddStep("Add random user", () => addUser(RNG.Next(100000), RNG.Next().ToString())); + AddRepeatStep("3 random users", () => addUser(RNG.Next(100000), RNG.Next().ToString()), 3); + AddStep("Add random channel", () => addChannel(RNG.Next().ToString())); + } + + private void addUser(long id, string name) + { + chatTabControl.AddItem(new UserChat(new User + { + Id = id, + Username = name + })); + } + + private void addChannel(string name) + { + this.chatTabControl.AddItem(new ChannelChat + { + Name = name + }); + } + } +} diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChatManager.cs index ba11ff9019..ebfdbb3650 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChatManager.cs @@ -7,6 +7,7 @@ using System.Collections.ObjectModel; using System.Linq; using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Online.API; @@ -18,7 +19,7 @@ namespace osu.Game.Online.Chat /// /// Manages everything chat related /// - public class ChatManager : IOnlineComponent + public class ChatManager : Component, IOnlineComponent { /// /// The channels the player joins on startup @@ -217,7 +218,7 @@ namespace osu.Game.Online.Chat foreach (var withoutReplyGroup in withoutReplyGroups) { - var chat = new UserChat(new User {Id = withoutReplyGroup.First().TargetId }); + var chat = new UserChat(new User { Id = withoutReplyGroup.First().TargetId }); chat.AddNewMessages(withoutReplyGroup.ToArray()); OpenedUserChats.Add(chat); diff --git a/osu.Game/Online/Chat/UserChat.cs b/osu.Game/Online/Chat/UserChat.cs index 77d8b53214..902d4eee11 100644 --- a/osu.Game/Online/Chat/UserChat.cs +++ b/osu.Game/Online/Chat/UserChat.cs @@ -38,5 +38,7 @@ namespace osu.Game.Online.Chat req.Failure += exception => Logger.Error(exception, $"Requesting details for user with Id:{User.Id} failed."); api.Queue(req); } + + public override string ToString() => User.Username ?? User.Id.ToString(); } } diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs new file mode 100644 index 0000000000..8ec60d5e8d --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -0,0 +1,99 @@ +using System; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTabControl : Container, IHasCurrentValue + { + public readonly ChannelTabControl channelTabControl; + private readonly UserTabControl userTabControl; + + public Bindable Current { get; } = new Bindable(); + public Action OnRequestLeave; + public Action OnRequestChannelSelection; + + public ChatTabControl() + { + Masking = false; + + Children = new Drawable[] + { + channelTabControl = new ChannelTabControl + { + Width = 0.5f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + OnRequestLeave = chat => OnRequestLeave?.Invoke(chat) + }, + userTabControl = new UserTabControl + { + Width = 0.5f, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + OnRequestLeave = chat => OnRequestLeave?.Invoke(chat) + }, + }; + + Current.ValueChanged += currentTabChanged; + channelTabControl.Current.ValueChanged += chat => + { + if (chat != null) + Current.Value = chat; + }; + userTabControl.Current.ValueChanged += chat => + { + if (chat != null) + Current.Value = chat; + }; + } + + private void currentTabChanged(ChatBase tab) + { + switch (tab) + { + case UserChat userChat: + userTabControl.Current.Value = userChat; + channelTabControl.Current.Value = null; + break; + case ChannelChat channelChat: + channelTabControl.Current.Value = channelChat; + userTabControl.Current.Value = null; + break; + } + } + + public void AddItem(ChatBase chat) + { + switch (chat) + { + case UserChat userChat: + userTabControl.AddItem(userChat); + break; + case ChannelChat channelChat: + channelTabControl.AddItem(channelChat); + break; + } + } + + public void RemoveItem(ChatBase chat) + { + switch (chat) + { + case UserChat userChat: + userTabControl.RemoveItem(userChat); + Current.Value = null; + break; + case ChannelChat channelChat: + channelTabControl.RemoveItem(channelChat); + Current.Value = null; + break; + } + } + } +} diff --git a/osu.Game/Overlays/Chat/DrawableChat.cs b/osu.Game/Overlays/Chat/DrawableChat.cs index 0efcf1ac00..4ebdc4bd31 100644 --- a/osu.Game/Overlays/Chat/DrawableChat.cs +++ b/osu.Game/Overlays/Chat/DrawableChat.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Chat [BackgroundDependencyLoader] private void load() { - newMessagesArrived(Chat.Messages); + Scheduler.Add(() => newMessagesArrived(Chat.Messages)); } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Chat/UserChatTabControl.cs b/osu.Game/Overlays/Chat/UserTabControl.cs similarity index 78% rename from osu.Game/Overlays/Chat/UserChatTabControl.cs rename to osu.Game/Overlays/Chat/UserTabControl.cs index 73dee8f714..cf1caaf4df 100644 --- a/osu.Game/Overlays/Chat/UserChatTabControl.cs +++ b/osu.Game/Overlays/Chat/UserTabControl.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; @@ -9,16 +10,22 @@ using OpenTK; namespace osu.Game.Overlays.Chat { - public class UserChatTabControl : OsuTabControl + public class UserTabControl : OsuTabControl { - protected override TabItem CreateTabItem(UserChat value) => new UserChatTabItem(value) { OnRequestClose = tabCloseRequested }; + protected override TabItem CreateTabItem(UserChat value) => new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + + protected override Dropdown CreateDropdown() => null; public Action OnRequestLeave; - public UserChatTabControl() + public UserTabControl() { TabContainer.Spacing = new Vector2(-10, 0); TabContainer.Masking = false; + Margin = new MarginPadding + { + Right = 10 + }; } protected override void AddTabItem(TabItem item, bool addToDropdown = true) @@ -46,6 +53,7 @@ namespace osu.Game.Overlays.Chat if (SelectedTab != null) SelectedTab.Active.Value = false; SelectedTab = null; + } } } diff --git a/osu.Game/Overlays/Chat/UserChatTabItem.cs b/osu.Game/Overlays/Chat/UserTabItem.cs similarity index 86% rename from osu.Game/Overlays/Chat/UserChatTabItem.cs rename to osu.Game/Overlays/Chat/UserTabItem.cs index 24a4c11784..e8031d60b9 100644 --- a/osu.Game/Overlays/Chat/UserChatTabItem.cs +++ b/osu.Game/Overlays/Chat/UserTabItem.cs @@ -19,7 +19,7 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Chat { - public class UserChatTabItem : TabItem + public class UserTabItem : TabItem { private static readonly Vector2 shear = new Vector2(1f / 5f, 0); private readonly UserChat chat; @@ -29,18 +29,19 @@ namespace osu.Game.Overlays.Chat private readonly Container backgroundContainer; private readonly Box backgroundBox; private readonly OsuSpriteText username; + private readonly Avatar avatarContainer; private readonly ChatTabItemCloseButton closeButton; - public UserChatTabItem(UserChat value) + public UserTabItem(UserChat value) : base(value) { chat = value; AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; + Height = 50; Origin = Anchor.BottomRight; Anchor = Anchor.BottomRight; - EdgeEffect = deactivateEdgeEffect; - Masking = false; + EdgeEffect = activateEdgeEffect; + Masking = true; Shear = shear; Children = new Drawable[] { @@ -116,7 +117,11 @@ namespace osu.Game.Overlays.Chat Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new Avatar(value.User) + Child = new DelayedLoadWrapper(new Avatar(value.User) + { + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), + }) { Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), } @@ -136,6 +141,10 @@ namespace osu.Game.Overlays.Chat Height = 1, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, + Margin = new MarginPadding + { + Right = 5 + }, RelativeSizeAxes = Axes.Y, Action = delegate { @@ -148,13 +157,13 @@ namespace osu.Game.Overlays.Chat }; } - public Action OnRequestClose; + public Action OnRequestClose; private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, - Radius = 30, - Colour = Color4.Black.Opacity(0.3f), + Radius = 15, + Colour = Color4.Black.Opacity(0.4f), }; protected override void OnActivated() @@ -168,7 +177,7 @@ namespace osu.Game.Overlays.Chat username.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); closeButton.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); closeButton.FadeIn(activate_length, Easing.OutQuint); - TweenEdgeEffectTo(activateEdgeEffect, activate_length); + // TweenEdgeEffectTo(activateEdgeEffect, activate_length); } private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters @@ -187,7 +196,7 @@ namespace osu.Game.Overlays.Chat username.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); closeButton.FadeOut(deactivate_length, Easing.OutQuint); closeButton.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); - TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); + // TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index bcda27e022..801cb894d4 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays private ChatManager chatManager; - private readonly Container currentChannelContainer; + private readonly Container currentChatContainer; private readonly List loadedChannels = new List(); private readonly LoadingAnimation loading; @@ -45,8 +45,7 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private readonly ChannelTabControl channelTabs; - private readonly UserChatTabControl userTabs; + private readonly ChatTabControl chatTabControl; private readonly Container chatContainer; private readonly Container tabsArea; @@ -105,7 +104,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, }, - currentChannelContainer = new Container + currentChatContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -155,15 +154,16 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - channelTabs = new ChannelTabControl + chatTabControl = new ChatTabControl { RelativeSizeAxes = Axes.Both, - OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel), - }, - userTabs = new UserChatTabControl - { - RelativeSizeAxes = Axes.Both, - OnRequestLeave = privateChat => chatManager.OpenedUserChats.Remove(privateChat), + OnRequestLeave = chat => + { + if (chat is ChannelChat channelChat) + chatManager.JoinedChannels.Remove(channelChat); + if (chat is UserChat userChat) + chatManager.OpenedUserChats.Remove(userChat); + } } } }, @@ -171,12 +171,11 @@ namespace osu.Game.Overlays }, }; - userTabs.Current.ValueChanged += user => chatManager.CurrentChat.Value = user; - channelTabs.Current.ValueChanged += newChannel => chatManager.CurrentChat.Value = newChannel; - channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; + chatTabControl.Current.ValueChanged += chat => chatManager.CurrentChat.Value = chat; + chatTabControl.channelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; channelSelection.StateChanged += state => { - channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible; + chatTabControl.channelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible; if (state == Visibility.Visible) { @@ -214,19 +213,16 @@ namespace osu.Game.Overlays case NotifyCollectionChangedAction.Add: foreach (ChannelChat newChannel in args.NewItems) { - channelTabs.AddItem(newChannel); + chatTabControl.AddItem(newChannel); newChannel.Joined.Value = true; - if (chatManager.CurrentChat.Value == null) - chatManager.CurrentChat.Value = newChannel; - - if (chatManager.CurrentChat.Value == newChannel) - channelTabs.Current.Value = newChannel; + //if (chatManager.CurrentChat.Value == null) + // chatManager.CurrentChat.Value = newChannel; } break; case NotifyCollectionChangedAction.Remove: foreach (ChannelChat removedChannel in args.OldItems) { - channelTabs.RemoveItem(removedChannel); + chatTabControl.RemoveItem(removedChannel); loadedChannels.Remove(loadedChannels.Find(c => c.Chat == removedChannel )); removedChannel.Joined.Value = false; if (chatManager.CurrentChat.Value == removedChannel) @@ -241,19 +237,19 @@ namespace osu.Game.Overlays if (chat == null) { textbox.Current.Disabled = true; - currentChannelContainer.Clear(false); + currentChatContainer.Clear(false); + chatTabControl.Current.Value = null; return; } textbox.Current.Disabled = chat.ReadOnly; - - userTabs.DeselectAll(); - channelTabs.DeselectAll(); + + Scheduler.Add(() => chatTabControl.Current.Value = chat); var loaded = loadedChannels.Find(d => d.Chat == chat); if (loaded == null) { - currentChannelContainer.FadeOut(500, Easing.OutQuint); + currentChatContainer.FadeOut(500, Easing.OutQuint); loading.Show(); loaded = new DrawableChat(chat); @@ -262,15 +258,15 @@ namespace osu.Game.Overlays { loading.Hide(); - currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); - currentChannelContainer.FadeIn(500, Easing.OutQuint); + currentChatContainer.Clear(false); + currentChatContainer.Add(loaded); + currentChatContainer.FadeIn(500, Easing.OutQuint); }); } else { - currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); + currentChatContainer.Clear(false); + currentChatContainer.Add(loaded); } } @@ -371,18 +367,15 @@ namespace osu.Game.Overlays case NotifyCollectionChangedAction.Add: foreach (UserChat chat in args.NewItems) { - userTabs.AddItem(args.NewItems[0] as UserChat); + chatTabControl.AddItem(args.NewItems[0] as UserChat); if (chatManager.CurrentChat.Value == chat) - userTabs.Current.Value = chat; + chatTabControl.Current.Value = chat; } break; case NotifyCollectionChangedAction.Remove: foreach (UserChat chat in args.OldItems) - userTabs.RemoveItem(chat); - break; - case NotifyCollectionChangedAction.Reset: - userTabs.Clear(); + chatTabControl.RemoveItem(chat); break; } } From 3860594f40973f704077b5ce92fe3726f13899f1 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 20:01:57 +0200 Subject: [PATCH 15/88] Rename everything into channel and remove everything chat --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 6 +- .../Visual/TestCaseChatTabControl.cs | 4 +- .../Graphics/Containers/LinkFlowContainer.cs | 6 +- .../API/Requests/GetChannelMessagesRequest.cs | 10 +- .../API/Requests/ListChannelsRequest.cs | 2 +- .../Online/Chat/{ChatBase.cs => Channel.cs} | 68 +++++++--- osu.Game/Online/Chat/ChannelChat.cs | 31 ----- .../{ChatManager.cs => ChannelManager.cs} | 119 ++++++++---------- osu.Game/Online/Chat/UserChat.cs | 44 ------- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Overlays/Chat/ChannelListItem.cs | 8 +- osu.Game/Overlays/Chat/ChannelSection.cs | 2 +- .../Overlays/Chat/ChannelSelectionOverlay.cs | 4 +- osu.Game/Overlays/Chat/ChannelTabControl.cs | 27 ++-- osu.Game/Overlays/Chat/ChatLine.cs | 8 +- osu.Game/Overlays/Chat/ChatTabControl.cs | 80 ++++++------ osu.Game/Overlays/Chat/DrawableChat.cs | 8 +- osu.Game/Overlays/Chat/UserTabControl.cs | 25 ++-- osu.Game/Overlays/Chat/UserTabItem.cs | 32 ++--- osu.Game/Overlays/ChatOverlay.cs | 56 ++------- 20 files changed, 232 insertions(+), 310 deletions(-) rename osu.Game/Online/Chat/{ChatBase.cs => Channel.cs} (64%) delete mode 100644 osu.Game/Online/Chat/ChannelChat.cs rename osu.Game/Online/Chat/{ChatManager.cs => ChannelManager.cs} (67%) delete mode 100644 osu.Game/Online/Chat/UserChat.cs diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 6f57312d84..66a28c5ee8 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -55,9 +55,9 @@ namespace osu.Game.Tests.Visual { linkColour = colours.Blue; - var chatManager = new ChatManager(Scheduler); - chatManager.AvailableChannels.Add(new ChannelChat { Name = "#english"}); - chatManager.AvailableChannels.Add(new ChannelChat { Name = "#japanese" }); + var chatManager = new ChannelManager(Scheduler); + chatManager.AvailableChannels.Add(new Channel { Name = "#english"}); + chatManager.AvailableChannels.Add(new Channel { Name = "#japanese" }); dependencies.Cache(chatManager); dependencies.Cache(new ChatOverlay()); diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index 0b7a66ccf2..ae7c6e2751 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual private void addUser(long id, string name) { - chatTabControl.AddItem(new UserChat(new User + chatTabControl.AddItem(new Channel(new User { Id = id, Username = name @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual private void addChannel(string name) { - this.chatTabControl.AddItem(new ChannelChat + this.chatTabControl.AddItem(new Channel { Name = name }); diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 627efbda76..aee2eb4597 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -23,11 +23,11 @@ namespace osu.Game.Graphics.Containers public override bool HandleMouseInput => true; private OsuGame game; - private ChatManager chatManager; + private ChannelManager chatManager; private Action showNotImplementedError; [BackgroundDependencyLoader(true)] - private void load(OsuGame game, NotificationOverlay notifications, ChatManager chatManager) + private void load(OsuGame game, NotificationOverlay notifications, ChannelManager chatManager) { // will be null in tests this.game = game; @@ -82,7 +82,7 @@ namespace osu.Game.Graphics.Containers case LinkAction.OpenChannel: var channel = chatManager.AvailableChannels.FirstOrDefault(c => c.Name == linkArgument); if (channel != null) - chatManager.CurrentChat.Value = channel; + chatManager.CurrentChannel.Value = channel; break; case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: diff --git a/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs b/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs index d463af6c25..c323cf0ff8 100644 --- a/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.IO.Network; @@ -10,11 +11,16 @@ namespace osu.Game.Online.API.Requests { public class GetChannelMessagesRequest : APIRequest> { - private readonly IEnumerable channels; + private readonly IEnumerable channels; private long? since; - public GetChannelMessagesRequest(IEnumerable channels, long? sinceId) + public GetChannelMessagesRequest(IEnumerable channels, long? sinceId) { + if (channels == null) + throw new ArgumentNullException(nameof(channels)); + if (channels.Any(c => c.Target != TargetType.Channel)) + throw new ArgumentException("All channels in the argument channels must have the targettype Channel"); + this.channels = channels; since = sinceId; } diff --git a/osu.Game/Online/API/Requests/ListChannelsRequest.cs b/osu.Game/Online/API/Requests/ListChannelsRequest.cs index 97ed3d3cbc..b387af9694 100644 --- a/osu.Game/Online/API/Requests/ListChannelsRequest.cs +++ b/osu.Game/Online/API/Requests/ListChannelsRequest.cs @@ -6,7 +6,7 @@ using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { - public class ListChannelsRequest : APIRequest> + public class ListChannelsRequest : APIRequest> { protected override string Target => @"chat/channels"; } diff --git a/osu.Game/Online/Chat/ChatBase.cs b/osu.Game/Online/Chat/Channel.cs similarity index 64% rename from osu.Game/Online/Chat/ChatBase.cs rename to osu.Game/Online/Chat/Channel.cs index 969d2c0f1f..fdaa690e9a 100644 --- a/osu.Game/Online/Chat/ChatBase.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -1,22 +1,55 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// 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.Collections.ObjectModel; using System.Linq; +using Newtonsoft.Json; using osu.Framework.Configuration; using osu.Framework.Lists; +using osu.Game.Users; namespace osu.Game.Online.Chat { - public abstract class ChatBase + public class Channel { public const int MAX_HISTORY = 300; - public bool ReadOnly { get; } = false; - public abstract TargetType Target { get; } - public abstract long ChatID { get; } - public Bindable Joined = new Bindable(); + [JsonProperty(@"name")] + public string Name; + + [JsonProperty(@"description")] + public string Topic; + + [JsonProperty(@"type")] + public string Type; + + [JsonProperty(@"channel_id")] + public long Id; + + [JsonConstructor] + public Channel() + { + } + + /// + /// Contructs a privatechannel + /// TODO this class needs to be serialized from something like channels/private, instead of creating from a contructor + /// + /// The user + public Channel(User user) + { + Target = TargetType.User; + Name = user.Username; + Id = user.Id; + JoinedUsers.Add(user); + } + + /// + /// Contains every joined user except yourself + /// + public ObservableCollection JoinedUsers = new ObservableCollection(); public readonly SortedList Messages = new SortedList(Comparer.Default); private readonly List pendingMessages = new List(); @@ -24,6 +57,10 @@ namespace osu.Game.Online.Chat public event Action PendingMessageResolved; public event Action MessageRemoved; + public Bindable Joined = new Bindable(); + public TargetType Target { get; set; } + public bool ReadOnly { get; set; } + public void AddLocalEcho(LocalEchoMessage message) { pendingMessages.Add(message); @@ -43,16 +80,8 @@ namespace osu.Game.Online.Chat NewMessagesArrived?.Invoke(messages); } - private void purgeOldMessages() - { - // never purge local echos - int messageCount = Messages.Count - pendingMessages.Count; - if (messageCount > MAX_HISTORY) - Messages.RemoveRange(0, messageCount - MAX_HISTORY); - } - /// - /// Replace or remove a message from the chat. + /// Replace or remove a message from the channel. /// /// The local echo message (client-side). /// The response message, or null if the message became invalid. @@ -81,5 +110,14 @@ namespace osu.Game.Online.Chat PendingMessageResolved?.Invoke(echo, final); } + private void purgeOldMessages() + { + // never purge local echos + int messageCount = Messages.Count - pendingMessages.Count; + if (messageCount > MAX_HISTORY) + Messages.RemoveRange(0, messageCount - MAX_HISTORY); + } + + public override string ToString() => Name; } } diff --git a/osu.Game/Online/Chat/ChannelChat.cs b/osu.Game/Online/Chat/ChannelChat.cs deleted file mode 100644 index fb24806294..0000000000 --- a/osu.Game/Online/Chat/ChannelChat.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; - -namespace osu.Game.Online.Chat -{ - public class ChannelChat : ChatBase - { - [JsonProperty(@"name")] - public string Name; - - [JsonProperty(@"description")] - public string Topic; - - [JsonProperty(@"type")] - public string Type; - - [JsonProperty(@"channel_id")] - public int Id; - - [JsonConstructor] - public ChannelChat() - { - } - - public override string ToString() => Name; - public override long ChatID => Id; - public override TargetType Target => TargetType.Channel; - } -} diff --git a/osu.Game/Online/Chat/ChatManager.cs b/osu.Game/Online/Chat/ChannelManager.cs similarity index 67% rename from osu.Game/Online/Chat/ChatManager.cs rename to osu.Game/Online/Chat/ChannelManager.cs index ebfdbb3650..e7ffb60f04 100644 --- a/osu.Game/Online/Chat/ChatManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -17,9 +17,9 @@ using osu.Game.Users; namespace osu.Game.Online.Chat { /// - /// Manages everything chat related + /// Manages everything channel related /// - public class ChatManager : Component, IOnlineComponent + public class ChannelManager : Component, IOnlineComponent { /// /// The channels the player joins on startup @@ -30,21 +30,17 @@ namespace osu.Game.Online.Chat }; /// - /// The currently opened chat + /// The currently opened channel /// - public Bindable CurrentChat { get; } = new Bindable(); + public Bindable CurrentChannel { get; } = new Bindable(); /// /// The Channels the player has joined /// - public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); + public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); /// /// The channels available for the player to join /// - public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); - /// - /// The user chats opened. - /// - public ObservableCollection OpenedUserChats { get; } = new ObservableCollection(); + public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); private APIAccess api; private readonly Scheduler scheduler; @@ -54,68 +50,49 @@ namespace osu.Game.Online.Chat private long? lastChannelMsgId; private long? lastUserMsgId; - public void OpenChannelChat(string name) + public void OpenChannel(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); - CurrentChat.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) + CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) ?? throw new ArgumentException($"Channel {name} was not found."); } - public void OpenUserChat(long userId) - { - var chat = OpenedUserChats.FirstOrDefault(c => c.ChatID == userId); - - if (chat == null) - { - chat = new UserChat(new User - { - Id = userId - }); - chat.RequestDetails(api); - } - - CurrentChat.Value = chat; - } - - public void OpenUserChat(User user) + public void OpenUserChannel(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); - CurrentChat.Value = OpenedUserChats.FirstOrDefault(c => c.ChatID == user.Id) - ?? new UserChat(user); + CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Target == TargetType.User && c.Id == user.Id) + ?? new Channel(user); } - public ChatManager(Scheduler scheduler) + public ChannelManager(Scheduler scheduler) { this.scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); - CurrentChat.ValueChanged += currentChatChanged; + CurrentChannel.ValueChanged += currentChannelChanged; } - private void currentChatChanged(ChatBase chatBase) + private void currentChannelChanged(Channel channel) { - if (chatBase is ChannelChat channel && !JoinedChannels.Contains(channel)) + if (!JoinedChannels.Contains(channel)) JoinedChannels.Add(channel); - - if (chatBase is UserChat userChat && !OpenedUserChats.Contains(userChat)) - OpenedUserChats.Add(userChat); } /// - /// Posts a message to the currently opened chat. + /// Posts a message to the currently opened channel. /// /// The message text that is going to be posted /// Is true if the message is an action, e.g.: user is currently eating public void PostMessage(string text, bool isAction = false) { - if (CurrentChat.Value == null) + if (CurrentChannel.Value == null) return; if (!api.IsLoggedIn) { - CurrentChat.Value.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + CurrentChannel.Value.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); return; } @@ -123,23 +100,23 @@ namespace osu.Game.Online.Chat { Sender = api.LocalUser.Value, Timestamp = DateTimeOffset.Now, - TargetType = CurrentChat.Value.Target, - TargetId = CurrentChat.Value.ChatID, + TargetType = CurrentChannel.Value.Target, + TargetId = CurrentChannel.Value.Id, IsAction = isAction, Content = text }; - CurrentChat.Value.AddLocalEcho(message); + CurrentChannel.Value.AddLocalEcho(message); var req = new PostMessageRequest(message); - req.Failure += e => CurrentChat.Value?.ReplaceMessage(message, null); - req.Success += m => CurrentChat.Value?.ReplaceMessage(message, m); + req.Failure += e => CurrentChannel.Value?.ReplaceMessage(message, null); + req.Success += m => CurrentChannel.Value?.ReplaceMessage(message, m); api.Queue(req); } public void PostCommand(string text) { - if (CurrentChat.Value == null) + if (CurrentChannel.Value == null) return; var parameters = text.Split(new[] { ' ' }, 2); @@ -151,18 +128,18 @@ namespace osu.Game.Online.Chat case "me": if (string.IsNullOrWhiteSpace(content)) { - CurrentChat.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); + CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); break; } PostMessage(content, true); break; case "help": - CurrentChat.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); + CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); break; default: - CurrentChat.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); + CurrentChannel.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); break; } } @@ -193,6 +170,8 @@ namespace osu.Game.Online.Chat private void handleUserMessages(IEnumerable messages) { + var joinedUserChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); + var outgoingMessages = messages.Where(m => m.Sender.Id == api.LocalUser.Value.Id); var outgoingMessagesGroups = outgoingMessages.GroupBy(m => m.TargetId); var incomingMessagesGroups = messages.Except(outgoingMessages).GroupBy(m => m.UserId); @@ -200,35 +179,43 @@ namespace osu.Game.Online.Chat foreach (var messageGroup in incomingMessagesGroups) { var targetUser = messageGroup.First().Sender; - var chat = OpenedUserChats.FirstOrDefault(c => c.User.Id == targetUser.Id); + var channel = joinedUserChannels.FirstOrDefault(c => c.Id == targetUser.Id); - if (chat == null) + if (channel == null) { - chat = new UserChat(targetUser); - OpenedUserChats.Add(chat); + channel = new Channel(targetUser); + JoinedChannels.Add(channel); + joinedUserChannels.Add(channel); } - chat.AddNewMessages(messageGroup.ToArray()); + channel.AddNewMessages(messageGroup.ToArray()); var outgoingTargetMessages = outgoingMessagesGroups.FirstOrDefault(g => g.Key == targetUser.Id); if (outgoingTargetMessages != null) - chat.AddNewMessages(outgoingTargetMessages.ToArray()); + channel.AddNewMessages(outgoingTargetMessages.ToArray()); } - var withoutReplyGroups = outgoingMessagesGroups.Where(g => OpenedUserChats.All(m => m.ChatID != g.Key)); + var withoutReplyGroups = outgoingMessagesGroups.Where(g => joinedUserChannels.All(m => m.Id != g.Key)); foreach (var withoutReplyGroup in withoutReplyGroups) { - var chat = new UserChat(new User { Id = withoutReplyGroup.First().TargetId }); + var userReq = new GetUserRequest(withoutReplyGroup.First().TargetId); - chat.AddNewMessages(withoutReplyGroup.ToArray()); - OpenedUserChats.Add(chat); - chat.RequestDetails(api); + userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations."); + userReq.Success += user => + { + var channel = new Channel(user); + + channel.AddNewMessages(withoutReplyGroup.ToArray()); + JoinedChannels.Add(channel); + }; + + api.Queue(userReq); } } private void fetchNewChannelMessages() { - fetchChannelMsgReq = new GetChannelMessagesRequest(JoinedChannels, lastChannelMsgId); + fetchChannelMsgReq = new GetChannelMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId); fetchChannelMsgReq.Success += messages => { @@ -257,22 +244,24 @@ namespace osu.Game.Online.Chat req.Success += channels => { - channels.Where(channel => AvailableChannels.All(c => c.ChatID != channel.ChatID)) + channels.Where(channel => AvailableChannels.All(c => c.Id != channel.Id)) .ForEach(channel => AvailableChannels.Add(channel)); channels.Where(channel => defaultChannels.Contains(channel.Name)) - .Where(channel => JoinedChannels.All(c => c.ChatID != channel.ChatID)) + .Where(channel => JoinedChannels.All(c => c.Id != channel.Id)) .ForEach(channel => { JoinedChannels.Add(channel); + var fetchInitialMsgReq = new GetChannelMessagesRequest(new[] {channel}, null); fetchInitialMsgReq.Success += handleChannelMessages; + fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); api.Queue(fetchInitialMsgReq); }); fetchNewMessages(); }; - req.Failure += error => Logger.Error(error, "Fetching channels failed"); + req.Failure += error => Logger.Error(error, "Fetching channel list failed"); api.Queue(req); } diff --git a/osu.Game/Online/Chat/UserChat.cs b/osu.Game/Online/Chat/UserChat.cs deleted file mode 100644 index 902d4eee11..0000000000 --- a/osu.Game/Online/Chat/UserChat.cs +++ /dev/null @@ -1,44 +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 osu.Framework.Logging; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Users; - -namespace osu.Game.Online.Chat -{ - public class UserChat : ChatBase - { - public User User { get; private set; } - public override TargetType Target => TargetType.User; - public override long ChatID => User.Id; - - public Action DetailsArrived; - - public UserChat(User user, Message[] messages = null) - { - User = user ?? throw new ArgumentNullException(nameof(user)); - - if (messages != null) AddNewMessages(messages); - } - - public void RequestDetails(IAPIProvider api) - { - if (api == null) - throw new ArgumentNullException(nameof(api)); - - var req = new GetUserRequest(User.Id); - req.Success += user => - { - User = user; - DetailsArrived?.Invoke(user); - }; - req.Failure += exception => Logger.Error(exception, $"Requesting details for user with Id:{User.Id} failed."); - api.Queue(req); - } - - public override string ToString() => User.Username ?? User.Id.ToString(); - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d247bc74ff..6b4cb731d7 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -113,7 +113,7 @@ namespace osu.Game dependencies.Cache(api); dependencies.CacheAs(api); - var chatManager = new ChatManager(Scheduler); + var chatManager = new ChannelManager(Scheduler); api.Register(chatManager); dependencies.Cache(chatManager); diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs index 9625c715d2..19418c63a8 100644 --- a/osu.Game/Overlays/Chat/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Chat private const float text_size = 15; private const float transition_duration = 100; - private readonly ChannelChat channel; + private readonly Channel channel; private readonly Bindable joinedBind = new Bindable(); private readonly OsuSpriteText name; @@ -44,10 +44,10 @@ namespace osu.Game.Overlays.Chat } } - public Action OnRequestJoin; - public Action OnRequestLeave; + public Action OnRequestJoin; + public Action OnRequestLeave; - public ChannelListItem(ChannelChat channel) + public ChannelListItem(Channel channel) { this.channel = channel; diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs index 6bec82f505..132891bcc0 100644 --- a/osu.Game/Overlays/Chat/ChannelSection.cs +++ b/osu.Game/Overlays/Chat/ChannelSection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Chat set { header.Text = value.ToUpper(); } } - public IEnumerable Channels + public IEnumerable Channels { set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); } } diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs index 598e1fe527..3684c47e40 100644 --- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs @@ -32,8 +32,8 @@ namespace osu.Game.Overlays.Chat private readonly SearchTextBox search; private readonly SearchContainer sectionsFlow; - public Action OnRequestJoin; - public Action OnRequestLeave; + public Action OnRequestJoin; + public Action OnRequestLeave; public IEnumerable Sections { diff --git a/osu.Game/Overlays/Chat/ChannelTabControl.cs b/osu.Game/Overlays/Chat/ChannelTabControl.cs index bf15aa51e9..6fb0bf92a4 100644 --- a/osu.Game/Overlays/Chat/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/ChannelTabControl.cs @@ -21,11 +21,11 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Chat { - public class ChannelTabControl : OsuTabControl + public class ChannelTabControl : OsuTabControl { private const float shear_width = 10; - public Action OnRequestLeave; + public Action OnRequestLeave; public readonly Bindable ChannelSelectorActive = new Bindable(); @@ -46,19 +46,12 @@ namespace osu.Game.Overlays.Chat Margin = new MarginPadding(10), }); - AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new ChannelChat { Name = "+" })); + AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); ChannelSelectorActive.BindTo(selectorTab.Active); } - public void DeselectAll() - { - if (SelectedTab != null) - SelectedTab.Active.Value = false; - SelectedTab = null; - } - - protected override void AddTabItem(TabItem item, bool addToDropdown = true) + protected override void AddTabItem(TabItem item, bool addToDropdown = true) { if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) // performTabSort might've made selectorTab's position wonky, fix it @@ -70,9 +63,9 @@ namespace osu.Game.Overlays.Chat SelectTab(item); } - protected override TabItem CreateTabItem(ChannelChat value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; - protected override void SelectTab(TabItem tab) + protected override void SelectTab(TabItem tab) { if (tab is ChannelTabItem.ChannelSelectorTabItem) { @@ -85,7 +78,7 @@ namespace osu.Game.Overlays.Chat base.SelectTab(tab); } - private void tabCloseRequested(TabItem tab) + private void tabCloseRequested(TabItem tab) { int totalTabs = TabContainer.Count - 1; // account for selectorTab int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); @@ -100,7 +93,7 @@ namespace osu.Game.Overlays.Chat OnRequestLeave?.Invoke(tab.Value); } - private class ChannelTabItem : TabItem + private class ChannelTabItem : TabItem { private Color4 backgroundInactive; private Color4 backgroundHover; @@ -182,7 +175,7 @@ namespace osu.Game.Overlays.Chat updateState(); } - public ChannelTabItem(ChannelChat value) : base(value) + public ChannelTabItem(Channel value) : base(value) { Width = 150; @@ -314,7 +307,7 @@ namespace osu.Game.Overlays.Chat { public override bool IsRemovable => false; - public ChannelSelectorTabItem(ChannelChat value) : base(value) + public ChannelSelectorTabItem(Channel value) : base(value) { Depth = float.MaxValue; Width = 45; diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 19ad452943..b020e13c38 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Chat Padding = new MarginPadding { Left = padding, Right = padding }; } - private ChatManager chatManager; + private ChannelManager chatManager; private Message message; private OsuSpriteText username; @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Chat } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, ChatManager chatManager) + private void load(OsuColour colours, ChannelManager chatManager) { this.chatManager = chatManager; customUsernameColour = colours.ChatBlue; @@ -245,10 +245,10 @@ namespace osu.Game.Overlays.Chat } [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profile, ChatManager chatManager) + private void load(UserProfileOverlay profile, ChannelManager chatManager) { Action = () => profile?.ShowUser(sender); - startChatAction = () => chatManager?.OpenUserChat(sender); + startChatAction = () => chatManager?.OpenUserChannel(sender); } public MenuItem[] ContextMenuItems => new MenuItem[] diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 8ec60d5e8d..fe6945523f 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -7,14 +8,13 @@ using osu.Game.Online.Chat; namespace osu.Game.Overlays.Chat { - public class ChatTabControl : Container, IHasCurrentValue + public class ChatTabControl : Container, IHasCurrentValue { - public readonly ChannelTabControl channelTabControl; - private readonly UserTabControl userTabControl; + public readonly ChannelTabControl ChannelTabControl; + public readonly UserTabControl UserTabControl; - public Bindable Current { get; } = new Bindable(); - public Action OnRequestLeave; - public Action OnRequestChannelSelection; + public Bindable Current { get; } = new Bindable(); + public Action OnRequestLeave; public ChatTabControl() { @@ -22,76 +22,80 @@ namespace osu.Game.Overlays.Chat Children = new Drawable[] { - channelTabControl = new ChannelTabControl + ChannelTabControl = new ChannelTabControl { Width = 0.5f, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - OnRequestLeave = chat => OnRequestLeave?.Invoke(chat) + OnRequestLeave = channel => OnRequestLeave?.Invoke(channel) }, - userTabControl = new UserTabControl + UserTabControl = new UserTabControl { Width = 0.5f, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, RelativeSizeAxes = Axes.Both, - OnRequestLeave = chat => OnRequestLeave?.Invoke(chat) + OnRequestLeave = channel => OnRequestLeave?.Invoke(channel) }, }; Current.ValueChanged += currentTabChanged; - channelTabControl.Current.ValueChanged += chat => + ChannelTabControl.Current.ValueChanged += channel => { - if (chat != null) - Current.Value = chat; + if (channel != null) + Current.Value = channel; }; - userTabControl.Current.ValueChanged += chat => + UserTabControl.Current.ValueChanged += channel => { - if (chat != null) - Current.Value = chat; + if (channel != null) + Current.Value = channel; }; } - private void currentTabChanged(ChatBase tab) + private void currentTabChanged(Channel channel) { - switch (tab) + switch (channel.Target) { - case UserChat userChat: - userTabControl.Current.Value = userChat; - channelTabControl.Current.Value = null; + case TargetType.User: + UserTabControl.Current.Value = channel; + ChannelTabControl.Current.Value = null; break; - case ChannelChat channelChat: - channelTabControl.Current.Value = channelChat; - userTabControl.Current.Value = null; + case TargetType.Channel: + ChannelTabControl.Current.Value = channel; + UserTabControl.Current.Value = null; break; } } - public void AddItem(ChatBase chat) + public void AddItem(Channel channel) { - switch (chat) + switch (channel.Target) { - case UserChat userChat: - userTabControl.AddItem(userChat); + case TargetType.User: + UserTabControl.AddItem(channel); break; - case ChannelChat channelChat: - channelTabControl.AddItem(channelChat); + case TargetType.Channel: + ChannelTabControl.AddItem(channel); break; } } - public void RemoveItem(ChatBase chat) + public void RemoveItem(Channel channel) { - switch (chat) + Channel nextSelectedChannel = null; + + switch (channel.Target) { - case UserChat userChat: - userTabControl.RemoveItem(userChat); - Current.Value = null; + case TargetType.User: + UserTabControl.RemoveItem(channel); + if (Current.Value == channel) + Current.Value = UserTabControl.Items.FirstOrDefault() ?? ChannelTabControl.Items.FirstOrDefault(); break; - case ChannelChat channelChat: - channelTabControl.RemoveItem(channelChat); - Current.Value = null; + case TargetType.Channel: + ChannelTabControl.RemoveItem(channel); + if (Current.Value == channel) + Current.Value = ChannelTabControl.Items.FirstOrDefault() ?? UserTabControl.Items.FirstOrDefault(); break; } } diff --git a/osu.Game/Overlays/Chat/DrawableChat.cs b/osu.Game/Overlays/Chat/DrawableChat.cs index 4ebdc4bd31..5e9f399779 100644 --- a/osu.Game/Overlays/Chat/DrawableChat.cs +++ b/osu.Game/Overlays/Chat/DrawableChat.cs @@ -17,11 +17,11 @@ namespace osu.Game.Overlays.Chat { public class DrawableChat : Container { - public readonly ChatBase Chat; + public readonly Channel Chat; private readonly ChatLineContainer flow; private readonly ScrollContainer scroll; - public DrawableChat(ChatBase chat) + public DrawableChat(Channel chat) { Chat = chat; @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { // Add up to last ChatBase.MAX_HISTORY messages - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - ChatBase.MAX_HISTORY)); + var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); flow.AddRange(displayMessages.Select(m => new ChatLine(m))); @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Chat scrollToEnd(); var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); - int count = staleMessages.Length - ChatBase.MAX_HISTORY; + int count = staleMessages.Length - Channel.MAX_HISTORY; for (int i = 0; i < count; i++) { diff --git a/osu.Game/Overlays/Chat/UserTabControl.cs b/osu.Game/Overlays/Chat/UserTabControl.cs index cf1caaf4df..99fc095331 100644 --- a/osu.Game/Overlays/Chat/UserTabControl.cs +++ b/osu.Game/Overlays/Chat/UserTabControl.cs @@ -10,13 +10,16 @@ using OpenTK; namespace osu.Game.Overlays.Chat { - public class UserTabControl : OsuTabControl + public class UserTabControl : OsuTabControl { - protected override TabItem CreateTabItem(UserChat value) => new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + protected override TabItem CreateTabItem(Channel value) + { + if (value.Target != TargetType.User) + throw new ArgumentException("Argument value needs to have the targettype user."); + return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + } - protected override Dropdown CreateDropdown() => null; - - public Action OnRequestLeave; + public Action OnRequestLeave; public UserTabControl() { @@ -28,7 +31,7 @@ namespace osu.Game.Overlays.Chat }; } - protected override void AddTabItem(TabItem item, bool addToDropdown = true) + protected override void AddTabItem(TabItem item, bool addToDropdown = true) { base.AddTabItem(item, addToDropdown); @@ -36,7 +39,7 @@ namespace osu.Game.Overlays.Chat SelectTab(item); } - private void tabCloseRequested(TabItem priv) + private void tabCloseRequested(TabItem priv) { int totalTabs = TabContainer.Count -1; // account for selectorTab int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(priv), 1, totalTabs); @@ -47,13 +50,5 @@ namespace osu.Game.Overlays.Chat OnRequestLeave?.Invoke(priv.Value); } - - public void DeselectAll() - { - if (SelectedTab != null) - SelectedTab.Active.Value = false; - SelectedTab = null; - - } } } diff --git a/osu.Game/Overlays/Chat/UserTabItem.cs b/osu.Game/Overlays/Chat/UserTabItem.cs index e8031d60b9..de6ff466df 100644 --- a/osu.Game/Overlays/Chat/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/UserTabItem.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -19,10 +20,10 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Chat { - public class UserTabItem : TabItem + public class UserTabItem : TabItem { private static readonly Vector2 shear = new Vector2(1f / 5f, 0); - private readonly UserChat chat; + private readonly Channel channel; public override bool IsRemovable => true; private readonly Box highlightBox; @@ -32,10 +33,13 @@ namespace osu.Game.Overlays.Chat private readonly Avatar avatarContainer; private readonly ChatTabItemCloseButton closeButton; - public UserTabItem(UserChat value) + public UserTabItem(Channel value) : base(value) { - chat = value; + if (value.Target != TargetType.User) + throw new ArgumentException("Argument value needs to have the targettype user!"); + + channel = value; AutoSizeAxes = Axes.X; Height = 50; Origin = Anchor.BottomRight; @@ -117,7 +121,7 @@ namespace osu.Game.Overlays.Chat Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new DelayedLoadWrapper(new Avatar(value.User) + Child = new DelayedLoadWrapper(new Avatar(value.JoinedUsers.First()) { Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), @@ -132,7 +136,7 @@ namespace osu.Game.Overlays.Chat { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Text = value.User.Username, + Text = value.Name, Margin = new MarginPadding(1), TextSize = 18, }, @@ -177,12 +181,14 @@ namespace osu.Game.Overlays.Chat username.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); closeButton.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); closeButton.FadeIn(activate_length, Easing.OutQuint); - // TweenEdgeEffectTo(activateEdgeEffect, activate_length); + TweenEdgeEffectTo(activateEdgeEffect, activate_length); } private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters { - Colour = Color4.Black.Opacity(0.0f), + Type = EdgeEffectType.Shadow, + Radius = 10, + Colour = Color4.Black.Opacity(0.2f), }; protected override void OnDeactivated() @@ -196,19 +202,15 @@ namespace osu.Game.Overlays.Chat username.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); closeButton.FadeOut(deactivate_length, Easing.OutQuint); closeButton.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); - // TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); + TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); } [BackgroundDependencyLoader] private void load(OsuColour colours, IAPIProvider api) { - backgroundBox.Colour = Value.User.Colour != null ? OsuColour.FromHex(Value.User.Colour) : colours.BlueDark; + var user = Value.JoinedUsers.First(); - if (chat.User.Username == null || chat.User.Id < 1) - { - chat.DetailsArrived += arrivedUser => { Scheduler.Add(() => { username.Text = arrivedUser.Username; }); }; - chat.RequestDetails(api); - } + backgroundBox.Colour = user.Colour != null ? OsuColour.FromHex(user.Colour) : colours.BlueDark; } } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 801cb894d4..33c22550a8 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays private const float textbox_height = 60; private const float channel_selection_min_height = 0.3f; - private ChatManager chatManager; + private ChannelManager chatManager; private readonly Container currentChatContainer; private readonly List loadedChannels = new List(); @@ -157,13 +157,7 @@ namespace osu.Game.Overlays chatTabControl = new ChatTabControl { RelativeSizeAxes = Axes.Both, - OnRequestLeave = chat => - { - if (chat is ChannelChat channelChat) - chatManager.JoinedChannels.Remove(channelChat); - if (chat is UserChat userChat) - chatManager.OpenedUserChats.Remove(userChat); - } + OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel) } } }, @@ -171,11 +165,11 @@ namespace osu.Game.Overlays }, }; - chatTabControl.Current.ValueChanged += chat => chatManager.CurrentChat.Value = chat; - chatTabControl.channelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; + chatTabControl.Current.ValueChanged += chat => chatManager.CurrentChannel.Value = chat; + chatTabControl.ChannelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; channelSelection.StateChanged += state => { - chatTabControl.channelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible; + chatTabControl.ChannelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible; if (state == Visibility.Visible) { @@ -211,28 +205,24 @@ namespace osu.Game.Overlays switch (args.Action) { case NotifyCollectionChangedAction.Add: - foreach (ChannelChat newChannel in args.NewItems) + foreach (Channel newChannel in args.NewItems) { chatTabControl.AddItem(newChannel); newChannel.Joined.Value = true; - //if (chatManager.CurrentChat.Value == null) - // chatManager.CurrentChat.Value = newChannel; } break; case NotifyCollectionChangedAction.Remove: - foreach (ChannelChat removedChannel in args.OldItems) + foreach (Channel removedChannel in args.OldItems) { chatTabControl.RemoveItem(removedChannel); loadedChannels.Remove(loadedChannels.Find(c => c.Chat == removedChannel )); removedChannel.Joined.Value = false; - if (chatManager.CurrentChat.Value == removedChannel) - chatManager.CurrentChat.Value = null; } break; } } - private void currentChatChanged(ChatBase chat) + private void currentChatChanged(Channel chat) { if (chat == null) { @@ -243,8 +233,9 @@ namespace osu.Game.Overlays } textbox.Current.Disabled = chat.ReadOnly; - - Scheduler.Add(() => chatTabControl.Current.Value = chat); + + if (chatTabControl.Current.Value != chat) + Scheduler.Add(() => chatTabControl.Current.Value = chat); var loaded = loadedChannels.Find(d => d.Chat == chat); if (loaded == null) @@ -337,7 +328,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, OsuConfigManager config, OsuColour colours, ChatManager chatManager) + private void load(APIAccess api, OsuConfigManager config, OsuColour colours, ChannelManager chatManager) { api.Register(chatManager); @@ -354,30 +345,9 @@ namespace osu.Game.Overlays loading.Show(); this.chatManager = chatManager; - chatManager.CurrentChat.ValueChanged += currentChatChanged; + chatManager.CurrentChannel.ValueChanged += currentChatChanged; chatManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; chatManager.AvailableChannels.CollectionChanged += availableChannelsChanged; - chatManager.OpenedUserChats.CollectionChanged += openedUserChatsChanged; - } - - private void openedUserChatsChanged(object sender, NotifyCollectionChangedEventArgs args) - { - switch (args.Action) - { - case NotifyCollectionChangedAction.Add: - foreach (UserChat chat in args.NewItems) - { - chatTabControl.AddItem(args.NewItems[0] as UserChat); - - if (chatManager.CurrentChat.Value == chat) - chatTabControl.Current.Value = chat; - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (UserChat chat in args.OldItems) - chatTabControl.RemoveItem(chat); - break; - } } private void postMessage(TextBox textbox, bool newText) From 697b551f3ed16ea37b3e2422db1d22ad437ae231 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 20:27:33 +0200 Subject: [PATCH 16/88] Fix Selected tab beeing choosen multiple times --- osu.Game/Overlays/Chat/ChannelTabControl.cs | 3 --- osu.Game/Overlays/Chat/ChatTabControl.cs | 2 ++ osu.Game/Overlays/Chat/UserTabControl.cs | 8 -------- osu.Game/Overlays/Chat/UserTabItem.cs | 2 ++ 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelTabControl.cs b/osu.Game/Overlays/Chat/ChannelTabControl.cs index 6fb0bf92a4..9c07294a50 100644 --- a/osu.Game/Overlays/Chat/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/ChannelTabControl.cs @@ -58,9 +58,6 @@ namespace osu.Game.Overlays.Chat TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); base.AddTabItem(item, addToDropdown); - - if (SelectedTab == null) - SelectTab(item); } protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index fe6945523f..fe2ec65827 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -79,6 +79,8 @@ namespace osu.Game.Overlays.Chat ChannelTabControl.AddItem(channel); break; } + + } public void RemoveItem(Channel channel) diff --git a/osu.Game/Overlays/Chat/UserTabControl.cs b/osu.Game/Overlays/Chat/UserTabControl.cs index 99fc095331..5e23b4c2eb 100644 --- a/osu.Game/Overlays/Chat/UserTabControl.cs +++ b/osu.Game/Overlays/Chat/UserTabControl.cs @@ -31,14 +31,6 @@ namespace osu.Game.Overlays.Chat }; } - protected override void AddTabItem(TabItem item, bool addToDropdown = true) - { - base.AddTabItem(item, addToDropdown); - - if (SelectedTab == null) - SelectTab(item); - } - private void tabCloseRequested(TabItem priv) { int totalTabs = TabContainer.Count -1; // account for selectorTab diff --git a/osu.Game/Overlays/Chat/UserTabItem.cs b/osu.Game/Overlays/Chat/UserTabItem.cs index de6ff466df..aaf0a7b295 100644 --- a/osu.Game/Overlays/Chat/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/UserTabItem.cs @@ -139,12 +139,14 @@ namespace osu.Game.Overlays.Chat Text = value.Name, Margin = new MarginPadding(1), TextSize = 18, + Alpha = 0, }, closeButton = new ChatTabItemCloseButton { Height = 1, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, + Alpha = 0, Margin = new MarginPadding { Right = 5 From 2056258defe93beb973a9a7fa49cce1e4971d480 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 20:31:16 +0200 Subject: [PATCH 17/88] Fix code sanity --- osu.Game.Tests/Visual/TestCaseChatTabControl.cs | 4 +++- osu.Game/Online/Chat/ChannelManager.cs | 2 +- osu.Game/Overlays/Chat/ChatTabControl.cs | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index ae7c6e2751..8318f89217 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -1,4 +1,6 @@ - +// 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 osu.Framework.Extensions.Color4Extensions; diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index e7ffb60f04..f67e42dfdb 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -197,7 +197,7 @@ namespace osu.Game.Online.Chat var withoutReplyGroups = outgoingMessagesGroups.Where(g => joinedUserChannels.All(m => m.Id != g.Key)); foreach (var withoutReplyGroup in withoutReplyGroups) - { + { var userReq = new GetUserRequest(withoutReplyGroup.First().TargetId); userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations."); diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index fe2ec65827..5d33843f97 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -79,8 +82,6 @@ namespace osu.Game.Overlays.Chat ChannelTabControl.AddItem(channel); break; } - - } public void RemoveItem(Channel channel) From c2020742b2dbc5b3ced0a2b4fc1acb1f03798945 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 20:37:51 +0200 Subject: [PATCH 18/88] Actually use the fact that ChannelManager is now a component --- osu.Game/Online/Chat/ChannelManager.cs | 28 +++++++++++++++++--------- osu.Game/OsuGameBase.cs | 5 +---- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f67e42dfdb..2d75881910 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; @@ -19,24 +20,28 @@ namespace osu.Game.Online.Chat /// /// Manages everything channel related /// - public class ChannelManager : Component, IOnlineComponent + public class ChannelManager : Component, IOnlineComponent, { /// /// The channels the player joins on startup /// private readonly string[] defaultChannels = { - @"#lazer", @"#osu", @"#lobby" + @"#lazer", + @"#osu", + @"#lobby" }; /// /// The currently opened channel /// public Bindable CurrentChannel { get; } = new Bindable(); + /// /// The Channels the player has joined /// public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); + /// /// The channels available for the player to join /// @@ -56,7 +61,7 @@ namespace osu.Game.Online.Chat throw new ArgumentNullException(nameof(name)); CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) - ?? throw new ArgumentException($"Channel {name} was not found."); + ?? throw new ArgumentException($"Channel {name} was not found."); } public void OpenUserChannel(User user) @@ -65,12 +70,11 @@ namespace osu.Game.Online.Chat throw new ArgumentNullException(nameof(user)); CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Target == TargetType.User && c.Id == user.Id) - ?? new Channel(user); + ?? new Channel(user); } - public ChannelManager(Scheduler scheduler) + public ChannelManager() { - this.scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); CurrentChannel.ValueChanged += currentChannelChanged; } @@ -131,6 +135,7 @@ namespace osu.Game.Online.Chat CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); break; } + PostMessage(content, true); break; @@ -253,7 +258,7 @@ namespace osu.Game.Online.Chat { JoinedChannels.Add(channel); - var fetchInitialMsgReq = new GetChannelMessagesRequest(new[] {channel}, null); + var fetchInitialMsgReq = new GetChannelMessagesRequest(new[] { channel }, null); fetchInitialMsgReq.Success += handleChannelMessages; fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); api.Queue(fetchInitialMsgReq); @@ -268,8 +273,6 @@ namespace osu.Game.Online.Chat public void APIStateChanged(APIAccess api, APIState state) { - this.api = api ?? throw new ArgumentNullException(nameof(api)); - switch (state) { case APIState.Online: @@ -285,5 +288,12 @@ namespace osu.Game.Online.Chat break; } } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + this.api = this.api; + api.Register(this); + } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 6b4cb731d7..613755ea5d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -113,10 +113,7 @@ namespace osu.Game dependencies.Cache(api); dependencies.CacheAs(api); - var chatManager = new ChannelManager(Scheduler); - api.Register(chatManager); - - dependencies.Cache(chatManager); + dependencies.Cache(new ChannelManager()); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); From 499ecd3843e0b8c92bb3ccbde9f6ae081f3a3746 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 20:44:35 +0200 Subject: [PATCH 19/88] Fix the warnings --- osu.Game.Tests/Visual/TestCaseChatTabControl.cs | 6 +++--- osu.Game/Overlays/Chat/UserTabItem.cs | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index 8318f89217..77cc62cbbd 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -27,10 +27,10 @@ namespace osu.Game.Tests.Visual }; private readonly ChatTabControl chatTabControl; - private readonly SpriteText currentText; public TestCaseChatTabControl() { + SpriteText currentText; Add(new Container { RelativeSizeAxes = Axes.X, @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual } }); - Add(new Container() + Add(new Container { Origin = Anchor.TopLeft, Anchor = Anchor.TopLeft, @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual private void addChannel(string name) { - this.chatTabControl.AddItem(new Channel + chatTabControl.AddItem(new Channel { Name = name }); diff --git a/osu.Game/Overlays/Chat/UserTabItem.cs b/osu.Game/Overlays/Chat/UserTabItem.cs index aaf0a7b295..3dfec0b7ea 100644 --- a/osu.Game/Overlays/Chat/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/UserTabItem.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Screens.Menu; using osu.Game.Users; @@ -23,7 +22,6 @@ namespace osu.Game.Overlays.Chat public class UserTabItem : TabItem { private static readonly Vector2 shear = new Vector2(1f / 5f, 0); - private readonly Channel channel; public override bool IsRemovable => true; private readonly Box highlightBox; @@ -39,7 +37,6 @@ namespace osu.Game.Overlays.Chat if (value.Target != TargetType.User) throw new ArgumentException("Argument value needs to have the targettype user!"); - channel = value; AutoSizeAxes = Axes.X; Height = 50; Origin = Anchor.BottomRight; @@ -208,7 +205,7 @@ namespace osu.Game.Overlays.Chat } [BackgroundDependencyLoader] - private void load(OsuColour colours, IAPIProvider api) + private void load(OsuColour colours) { var user = Value.JoinedUsers.First(); From a5e0311253e1d12015a3b61816ed734fcd51d7d9 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 20:53:35 +0200 Subject: [PATCH 20/88] Trim whitespaces --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 4 ++-- osu.Game/Online/Chat/ChannelManager.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 66a28c5ee8..e79e52c890 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -51,11 +51,11 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(OsuColour colours, IAPIProvider api) + private void load(OsuColour colours) { linkColour = colours.Blue; - var chatManager = new ChannelManager(Scheduler); + var chatManager = new ChannelManager(); chatManager.AvailableChannels.Add(new Channel { Name = "#english"}); chatManager.AvailableChannels.Add(new Channel { Name = "#japanese" }); dependencies.Cache(chatManager); diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 2d75881910..ea4d746bac 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -20,7 +20,7 @@ namespace osu.Game.Online.Chat /// /// Manages everything channel related /// - public class ChannelManager : Component, IOnlineComponent, + public class ChannelManager : Component, IOnlineComponent { /// /// The channels the player joins on startup @@ -284,7 +284,6 @@ namespace osu.Game.Online.Chat fetchChannelMsgReq?.Cancel(); fetchChannelMsgReq = null; fetchMessagesScheduleder?.Cancel(); - break; } } From b997f0f3fa58ffd2bee84516a41f2f8ff6b92438 Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 11 Apr 2018 21:09:38 +0200 Subject: [PATCH 21/88] Remove not needed using --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index e79e52c890..cf2e0081f6 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -15,7 +15,6 @@ using System.Linq; using NUnit.Framework; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; using osu.Game.Overlays; namespace osu.Game.Tests.Visual From e39f5a1adfe887f80f95f5fcbe367f3ea70f6b2d Mon Sep 17 00:00:00 2001 From: miterosan Date: Thu, 12 Apr 2018 23:19:13 +0200 Subject: [PATCH 22/88] Rename var chatmanager -> channelManager Apply requested changes --- .../Visual/TestCaseChatTabControl.cs | 3 +- .../Graphics/Containers/LinkFlowContainer.cs | 17 ++++++---- ...ssagesRequest.cs => GetMessagesRequest.cs} | 4 +-- ...equest.cs => GetPrivateMessagesRequest.cs} | 4 +-- osu.Game/Online/Chat/Channel.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 34 +++++++++---------- osu.Game/Overlays/ChatOverlay.cs | 30 ++++++++-------- 7 files changed, 48 insertions(+), 46 deletions(-) rename osu.Game/Online/API/Requests/{GetChannelMessagesRequest.cs => GetMessagesRequest.cs} (86%) rename osu.Game/Online/API/Requests/{GetUserMessagesRequest.cs => GetPrivateMessagesRequest.cs} (81%) diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index 77cc62cbbd..0e0a44ba5d 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -22,8 +22,7 @@ namespace osu.Game.Tests.Visual { typeof(ChatTabControl), typeof(ChannelTabControl), - typeof(UserTabControl), - + typeof(UserTabControl) }; private readonly ChatTabControl chatTabControl; diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index aee2eb4597..45feab3076 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -23,15 +23,15 @@ namespace osu.Game.Graphics.Containers public override bool HandleMouseInput => true; private OsuGame game; - private ChannelManager chatManager; + private ChannelManager channelManager; private Action showNotImplementedError; [BackgroundDependencyLoader(true)] - private void load(OsuGame game, NotificationOverlay notifications, ChannelManager chatManager) + private void load(OsuGame game, NotificationOverlay notifications, ChannelManager channelManager) { // will be null in tests this.game = game; - this.chatManager = chatManager; + this.channelManager = channelManager; showNotImplementedError = () => notifications?.Post(new SimpleNotification { Text = @"This link type is not yet supported!", @@ -80,9 +80,14 @@ namespace osu.Game.Graphics.Containers game?.ShowBeatmapSet(setId); break; case LinkAction.OpenChannel: - var channel = chatManager.AvailableChannels.FirstOrDefault(c => c.Name == linkArgument); - if (channel != null) - chatManager.CurrentChannel.Value = channel; + try + { + channelManager.OpenChannel(linkArgument); + } + catch (ArgumentException) + { + //channel was not found + } break; case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: diff --git a/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs similarity index 86% rename from osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs rename to osu.Game/Online/API/Requests/GetMessagesRequest.cs index c323cf0ff8..d2c9169c51 100644 --- a/osu.Game/Online/API/Requests/GetChannelMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -9,12 +9,12 @@ using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { - public class GetChannelMessagesRequest : APIRequest> + public class GetMessagesRequest : APIRequest> { private readonly IEnumerable channels; private long? since; - public GetChannelMessagesRequest(IEnumerable channels, long? sinceId) + public GetMessagesRequest(IEnumerable channels, long? sinceId) { if (channels == null) throw new ArgumentNullException(nameof(channels)); diff --git a/osu.Game/Online/API/Requests/GetUserMessagesRequest.cs b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs similarity index 81% rename from osu.Game/Online/API/Requests/GetUserMessagesRequest.cs rename to osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs index ef9871c5d2..9ff70f8580 100644 --- a/osu.Game/Online/API/Requests/GetUserMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs @@ -7,11 +7,11 @@ using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { - public class GetUserMessagesRequest : APIRequest> + public class GetPrivateMessagesRequest : APIRequest> { private long? since; - public GetUserMessagesRequest(long? sinceId = null) + public GetPrivateMessagesRequest(long? sinceId = null) { since = sinceId; } diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index fdaa690e9a..93b26363ed 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online.Chat } /// - /// Contructs a privatechannel + /// Contructs a private channel /// TODO this class needs to be serialized from something like channels/private, instead of creating from a contructor /// /// The user diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index ea4d746bac..e5fe3fb56e 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -50,8 +50,8 @@ namespace osu.Game.Online.Chat private APIAccess api; private readonly Scheduler scheduler; private ScheduledDelegate fetchMessagesScheduleder; - private GetChannelMessagesRequest fetchChannelMsgReq; - private GetUserMessagesRequest fetchUserMsgReq; + private GetMessagesRequest fetchMsgReq; + private GetPrivateMessagesRequest fetchPrivateMsgReq; private long? lastChannelMsgId; private long? lastUserMsgId; @@ -151,26 +151,26 @@ namespace osu.Game.Online.Chat private void fetchNewMessages() { - if (fetchChannelMsgReq == null) + if (fetchMsgReq == null) fetchNewChannelMessages(); - if (fetchUserMsgReq == null) + if (fetchPrivateMsgReq == null) fetchNewUserMessages(); } private void fetchNewUserMessages() { - fetchUserMsgReq = new GetUserMessagesRequest(lastUserMsgId); + fetchPrivateMsgReq = new GetPrivateMessagesRequest(lastUserMsgId); - fetchUserMsgReq.Success += messages => + fetchPrivateMsgReq.Success += messages => { handleUserMessages(messages); lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; - fetchUserMsgReq = null; + fetchPrivateMsgReq = null; }; - fetchUserMsgReq.Failure += exception => Logger.Error(exception, "Fetching user messages failed."); + fetchPrivateMsgReq.Failure += exception => Logger.Error(exception, "Fetching user messages failed."); - api.Queue(fetchUserMsgReq); + api.Queue(fetchPrivateMsgReq); } private void handleUserMessages(IEnumerable messages) @@ -220,19 +220,19 @@ namespace osu.Game.Online.Chat private void fetchNewChannelMessages() { - fetchChannelMsgReq = new GetChannelMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId); + fetchMsgReq = new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId); - fetchChannelMsgReq.Success += messages => + fetchMsgReq.Success += messages => { if (messages == null) return; handleChannelMessages(messages); lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; - fetchChannelMsgReq = null; + fetchMsgReq = null; }; - fetchChannelMsgReq.Failure += exception => Logger.Error(exception, "Fetching channel messages failed."); + fetchMsgReq.Failure += exception => Logger.Error(exception, "Fetching channel messages failed."); - api.Queue(fetchChannelMsgReq); + api.Queue(fetchMsgReq); } private void handleChannelMessages(IEnumerable messages) @@ -258,7 +258,7 @@ namespace osu.Game.Online.Chat { JoinedChannels.Add(channel); - var fetchInitialMsgReq = new GetChannelMessagesRequest(new[] { channel }, null); + var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); fetchInitialMsgReq.Success += handleChannelMessages; fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); api.Queue(fetchInitialMsgReq); @@ -281,8 +281,8 @@ namespace osu.Game.Online.Chat fetchMessagesScheduleder = scheduler.AddDelayed(fetchNewMessages, 1000, true); break; default: - fetchChannelMsgReq?.Cancel(); - fetchChannelMsgReq = null; + fetchMsgReq?.Cancel(); + fetchMsgReq = null; fetchMessagesScheduleder?.Cancel(); break; } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 33c22550a8..8b405ec980 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays private const float textbox_height = 60; private const float channel_selection_min_height = 0.3f; - private ChannelManager chatManager; + private ChannelManager channelManager; private readonly Container currentChatContainer; private readonly List loadedChannels = new List(); @@ -157,7 +157,7 @@ namespace osu.Game.Overlays chatTabControl = new ChatTabControl { RelativeSizeAxes = Axes.Both, - OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel) + OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel) } } }, @@ -165,7 +165,7 @@ namespace osu.Game.Overlays }, }; - chatTabControl.Current.ValueChanged += chat => chatManager.CurrentChannel.Value = chat; + chatTabControl.Current.ValueChanged += chat => channelManager.CurrentChannel.Value = chat; chatTabControl.ChannelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; channelSelection.StateChanged += state => { @@ -182,10 +182,10 @@ namespace osu.Game.Overlays }; channelSelection.OnRequestJoin = channel => { - if (!chatManager.JoinedChannels.Contains(channel)) - chatManager.JoinedChannels.Add(channel); + if (!channelManager.JoinedChannels.Contains(channel)) + channelManager.JoinedChannels.Add(channel); }; - channelSelection.OnRequestLeave = channel => chatManager.JoinedChannels.Remove(channel); + channelSelection.OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel); } private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) @@ -195,7 +195,7 @@ namespace osu.Game.Overlays new ChannelSection { Header = "All Channels", - Channels = chatManager.AvailableChannels, + Channels = channelManager.AvailableChannels, }, }; } @@ -328,10 +328,8 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, OsuConfigManager config, OsuColour colours, ChannelManager chatManager) + private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager) { - api.Register(chatManager); - ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); ChatHeight.ValueChanged += h => { @@ -344,10 +342,10 @@ namespace osu.Game.Overlays chatBackground.Colour = colours.ChatBlue; loading.Show(); - this.chatManager = chatManager; - chatManager.CurrentChannel.ValueChanged += currentChatChanged; - chatManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; - chatManager.AvailableChannels.CollectionChanged += availableChannelsChanged; + this.channelManager = channelManager; + channelManager.CurrentChannel.ValueChanged += currentChatChanged; + channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; + channelManager.AvailableChannels.CollectionChanged += availableChannelsChanged; } private void postMessage(TextBox textbox, bool newText) @@ -358,9 +356,9 @@ namespace osu.Game.Overlays return; if (text[0] == '/') - chatManager.PostCommand(text.Substring(1)); + channelManager.PostCommand(text.Substring(1)); else - chatManager.PostMessage(text); + channelManager.PostMessage(text); textbox.Text = string.Empty; } From a9f3885d28c0f8aaf37404fff5908c2d58b93a7a Mon Sep 17 00:00:00 2001 From: miterosan Date: Thu, 12 Apr 2018 23:29:23 +0200 Subject: [PATCH 23/88] Remove not needed using --- osu.Game/Overlays/ChatOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8b405ec980..176c01620f 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -19,7 +19,6 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; From 1b51da70af764574b89af8d8c0c1fa1ee74ea6d1 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 14 Apr 2018 13:23:16 +0200 Subject: [PATCH 24/88] Create an abstraction for APIMessagesRequest --- osu.Game/Online/API/APIMessagesRequest.cs | 28 +++++++++ .../Online/API/Requests/GetMessagesRequest.cs | 7 +-- .../API/Requests/GetPrivateMessagesRequest.cs | 17 +---- osu.Game/Online/Chat/ChannelManager.cs | 62 ++++++++++--------- 4 files changed, 65 insertions(+), 49 deletions(-) create mode 100644 osu.Game/Online/API/APIMessagesRequest.cs diff --git a/osu.Game/Online/API/APIMessagesRequest.cs b/osu.Game/Online/API/APIMessagesRequest.cs new file mode 100644 index 0000000000..e1be6cdb19 --- /dev/null +++ b/osu.Game/Online/API/APIMessagesRequest.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API +{ + public abstract class APIMessagesRequest : APIRequest> + { + private long? sinceId; + + protected APIMessagesRequest(long? sinceId) + { + this.sinceId = sinceId; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + if (sinceId.HasValue) req.AddParameter(@"since", sinceId.Value.ToString()); + + return req; + } + } +} diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index d2c9169c51..cf6a0cb6fd 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -9,12 +9,11 @@ using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { - public class GetMessagesRequest : APIRequest> + public class GetMessagesRequest : APIMessagesRequest { private readonly IEnumerable channels; - private long? since; - public GetMessagesRequest(IEnumerable channels, long? sinceId) + public GetMessagesRequest(IEnumerable channels, long? sinceId) : base(sinceId) { if (channels == null) throw new ArgumentNullException(nameof(channels)); @@ -22,7 +21,6 @@ namespace osu.Game.Online.API.Requests throw new ArgumentException("All channels in the argument channels must have the targettype Channel"); this.channels = channels; - since = sinceId; } protected override WebRequest CreateWebRequest() @@ -31,7 +29,6 @@ namespace osu.Game.Online.API.Requests var req = base.CreateWebRequest(); req.AddParameter(@"channels", channelString); - if (since.HasValue) req.AddParameter(@"since", since.Value.ToString()); return req; } diff --git a/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs index 9ff70f8580..b5d7bb06ee 100644 --- a/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs @@ -1,28 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; -using osu.Framework.IO.Network; -using osu.Game.Online.Chat; - namespace osu.Game.Online.API.Requests { - public class GetPrivateMessagesRequest : APIRequest> + public class GetPrivateMessagesRequest : APIMessagesRequest { private long? since; public GetPrivateMessagesRequest(long? sinceId = null) + : base(sinceId) { - since = sinceId; - } - - protected override WebRequest CreateWebRequest() - { - var request = base.CreateWebRequest(); - if (since.HasValue) - request.AddParameter(@"since", since.Value.ToString()); - - return request; } protected override string Target => @"chat/messages/private"; diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index e5fe3fb56e..3709e42880 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -152,25 +152,46 @@ namespace osu.Game.Online.Chat private void fetchNewMessages() { if (fetchMsgReq == null) - fetchNewChannelMessages(); + fetchMessages( + () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), + messages => + { + if (messages == null) + return; + handleChannelMessages(messages); + lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; + fetchMsgReq = null; + } + ); + if (fetchPrivateMsgReq == null) - fetchNewUserMessages(); + fetchMessages( + () => new GetPrivateMessagesRequest(lastChannelMsgId), + messages => + { + if (messages == null) + return; + handleUserMessages(messages); + lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; + fetchPrivateMsgReq = null; + } + ); } - private void fetchNewUserMessages() + private void fetchMessages(Func messagesRequest, Action> handler) { - fetchPrivateMsgReq = new GetPrivateMessagesRequest(lastUserMsgId); + if (messagesRequest == null) + throw new ArgumentNullException(nameof(messagesRequest)); + if (handler == null) + throw new ArgumentNullException(nameof(handler)); - fetchPrivateMsgReq.Success += messages => - { - handleUserMessages(messages); - lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; - fetchPrivateMsgReq = null; - }; - fetchPrivateMsgReq.Failure += exception => Logger.Error(exception, "Fetching user messages failed."); + var messagesReq = messagesRequest.Invoke(); - api.Queue(fetchPrivateMsgReq); + messagesReq.Success += handler.Invoke; + messagesReq.Failure += exception => Logger.Error(exception, "Fetching messages failed."); + + api.Queue(messagesReq); } private void handleUserMessages(IEnumerable messages) @@ -218,23 +239,6 @@ namespace osu.Game.Online.Chat } } - private void fetchNewChannelMessages() - { - fetchMsgReq = new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId); - - fetchMsgReq.Success += messages => - { - if (messages == null) - return; - handleChannelMessages(messages); - lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; - fetchMsgReq = null; - }; - fetchMsgReq.Failure += exception => Logger.Error(exception, "Fetching channel messages failed."); - - api.Queue(fetchMsgReq); - } - private void handleChannelMessages(IEnumerable messages) { var channels = JoinedChannels.ToList(); From 142e1b858707d2ce8f662e9b8ddb70cf2661a0c8 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 14 Apr 2018 13:32:48 +0200 Subject: [PATCH 25/88] update the line endings --- .../Visual/TestCaseChatTabControl.cs | 194 ++--- osu.Game/Online/API/APIMessagesRequest.cs | 56 +- .../API/Requests/GetPrivateMessagesRequest.cs | 34 +- osu.Game/Online/Chat/ChannelManager.cs | 604 ++++++++-------- osu.Game/Overlays/Chat/ChannelTabControl.cs | 662 +++++++++--------- .../Overlays/Chat/ChatTabItemCloseButton.cs | 110 +-- osu.Game/Overlays/Chat/UserTabControl.cs | 92 +-- osu.Game/Overlays/Chat/UserTabItem.cs | 430 ++++++------ 8 files changed, 1091 insertions(+), 1091 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index 0e0a44ba5d..ac5d59a74f 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -1,97 +1,97 @@ -// 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 osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; -using osu.Game.Online.Chat; -using osu.Game.Overlays.Chat; -using osu.Game.Users; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - public class TestCaseChatTabControl : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChatTabControl), - typeof(ChannelTabControl), - typeof(UserTabControl) - }; - - private readonly ChatTabControl chatTabControl; - - public TestCaseChatTabControl() - { - SpriteText currentText; - Add(new Container - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Children = new Drawable[] - { - chatTabControl = new ChatTabControl - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Height = 50 - }, - new Box - { - Colour = Color4.Black.Opacity(0.1f), - RelativeSizeAxes = Axes.X, - Height = 50, - Depth = -1, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - } - }); - - Add(new Container - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Children = new Drawable[] - { - currentText = new SpriteText - { - Text = "Currently selected chat: " - } - } - }); - - chatTabControl.OnRequestLeave += chat => chatTabControl.RemoveItem(chat); - chatTabControl.Current.ValueChanged += chat => currentText.Text = "Currently selected chat: " + chat.ToString(); - - AddStep("Add random user", () => addUser(RNG.Next(100000), RNG.Next().ToString())); - AddRepeatStep("3 random users", () => addUser(RNG.Next(100000), RNG.Next().ToString()), 3); - AddStep("Add random channel", () => addChannel(RNG.Next().ToString())); - } - - private void addUser(long id, string name) - { - chatTabControl.AddItem(new Channel(new User - { - Id = id, - Username = name - })); - } - - private void addChannel(string name) - { - chatTabControl.AddItem(new Channel - { - Name = name - }); - } - } -} +// 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 osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.MathUtils; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; +using osu.Game.Users; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseChatTabControl : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ChatTabControl), + typeof(ChannelTabControl), + typeof(UserTabControl) + }; + + private readonly ChatTabControl chatTabControl; + + public TestCaseChatTabControl() + { + SpriteText currentText; + Add(new Container + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Children = new Drawable[] + { + chatTabControl = new ChatTabControl + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Height = 50 + }, + new Box + { + Colour = Color4.Black.Opacity(0.1f), + RelativeSizeAxes = Axes.X, + Height = 50, + Depth = -1, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + } + }); + + Add(new Container + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Children = new Drawable[] + { + currentText = new SpriteText + { + Text = "Currently selected chat: " + } + } + }); + + chatTabControl.OnRequestLeave += chat => chatTabControl.RemoveItem(chat); + chatTabControl.Current.ValueChanged += chat => currentText.Text = "Currently selected chat: " + chat.ToString(); + + AddStep("Add random user", () => addUser(RNG.Next(100000), RNG.Next().ToString())); + AddRepeatStep("3 random users", () => addUser(RNG.Next(100000), RNG.Next().ToString()), 3); + AddStep("Add random channel", () => addChannel(RNG.Next().ToString())); + } + + private void addUser(long id, string name) + { + chatTabControl.AddItem(new Channel(new User + { + Id = id, + Username = name + })); + } + + private void addChannel(string name) + { + chatTabControl.AddItem(new Channel + { + Name = name + }); + } + } +} diff --git a/osu.Game/Online/API/APIMessagesRequest.cs b/osu.Game/Online/API/APIMessagesRequest.cs index e1be6cdb19..c957564771 100644 --- a/osu.Game/Online/API/APIMessagesRequest.cs +++ b/osu.Game/Online/API/APIMessagesRequest.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.IO.Network; -using osu.Game.Online.Chat; - -namespace osu.Game.Online.API -{ - public abstract class APIMessagesRequest : APIRequest> - { - private long? sinceId; - - protected APIMessagesRequest(long? sinceId) - { - this.sinceId = sinceId; - } - - protected override WebRequest CreateWebRequest() - { - var req = base.CreateWebRequest(); - - if (sinceId.HasValue) req.AddParameter(@"since", sinceId.Value.ToString()); - - return req; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API +{ + public abstract class APIMessagesRequest : APIRequest> + { + private long? sinceId; + + protected APIMessagesRequest(long? sinceId) + { + this.sinceId = sinceId; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + if (sinceId.HasValue) req.AddParameter(@"since", sinceId.Value.ToString()); + + return req; + } + } +} diff --git a/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs index b5d7bb06ee..dddcbe8939 100644 --- a/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Online.API.Requests -{ - public class GetPrivateMessagesRequest : APIMessagesRequest - { - private long? since; - - public GetPrivateMessagesRequest(long? sinceId = null) - : base(sinceId) - { - } - - protected override string Target => @"chat/messages/private"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Online.API.Requests +{ + public class GetPrivateMessagesRequest : APIMessagesRequest + { + private long? since; + + public GetPrivateMessagesRequest(long? sinceId = null) + : base(sinceId) + { + } + + protected override string Target => @"chat/messages/private"; + } +} diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 3709e42880..069b88565d 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -1,302 +1,302 @@ -// 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.Collections.ObjectModel; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Logging; -using osu.Framework.Threading; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Users; - -namespace osu.Game.Online.Chat -{ - /// - /// Manages everything channel related - /// - public class ChannelManager : Component, IOnlineComponent - { - /// - /// The channels the player joins on startup - /// - private readonly string[] defaultChannels = - { - @"#lazer", - @"#osu", - @"#lobby" - }; - - /// - /// The currently opened channel - /// - public Bindable CurrentChannel { get; } = new Bindable(); - - /// - /// The Channels the player has joined - /// - public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); - - /// - /// The channels available for the player to join - /// - public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); - - private APIAccess api; - private readonly Scheduler scheduler; - private ScheduledDelegate fetchMessagesScheduleder; - private GetMessagesRequest fetchMsgReq; - private GetPrivateMessagesRequest fetchPrivateMsgReq; - private long? lastChannelMsgId; - private long? lastUserMsgId; - - public void OpenChannel(string name) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) - ?? throw new ArgumentException($"Channel {name} was not found."); - } - - public void OpenUserChannel(User user) - { - if (user == null) - throw new ArgumentNullException(nameof(user)); - - CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Target == TargetType.User && c.Id == user.Id) - ?? new Channel(user); - } - - public ChannelManager() - { - CurrentChannel.ValueChanged += currentChannelChanged; - } - - private void currentChannelChanged(Channel channel) - { - if (!JoinedChannels.Contains(channel)) - JoinedChannels.Add(channel); - } - - /// - /// Posts a message to the currently opened channel. - /// - /// The message text that is going to be posted - /// Is true if the message is an action, e.g.: user is currently eating - public void PostMessage(string text, bool isAction = false) - { - if (CurrentChannel.Value == null) - return; - - if (!api.IsLoggedIn) - { - CurrentChannel.Value.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); - return; - } - - var message = new LocalEchoMessage - { - Sender = api.LocalUser.Value, - Timestamp = DateTimeOffset.Now, - TargetType = CurrentChannel.Value.Target, - TargetId = CurrentChannel.Value.Id, - IsAction = isAction, - Content = text - }; - - CurrentChannel.Value.AddLocalEcho(message); - - var req = new PostMessageRequest(message); - req.Failure += e => CurrentChannel.Value?.ReplaceMessage(message, null); - req.Success += m => CurrentChannel.Value?.ReplaceMessage(message, m); - api.Queue(req); - } - - public void PostCommand(string text) - { - if (CurrentChannel.Value == null) - return; - - var parameters = text.Split(new[] { ' ' }, 2); - string command = parameters[0]; - string content = parameters.Length == 2 ? parameters[1] : string.Empty; - - switch (command) - { - case "me": - if (string.IsNullOrWhiteSpace(content)) - { - CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); - break; - } - - PostMessage(content, true); - break; - - case "help": - CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); - break; - - default: - CurrentChannel.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); - break; - } - } - - private void fetchNewMessages() - { - if (fetchMsgReq == null) - fetchMessages( - () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), - messages => - { - if (messages == null) - return; - handleChannelMessages(messages); - lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; - fetchMsgReq = null; - } - ); - - - if (fetchPrivateMsgReq == null) - fetchMessages( - () => new GetPrivateMessagesRequest(lastChannelMsgId), - messages => - { - if (messages == null) - return; - handleUserMessages(messages); - lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; - fetchPrivateMsgReq = null; - } - ); - } - - private void fetchMessages(Func messagesRequest, Action> handler) - { - if (messagesRequest == null) - throw new ArgumentNullException(nameof(messagesRequest)); - if (handler == null) - throw new ArgumentNullException(nameof(handler)); - - var messagesReq = messagesRequest.Invoke(); - - messagesReq.Success += handler.Invoke; - messagesReq.Failure += exception => Logger.Error(exception, "Fetching messages failed."); - - api.Queue(messagesReq); - } - - private void handleUserMessages(IEnumerable messages) - { - var joinedUserChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); - - var outgoingMessages = messages.Where(m => m.Sender.Id == api.LocalUser.Value.Id); - var outgoingMessagesGroups = outgoingMessages.GroupBy(m => m.TargetId); - var incomingMessagesGroups = messages.Except(outgoingMessages).GroupBy(m => m.UserId); - - foreach (var messageGroup in incomingMessagesGroups) - { - var targetUser = messageGroup.First().Sender; - var channel = joinedUserChannels.FirstOrDefault(c => c.Id == targetUser.Id); - - if (channel == null) - { - channel = new Channel(targetUser); - JoinedChannels.Add(channel); - joinedUserChannels.Add(channel); - } - - channel.AddNewMessages(messageGroup.ToArray()); - var outgoingTargetMessages = outgoingMessagesGroups.FirstOrDefault(g => g.Key == targetUser.Id); - if (outgoingTargetMessages != null) - channel.AddNewMessages(outgoingTargetMessages.ToArray()); - } - - var withoutReplyGroups = outgoingMessagesGroups.Where(g => joinedUserChannels.All(m => m.Id != g.Key)); - - foreach (var withoutReplyGroup in withoutReplyGroups) - { - var userReq = new GetUserRequest(withoutReplyGroup.First().TargetId); - - userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations."); - userReq.Success += user => - { - var channel = new Channel(user); - - channel.AddNewMessages(withoutReplyGroup.ToArray()); - JoinedChannels.Add(channel); - }; - - api.Queue(userReq); - } - } - - private void handleChannelMessages(IEnumerable messages) - { - var channels = JoinedChannels.ToList(); - - foreach (var group in messages.GroupBy(m => m.TargetId)) - channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); - } - - private void initializeDefaultChannels() - { - var req = new ListChannelsRequest(); - - req.Success += channels => - { - channels.Where(channel => AvailableChannels.All(c => c.Id != channel.Id)) - .ForEach(channel => AvailableChannels.Add(channel)); - - channels.Where(channel => defaultChannels.Contains(channel.Name)) - .Where(channel => JoinedChannels.All(c => c.Id != channel.Id)) - .ForEach(channel => - { - JoinedChannels.Add(channel); - - var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); - fetchInitialMsgReq.Success += handleChannelMessages; - fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); - api.Queue(fetchInitialMsgReq); - }); - - fetchNewMessages(); - }; - req.Failure += error => Logger.Error(error, "Fetching channel list failed"); - - api.Queue(req); - } - - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - case APIState.Online: - if (JoinedChannels.Count == 0) - initializeDefaultChannels(); - fetchMessagesScheduleder = scheduler.AddDelayed(fetchNewMessages, 1000, true); - break; - default: - fetchMsgReq?.Cancel(); - fetchMsgReq = null; - fetchMessagesScheduleder?.Cancel(); - break; - } - } - - [BackgroundDependencyLoader] - private void load(IAPIProvider api) - { - this.api = this.api; - api.Register(this); - } - } -} +// 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.Collections.ObjectModel; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Framework.Threading; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Users; + +namespace osu.Game.Online.Chat +{ + /// + /// Manages everything channel related + /// + public class ChannelManager : Component, IOnlineComponent + { + /// + /// The channels the player joins on startup + /// + private readonly string[] defaultChannels = + { + @"#lazer", + @"#osu", + @"#lobby" + }; + + /// + /// The currently opened channel + /// + public Bindable CurrentChannel { get; } = new Bindable(); + + /// + /// The Channels the player has joined + /// + public ObservableCollection JoinedChannels { get; } = new ObservableCollection(); + + /// + /// The channels available for the player to join + /// + public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); + + private APIAccess api; + private readonly Scheduler scheduler; + private ScheduledDelegate fetchMessagesScheduleder; + private GetMessagesRequest fetchMsgReq; + private GetPrivateMessagesRequest fetchPrivateMsgReq; + private long? lastChannelMsgId; + private long? lastUserMsgId; + + public void OpenChannel(string name) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) + ?? throw new ArgumentException($"Channel {name} was not found."); + } + + public void OpenUserChannel(User user) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + + CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Target == TargetType.User && c.Id == user.Id) + ?? new Channel(user); + } + + public ChannelManager() + { + CurrentChannel.ValueChanged += currentChannelChanged; + } + + private void currentChannelChanged(Channel channel) + { + if (!JoinedChannels.Contains(channel)) + JoinedChannels.Add(channel); + } + + /// + /// Posts a message to the currently opened channel. + /// + /// The message text that is going to be posted + /// Is true if the message is an action, e.g.: user is currently eating + public void PostMessage(string text, bool isAction = false) + { + if (CurrentChannel.Value == null) + return; + + if (!api.IsLoggedIn) + { + CurrentChannel.Value.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + return; + } + + var message = new LocalEchoMessage + { + Sender = api.LocalUser.Value, + Timestamp = DateTimeOffset.Now, + TargetType = CurrentChannel.Value.Target, + TargetId = CurrentChannel.Value.Id, + IsAction = isAction, + Content = text + }; + + CurrentChannel.Value.AddLocalEcho(message); + + var req = new PostMessageRequest(message); + req.Failure += e => CurrentChannel.Value?.ReplaceMessage(message, null); + req.Success += m => CurrentChannel.Value?.ReplaceMessage(message, m); + api.Queue(req); + } + + public void PostCommand(string text) + { + if (CurrentChannel.Value == null) + return; + + var parameters = text.Split(new[] { ' ' }, 2); + string command = parameters[0]; + string content = parameters.Length == 2 ? parameters[1] : string.Empty; + + switch (command) + { + case "me": + if (string.IsNullOrWhiteSpace(content)) + { + CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); + break; + } + + PostMessage(content, true); + break; + + case "help": + CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); + break; + + default: + CurrentChannel.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); + break; + } + } + + private void fetchNewMessages() + { + if (fetchMsgReq == null) + fetchMessages( + () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), + messages => + { + if (messages == null) + return; + handleChannelMessages(messages); + lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; + fetchMsgReq = null; + } + ); + + + if (fetchPrivateMsgReq == null) + fetchMessages( + () => new GetPrivateMessagesRequest(lastChannelMsgId), + messages => + { + if (messages == null) + return; + handleUserMessages(messages); + lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; + fetchPrivateMsgReq = null; + } + ); + } + + private void fetchMessages(Func messagesRequest, Action> handler) + { + if (messagesRequest == null) + throw new ArgumentNullException(nameof(messagesRequest)); + if (handler == null) + throw new ArgumentNullException(nameof(handler)); + + var messagesReq = messagesRequest.Invoke(); + + messagesReq.Success += handler.Invoke; + messagesReq.Failure += exception => Logger.Error(exception, "Fetching messages failed."); + + api.Queue(messagesReq); + } + + private void handleUserMessages(IEnumerable messages) + { + var joinedUserChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); + + var outgoingMessages = messages.Where(m => m.Sender.Id == api.LocalUser.Value.Id); + var outgoingMessagesGroups = outgoingMessages.GroupBy(m => m.TargetId); + var incomingMessagesGroups = messages.Except(outgoingMessages).GroupBy(m => m.UserId); + + foreach (var messageGroup in incomingMessagesGroups) + { + var targetUser = messageGroup.First().Sender; + var channel = joinedUserChannels.FirstOrDefault(c => c.Id == targetUser.Id); + + if (channel == null) + { + channel = new Channel(targetUser); + JoinedChannels.Add(channel); + joinedUserChannels.Add(channel); + } + + channel.AddNewMessages(messageGroup.ToArray()); + var outgoingTargetMessages = outgoingMessagesGroups.FirstOrDefault(g => g.Key == targetUser.Id); + if (outgoingTargetMessages != null) + channel.AddNewMessages(outgoingTargetMessages.ToArray()); + } + + var withoutReplyGroups = outgoingMessagesGroups.Where(g => joinedUserChannels.All(m => m.Id != g.Key)); + + foreach (var withoutReplyGroup in withoutReplyGroups) + { + var userReq = new GetUserRequest(withoutReplyGroup.First().TargetId); + + userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations."); + userReq.Success += user => + { + var channel = new Channel(user); + + channel.AddNewMessages(withoutReplyGroup.ToArray()); + JoinedChannels.Add(channel); + }; + + api.Queue(userReq); + } + } + + private void handleChannelMessages(IEnumerable messages) + { + var channels = JoinedChannels.ToList(); + + foreach (var group in messages.GroupBy(m => m.TargetId)) + channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); + } + + private void initializeDefaultChannels() + { + var req = new ListChannelsRequest(); + + req.Success += channels => + { + channels.Where(channel => AvailableChannels.All(c => c.Id != channel.Id)) + .ForEach(channel => AvailableChannels.Add(channel)); + + channels.Where(channel => defaultChannels.Contains(channel.Name)) + .Where(channel => JoinedChannels.All(c => c.Id != channel.Id)) + .ForEach(channel => + { + JoinedChannels.Add(channel); + + var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); + fetchInitialMsgReq.Success += handleChannelMessages; + fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); + api.Queue(fetchInitialMsgReq); + }); + + fetchNewMessages(); + }; + req.Failure += error => Logger.Error(error, "Fetching channel list failed"); + + api.Queue(req); + } + + public void APIStateChanged(APIAccess api, APIState state) + { + switch (state) + { + case APIState.Online: + if (JoinedChannels.Count == 0) + initializeDefaultChannels(); + fetchMessagesScheduleder = scheduler.AddDelayed(fetchNewMessages, 1000, true); + break; + default: + fetchMsgReq?.Cancel(); + fetchMsgReq = null; + fetchMessagesScheduleder?.Cancel(); + break; + } + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + this.api = this.api; + api.Register(this); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelTabControl.cs b/osu.Game/Overlays/Chat/ChannelTabControl.cs index 9c07294a50..66ee4285d3 100644 --- a/osu.Game/Overlays/Chat/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/ChannelTabControl.cs @@ -1,331 +1,331 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Chat; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using System; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Chat -{ - public class ChannelTabControl : OsuTabControl - { - private const float shear_width = 10; - - public Action OnRequestLeave; - - public readonly Bindable ChannelSelectorActive = new Bindable(); - - private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; - - public ChannelTabControl() - { - TabContainer.Margin = new MarginPadding { Left = 50 }; - TabContainer.Spacing = new Vector2(-shear_width, 0); - TabContainer.Masking = false; - - AddInternal(new SpriteIcon - { - Icon = FontAwesome.fa_comments, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Margin = new MarginPadding(10), - }); - - AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); - - ChannelSelectorActive.BindTo(selectorTab.Active); - } - - protected override void AddTabItem(TabItem item, bool addToDropdown = true) - { - if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) - // performTabSort might've made selectorTab's position wonky, fix it - TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); - - base.AddTabItem(item, addToDropdown); - } - - protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; - - protected override void SelectTab(TabItem tab) - { - if (tab is ChannelTabItem.ChannelSelectorTabItem) - { - tab.Active.Toggle(); - return; - } - - selectorTab.Active.Value = false; - - base.SelectTab(tab); - } - - private void tabCloseRequested(TabItem tab) - { - int totalTabs = TabContainer.Count - 1; // account for selectorTab - int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); - - if (tab == SelectedTab && totalTabs > 1) - // Select the tab after tab-to-be-removed's index, or the tab before if current == last - SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); - else if (totalTabs == 1 && !selectorTab.Active) - // Open channel selection overlay if all channel tabs will be closed after removing this tab - SelectTab(selectorTab); - - OnRequestLeave?.Invoke(tab.Value); - } - - private class ChannelTabItem : TabItem - { - private Color4 backgroundInactive; - private Color4 backgroundHover; - private Color4 backgroundActive; - - public override bool IsRemovable => !Pinned; - - private readonly SpriteText text; - private readonly SpriteText textBold; - private readonly ClickableContainer closeButton; - private readonly Box box; - private readonly Box highlightBox; - private readonly SpriteIcon icon; - - public Action OnRequestClose; - - private void updateState() - { - if (Active) - fadeActive(); - else - fadeInactive(); - } - - private const float transition_length = 400; - - private void fadeActive() - { - this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); - - box.FadeColour(backgroundActive, transition_length, Easing.OutQuint); - highlightBox.FadeIn(transition_length, Easing.OutQuint); - - text.FadeOut(transition_length, Easing.OutQuint); - textBold.FadeIn(transition_length, Easing.OutQuint); - } - - private void fadeInactive() - { - this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); - - box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint); - highlightBox.FadeOut(transition_length, Easing.OutQuint); - - text.FadeIn(transition_length, Easing.OutQuint); - textBold.FadeOut(transition_length, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - if (IsRemovable) - closeButton.FadeIn(200, Easing.OutQuint); - - if (!Active) - box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - closeButton.FadeOut(200, Easing.OutQuint); - updateState(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - backgroundActive = colours.ChatBlue; - backgroundInactive = colours.Gray4; - backgroundHover = colours.Gray7; - - highlightBox.Colour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - public ChannelTabItem(Channel value) : base(value) - { - Width = 150; - - RelativeSizeAxes = Axes.Y; - - Anchor = Anchor.BottomLeft; - Origin = Anchor.BottomLeft; - - Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); - - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 10, - Colour = Color4.Black.Opacity(0.2f), - }; - - Children = new Drawable[] - { - box = new Box - { - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Both, - }, - highlightBox = new Box - { - Width = 5, - Alpha = 0, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Y, - }, - new Container - { - Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - icon = new SpriteIcon - { - Icon = FontAwesome.fa_hashtag, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = Color4.Black, - X = -10, - Alpha = 0.2f, - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - }, - text = new OsuSpriteText - { - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - TextSize = 18, - }, - textBold = new OsuSpriteText - { - Alpha = 0, - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - Font = @"Exo2.0-Bold", - TextSize = 18, - }, - closeButton = new CloseButton - { - Alpha = 0, - Margin = new MarginPadding { Right = 20 }, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Action = delegate - { - if (IsRemovable) OnRequestClose?.Invoke(this); - }, - }, - }, - }, - }; - } - - public class CloseButton : OsuClickableContainer - { - private readonly SpriteIcon icon; - - public CloseButton() - { - Size = new Vector2(20); - - Child = icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.75f), - Icon = FontAwesome.fa_close, - RelativeSizeAxes = Axes.Both, - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - icon.ScaleTo(0.5f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - icon.ScaleTo(0.75f, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - protected override bool OnHover(InputState state) - { - icon.FadeColour(Color4.Red, 200, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - icon.FadeColour(Color4.White, 200, Easing.OutQuint); - base.OnHoverLost(state); - } - } - - public class ChannelSelectorTabItem : ChannelTabItem - { - public override bool IsRemovable => false; - - public ChannelSelectorTabItem(Channel value) : base(value) - { - Depth = float.MaxValue; - Width = 45; - - icon.Alpha = 0; - - text.TextSize = 45; - textBold.TextSize = 45; - } - - [BackgroundDependencyLoader] - private new void load(OsuColour colour) - { - backgroundInactive = colour.Gray2; - backgroundActive = colour.Gray3; - } - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using System; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelTabControl : OsuTabControl + { + private const float shear_width = 10; + + public Action OnRequestLeave; + + public readonly Bindable ChannelSelectorActive = new Bindable(); + + private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; + + public ChannelTabControl() + { + TabContainer.Margin = new MarginPadding { Left = 50 }; + TabContainer.Spacing = new Vector2(-shear_width, 0); + TabContainer.Masking = false; + + AddInternal(new SpriteIcon + { + Icon = FontAwesome.fa_comments, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Margin = new MarginPadding(10), + }); + + AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); + + ChannelSelectorActive.BindTo(selectorTab.Active); + } + + protected override void AddTabItem(TabItem item, bool addToDropdown = true) + { + if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) + // performTabSort might've made selectorTab's position wonky, fix it + TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); + + base.AddTabItem(item, addToDropdown); + } + + protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + + protected override void SelectTab(TabItem tab) + { + if (tab is ChannelTabItem.ChannelSelectorTabItem) + { + tab.Active.Toggle(); + return; + } + + selectorTab.Active.Value = false; + + base.SelectTab(tab); + } + + private void tabCloseRequested(TabItem tab) + { + int totalTabs = TabContainer.Count - 1; // account for selectorTab + int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); + + if (tab == SelectedTab && totalTabs > 1) + // Select the tab after tab-to-be-removed's index, or the tab before if current == last + SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); + else if (totalTabs == 1 && !selectorTab.Active) + // Open channel selection overlay if all channel tabs will be closed after removing this tab + SelectTab(selectorTab); + + OnRequestLeave?.Invoke(tab.Value); + } + + private class ChannelTabItem : TabItem + { + private Color4 backgroundInactive; + private Color4 backgroundHover; + private Color4 backgroundActive; + + public override bool IsRemovable => !Pinned; + + private readonly SpriteText text; + private readonly SpriteText textBold; + private readonly ClickableContainer closeButton; + private readonly Box box; + private readonly Box highlightBox; + private readonly SpriteIcon icon; + + public Action OnRequestClose; + + private void updateState() + { + if (Active) + fadeActive(); + else + fadeInactive(); + } + + private const float transition_length = 400; + + private void fadeActive() + { + this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); + + box.FadeColour(backgroundActive, transition_length, Easing.OutQuint); + highlightBox.FadeIn(transition_length, Easing.OutQuint); + + text.FadeOut(transition_length, Easing.OutQuint); + textBold.FadeIn(transition_length, Easing.OutQuint); + } + + private void fadeInactive() + { + this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); + + box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint); + highlightBox.FadeOut(transition_length, Easing.OutQuint); + + text.FadeIn(transition_length, Easing.OutQuint); + textBold.FadeOut(transition_length, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + if (IsRemovable) + closeButton.FadeIn(200, Easing.OutQuint); + + if (!Active) + box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + closeButton.FadeOut(200, Easing.OutQuint); + updateState(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + backgroundActive = colours.ChatBlue; + backgroundInactive = colours.Gray4; + backgroundHover = colours.Gray7; + + highlightBox.Colour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + } + + public ChannelTabItem(Channel value) : base(value) + { + Width = 150; + + RelativeSizeAxes = Axes.Y; + + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + + Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 10, + Colour = Color4.Black.Opacity(0.2f), + }; + + Children = new Drawable[] + { + box = new Box + { + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Both, + }, + highlightBox = new Box + { + Width = 5, + Alpha = 0, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Y, + }, + new Container + { + Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + icon = new SpriteIcon + { + Icon = FontAwesome.fa_hashtag, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = Color4.Black, + X = -10, + Alpha = 0.2f, + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + }, + text = new OsuSpriteText + { + Margin = new MarginPadding(5), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.ToString(), + TextSize = 18, + }, + textBold = new OsuSpriteText + { + Alpha = 0, + Margin = new MarginPadding(5), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.ToString(), + Font = @"Exo2.0-Bold", + TextSize = 18, + }, + closeButton = new CloseButton + { + Alpha = 0, + Margin = new MarginPadding { Right = 20 }, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Action = delegate + { + if (IsRemovable) OnRequestClose?.Invoke(this); + }, + }, + }, + }, + }; + } + + public class CloseButton : OsuClickableContainer + { + private readonly SpriteIcon icon; + + public CloseButton() + { + Size = new Vector2(20); + + Child = icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.75f), + Icon = FontAwesome.fa_close, + RelativeSizeAxes = Axes.Both, + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.5f, 1000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(0.75f, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnHover(InputState state) + { + icon.FadeColour(Color4.Red, 200, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + icon.FadeColour(Color4.White, 200, Easing.OutQuint); + base.OnHoverLost(state); + } + } + + public class ChannelSelectorTabItem : ChannelTabItem + { + public override bool IsRemovable => false; + + public ChannelSelectorTabItem(Channel value) : base(value) + { + Depth = float.MaxValue; + Width = 45; + + icon.Alpha = 0; + + text.TextSize = 45; + textBold.TextSize = 45; + } + + [BackgroundDependencyLoader] + private new void load(OsuColour colour) + { + backgroundInactive = colour.Gray2; + backgroundActive = colour.Gray3; + } + } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs b/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs index e87396356a..0ec2e52963 100644 --- a/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs +++ b/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs @@ -1,55 +1,55 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Chat -{ - public class ChatTabItemCloseButton : OsuClickableContainer - { - private readonly SpriteIcon icon; - - public ChatTabItemCloseButton() - { - Size = new Vector2(20); - - Child = icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.75f), - Icon = FontAwesome.fa_close, - RelativeSizeAxes = Axes.Both, - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - icon.ScaleTo(0.5f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - icon.ScaleTo(0.75f, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - protected override bool OnHover(InputState state) - { - icon.FadeColour(Color4.Red, 200, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - icon.FadeColour(Color4.White, 200, Easing.OutQuint); - base.OnHoverLost(state); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTabItemCloseButton : OsuClickableContainer + { + private readonly SpriteIcon icon; + + public ChatTabItemCloseButton() + { + Size = new Vector2(20); + + Child = icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.75f), + Icon = FontAwesome.fa_close, + RelativeSizeAxes = Axes.Both, + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.5f, 1000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(0.75f, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnHover(InputState state) + { + icon.FadeColour(Color4.Red, 200, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + icon.FadeColour(Color4.White, 200, Easing.OutQuint); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Chat/UserTabControl.cs b/osu.Game/Overlays/Chat/UserTabControl.cs index 5e23b4c2eb..7da7cab900 100644 --- a/osu.Game/Overlays/Chat/UserTabControl.cs +++ b/osu.Game/Overlays/Chat/UserTabControl.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Chat; -using OpenTK; - -namespace osu.Game.Overlays.Chat -{ - public class UserTabControl : OsuTabControl - { - protected override TabItem CreateTabItem(Channel value) - { - if (value.Target != TargetType.User) - throw new ArgumentException("Argument value needs to have the targettype user."); - return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; - } - - public Action OnRequestLeave; - - public UserTabControl() - { - TabContainer.Spacing = new Vector2(-10, 0); - TabContainer.Masking = false; - Margin = new MarginPadding - { - Right = 10 - }; - } - - private void tabCloseRequested(TabItem priv) - { - int totalTabs = TabContainer.Count -1; // account for selectorTab - int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(priv), 1, totalTabs); - - if (priv == SelectedTab && totalTabs > 1) - // Select the tab after tab-to-be-removed's index, or the tab before if current == last - SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); - - OnRequestLeave?.Invoke(priv.Value); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; +using OpenTK; + +namespace osu.Game.Overlays.Chat +{ + public class UserTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(Channel value) + { + if (value.Target != TargetType.User) + throw new ArgumentException("Argument value needs to have the targettype user."); + return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + } + + public Action OnRequestLeave; + + public UserTabControl() + { + TabContainer.Spacing = new Vector2(-10, 0); + TabContainer.Masking = false; + Margin = new MarginPadding + { + Right = 10 + }; + } + + private void tabCloseRequested(TabItem priv) + { + int totalTabs = TabContainer.Count -1; // account for selectorTab + int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(priv), 1, totalTabs); + + if (priv == SelectedTab && totalTabs > 1) + // Select the tab after tab-to-be-removed's index, or the tab before if current == last + SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); + + OnRequestLeave?.Invoke(priv.Value); + } + } +} diff --git a/osu.Game/Overlays/Chat/UserTabItem.cs b/osu.Game/Overlays/Chat/UserTabItem.cs index 3dfec0b7ea..86dc6c23e6 100644 --- a/osu.Game/Overlays/Chat/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/UserTabItem.cs @@ -1,215 +1,215 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; -using osu.Game.Screens.Menu; -using osu.Game.Users; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Chat -{ - public class UserTabItem : TabItem - { - private static readonly Vector2 shear = new Vector2(1f / 5f, 0); - public override bool IsRemovable => true; - - private readonly Box highlightBox; - private readonly Container backgroundContainer; - private readonly Box backgroundBox; - private readonly OsuSpriteText username; - private readonly Avatar avatarContainer; - private readonly ChatTabItemCloseButton closeButton; - - public UserTabItem(Channel value) - : base(value) - { - if (value.Target != TargetType.User) - throw new ArgumentException("Argument value needs to have the targettype user!"); - - AutoSizeAxes = Axes.X; - Height = 50; - Origin = Anchor.BottomRight; - Anchor = Anchor.BottomRight; - EdgeEffect = activateEdgeEffect; - Masking = true; - Shear = shear; - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - backgroundBox = new Box - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - EdgeSmoothness = new Vector2(1, 0), - }, - } - }, - highlightBox = new Box - { - Width = 5, - BypassAutoSizeAxes = Axes.X, - Alpha = 0, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Y, - Colour = new OsuColour().Yellow - }, - new Container - { - Masking = true, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Child = new FlowContainerWithOrigin - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - X = -5, - Direction = FillDirection.Horizontal, - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Shear = -shear, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Margin = new MarginPadding - { - Horizontal = 5 - }, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_eercast, - Origin = Anchor.Centre, - Scale = new Vector2(1.2f), - X = -5, - Y = 5, - Anchor = Anchor.Centre, - Colour = new OsuColour().BlueDarker, - RelativeSizeAxes = Axes.Both, - }, - new CircularContainer - { - RelativeSizeAxes = Axes.Y, - Scale = new Vector2(0.95f), - AutoSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - Child = new DelayedLoadWrapper(new Avatar(value.JoinedUsers.First()) - { - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), - }) - { - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - } - }, - } - }, - username = new OsuSpriteText - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.Name, - Margin = new MarginPadding(1), - TextSize = 18, - Alpha = 0, - }, - closeButton = new ChatTabItemCloseButton - { - Height = 1, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Alpha = 0, - Margin = new MarginPadding - { - Right = 5 - }, - RelativeSizeAxes = Axes.Y, - Action = delegate - { - if (IsRemovable) OnRequestClose?.Invoke(this); - }, - }, - } - } - } - }; - } - - public Action OnRequestClose; - - private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 15, - Colour = Color4.Black.Opacity(0.4f), - }; - - protected override void OnActivated() - { - const int activate_length = 1000; - - backgroundBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); - highlightBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); - highlightBox.FadeIn(activate_length, Easing.OutQuint); - username.FadeIn(activate_length, Easing.OutQuint); - username.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); - closeButton.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); - closeButton.FadeIn(activate_length, Easing.OutQuint); - TweenEdgeEffectTo(activateEdgeEffect, activate_length); - } - - private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 10, - Colour = Color4.Black.Opacity(0.2f), - }; - - protected override void OnDeactivated() - { - const int deactivate_length = 500; - - backgroundBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); - highlightBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); - highlightBox.FadeOut(deactivate_length, Easing.OutQuint); - username.FadeOut(deactivate_length, Easing.OutQuint); - username.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); - closeButton.FadeOut(deactivate_length, Easing.OutQuint); - closeButton.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); - TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - var user = Value.JoinedUsers.First(); - - backgroundBox.Colour = user.Colour != null ? OsuColour.FromHex(user.Colour) : colours.BlueDark; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osu.Game.Screens.Menu; +using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Chat +{ + public class UserTabItem : TabItem + { + private static readonly Vector2 shear = new Vector2(1f / 5f, 0); + public override bool IsRemovable => true; + + private readonly Box highlightBox; + private readonly Container backgroundContainer; + private readonly Box backgroundBox; + private readonly OsuSpriteText username; + private readonly Avatar avatarContainer; + private readonly ChatTabItemCloseButton closeButton; + + public UserTabItem(Channel value) + : base(value) + { + if (value.Target != TargetType.User) + throw new ArgumentException("Argument value needs to have the targettype user!"); + + AutoSizeAxes = Axes.X; + Height = 50; + Origin = Anchor.BottomRight; + Anchor = Anchor.BottomRight; + EdgeEffect = activateEdgeEffect; + Masking = true; + Shear = shear; + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + backgroundBox = new Box + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + EdgeSmoothness = new Vector2(1, 0), + }, + } + }, + highlightBox = new Box + { + Width = 5, + BypassAutoSizeAxes = Axes.X, + Alpha = 0, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Y, + Colour = new OsuColour().Yellow + }, + new Container + { + Masking = true, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Child = new FlowContainerWithOrigin + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + X = -5, + Direction = FillDirection.Horizontal, + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Shear = -shear, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding + { + Horizontal = 5 + }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_eercast, + Origin = Anchor.Centre, + Scale = new Vector2(1.2f), + X = -5, + Y = 5, + Anchor = Anchor.Centre, + Colour = new OsuColour().BlueDarker, + RelativeSizeAxes = Axes.Both, + }, + new CircularContainer + { + RelativeSizeAxes = Axes.Y, + Scale = new Vector2(0.95f), + AutoSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Child = new DelayedLoadWrapper(new Avatar(value.JoinedUsers.First()) + { + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), + }) + { + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + } + }, + } + }, + username = new OsuSpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.Name, + Margin = new MarginPadding(1), + TextSize = 18, + Alpha = 0, + }, + closeButton = new ChatTabItemCloseButton + { + Height = 1, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Alpha = 0, + Margin = new MarginPadding + { + Right = 5 + }, + RelativeSizeAxes = Axes.Y, + Action = delegate + { + if (IsRemovable) OnRequestClose?.Invoke(this); + }, + }, + } + } + } + }; + } + + public Action OnRequestClose; + + private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 15, + Colour = Color4.Black.Opacity(0.4f), + }; + + protected override void OnActivated() + { + const int activate_length = 1000; + + backgroundBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); + highlightBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); + highlightBox.FadeIn(activate_length, Easing.OutQuint); + username.FadeIn(activate_length, Easing.OutQuint); + username.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); + closeButton.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); + closeButton.FadeIn(activate_length, Easing.OutQuint); + TweenEdgeEffectTo(activateEdgeEffect, activate_length); + } + + private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 10, + Colour = Color4.Black.Opacity(0.2f), + }; + + protected override void OnDeactivated() + { + const int deactivate_length = 500; + + backgroundBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); + highlightBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); + highlightBox.FadeOut(deactivate_length, Easing.OutQuint); + username.FadeOut(deactivate_length, Easing.OutQuint); + username.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); + closeButton.FadeOut(deactivate_length, Easing.OutQuint); + closeButton.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); + TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + var user = Value.JoinedUsers.First(); + + backgroundBox.Colour = user.Colour != null ? OsuColour.FromHex(user.Colour) : colours.BlueDark; + } + } +} From 29e8c70ed77766f5718c3b272c53ddb425a943cc Mon Sep 17 00:00:00 2001 From: miterosan Date: Wed, 18 Apr 2018 20:46:42 +0200 Subject: [PATCH 26/88] Only use one tabControl (channeltabcontrol). Fix that the Channel messages did not refresh. --- .../Visual/TestCaseChatTabControl.cs | 3 +- osu.Game/Online/Chat/ChannelManager.cs | 7 +-- osu.Game/OsuGameBase.cs | 4 +- osu.Game/Overlays/Chat/ChannelTabControl.cs | 13 ++++- osu.Game/Overlays/Chat/ChatTabControl.cs | 57 +++---------------- osu.Game/Overlays/Chat/DrawableChat.cs | 2 +- osu.Game/Overlays/Chat/UserTabControl.cs | 46 --------------- osu.Game/Overlays/Chat/UserTabItem.cs | 4 +- osu.Game/Overlays/ChatOverlay.cs | 1 + 9 files changed, 30 insertions(+), 107 deletions(-) delete mode 100644 osu.Game/Overlays/Chat/UserTabControl.cs diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index ac5d59a74f..4c408806a4 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -21,8 +21,7 @@ namespace osu.Game.Tests.Visual public override IReadOnlyList RequiredTypes => new[] { typeof(ChatTabControl), - typeof(ChannelTabControl), - typeof(UserTabControl) + typeof(ChannelTabControl) }; private readonly ChatTabControl chatTabControl; diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 069b88565d..0b658428fe 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -47,8 +47,7 @@ namespace osu.Game.Online.Chat /// public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); - private APIAccess api; - private readonly Scheduler scheduler; + private IAPIProvider api; private ScheduledDelegate fetchMessagesScheduleder; private GetMessagesRequest fetchMsgReq; private GetPrivateMessagesRequest fetchPrivateMsgReq; @@ -282,7 +281,7 @@ namespace osu.Game.Online.Chat case APIState.Online: if (JoinedChannels.Count == 0) initializeDefaultChannels(); - fetchMessagesScheduleder = scheduler.AddDelayed(fetchNewMessages, 1000, true); + fetchMessagesScheduleder = Scheduler.AddDelayed(fetchNewMessages, 1000, true); break; default: fetchMsgReq?.Cancel(); @@ -295,7 +294,7 @@ namespace osu.Game.Online.Chat [BackgroundDependencyLoader] private void load(IAPIProvider api) { - this.api = this.api; + this.api = api; api.Register(this); } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3c34c0c108..c3e3881a28 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -118,7 +118,9 @@ namespace osu.Game dependencies.Cache(api); dependencies.CacheAs(api); - dependencies.Cache(new ChannelManager()); + var channelManager = new ChannelManager(); + dependencies.Inject(channelManager); + dependencies.Cache(channelManager); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); diff --git a/osu.Game/Overlays/Chat/ChannelTabControl.cs b/osu.Game/Overlays/Chat/ChannelTabControl.cs index 66ee4285d3..b6c3d4900b 100644 --- a/osu.Game/Overlays/Chat/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/ChannelTabControl.cs @@ -60,7 +60,18 @@ namespace osu.Game.Overlays.Chat base.AddTabItem(item, addToDropdown); } - protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + protected override TabItem CreateTabItem(Channel value) + { + switch (value.Target) + { + case TargetType.Channel: + return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + case TargetType.User: + return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + default: + throw new InvalidOperationException("Only TargetType User and Channel are supported."); + } + } protected override void SelectTab(TabItem tab) { diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index a95b96b8fb..42ea643087 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -14,7 +14,6 @@ namespace osu.Game.Overlays.Chat public class ChatTabControl : Container, IHasCurrentValue { public readonly ChannelTabControl ChannelTabControl; - public readonly UserTabControl UserTabControl; public Bindable Current { get; } = new Bindable(); public Action OnRequestLeave; @@ -27,20 +26,11 @@ namespace osu.Game.Overlays.Chat { ChannelTabControl = new ChannelTabControl { - Width = 0.5f, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, OnRequestLeave = channel => OnRequestLeave?.Invoke(channel) }, - UserTabControl = new UserTabControl - { - Width = 0.5f, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - RelativeSizeAxes = Axes.Both, - OnRequestLeave = channel => OnRequestLeave?.Invoke(channel) - }, }; Current.ValueChanged += currentTabChanged; @@ -49,58 +39,25 @@ namespace osu.Game.Overlays.Chat if (channel != null) Current.Value = channel; }; - UserTabControl.Current.ValueChanged += channel => - { - if (channel != null) - Current.Value = channel; - }; } private void currentTabChanged(Channel channel) { - switch (channel.Target) - { - case TargetType.User: - UserTabControl.Current.Value = channel; - ChannelTabControl.Current.Value = null; - break; - case TargetType.Channel: - ChannelTabControl.Current.Value = channel; - UserTabControl.Current.Value = null; - break; - } + ChannelTabControl.Current.Value = channel; } public void AddItem(Channel channel) { - switch (channel.Target) - { - case TargetType.User: - UserTabControl.AddItem(channel); - break; - case TargetType.Channel: - ChannelTabControl.AddItem(channel); - break; - } + ChannelTabControl.AddItem(channel); + if (Current.Value == null) + Current.Value = channel; } public void RemoveItem(Channel channel) { - Channel nextSelectedChannel = null; - - switch (channel.Target) - { - case TargetType.User: - UserTabControl.RemoveItem(channel); - if (Current.Value == channel) - Current.Value = UserTabControl.Items.FirstOrDefault() ?? ChannelTabControl.Items.FirstOrDefault(); - break; - case TargetType.Channel: - ChannelTabControl.RemoveItem(channel); - if (Current.Value == channel) - Current.Value = ChannelTabControl.Items.FirstOrDefault() ?? UserTabControl.Items.FirstOrDefault(); - break; - } + ChannelTabControl.RemoveItem(channel); + if (Current.Value == channel) + Current.Value = ChannelTabControl.Items.FirstOrDefault(); } } } diff --git a/osu.Game/Overlays/Chat/DrawableChat.cs b/osu.Game/Overlays/Chat/DrawableChat.cs index 83cabfafa1..30ba105b5c 100644 --- a/osu.Game/Overlays/Chat/DrawableChat.cs +++ b/osu.Game/Overlays/Chat/DrawableChat.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Chat [BackgroundDependencyLoader] private void load() { - Scheduler.Add(() => newMessagesArrived(Chat.Messages)); + newMessagesArrived(Chat.Messages); } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Chat/UserTabControl.cs b/osu.Game/Overlays/Chat/UserTabControl.cs deleted file mode 100644 index 7da7cab900..0000000000 --- a/osu.Game/Overlays/Chat/UserTabControl.cs +++ /dev/null @@ -1,46 +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 osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Chat; -using OpenTK; - -namespace osu.Game.Overlays.Chat -{ - public class UserTabControl : OsuTabControl - { - protected override TabItem CreateTabItem(Channel value) - { - if (value.Target != TargetType.User) - throw new ArgumentException("Argument value needs to have the targettype user."); - return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; - } - - public Action OnRequestLeave; - - public UserTabControl() - { - TabContainer.Spacing = new Vector2(-10, 0); - TabContainer.Masking = false; - Margin = new MarginPadding - { - Right = 10 - }; - } - - private void tabCloseRequested(TabItem priv) - { - int totalTabs = TabContainer.Count -1; // account for selectorTab - int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(priv), 1, totalTabs); - - if (priv == SelectedTab && totalTabs > 1) - // Select the tab after tab-to-be-removed's index, or the tab before if current == last - SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); - - OnRequestLeave?.Invoke(priv.Value); - } - } -} diff --git a/osu.Game/Overlays/Chat/UserTabItem.cs b/osu.Game/Overlays/Chat/UserTabItem.cs index 86dc6c23e6..5f81e9fe9e 100644 --- a/osu.Game/Overlays/Chat/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/UserTabItem.cs @@ -39,8 +39,8 @@ namespace osu.Game.Overlays.Chat AutoSizeAxes = Axes.X; Height = 50; - Origin = Anchor.BottomRight; - Anchor = Anchor.BottomRight; + Origin = Anchor.BottomLeft; + Anchor = Anchor.BottomLeft; EdgeEffect = activateEdgeEffect; Masking = true; Shear = shear; diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index ec8851799a..4d13e05c9e 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -345,6 +345,7 @@ namespace osu.Game.Overlays channelManager.CurrentChannel.ValueChanged += currentChatChanged; channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; channelManager.AvailableChannels.CollectionChanged += availableChannelsChanged; + Add(channelManager); } private void postMessage(TextBox textbox, bool newText) From 1bbeb6d70e6210ea011bf12635622a0690e33b7e Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 6 May 2018 19:54:41 +0200 Subject: [PATCH 27/88] Make the sinceId readonly --- osu.Game/Online/API/APIMessagesRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMessagesRequest.cs b/osu.Game/Online/API/APIMessagesRequest.cs index c957564771..991096c0af 100644 --- a/osu.Game/Online/API/APIMessagesRequest.cs +++ b/osu.Game/Online/API/APIMessagesRequest.cs @@ -9,7 +9,7 @@ namespace osu.Game.Online.API { public abstract class APIMessagesRequest : APIRequest> { - private long? sinceId; + private readonly long? sinceId; protected APIMessagesRequest(long? sinceId) { From 6e0099d2b137dfe0fae02900d7d7eb0fede2763c Mon Sep 17 00:00:00 2001 From: miterosan Date: Thu, 31 May 2018 22:56:12 +0200 Subject: [PATCH 28/88] Fix webexception due to invalid user ids --- .../Visual/TestCaseChatTabControl.cs | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index 4c408806a4..e530c1b72a 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -3,12 +3,16 @@ using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Users; @@ -71,18 +75,19 @@ namespace osu.Game.Tests.Visual chatTabControl.OnRequestLeave += chat => chatTabControl.RemoveItem(chat); chatTabControl.Current.ValueChanged += chat => currentText.Text = "Currently selected chat: " + chat.ToString(); - AddStep("Add random user", () => addUser(RNG.Next(100000), RNG.Next().ToString())); - AddRepeatStep("3 random users", () => addUser(RNG.Next(100000), RNG.Next().ToString()), 3); + AddStep("Add random user", addRandomUser); + AddRepeatStep("Add 3 random users", addRandomUser, 3); AddStep("Add random channel", () => addChannel(RNG.Next().ToString())); } - private void addUser(long id, string name) + private List users; + + private void addRandomUser() { - chatTabControl.AddItem(new Channel(new User - { - Id = id, - Username = name - })); + if (users == null || users.Count == 0) + return; + + chatTabControl.AddItem(new Channel(users[RNG.Next(0, users.Count - 1)])); } private void addChannel(string name) @@ -92,5 +97,14 @@ namespace osu.Game.Tests.Visual Name = name }); } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + GetUsersRequest req = new GetUsersRequest(); + req.Success += list => users = list.Select(e => e.User).ToList(); + + api.Queue(req); + } } } From 1f04dd9adabe868b2ab0a6f04b3dac072554f990 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 17 Jun 2018 15:08:13 +0200 Subject: [PATCH 29/88] Cache the dependencies using the static method of the class Dependencies. --- osu.Game.Tests/Visual/TestCaseChatLink.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 071a79dc8c..51def9be7d 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -54,9 +54,9 @@ namespace osu.Game.Tests.Visual var chatManager = new ChannelManager(); chatManager.AvailableChannels.Add(new Channel { Name = "#english"}); chatManager.AvailableChannels.Add(new Channel { Name = "#japanese" }); - dependencies.Cache(chatManager); + Dependencies.Cache(chatManager); - dependencies.Cache(new ChatOverlay()); + Dependencies.Cache(new ChatOverlay()); testLinksGeneral(); testEcho(); From 709d134f49c35e31606ef242532c6bb4b52c34f6 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 18:23:40 +0200 Subject: [PATCH 30/88] Rename DrawableChat to DrawableChannel --- .../Overlays/Chat/{DrawableChat.cs => DrawableChannel.cs} | 0 osu.Game/Overlays/ChatOverlay.cs | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Overlays/Chat/{DrawableChat.cs => DrawableChannel.cs} (100%) diff --git a/osu.Game/Overlays/Chat/DrawableChat.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs similarity index 100% rename from osu.Game/Overlays/Chat/DrawableChat.cs rename to osu.Game/Overlays/Chat/DrawableChannel.cs diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 438277bfe6..3df57f86b7 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -29,8 +29,8 @@ namespace osu.Game.Overlays private ChannelManager channelManager; - private readonly Container currentChatContainer; - private readonly List loadedChannels = new List(); + private readonly Container currentChatContainer; + private readonly List loadedChannels = new List(); private readonly LoadingAnimation loading; @@ -101,7 +101,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, }, - currentChatContainer = new Container + currentChatContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -240,7 +240,7 @@ namespace osu.Game.Overlays currentChatContainer.FadeOut(500, Easing.OutQuint); loading.Show(); - loaded = new DrawableChat(chat); + loaded = new DrawableChannel(chat); loadedChannels.Add(loaded); LoadComponentAsync(loaded, l => { From 5e95995429718caaae78d9b54bf1edb7a3ddfaf3 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 18:30:41 +0200 Subject: [PATCH 31/88] Rename chat to channel --- osu.Game/Overlays/Chat/DrawableChannel.cs | 24 +++++++++++------------ osu.Game/Overlays/ChatOverlay.cs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 30ba105b5c..bcc8879902 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -15,15 +15,15 @@ using osu.Game.Online.Chat; namespace osu.Game.Overlays.Chat { - public class DrawableChat : Container + public class DrawableChannel : Container { - public readonly Channel Chat; + public readonly Channel Channel; private readonly ChatLineContainer flow; private readonly ScrollContainer scroll; - public DrawableChat(Channel chat) + public DrawableChannel(Channel channel) { - Chat = chat; + Channel = channel; RelativeSizeAxes = Axes.Both; @@ -50,15 +50,15 @@ namespace osu.Game.Overlays.Chat } }; - Chat.NewMessagesArrived += newMessagesArrived; - Chat.MessageRemoved += messageRemoved; - Chat.PendingMessageResolved += pendingMessageResolved; + Channel.NewMessagesArrived += newMessagesArrived; + Channel.MessageRemoved += messageRemoved; + Channel.PendingMessageResolved += pendingMessageResolved; } [BackgroundDependencyLoader] private void load() { - newMessagesArrived(Chat.Messages); + newMessagesArrived(Channel.Messages); } protected override void LoadComplete() @@ -71,14 +71,14 @@ namespace osu.Game.Overlays.Chat { base.Dispose(isDisposing); - Chat.NewMessagesArrived -= newMessagesArrived; - Chat.MessageRemoved -= messageRemoved; - Chat.PendingMessageResolved -= pendingMessageResolved; + Channel.NewMessagesArrived -= newMessagesArrived; + Channel.MessageRemoved -= messageRemoved; + Channel.PendingMessageResolved -= pendingMessageResolved; } private void newMessagesArrived(IEnumerable newMessages) { - // Add up to last ChatBase.MAX_HISTORY messages + // Add up to last Channel.MAX_HISTORY messages var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); flow.AddRange(displayMessages.Select(m => new ChatLine(m))); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 3df57f86b7..403508627e 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -212,7 +212,7 @@ namespace osu.Game.Overlays foreach (Channel removedChannel in args.OldItems) { chatTabControl.RemoveItem(removedChannel); - loadedChannels.Remove(loadedChannels.Find(c => c.Chat == removedChannel )); + loadedChannels.Remove(loadedChannels.Find(c => c.Channel == removedChannel )); removedChannel.Joined.Value = false; } break; @@ -234,7 +234,7 @@ namespace osu.Game.Overlays if (chatTabControl.Current.Value != chat) Scheduler.Add(() => chatTabControl.Current.Value = chat); - var loaded = loadedChannels.Find(d => d.Chat == chat); + var loaded = loadedChannels.Find(d => d.Channel == chat); if (loaded == null) { currentChatContainer.FadeOut(500, Easing.OutQuint); From 263e68de91ebabe1aec5be4d04d7b913d91cc854 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 18:45:11 +0200 Subject: [PATCH 32/88] Use a custom channel not found exception. --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 51b9acff44..0e3c1592a3 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -84,7 +84,7 @@ namespace osu.Game.Graphics.Containers { channelManager.OpenChannel(linkArgument); } - catch (ArgumentException) + catch (ChannelNotFoundException) { //channel was not found } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 0b658428fe..60e7217756 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -60,7 +60,7 @@ namespace osu.Game.Online.Chat throw new ArgumentNullException(nameof(name)); CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) - ?? throw new ArgumentException($"Channel {name} was not found."); + ?? throw new ChannelNotFoundException(name); } public void OpenUserChannel(User user) @@ -298,4 +298,14 @@ namespace osu.Game.Online.Chat api.Register(this); } } + + + public class ChannelNotFoundException : Exception + { + public ChannelNotFoundException(string channelName) + : base($"A channel with the name {channelName} could not be found.") + { + + } + } } From 4b638db4752206f33d60646e689b0f97cfa6ed9d Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 18:52:28 +0200 Subject: [PATCH 33/88] Reorder the properties and fields on Channel. Make MAX_HISTORY because cause can not be public. --- osu.Game/Online/Chat/Channel.cs | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index e9cd7e51e0..1196738bd7 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -14,7 +14,22 @@ namespace osu.Game.Online.Chat { public class Channel { - public const int MAX_HISTORY = 300; + public readonly int MAX_HISTORY = 300; + + /// + /// Contains every joined user except yourself + /// + public readonly ObservableCollection JoinedUsers = new ObservableCollection(); + public readonly SortedList Messages = new SortedList(Comparer.Default); + + public event Action> NewMessagesArrived; + public event Action PendingMessageResolved; + public event Action MessageRemoved; + + public readonly Bindable Joined = new Bindable(); + public TargetType Target { get; } + public bool ReadOnly { get; set; } + public override string ToString() => Name; [JsonProperty(@"name")] public string Name; @@ -28,6 +43,8 @@ namespace osu.Game.Online.Chat [JsonProperty(@"channel_id")] public long Id; + private readonly List pendingMessages = new List(); + [JsonConstructor] public Channel() { @@ -37,7 +54,7 @@ namespace osu.Game.Online.Chat /// Contructs a private channel /// TODO this class needs to be serialized from something like channels/private, instead of creating from a contructor /// - /// The user + /// The user public Channel(User user) { Target = TargetType.User; @@ -46,20 +63,8 @@ namespace osu.Game.Online.Chat JoinedUsers.Add(user); } - /// - /// Contains every joined user except yourself - /// - public ObservableCollection JoinedUsers = new ObservableCollection(); - public readonly SortedList Messages = new SortedList(Comparer.Default); - private readonly List pendingMessages = new List(); + - public event Action> NewMessagesArrived; - public event Action PendingMessageResolved; - public event Action MessageRemoved; - - public Bindable Joined = new Bindable(); - public TargetType Target { get; set; } - public bool ReadOnly { get; set; } public void AddLocalEcho(LocalEchoMessage message) { @@ -118,6 +123,6 @@ namespace osu.Game.Online.Chat Messages.RemoveRange(0, messageCount - MAX_HISTORY); } - public override string ToString() => Name; + } } From 66378d5847327e24e2684b177e84375fd3776647 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 18:58:10 +0200 Subject: [PATCH 34/88] Remove the highlight from "Start Chat" --- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 50d09b6ef6..f8fb9e01f3 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -254,7 +254,7 @@ namespace osu.Game.Overlays.Chat public MenuItem[] ContextMenuItems => new MenuItem[] { new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action), - new OsuMenuItem("Start Chat", MenuItemType.Highlighted, startChatAction), + new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction), }; } } From 16d3815a5943ec14b3eb66efd1c8f6b8f705f8d3 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 18:58:33 +0200 Subject: [PATCH 35/88] Clean Channel up and reword two comments --- osu.Game/Online/Chat/Channel.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 1196738bd7..b7893ed9ff 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat public readonly int MAX_HISTORY = 300; /// - /// Contains every joined user except yourself + /// Contains every joined user except the current logged in user. /// public readonly ObservableCollection JoinedUsers = new ObservableCollection(); public readonly SortedList Messages = new SortedList(Comparer.Default); @@ -52,7 +52,7 @@ namespace osu.Game.Online.Chat /// /// Contructs a private channel - /// TODO this class needs to be serialized from something like channels/private, instead of creating from a contructor + /// TODO this class needs to be serialized from something like channels/private, instead of creating from the contructor /// /// The user public Channel(User user) @@ -62,10 +62,7 @@ namespace osu.Game.Online.Chat Id = user.Id; JoinedUsers.Add(user); } - - - - + public void AddLocalEcho(LocalEchoMessage message) { pendingMessages.Add(message); From 1589b65494f9df6fc9aedc0c1016e6d013767eb2 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 19:42:57 +0200 Subject: [PATCH 36/88] Move tab related stuff to /tabs, move selection related stuff to /selection, remove channeltabcontrol --- osu.Game/Overlays/Chat/ChannelTabControl.cs | 342 ------------------ osu.Game/Overlays/Chat/ChatTabControl.cs | 1 + .../Chat/{ => Selection}/ChannelListItem.cs | 2 +- .../Chat/{ => Selection}/ChannelSection.cs | 2 +- .../ChannelSelectionOverlay.cs | 2 +- .../Chat/Tabs/ChannelSelectorTabItem.cs | 32 ++ .../Overlays/Chat/Tabs/ChannelTabControl.cs | 96 +++++ osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 194 ++++++++++ .../TabCloseButton.cs} | 6 +- .../Overlays/Chat/{ => Tabs}/UserTabItem.cs | 6 +- osu.Game/Overlays/ChatOverlay.cs | 1 + 11 files changed, 333 insertions(+), 351 deletions(-) delete mode 100644 osu.Game/Overlays/Chat/ChannelTabControl.cs rename osu.Game/Overlays/Chat/{ => Selection}/ChannelListItem.cs (99%) rename osu.Game/Overlays/Chat/{ => Selection}/ChannelSection.cs (97%) rename osu.Game/Overlays/Chat/{ => Selection}/ChannelSelectionOverlay.cs (99%) create mode 100644 osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs create mode 100644 osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs create mode 100644 osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs rename osu.Game/Overlays/Chat/{ChatTabItemCloseButton.cs => Tabs/TabCloseButton.cs} (91%) rename osu.Game/Overlays/Chat/{ => Tabs}/UserTabItem.cs (98%) diff --git a/osu.Game/Overlays/Chat/ChannelTabControl.cs b/osu.Game/Overlays/Chat/ChannelTabControl.cs deleted file mode 100644 index b6c3d4900b..0000000000 --- a/osu.Game/Overlays/Chat/ChannelTabControl.cs +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Chat; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using System; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Chat -{ - public class ChannelTabControl : OsuTabControl - { - private const float shear_width = 10; - - public Action OnRequestLeave; - - public readonly Bindable ChannelSelectorActive = new Bindable(); - - private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; - - public ChannelTabControl() - { - TabContainer.Margin = new MarginPadding { Left = 50 }; - TabContainer.Spacing = new Vector2(-shear_width, 0); - TabContainer.Masking = false; - - AddInternal(new SpriteIcon - { - Icon = FontAwesome.fa_comments, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Margin = new MarginPadding(10), - }); - - AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); - - ChannelSelectorActive.BindTo(selectorTab.Active); - } - - protected override void AddTabItem(TabItem item, bool addToDropdown = true) - { - if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) - // performTabSort might've made selectorTab's position wonky, fix it - TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); - - base.AddTabItem(item, addToDropdown); - } - - protected override TabItem CreateTabItem(Channel value) - { - switch (value.Target) - { - case TargetType.Channel: - return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; - case TargetType.User: - return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; - default: - throw new InvalidOperationException("Only TargetType User and Channel are supported."); - } - } - - protected override void SelectTab(TabItem tab) - { - if (tab is ChannelTabItem.ChannelSelectorTabItem) - { - tab.Active.Toggle(); - return; - } - - selectorTab.Active.Value = false; - - base.SelectTab(tab); - } - - private void tabCloseRequested(TabItem tab) - { - int totalTabs = TabContainer.Count - 1; // account for selectorTab - int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); - - if (tab == SelectedTab && totalTabs > 1) - // Select the tab after tab-to-be-removed's index, or the tab before if current == last - SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); - else if (totalTabs == 1 && !selectorTab.Active) - // Open channel selection overlay if all channel tabs will be closed after removing this tab - SelectTab(selectorTab); - - OnRequestLeave?.Invoke(tab.Value); - } - - private class ChannelTabItem : TabItem - { - private Color4 backgroundInactive; - private Color4 backgroundHover; - private Color4 backgroundActive; - - public override bool IsRemovable => !Pinned; - - private readonly SpriteText text; - private readonly SpriteText textBold; - private readonly ClickableContainer closeButton; - private readonly Box box; - private readonly Box highlightBox; - private readonly SpriteIcon icon; - - public Action OnRequestClose; - - private void updateState() - { - if (Active) - fadeActive(); - else - fadeInactive(); - } - - private const float transition_length = 400; - - private void fadeActive() - { - this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); - - box.FadeColour(backgroundActive, transition_length, Easing.OutQuint); - highlightBox.FadeIn(transition_length, Easing.OutQuint); - - text.FadeOut(transition_length, Easing.OutQuint); - textBold.FadeIn(transition_length, Easing.OutQuint); - } - - private void fadeInactive() - { - this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); - - box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint); - highlightBox.FadeOut(transition_length, Easing.OutQuint); - - text.FadeIn(transition_length, Easing.OutQuint); - textBold.FadeOut(transition_length, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - if (IsRemovable) - closeButton.FadeIn(200, Easing.OutQuint); - - if (!Active) - box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - closeButton.FadeOut(200, Easing.OutQuint); - updateState(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - backgroundActive = colours.ChatBlue; - backgroundInactive = colours.Gray4; - backgroundHover = colours.Gray7; - - highlightBox.Colour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - public ChannelTabItem(Channel value) : base(value) - { - Width = 150; - - RelativeSizeAxes = Axes.Y; - - Anchor = Anchor.BottomLeft; - Origin = Anchor.BottomLeft; - - Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); - - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 10, - Colour = Color4.Black.Opacity(0.2f), - }; - - Children = new Drawable[] - { - box = new Box - { - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Both, - }, - highlightBox = new Box - { - Width = 5, - Alpha = 0, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Y, - }, - new Container - { - Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - icon = new SpriteIcon - { - Icon = FontAwesome.fa_hashtag, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = Color4.Black, - X = -10, - Alpha = 0.2f, - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - }, - text = new OsuSpriteText - { - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - TextSize = 18, - }, - textBold = new OsuSpriteText - { - Alpha = 0, - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - Font = @"Exo2.0-Bold", - TextSize = 18, - }, - closeButton = new CloseButton - { - Alpha = 0, - Margin = new MarginPadding { Right = 20 }, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Action = delegate - { - if (IsRemovable) OnRequestClose?.Invoke(this); - }, - }, - }, - }, - }; - } - - public class CloseButton : OsuClickableContainer - { - private readonly SpriteIcon icon; - - public CloseButton() - { - Size = new Vector2(20); - - Child = icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.75f), - Icon = FontAwesome.fa_close, - RelativeSizeAxes = Axes.Both, - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - icon.ScaleTo(0.5f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - icon.ScaleTo(0.75f, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - protected override bool OnHover(InputState state) - { - icon.FadeColour(Color4.Red, 200, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - icon.FadeColour(Color4.White, 200, Easing.OutQuint); - base.OnHoverLost(state); - } - } - - public class ChannelSelectorTabItem : ChannelTabItem - { - public override bool IsRemovable => false; - - public ChannelSelectorTabItem(Channel value) : base(value) - { - Depth = float.MaxValue; - Width = 45; - - icon.Alpha = 0; - - text.TextSize = 45; - textBold.TextSize = 45; - } - - [BackgroundDependencyLoader] - private new void load(OsuColour colour) - { - backgroundInactive = colour.Gray2; - backgroundActive = colour.Gray3; - } - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); - } - } -} diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 42ea643087..c668f78ff4 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat.Tabs; namespace osu.Game.Overlays.Chat { diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs similarity index 99% rename from osu.Game/Overlays/Chat/ChannelListItem.cs rename to osu.Game/Overlays/Chat/Selection/ChannelListItem.cs index 910c87e0a8..175d7fd0d2 100644 --- a/osu.Game/Overlays/Chat/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs @@ -15,7 +15,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osu.Game.Graphics.Containers; -namespace osu.Game.Overlays.Chat +namespace osu.Game.Overlays.Chat.Selection { public class ChannelListItem : OsuClickableContainer, IFilterable { diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/Selection/ChannelSection.cs similarity index 97% rename from osu.Game/Overlays/Chat/ChannelSection.cs rename to osu.Game/Overlays/Chat/Selection/ChannelSection.cs index 89d9d2231c..c9097fe4db 100644 --- a/osu.Game/Overlays/Chat/ChannelSection.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSection.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; -namespace osu.Game.Overlays.Chat +namespace osu.Game.Overlays.Chat.Selection { public class ChannelSection : Container, IHasFilterableChildren { diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs similarity index 99% rename from osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs rename to osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index 57f2cd405d..8a18f6e0fa 100644 --- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -18,7 +18,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Graphics.Containers; -namespace osu.Game.Overlays.Chat +namespace osu.Game.Overlays.Chat.Selection { public class ChannelSelectionOverlay : OsuFocusedOverlayContainer { diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs new file mode 100644 index 0000000000..0b1721741a --- /dev/null +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat.Tabs +{ + public class ChannelSelectorTabItem : ChannelTabItem + { + public override bool IsRemovable => false; + + public ChannelSelectorTabItem(Channel value) : base(value) + { + Depth = float.MaxValue; + Width = 45; + + Icon.Alpha = 0; + + Text.TextSize = 45; + TextBold.TextSize = 45; + } + + [BackgroundDependencyLoader] + private new void load(OsuColour colour) + { + BackgroundInactive = colour.Gray2; + BackgroundActive = colour.Gray3; + } + } +} diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs new file mode 100644 index 0000000000..1df26fb118 --- /dev/null +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -0,0 +1,96 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; +using OpenTK; +using osu.Framework.Configuration; +using System; +using osu.Game.Overlays.Chat.Tabs; + +namespace osu.Game.Overlays.Chat.Tabs +{ + public class ChannelTabControl : OsuTabControl + { + public static readonly float shear_width = 10; + + public Action OnRequestLeave; + + public readonly Bindable ChannelSelectorActive = new Bindable(); + + private readonly ChannelSelectorTabItem selectorTab; + + public ChannelTabControl() + { + TabContainer.Margin = new MarginPadding { Left = 50 }; + TabContainer.Spacing = new Vector2(-shear_width, 0); + TabContainer.Masking = false; + + AddInternal(new SpriteIcon + { + Icon = FontAwesome.fa_comments, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Margin = new MarginPadding(10), + }); + + AddTabItem(selectorTab = new ChannelSelectorTabItem(new Channel { Name = "+" })); + + ChannelSelectorActive.BindTo(selectorTab.Active); + } + + protected override void AddTabItem(TabItem item, bool addToDropdown = true) + { + if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) + // performTabSort might've made selectorTab's position wonky, fix it + TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); + + base.AddTabItem(item, addToDropdown); + } + + protected override TabItem CreateTabItem(Channel value) + { + switch (value.Target) + { + case TargetType.Channel: + return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + case TargetType.User: + return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + default: + throw new InvalidOperationException("Only TargetType User and Channel are supported."); + } + } + + protected override void SelectTab(TabItem tab) + { + if (tab is ChannelSelectorTabItem) + { + tab.Active.Toggle(); + return; + } + + selectorTab.Active.Value = false; + + base.SelectTab(tab); + } + + private void tabCloseRequested(TabItem tab) + { + int totalTabs = TabContainer.Count - 1; // account for selectorTab + int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); + + if (tab == SelectedTab && totalTabs > 1) + // Select the tab after tab-to-be-removed's index, or the tab before if current == last + SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); + else if (totalTabs == 1 && !selectorTab.Active) + // Open channel selection overlay if all channel tabs will be closed after removing this tab + SelectTab(selectorTab); + + OnRequestLeave?.Invoke(tab.Value); + } + } +} diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs new file mode 100644 index 0000000000..592f2eead8 --- /dev/null +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -0,0 +1,194 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Chat.Tabs +{ + public class ChannelTabItem : TabItem + { + + protected Color4 BackgroundInactive; + private Color4 backgroundHover; + protected Color4 BackgroundActive; + + public override bool IsRemovable => !Pinned; + + protected readonly SpriteText Text; + protected readonly SpriteText TextBold; + private readonly ClickableContainer closeButton; + private readonly Box box; + private readonly Box highlightBox; + protected readonly SpriteIcon Icon; + + public Action OnRequestClose; + + private void updateState() + { + if (Active) + fadeActive(); + else + fadeInactive(); + } + + private const float transition_length = 400; + + private void fadeActive() + { + this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); + + box.FadeColour(BackgroundActive, transition_length, Easing.OutQuint); + highlightBox.FadeIn(transition_length, Easing.OutQuint); + + Text.FadeOut(transition_length, Easing.OutQuint); + TextBold.FadeIn(transition_length, Easing.OutQuint); + } + + private void fadeInactive() + { + this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); + + box.FadeColour(BackgroundInactive, transition_length, Easing.OutQuint); + highlightBox.FadeOut(transition_length, Easing.OutQuint); + + Text.FadeIn(transition_length, Easing.OutQuint); + TextBold.FadeOut(transition_length, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + if (IsRemovable) + closeButton.FadeIn(200, Easing.OutQuint); + + if (!Active) + box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + closeButton.FadeOut(200, Easing.OutQuint); + updateState(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundActive = colours.ChatBlue; + BackgroundInactive = colours.Gray4; + backgroundHover = colours.Gray7; + + highlightBox.Colour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); + + public ChannelTabItem(Channel value) + : base(value) + { + Width = 150; + + RelativeSizeAxes = Axes.Y; + + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + + Shear = new Vector2(ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 10, + Colour = Color4.Black.Opacity(0.2f), + }; + + Children = new Drawable[] + { + box = new Box + { + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Both, + }, + highlightBox = new Box + { + Width = 5, + Alpha = 0, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Y, + }, + new Container + { + Shear = new Vector2(-ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + Icon = new SpriteIcon + { + Icon = FontAwesome.fa_hashtag, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = Color4.Black, + X = -10, + Alpha = 0.2f, + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + }, + Text = new OsuSpriteText + { + Margin = new MarginPadding(5), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.ToString(), + TextSize = 18, + }, + TextBold = new OsuSpriteText + { + Alpha = 0, + Margin = new MarginPadding(5), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.ToString(), + Font = @"Exo2.0-Bold", + TextSize = 18, + }, + closeButton = new TabCloseButton + { + Alpha = 0, + Margin = new MarginPadding { Right = 20 }, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Action = delegate + { + if (IsRemovable) OnRequestClose?.Invoke(this); + }, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs similarity index 91% rename from osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs rename to osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs index 0ec2e52963..f6af2f2a4a 100644 --- a/osu.Game/Overlays/Chat/ChatTabItemCloseButton.cs +++ b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs @@ -8,13 +8,13 @@ using osu.Game.Graphics.Containers; using OpenTK; using OpenTK.Graphics; -namespace osu.Game.Overlays.Chat +namespace osu.Game.Overlays.Chat.Tabs { - public class ChatTabItemCloseButton : OsuClickableContainer + public class TabCloseButton : OsuClickableContainer { private readonly SpriteIcon icon; - public ChatTabItemCloseButton() + public TabCloseButton() { Size = new Vector2(20); diff --git a/osu.Game/Overlays/Chat/UserTabItem.cs b/osu.Game/Overlays/Chat/Tabs/UserTabItem.cs similarity index 98% rename from osu.Game/Overlays/Chat/UserTabItem.cs rename to osu.Game/Overlays/Chat/Tabs/UserTabItem.cs index 5f81e9fe9e..075e4ae965 100644 --- a/osu.Game/Overlays/Chat/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/UserTabItem.cs @@ -17,7 +17,7 @@ using osu.Game.Users; using OpenTK; using OpenTK.Graphics; -namespace osu.Game.Overlays.Chat +namespace osu.Game.Overlays.Chat.Tabs { public class UserTabItem : TabItem { @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Chat private readonly Box backgroundBox; private readonly OsuSpriteText username; private readonly Avatar avatarContainer; - private readonly ChatTabItemCloseButton closeButton; + private readonly TabCloseButton closeButton; public UserTabItem(Channel value) : base(value) @@ -138,7 +138,7 @@ namespace osu.Game.Overlays.Chat TextSize = 18, Alpha = 0, }, - closeButton = new ChatTabItemCloseButton + closeButton = new TabCloseButton { Height = 1, Origin = Anchor.BottomLeft, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 403508627e..01de1dd9d7 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.Selection; namespace osu.Game.Overlays { From 0c62726fd72a4585351d27d671327d755eef5882 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 19:48:05 +0200 Subject: [PATCH 37/88] Rename UserTabItem to UserChannelTabItem --- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 2 +- .../Chat/Tabs/{UserTabItem.cs => UserChannelTabItem.cs} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Overlays/Chat/Tabs/{UserTabItem.cs => UserChannelTabItem.cs} (98%) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 1df26fb118..de5ef4d1d1 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Chat.Tabs case TargetType.Channel: return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; case TargetType.User: - return new UserTabItem(value) { OnRequestClose = tabCloseRequested }; + return new UserChannelTabItem(value) { OnRequestClose = tabCloseRequested }; default: throw new InvalidOperationException("Only TargetType User and Channel are supported."); } diff --git a/osu.Game/Overlays/Chat/Tabs/UserTabItem.cs b/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs similarity index 98% rename from osu.Game/Overlays/Chat/Tabs/UserTabItem.cs rename to osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs index 075e4ae965..c4b84e7c21 100644 --- a/osu.Game/Overlays/Chat/Tabs/UserTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs @@ -19,7 +19,7 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Chat.Tabs { - public class UserTabItem : TabItem + public class UserChannelTabItem : TabItem { private static readonly Vector2 shear = new Vector2(1f / 5f, 0); public override bool IsRemovable => true; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Chat.Tabs private readonly Avatar avatarContainer; private readonly TabCloseButton closeButton; - public UserTabItem(Channel value) + public UserChannelTabItem(Channel value) : base(value) { if (value.Target != TargetType.User) @@ -160,7 +160,7 @@ namespace osu.Game.Overlays.Chat.Tabs }; } - public Action OnRequestClose; + public Action OnRequestClose; private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters { From f22f62ef40a4444bd65ea92a270a167c0c13529e Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 20:13:34 +0200 Subject: [PATCH 38/88] Rename the currentChatChanged to currentChannelContainer, move drawing specific part into from chatoverlay to ChannelSelectionOverlay --- .../Chat/Selection/ChannelSelectionOverlay.cs | 38 +++++++++-------- osu.Game/Overlays/ChatOverlay.cs | 42 ++++++++----------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index 8a18f6e0fa..b51d7f9904 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -35,23 +35,6 @@ namespace osu.Game.Overlays.Chat.Selection public Action OnRequestJoin; public Action OnRequestLeave; - public IEnumerable Sections - { - set - { - sectionsFlow.ChildrenEnumerable = value; - - foreach (ChannelSection s in sectionsFlow.Children) - { - foreach (ChannelListItem c in s.ChannelFlow.Children) - { - c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; - c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; - } - } - } - } - public ChannelSelectionOverlay() { RelativeSizeAxes = Axes.X; @@ -140,6 +123,27 @@ namespace osu.Game.Overlays.Chat.Selection search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue; } + public void UpdateAvailableChannels(IEnumerable channels) + { + sectionsFlow.ChildrenEnumerable = new[] + { + new ChannelSection + { + Header = "All Channels", + Channels = channels, + }, + }; + + foreach (ChannelSection s in sectionsFlow.Children) + { + foreach (ChannelListItem c in s.ChannelFlow.Children) + { + c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; + c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; + } + } + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 01de1dd9d7..d1982e109d 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays private ChannelManager channelManager; - private readonly Container currentChatContainer; + private readonly Container currentChannelContainer; private readonly List loadedChannels = new List(); private readonly LoadingAnimation loading; @@ -102,7 +102,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, }, - currentChatContainer = new Container + currentChannelContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -188,14 +188,8 @@ namespace osu.Game.Overlays private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { - channelSelection.Sections = new[] - { - new ChannelSection - { - Header = "All Channels", - Channels = channelManager.AvailableChannels, - }, - }; + channelSelection.UpdateAvailableChannels(channelManager.); + } private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) @@ -220,42 +214,42 @@ namespace osu.Game.Overlays } } - private void currentChatChanged(Channel chat) + private void currentChatChanged(Channel channel) { - if (chat == null) + if (channel == null) { textbox.Current.Disabled = true; - currentChatContainer.Clear(false); + currentChannelContainer.Clear(false); chatTabControl.Current.Value = null; return; } - textbox.Current.Disabled = chat.ReadOnly; + textbox.Current.Disabled = channel.ReadOnly; - if (chatTabControl.Current.Value != chat) - Scheduler.Add(() => chatTabControl.Current.Value = chat); + if (chatTabControl.Current.Value != channel) + Scheduler.Add(() => chatTabControl.Current.Value = channel); - var loaded = loadedChannels.Find(d => d.Channel == chat); + var loaded = loadedChannels.Find(d => d.Channel == channel); if (loaded == null) { - currentChatContainer.FadeOut(500, Easing.OutQuint); + currentChannelContainer.FadeOut(500, Easing.OutQuint); loading.Show(); - loaded = new DrawableChannel(chat); + loaded = new DrawableChannel(channel); loadedChannels.Add(loaded); LoadComponentAsync(loaded, l => { loading.Hide(); - currentChatContainer.Clear(false); - currentChatContainer.Add(loaded); - currentChatContainer.FadeIn(500, Easing.OutQuint); + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + currentChannelContainer.FadeIn(500, Easing.OutQuint); }); } else { - currentChatContainer.Clear(false); - currentChatContainer.Add(loaded); + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); } } From f681ef41ac2e8735713547343758719b2b16e9a8 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 20:39:16 +0200 Subject: [PATCH 39/88] Rename MAX_HISTORY to MaxHistory, added some logging on failures, use a lamda in ChatOverlay instead of a method pointer. --- osu.Game/Online/Chat/Channel.cs | 16 +++++----------- osu.Game/Online/Chat/ChannelManager.cs | 7 +++++-- osu.Game/Overlays/Chat/DrawableChannel.cs | 4 ++-- osu.Game/Overlays/ChatOverlay.cs | 8 +------- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index b7893ed9ff..c515557c3e 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -14,13 +14,14 @@ namespace osu.Game.Online.Chat { public class Channel { - public readonly int MAX_HISTORY = 300; + public readonly int MaxHistory = 300; /// /// Contains every joined user except the current logged in user. /// public readonly ObservableCollection JoinedUsers = new ObservableCollection(); public readonly SortedList Messages = new SortedList(Comparer.Default); + private readonly List pendingMessages = new List(); public event Action> NewMessagesArrived; public event Action PendingMessageResolved; @@ -43,8 +44,6 @@ namespace osu.Game.Online.Chat [JsonProperty(@"channel_id")] public long Id; - private readonly List pendingMessages = new List(); - [JsonConstructor] public Channel() { @@ -101,12 +100,7 @@ namespace osu.Game.Online.Chat } if (Messages.Contains(final)) - { - // message already inserted, so let's throw away this update. - // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed. - MessageRemoved?.Invoke(echo); - return; - } + throw new InvalidOperationException("Attempted to add the same message again"); Messages.Add(final); PendingMessageResolved?.Invoke(echo, final); @@ -116,8 +110,8 @@ namespace osu.Game.Online.Chat { // never purge local echos int messageCount = Messages.Count - pendingMessages.Count; - if (messageCount > MAX_HISTORY) - Messages.RemoveRange(0, messageCount - MAX_HISTORY); + if (messageCount > MaxHistory) + Messages.RemoveRange(0, messageCount - MaxHistory); } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 60e7217756..027f18ce3f 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -112,7 +112,11 @@ namespace osu.Game.Online.Chat CurrentChannel.Value.AddLocalEcho(message); var req = new PostMessageRequest(message); - req.Failure += e => CurrentChannel.Value?.ReplaceMessage(message, null); + req.Failure += exception => + { + Logger.Error(exception, "Posting message failed."); + CurrentChannel.Value?.ReplaceMessage(message, null); + }; req.Success += m => CurrentChannel.Value?.ReplaceMessage(message, m); api.Queue(req); } @@ -305,7 +309,6 @@ namespace osu.Game.Online.Chat public ChannelNotFoundException(string channelName) : base($"A channel with the name {channelName} could not be found.") { - } } } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index bcc8879902..8cc77f82f3 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { // Add up to last Channel.MAX_HISTORY messages - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); + var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory)); flow.AddRange(displayMessages.Select(m => new ChatLine(m))); @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Chat scrollToEnd(); var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); - int count = staleMessages.Length - Channel.MAX_HISTORY; + int count = staleMessages.Length - Channel.MaxHistory; for (int i = 0; i < count; i++) { diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index d1982e109d..de241dd9c0 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -186,12 +186,6 @@ namespace osu.Game.Overlays channelSelection.OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel); } - private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) - { - channelSelection.UpdateAvailableChannels(channelManager.); - - } - private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { switch (args.Action) @@ -337,7 +331,7 @@ namespace osu.Game.Overlays this.channelManager = channelManager; channelManager.CurrentChannel.ValueChanged += currentChatChanged; channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; - channelManager.AvailableChannels.CollectionChanged += availableChannelsChanged; + channelManager.AvailableChannels.CollectionChanged += (sender, args) => channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); Add(channelManager); } From ec914a5095ddaaebbe7c24ad3ad7d49abc1c2252 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 21:00:39 +0200 Subject: [PATCH 40/88] Fix crash when the local echo is send to the wrong channel. --- osu.Game/Online/Chat/Channel.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index c515557c3e..666a41f9d3 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -29,7 +29,7 @@ namespace osu.Game.Online.Chat public readonly Bindable Joined = new Bindable(); public TargetType Target { get; } - public bool ReadOnly { get; set; } + public bool ReadOnly => false; //todo not yet used. public override string ToString() => Name; [JsonProperty(@"name")] diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 027f18ce3f..233ed00261 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -93,9 +93,11 @@ namespace osu.Game.Online.Chat if (CurrentChannel.Value == null) return; + var currentChannel = CurrentChannel.Value; + if (!api.IsLoggedIn) { - CurrentChannel.Value.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + currentChannel.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); return; } @@ -109,15 +111,15 @@ namespace osu.Game.Online.Chat Content = text }; - CurrentChannel.Value.AddLocalEcho(message); + currentChannel.AddLocalEcho(message); var req = new PostMessageRequest(message); req.Failure += exception => { Logger.Error(exception, "Posting message failed."); - CurrentChannel.Value?.ReplaceMessage(message, null); + currentChannel.ReplaceMessage(message, null); }; - req.Success += m => CurrentChannel.Value?.ReplaceMessage(message, m); + req.Success += m => currentChannel.ReplaceMessage(message, m); api.Queue(req); } From 290b6e5f1de32c206d9adbe57e2ee41716b9c2f4 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:09:27 +0200 Subject: [PATCH 41/88] Fix the crash in the visual testcase --- osu.Game.Tests/Visual/TestCaseChatDisplay.cs | 1 + osu.Game.Tests/Visual/TestCaseChatTabControl.cs | 1 + osu.Game/Online/Chat/ChannelManager.cs | 10 +++++----- osu.Game/OsuGameBase.cs | 1 + osu.Game/Overlays/ChatOverlay.cs | 1 - 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs index c03b12bdc1..9057ec0d79 100644 --- a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using osu.Framework.Graphics.Containers; +using osu.Game.Online.Chat; using osu.Game.Overlays; namespace osu.Game.Tests.Visual diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index e530c1b72a..a1b8eaf051 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -15,6 +15,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.Tabs; using osu.Game.Users; using OpenTK.Graphics; diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 233ed00261..ed8677097b 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -50,7 +50,7 @@ namespace osu.Game.Online.Chat private IAPIProvider api; private ScheduledDelegate fetchMessagesScheduleder; private GetMessagesRequest fetchMsgReq; - private GetPrivateMessagesRequest fetchPrivateMsgReq; + private GetPrivateMessagesRequest fetchUserMsgReq; private long? lastChannelMsgId; private long? lastUserMsgId; @@ -170,16 +170,16 @@ namespace osu.Game.Online.Chat ); - if (fetchPrivateMsgReq == null) + if (fetchUserMsgReq == null) fetchMessages( - () => new GetPrivateMessagesRequest(lastChannelMsgId), + () => new GetPrivateMessagesRequest(lastUserMsgId), messages => { if (messages == null) return; handleUserMessages(messages); - lastUserMsgId = messages.LastOrDefault()?.Id ?? lastUserMsgId; - fetchPrivateMsgReq = null; + lastUserMsgId = messages.Max(m => m.Id) ?? lastUserMsgId; + fetchUserMsgReq = null; } ); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index cae5352739..b32585e129 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -124,6 +124,7 @@ namespace osu.Game var channelManager = new ChannelManager(); dependencies.Inject(channelManager); dependencies.Cache(channelManager); + AddInternal(channelManager); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index de241dd9c0..8132c3c7ed 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -332,7 +332,6 @@ namespace osu.Game.Overlays channelManager.CurrentChannel.ValueChanged += currentChatChanged; channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; channelManager.AvailableChannels.CollectionChanged += (sender, args) => channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); - Add(channelManager); } private void postMessage(TextBox textbox, bool newText) From 3140b2e15c67089e4b62f0538f032af3d46c9348 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:14:21 +0200 Subject: [PATCH 42/88] Fix duplicate messages appearing --- osu.Game/Online/Chat/ChannelManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index ed8677097b..f4ff523689 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -158,7 +158,7 @@ namespace osu.Game.Online.Chat { if (fetchMsgReq == null) fetchMessages( - () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), + () => fetchMsgReq = new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), messages => { if (messages == null) @@ -172,7 +172,7 @@ namespace osu.Game.Online.Chat if (fetchUserMsgReq == null) fetchMessages( - () => new GetPrivateMessagesRequest(lastUserMsgId), + () => fetchUserMsgReq = new GetPrivateMessagesRequest(lastUserMsgId), messages => { if (messages == null) From 72ea3128faaa58429e0fd0840bf11fad3187e94e Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:14:47 +0200 Subject: [PATCH 43/88] Use the AT (@) symbol for the background --- osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs index c4b84e7c21..e729e72d36 100644 --- a/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs @@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Chat.Tabs { new SpriteIcon { - Icon = FontAwesome.fa_eercast, + Icon = FontAwesome.fa_at, Origin = Anchor.Centre, Scale = new Vector2(1.2f), X = -5, From ea597916ca2837e83ed93804b9ef2f93d9a4357a Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:41:53 +0200 Subject: [PATCH 44/88] Code cleanups --- osu.Game.Tests/Visual/TestCaseChatDisplay.cs | 1 - osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 1 - osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs index 9057ec0d79..c03b12bdc1 100644 --- a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs @@ -3,7 +3,6 @@ using System.ComponentModel; using osu.Framework.Graphics.Containers; -using osu.Game.Online.Chat; using osu.Game.Overlays; namespace osu.Game.Tests.Visual diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index de5ef4d1d1..69df971df4 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -9,7 +9,6 @@ using osu.Game.Online.Chat; using OpenTK; using osu.Framework.Configuration; using System; -using osu.Game.Overlays.Chat.Tabs; namespace osu.Game.Overlays.Chat.Tabs { diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 592f2eead8..389861ca9c 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; - Shear = new Vector2(ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); + Shear = new Vector2(ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0); Masking = true; EdgeEffect = new EdgeEffectParameters @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Chat.Tabs }, new Container { - Shear = new Vector2(-ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), + Shear = new Vector2(-ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0), RelativeSizeAxes = Axes.Both, Children = new Drawable[] { From 8e93269885300b1aa8ac3c9d32409de61bbc2411 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:47:01 +0200 Subject: [PATCH 45/88] Remove whitespace --- osu.Game/Online/Chat/Channel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 666a41f9d3..e749a7c946 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Chat Id = user.Id; JoinedUsers.Add(user); } - + public void AddLocalEcho(LocalEchoMessage message) { pendingMessages.Add(message); From 73a87914f26a1749b1e9d8bbeb1c4b6688ec7ca1 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:52:14 +0200 Subject: [PATCH 46/88] Rename uppcase shear width to lowercase shearwidth --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 389861ca9c..592f2eead8 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; - Shear = new Vector2(ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0); + Shear = new Vector2(ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); Masking = true; EdgeEffect = new EdgeEffectParameters @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Chat.Tabs }, new Container { - Shear = new Vector2(-ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0), + Shear = new Vector2(-ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), RelativeSizeAxes = Axes.Both, Children = new Drawable[] { From 9f9444d65af583ec8c9b27b5ce4a6b404525ace1 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 22:59:29 +0200 Subject: [PATCH 47/88] Uppercase shear... wtf --- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 4 ++-- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 69df971df4..e1f02e9c79 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Chat.Tabs { public class ChannelTabControl : OsuTabControl { - public static readonly float shear_width = 10; + public static readonly float SHEAR_WIDTH = 10; public Action OnRequestLeave; @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs public ChannelTabControl() { TabContainer.Margin = new MarginPadding { Left = 50 }; - TabContainer.Spacing = new Vector2(-shear_width, 0); + TabContainer.Spacing = new Vector2(-SHEAR_WIDTH, 0); TabContainer.Masking = false; AddInternal(new SpriteIcon diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 592f2eead8..389861ca9c 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; - Shear = new Vector2(ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); + Shear = new Vector2(ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0); Masking = true; EdgeEffect = new EdgeEffectParameters @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Chat.Tabs }, new Container { - Shear = new Vector2(-ChannelTabControl.shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), + Shear = new Vector2(-ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0), RelativeSizeAxes = Axes.Both, Children = new Drawable[] { From d03367ef9eb22adb1465530849c4e398f4e05f97 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 9 Jul 2018 23:12:41 +0200 Subject: [PATCH 48/88] Fix the start animations of username and the closeButton --- osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs index e729e72d36..6b11c7860f 100644 --- a/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs @@ -158,6 +158,9 @@ namespace osu.Game.Overlays.Chat.Tabs } } }; + + username.ScaleTo(new Vector2(0, 1)); + closeButton.ScaleTo(new Vector2(0, 1)); } public Action OnRequestClose; From 344ec40a2767ebd3a015c542b051fbebb0f03501 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 23 Jul 2018 15:48:48 +0200 Subject: [PATCH 49/88] Readd usings I removed with my merge --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 1 + osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs | 2 ++ osu.Game/Overlays/ChatOverlay.cs | 1 + 3 files changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 389861ca9c..fede67529b 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; +using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; diff --git a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs index f6af2f2a4a..ec53c9a353 100644 --- a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs +++ b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs @@ -3,6 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Input; +using osu.Framework.Input.EventArgs; +using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using OpenTK; diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8132c3c7ed..36ee49a3d9 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; +using osu.Framework.Input.States; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; From 2726d91594fb4efca6f8c03099f88104f23e8579 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 23 Jul 2018 16:05:39 +0200 Subject: [PATCH 50/88] Remove unessary usings --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 1 - osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs | 1 - osu.Game/Overlays/ChatOverlay.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index fede67529b..28a1f91df4 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs index ec53c9a353..4476e61dfc 100644 --- a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs +++ b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Input; using osu.Framework.Input.EventArgs; using osu.Framework.Input.States; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 36ee49a3d9..cee0a19868 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; using osu.Framework.Input.States; using osu.Game.Configuration; using osu.Game.Graphics; From 0aacde836ac04281901c6dc8e6d3848c1f8e4f29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Jul 2018 20:46:44 +0200 Subject: [PATCH 51/88] Move private channel constructor to own class --- .../Visual/TestCaseChatTabControl.cs | 2 +- .../Online/API/Requests/GetMessagesRequest.cs | 2 +- osu.Game/Online/Chat/Channel.cs | 18 ++---------- osu.Game/Online/Chat/ChannelManager.cs | 6 ++-- osu.Game/Online/Chat/PrivateChannel.cs | 29 +++++++++++++++++++ 5 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 osu.Game/Online/Chat/PrivateChannel.cs diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs index a1b8eaf051..c7d88a4d0e 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChatTabControl.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual if (users == null || users.Count == 0) return; - chatTabControl.AddItem(new Channel(users[RNG.Next(0, users.Count - 1)])); + chatTabControl.AddItem(new PrivateChannel { User = users[RNG.Next(0, users.Count - 1)] }); } private void addChannel(string name) diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index 5ab621c662..f959f94b9c 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.API.Requests if (channels == null) throw new ArgumentNullException(nameof(channels)); if (channels.Any(c => c.Target != TargetType.Channel)) - throw new ArgumentException("All channels in the argument channels must have the targettype Channel"); + throw new ArgumentException($"All channels in the argument channels must have the {nameof(Channel.Target)} Channel"); this.channels = channels; } diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index e749a7c946..cf702f2608 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -20,6 +20,7 @@ namespace osu.Game.Online.Chat /// Contains every joined user except the current logged in user. /// public readonly ObservableCollection JoinedUsers = new ObservableCollection(); + public readonly SortedList Messages = new SortedList(Comparer.Default); private readonly List pendingMessages = new List(); @@ -28,7 +29,7 @@ namespace osu.Game.Online.Chat public event Action MessageRemoved; public readonly Bindable Joined = new Bindable(); - public TargetType Target { get; } + public TargetType Target { get; protected set; } public bool ReadOnly => false; //todo not yet used. public override string ToString() => Name; @@ -49,19 +50,6 @@ namespace osu.Game.Online.Chat { } - /// - /// Contructs a private channel - /// TODO this class needs to be serialized from something like channels/private, instead of creating from the contructor - /// - /// The user - public Channel(User user) - { - Target = TargetType.User; - Name = user.Username; - Id = user.Id; - JoinedUsers.Add(user); - } - public void AddLocalEcho(LocalEchoMessage message) { pendingMessages.Add(message); @@ -113,7 +101,5 @@ namespace osu.Game.Online.Chat if (messageCount > MaxHistory) Messages.RemoveRange(0, messageCount - MaxHistory); } - - } } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f4ff523689..c881fb4fe8 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -69,7 +69,7 @@ namespace osu.Game.Online.Chat throw new ArgumentNullException(nameof(user)); CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Target == TargetType.User && c.Id == user.Id) - ?? new Channel(user); + ?? new PrivateChannel { User = user }; } public ChannelManager() @@ -214,7 +214,7 @@ namespace osu.Game.Online.Chat if (channel == null) { - channel = new Channel(targetUser); + channel = new PrivateChannel { User = targetUser }; JoinedChannels.Add(channel); joinedUserChannels.Add(channel); } @@ -234,7 +234,7 @@ namespace osu.Game.Online.Chat userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations."); userReq.Success += user => { - var channel = new Channel(user); + var channel = new PrivateChannel { User = user }; channel.AddNewMessages(withoutReplyGroup.ToArray()); JoinedChannels.Add(channel); diff --git a/osu.Game/Online/Chat/PrivateChannel.cs b/osu.Game/Online/Chat/PrivateChannel.cs new file mode 100644 index 0000000000..aac88ecb27 --- /dev/null +++ b/osu.Game/Online/Chat/PrivateChannel.cs @@ -0,0 +1,29 @@ +// 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; + } + } +} From 1ab75529a198fecba829a8584fdff792f3a656f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Jul 2018 21:15:52 +0200 Subject: [PATCH 52/88] Simplify user channel message population code --- osu.Game/Online/Chat/ChannelManager.cs | 39 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index c881fb4fe8..fd8454337e 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -203,29 +203,42 @@ namespace osu.Game.Online.Chat { var joinedUserChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); - var outgoingMessages = messages.Where(m => m.Sender.Id == api.LocalUser.Value.Id); - var outgoingMessagesGroups = outgoingMessages.GroupBy(m => m.TargetId); - var incomingMessagesGroups = messages.Except(outgoingMessages).GroupBy(m => m.UserId); - - foreach (var messageGroup in incomingMessagesGroups) + Channel getChannelForUser(User user) { - var targetUser = messageGroup.First().Sender; - var channel = joinedUserChannels.FirstOrDefault(c => c.Id == targetUser.Id); + var channel = joinedUserChannels.FirstOrDefault(c => c.Id == user.Id); if (channel == null) { - channel = new PrivateChannel { User = targetUser }; + channel = new PrivateChannel { User = user }; JoinedChannels.Add(channel); joinedUserChannels.Add(channel); } - channel.AddNewMessages(messageGroup.ToArray()); - var outgoingTargetMessages = outgoingMessagesGroups.FirstOrDefault(g => g.Key == targetUser.Id); + return channel; + } + + long localUserId = api.LocalUser.Value.Id; + + var outgoingGroups = messages.Where(m => m.Sender.Id == localUserId).GroupBy(m => m.TargetId); + 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()); } - var withoutReplyGroups = outgoingMessagesGroups.Where(g => joinedUserChannels.All(m => m.Id != g.Key)); + // 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 => joinedUserChannels.All(m => m.Id != g.Key)); foreach (var withoutReplyGroup in withoutReplyGroups) { @@ -234,10 +247,8 @@ namespace osu.Game.Online.Chat userReq.Failure += exception => Logger.Error(exception, "Failed to get user informations."); userReq.Success += user => { - var channel = new PrivateChannel { User = user }; - + var channel = getChannelForUser(user); channel.AddNewMessages(withoutReplyGroup.ToArray()); - JoinedChannels.Add(channel); }; api.Queue(userReq); From d9611dcffbf2de4dad02e90146e79033266562c3 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 23 Jul 2018 22:06:40 +0200 Subject: [PATCH 53/88] Instead of Doing nothing at LinkFlowContainer if no link was found, we log it as an error. --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 0576fc3918..d9be374ced 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using System.Collections.Generic; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -87,9 +88,9 @@ namespace osu.Game.Graphics.Containers { channelManager.OpenChannel(linkArgument); } - catch (ChannelNotFoundException) + catch (ChannelNotFoundException e) { - //channel was not found + Logger.Error(e, "It should not be possible that the user is able to click a invalid channel link."); } break; case LinkAction.OpenEditorTimestamp: From 2ae890366ad0b06cb3229b33435a763680e26772 Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 23 Jul 2018 22:08:37 +0200 Subject: [PATCH 54/88] Nicefy the errormessage in the constructor of GetMessagesRequest --- osu.Game/Online/API/Requests/GetMessagesRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index f959f94b9c..d82046e1e5 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.API.Requests if (channels == null) throw new ArgumentNullException(nameof(channels)); if (channels.Any(c => c.Target != TargetType.Channel)) - throw new ArgumentException($"All channels in the argument channels must have the {nameof(Channel.Target)} Channel"); + throw new ArgumentException($"All channels in the argument channels must have a {nameof(Channel.Target)} of {nameof(TargetType.Channel)}"); this.channels = channels; } From 9a6d92bb22558ba1f242c8ca252498bca4c03e6e Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 23 Jul 2018 22:09:05 +0200 Subject: [PATCH 55/88] Rename fetchMsgReq to fetchMessageReq in ChannelManager.cs --- osu.Game/Online/Chat/ChannelManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index fd8454337e..57f55492ad 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.Chat private IAPIProvider api; private ScheduledDelegate fetchMessagesScheduleder; - private GetMessagesRequest fetchMsgReq; + private GetMessagesRequest fetchMessageReq; private GetPrivateMessagesRequest fetchUserMsgReq; private long? lastChannelMsgId; private long? lastUserMsgId; @@ -156,16 +156,16 @@ namespace osu.Game.Online.Chat private void fetchNewMessages() { - if (fetchMsgReq == null) + if (fetchMessageReq == null) fetchMessages( - () => fetchMsgReq = new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), + () => fetchMessageReq = new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), messages => { if (messages == null) return; handleChannelMessages(messages); lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; - fetchMsgReq = null; + fetchMessageReq = null; } ); @@ -301,8 +301,8 @@ namespace osu.Game.Online.Chat fetchMessagesScheduleder = Scheduler.AddDelayed(fetchNewMessages, 1000, true); break; default: - fetchMsgReq?.Cancel(); - fetchMsgReq = null; + fetchMessageReq?.Cancel(); + fetchMessageReq = null; fetchMessagesScheduleder?.Cancel(); break; } From 16db81e9b56e796e00c1a8d41a14d0ac04eb73a3 Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 24 Jul 2018 04:54:11 +0200 Subject: [PATCH 56/88] Extract the message hadling logic into IncomingMessagesHandler --- osu.Game/Online/Chat/ChannelManager.cs | 71 ++++++++++--------- .../Online/Chat/IncomingMessagesHandler.cs | 66 +++++++++++++++++ 2 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Online/Chat/IncomingMessagesHandler.cs diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 57f55492ad..9b76c31c80 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -47,13 +47,17 @@ namespace osu.Game.Online.Chat /// public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); + private readonly IncomingMessagesHandler channelMessagesHandler; + private readonly IncomingMessagesHandler privateMessagesHandler; + private IAPIProvider api; private ScheduledDelegate fetchMessagesScheduleder; - private GetMessagesRequest fetchMessageReq; - private GetPrivateMessagesRequest fetchUserMsgReq; - private long? lastChannelMsgId; - private long? lastUserMsgId; + /// + /// Opens a channel or switches to the channel if already opened. + /// + /// If the name of the specifed channel was not found this exception will be thrown. + /// public void OpenChannel(string name) { if (name == null) @@ -63,7 +67,11 @@ namespace osu.Game.Online.Chat ?? throw new ChannelNotFoundException(name); } - public void OpenUserChannel(User user) + /// + /// Opens a new private channel. + /// + /// + public void OpenPrivateChannel(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); @@ -75,6 +83,14 @@ namespace osu.Game.Online.Chat public ChannelManager() { CurrentChannel.ValueChanged += currentChannelChanged; + + channelMessagesHandler = new IncomingMessagesHandler(); + channelMessagesHandler.CreateMessagesRequest = () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), channelMessagesHandler.LastMessageId); + channelMessagesHandler.OnNewMessages = handleChannelMessages; + + privateMessagesHandler = new IncomingMessagesHandler(); + privateMessagesHandler.CreateMessagesRequest = () => new GetPrivateMessagesRequest(privateMessagesHandler.LastMessageId); + privateMessagesHandler.OnNewMessages = handleUserMessages; } private void currentChannelChanged(Channel channel) @@ -156,32 +172,11 @@ namespace osu.Game.Online.Chat private void fetchNewMessages() { - if (fetchMessageReq == null) - fetchMessages( - () => fetchMessageReq = new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastChannelMsgId), - messages => - { - if (messages == null) - return; - handleChannelMessages(messages); - lastChannelMsgId = messages.LastOrDefault()?.Id ?? lastChannelMsgId; - fetchMessageReq = null; - } - ); + if (channelMessagesHandler.CanRequestNewMessages) + channelMessagesHandler.RequestNewMessages(api); - - if (fetchUserMsgReq == null) - fetchMessages( - () => fetchUserMsgReq = new GetPrivateMessagesRequest(lastUserMsgId), - messages => - { - if (messages == null) - return; - handleUserMessages(messages); - lastUserMsgId = messages.Max(m => m.Id) ?? lastUserMsgId; - fetchUserMsgReq = null; - } - ); + if (privateMessagesHandler.CanRequestNewMessages) + privateMessagesHandler.RequestNewMessages(api); } private void fetchMessages(Func messagesRequest, Action> handler) @@ -272,7 +267,7 @@ namespace osu.Game.Online.Chat channels.Where(channel => AvailableChannels.All(c => c.Id != channel.Id)) .ForEach(channel => AvailableChannels.Add(channel)); - channels.Where(channel => defaultChannels.Contains(channel.Name)) + channels.Where(channel => defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase))) .Where(channel => JoinedChannels.All(c => c.Id != channel.Id)) .ForEach(channel => { @@ -286,7 +281,12 @@ namespace osu.Game.Online.Chat fetchNewMessages(); }; - req.Failure += error => Logger.Error(error, "Fetching channel list failed"); + req.Failure += error => + { + Logger.Error(error, "Fetching channel list failed"); + + initializeDefaultChannels(); + }; api.Queue(req); } @@ -298,12 +298,15 @@ namespace osu.Game.Online.Chat case APIState.Online: if (JoinedChannels.Count == 0) initializeDefaultChannels(); + fetchMessagesScheduleder = Scheduler.AddDelayed(fetchNewMessages, 1000, true); break; default: - fetchMessageReq?.Cancel(); - fetchMessageReq = null; + channelMessagesHandler.CancelOngoingRequests(); + privateMessagesHandler.CancelOngoingRequests(); + fetchMessagesScheduleder?.Cancel(); + fetchMessagesScheduleder = null; break; } } diff --git a/osu.Game/Online/Chat/IncomingMessagesHandler.cs b/osu.Game/Online/Chat/IncomingMessagesHandler.cs new file mode 100644 index 0000000000..dc2e1cdf6b --- /dev/null +++ b/osu.Game/Online/Chat/IncomingMessagesHandler.cs @@ -0,0 +1,66 @@ +// 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 osu.Framework.Logging; +using osu.Game.Online.API; + +namespace osu.Game.Online.Chat +{ + public class IncomingMessagesHandler + { + public long? LastMessageId { get; private set; } + + private APIMessagesRequest getMessagesRequest; + + public Func CreateMessagesRequest { set; private get; } + + public Action> OnNewMessages { set; private get; } + + public bool CanRequestNewMessages => getMessagesRequest == null; + + public void RequestNewMessages(IAPIProvider api) + { + if (!CanRequestNewMessages) + throw new InvalidOperationException("Requesting new messages is not possible yet, because the old request is still ongoing."); + + if (OnNewMessages == null) + throw new InvalidOperationException($"You need to set an handler for the new incoming messages ({nameof(OnNewMessages)}) first before using {nameof(RequestNewMessages)}."); + + getMessagesRequest = CreateMessagesRequest.Invoke(); + + 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(); + } + } +} From 55f0cbf63e11e438b2041c0e4795d835b769dcee Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 24 Jul 2018 04:56:34 +0200 Subject: [PATCH 57/88] Finding peace with the UI thread. There is the issue that in some cases that the ui thread blocked. --- osu.Game/Overlays/Chat/ChatTabControl.cs | 6 +++-- osu.Game/Overlays/Chat/DrawableChannel.cs | 8 ++---- .../Chat/Selection/ChannelSelectionOverlay.cs | 27 ++++++++++--------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index c668f78ff4..5c09e81726 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -48,8 +48,10 @@ namespace osu.Game.Overlays.Chat } public void AddItem(Channel channel) - { - ChannelTabControl.AddItem(channel); + { + if (!ChannelTabControl.Items.Contains(channel)) + ChannelTabControl.AddItem(channel); + if (Current.Value == null) Current.Value = channel; } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 8cc77f82f3..cfefff9067 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -55,15 +55,11 @@ namespace osu.Game.Overlays.Chat Channel.PendingMessageResolved += pendingMessageResolved; } - [BackgroundDependencyLoader] - private void load() - { - newMessagesArrived(Channel.Messages); - } - protected override void LoadComplete() { base.LoadComplete(); + + newMessagesArrived(Channel.Messages); scrollToEnd(); } diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index df4a8f5d24..e2d064474f 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -125,23 +125,26 @@ namespace osu.Game.Overlays.Chat.Selection public void UpdateAvailableChannels(IEnumerable channels) { - sectionsFlow.ChildrenEnumerable = new[] + Scheduler.Add(() => { - new ChannelSection + sectionsFlow.ChildrenEnumerable = new[] { - Header = "All Channels", - Channels = channels, - }, - }; + new ChannelSection + { + Header = "All Channels", + Channels = channels, + }, + }; - foreach (ChannelSection s in sectionsFlow.Children) - { - foreach (ChannelListItem c in s.ChannelFlow.Children) + foreach (ChannelSection s in sectionsFlow.Children) { - c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; - c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; + foreach (ChannelListItem c in s.ChannelFlow.Children) + { + c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; + c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; + } } - } + }); } [BackgroundDependencyLoader] From 3df1842e1c3b439ac469f2ecd985d89097a4ba75 Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 24 Jul 2018 04:58:40 +0200 Subject: [PATCH 58/88] Fix that in some timing specific cases the fetched channels are not getting an visual representation. Sadly there is not a nice way of fixing this. --- osu.Game/Overlays/ChatOverlay.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index cee0a19868..4a286346d1 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -332,6 +332,10 @@ namespace osu.Game.Overlays channelManager.CurrentChannel.ValueChanged += currentChatChanged; channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; channelManager.AvailableChannels.CollectionChanged += (sender, args) => channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); + + //for the case that channelmanager was faster at fetching the channels than our attachment to CollectionChanged. + channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); + joinedChannelsChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, channelManager.JoinedChannels)); } private void postMessage(TextBox textbox, bool newText) From e769c15d284e424a1dcdb55c366f0654567e5408 Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 24 Jul 2018 05:14:33 +0200 Subject: [PATCH 59/88] Provide XML doc in Channel.cs and ChannelManager.cs and ChatTabControl.cs --- osu.Game/Online/Chat/Channel.cs | 36 ++++++++++++++++++++++++ osu.Game/Online/Chat/ChannelManager.cs | 10 +++++-- osu.Game/Overlays/Chat/ChatTabControl.cs | 10 +++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index cf702f2608..5f11739227 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -21,16 +21,44 @@ namespace osu.Game.Online.Chat /// public readonly ObservableCollection JoinedUsers = new ObservableCollection(); + /// + /// Contains all the messages send in the channel. + /// public readonly SortedList Messages = new SortedList(Comparer.Default); + + /// + /// Contains all the messages that are still pending for submission to the server. + /// private readonly List pendingMessages = new List(); + + /// + /// An event that fires when new messages arrived. + /// public event Action> NewMessagesArrived; + + /// + /// An event that fires when a pending message gets resolved. + /// public event Action PendingMessageResolved; + + /// + /// An event that fires when a pending message gets removed. + /// public event Action MessageRemoved; + /// + /// Signalles if the current user joined this channel or not. Defaults to false. + /// public readonly Bindable Joined = new Bindable(); + + /// + /// 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; [JsonProperty(@"name")] @@ -50,6 +78,10 @@ namespace osu.Game.Online.Chat { } + /// + /// Adds the argument message as a local echo. When this local echo is resolved will get called. + /// + /// public void AddLocalEcho(LocalEchoMessage message) { pendingMessages.Add(message); @@ -58,6 +90,10 @@ namespace osu.Game.Online.Chat NewMessagesArrived?.Invoke(new[] { message }); } + /// + /// Adds new messages to the channel and purges old messages. Triggers the event. + /// + /// public void AddNewMessages(params Message[] messages) { messages = messages.Except(Messages).ToArray(); diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 9b76c31c80..cf63ef4efe 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online.Chat /// /// Opens a new private channel. /// - /// + /// The user the private channel is opened with. public void OpenPrivateChannel(User user) { if (user == null) @@ -139,6 +139,10 @@ namespace osu.Game.Online.Chat api.Queue(req); } + /// + /// Posts a command locally. Commands like /help will result in a help message written in the current channel. + /// + /// the text containing the command identifier and command parameters. public void PostCommand(string text) { if (CurrentChannel.Value == null) @@ -319,7 +323,9 @@ namespace osu.Game.Online.Chat } } - + /// + /// An exception thrown when a channel could not been found. + /// public class ChannelNotFoundException : Exception { public ChannelNotFoundException(string channelName) diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 5c09e81726..8006635ce3 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -47,6 +47,11 @@ namespace osu.Game.Overlays.Chat ChannelTabControl.Current.Value = channel; } + /// + /// Adds a channel to the ChatTabControl. + /// The first channel added will automaticly selected. + /// + /// The channel that is going to be added. public void AddItem(Channel channel) { if (!ChannelTabControl.Items.Contains(channel)) @@ -56,6 +61,11 @@ namespace osu.Game.Overlays.Chat Current.Value = channel; } + /// + /// Removes a channel from the ChatTabControl. + /// If the selected channel is the one that is beeing removed, the next available channel will be selected. + /// + /// The channel that is going to be removed. public void RemoveItem(Channel channel) { ChannelTabControl.RemoveItem(channel); From 9bc225e14b7a6a079cf4bdb1bff3413e24ad09e5 Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 24 Jul 2018 05:14:47 +0200 Subject: [PATCH 60/88] rename user to private --- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index f8fb9e01f3..770f528e17 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -248,7 +248,7 @@ namespace osu.Game.Overlays.Chat private void load(UserProfileOverlay profile, ChannelManager chatManager) { Action = () => profile?.ShowUser(sender); - startChatAction = () => chatManager?.OpenUserChannel(sender); + startChatAction = () => chatManager?.OpenPrivateChannel(sender); } public MenuItem[] ContextMenuItems => new MenuItem[] From 42df0c974fea494266574a975e3f4daf98677710 Mon Sep 17 00:00:00 2001 From: miterosan Date: Tue, 24 Jul 2018 05:17:57 +0200 Subject: [PATCH 61/88] Rename UserChannel to private channel. --- osu.Game/Online/Chat/ChannelManager.cs | 8 ++++---- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 2 +- .../{UserChannelTabItem.cs => PrivateChannelTabItem.cs} | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Overlays/Chat/Tabs/{UserChannelTabItem.cs => PrivateChannelTabItem.cs} (98%) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index cf63ef4efe..7a914cc627 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -200,17 +200,17 @@ namespace osu.Game.Online.Chat private void handleUserMessages(IEnumerable messages) { - var joinedUserChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); + var joinedPrivateChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); Channel getChannelForUser(User user) { - var channel = joinedUserChannels.FirstOrDefault(c => c.Id == user.Id); + var channel = joinedPrivateChannels.FirstOrDefault(c => c.Id == user.Id); if (channel == null) { channel = new PrivateChannel { User = user }; JoinedChannels.Add(channel); - joinedUserChannels.Add(channel); + joinedPrivateChannels.Add(channel); } return channel; @@ -237,7 +237,7 @@ namespace osu.Game.Online.Chat // 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 => joinedUserChannels.All(m => m.Id != g.Key)); + var withoutReplyGroups = outgoingGroups.Where(g => joinedPrivateChannels.All(m => m.Id != g.Key)); foreach (var withoutReplyGroup in withoutReplyGroups) { diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index e1f02e9c79..1eb5f1ac6e 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Chat.Tabs case TargetType.Channel: return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; case TargetType.User: - return new UserChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + 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/UserChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs similarity index 98% rename from osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs rename to osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 6b11c7860f..3b837d0a79 100644 --- a/osu.Game/Overlays/Chat/Tabs/UserChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -19,7 +19,7 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Chat.Tabs { - public class UserChannelTabItem : TabItem + public class PrivateChannelTabItem : TabItem { private static readonly Vector2 shear = new Vector2(1f / 5f, 0); public override bool IsRemovable => true; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Chat.Tabs private readonly Avatar avatarContainer; private readonly TabCloseButton closeButton; - public UserChannelTabItem(Channel value) + public PrivateChannelTabItem(Channel value) : base(value) { if (value.Target != TargetType.User) @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.Chat.Tabs closeButton.ScaleTo(new Vector2(0, 1)); } - public Action OnRequestClose; + public Action OnRequestClose; private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters { From 53f556de9afb0227e5fa24e2c627da89ae1cc310 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Jul 2018 15:10:55 +0200 Subject: [PATCH 62/88] Trim whitespace --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 3 ++- osu.Game/Overlays/Chat/ChatTabControl.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index d9be374ced..8d3c7d1caf 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -90,8 +90,9 @@ namespace osu.Game.Graphics.Containers } catch (ChannelNotFoundException e) { - Logger.Error(e, "It should not be possible that the user is able to click a invalid channel link."); + Logger.Error(e, "It should not be possible that the user is able to click a invalid channel link."); } + break; case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 8006635ce3..1df89b668f 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Chat /// /// The channel that is going to be added. public void AddItem(Channel channel) - { + { if (!ChannelTabControl.Items.Contains(channel)) ChannelTabControl.AddItem(channel); From da730269a95960d6591b6b7f3b412f2cda55341a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Jul 2018 15:19:50 +0200 Subject: [PATCH 63/88] Formatting and ctor usage for required parameters --- osu.Game/Online/Chat/ChannelManager.cs | 10 +++---- .../Online/Chat/IncomingMessagesHandler.cs | 30 +++++++++++-------- osu.Game/Overlays/Chat/DrawableChannel.cs | 1 - 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 7a914cc627..2c73535668 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -84,13 +84,11 @@ namespace osu.Game.Online.Chat { CurrentChannel.ValueChanged += currentChannelChanged; - channelMessagesHandler = new IncomingMessagesHandler(); - channelMessagesHandler.CreateMessagesRequest = () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), channelMessagesHandler.LastMessageId); - channelMessagesHandler.OnNewMessages = handleChannelMessages; + channelMessagesHandler = new IncomingMessagesHandler( + () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), channelMessagesHandler.LastMessageId), handleChannelMessages); - privateMessagesHandler = new IncomingMessagesHandler(); - privateMessagesHandler.CreateMessagesRequest = () => new GetPrivateMessagesRequest(privateMessagesHandler.LastMessageId); - privateMessagesHandler.OnNewMessages = handleUserMessages; + privateMessagesHandler = new IncomingMessagesHandler( + () => new GetPrivateMessagesRequest(privateMessagesHandler.LastMessageId),handleUserMessages); } private void currentChannelChanged(Channel channel) diff --git a/osu.Game/Online/Chat/IncomingMessagesHandler.cs b/osu.Game/Online/Chat/IncomingMessagesHandler.cs index dc2e1cdf6b..eb70285f0b 100644 --- a/osu.Game/Online/Chat/IncomingMessagesHandler.cs +++ b/osu.Game/Online/Chat/IncomingMessagesHandler.cs @@ -4,39 +4,44 @@ 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 long? LastMessageId { get; private set; } private APIMessagesRequest getMessagesRequest; - public Func CreateMessagesRequest { set; private get; } - - public Action> OnNewMessages { set; private get; } + private readonly Func createRequest; + private readonly Action> onNewMessages; public bool CanRequestNewMessages => getMessagesRequest == null; + public IncomingMessagesHandler([NotNull] Func 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."); - if (OnNewMessages == null) - throw new InvalidOperationException($"You need to set an handler for the new incoming messages ({nameof(OnNewMessages)}) first before using {nameof(RequestNewMessages)}."); - - getMessagesRequest = CreateMessagesRequest.Invoke(); - + getMessagesRequest = createRequest.Invoke(); getMessagesRequest.Success += handleNewMessages; getMessagesRequest.Failure += exception => { Logger.Error(exception, "Fetching messages failed."); - //allowing new messages to be requested even after the fail. + // allowing new messages to be requested even after the fail. getMessagesRequest = null; }; @@ -45,15 +50,14 @@ namespace osu.Game.Online.Chat private void handleNewMessages(List messages) { - - //allowing new messages to be requested. + // allowing new messages to be requested. getMessagesRequest = null; - //in case of no new messages we simply do nothing. + // in case of no new messages we simply do nothing. if (messages == null || messages.Count == 0) return; - OnNewMessages.Invoke(messages); + onNewMessages.Invoke(messages); LastMessageId = messages.Max(m => m.Id) ?? LastMessageId; } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index cfefff9067..894fd84b93 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using OpenTK.Graphics; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; From 2d861f5897c3db2e26fbd447d6fc46cf541a0ea0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Jul 2018 15:26:02 +0200 Subject: [PATCH 64/88] Remove unnecessary inject --- osu.Game/OsuGameBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 93cbebbf26..6ba724524c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -126,7 +126,6 @@ namespace osu.Game dependencies.CacheAs(api); var channelManager = new ChannelManager(); - dependencies.Inject(channelManager); dependencies.Cache(channelManager); AddInternal(channelManager); From 090d197b21e3e41319d2157a6e9555dfed8f484f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Jul 2018 17:35:08 +0200 Subject: [PATCH 65/88] Remove unnecessary using --- osu.Game/Online/Chat/ChannelManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 2c73535668..ca28161909 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -7,7 +7,6 @@ using System.Collections.ObjectModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Threading; From 6937cf27a7084339c964835da0a46920cdfdfdd3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Jul 2018 17:51:20 +0200 Subject: [PATCH 66/88] Tidy up channel join logic --- osu.Game/Online/Chat/ChannelManager.cs | 32 ++++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index ca28161909..da6cc58e02 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -265,20 +265,28 @@ namespace osu.Game.Online.Chat req.Success += channels => { - channels.Where(channel => AvailableChannels.All(c => c.Id != channel.Id)) - .ForEach(channel => AvailableChannels.Add(channel)); + foreach (var channel in channels) + { + if (JoinedChannels.Any(c => c.Id == channel.Id)) + continue; - channels.Where(channel => defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase))) - .Where(channel => JoinedChannels.All(c => c.Id != channel.Id)) - .ForEach(channel => - { - JoinedChannels.Add(channel); + // add as available if not already + if (AvailableChannels.All(c => c.Id != channel.Id)) + AvailableChannels.Add(channel); - var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); - fetchInitialMsgReq.Success += handleChannelMessages; - fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); - api.Queue(fetchInitialMsgReq); - }); + // join any channels classified as "defaults" + if (defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase))) + { + JoinedChannels.Add(channel); + + // TODO: remove this when the API supports returning initial fetch messages for more than one channel. + // right now it caps out at 50 messages and therefore only returns one channel's worth of content. + var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); + fetchInitialMsgReq.Success += handleChannelMessages; + fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); + api.Queue(fetchInitialMsgReq); + } + } fetchNewMessages(); }; From 7b653fab1743c45270359477586a0e512dc9c1ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Jul 2018 18:01:28 +0200 Subject: [PATCH 67/88] Pass in lastMessageId instead of self referencing --- osu.Game/Online/Chat/ChannelManager.cs | 4 ++-- osu.Game/Online/Chat/IncomingMessagesHandler.cs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index da6cc58e02..f34321f597 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -84,10 +84,10 @@ namespace osu.Game.Online.Chat CurrentChannel.ValueChanged += currentChannelChanged; channelMessagesHandler = new IncomingMessagesHandler( - () => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), channelMessagesHandler.LastMessageId), handleChannelMessages); + lastId => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastId), handleChannelMessages); privateMessagesHandler = new IncomingMessagesHandler( - () => new GetPrivateMessagesRequest(privateMessagesHandler.LastMessageId),handleUserMessages); + lastId => new GetPrivateMessagesRequest(lastId),handleUserMessages); } private void currentChannelChanged(Channel channel) diff --git a/osu.Game/Online/Chat/IncomingMessagesHandler.cs b/osu.Game/Online/Chat/IncomingMessagesHandler.cs index eb70285f0b..46f2b805b3 100644 --- a/osu.Game/Online/Chat/IncomingMessagesHandler.cs +++ b/osu.Game/Online/Chat/IncomingMessagesHandler.cs @@ -15,16 +15,18 @@ namespace osu.Game.Online.Chat /// public class IncomingMessagesHandler { + public delegate APIMessagesRequest CreateRequestDelegate(long? lastMessageId); + public long? LastMessageId { get; private set; } private APIMessagesRequest getMessagesRequest; - private readonly Func createRequest; + private readonly CreateRequestDelegate createRequest; private readonly Action> onNewMessages; public bool CanRequestNewMessages => getMessagesRequest == null; - public IncomingMessagesHandler([NotNull] Func createRequest, [NotNull] Action> onNewMessages) + public IncomingMessagesHandler([NotNull] CreateRequestDelegate createRequest, [NotNull] Action> onNewMessages) { this.createRequest = createRequest ?? throw new ArgumentNullException(nameof(createRequest)); this.onNewMessages = onNewMessages ?? throw new ArgumentNullException(nameof(onNewMessages)); @@ -35,7 +37,7 @@ namespace osu.Game.Online.Chat if (!CanRequestNewMessages) throw new InvalidOperationException("Requesting new messages is not possible yet, because the old request is still ongoing."); - getMessagesRequest = createRequest.Invoke(); + getMessagesRequest = createRequest.Invoke(LastMessageId); getMessagesRequest.Success += handleNewMessages; getMessagesRequest.Failure += exception => { From 95cb21299a30a89113bb482fb26f49ba8490a453 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 29 Jul 2018 21:18:37 +0200 Subject: [PATCH 68/88] Remove chatTabControl and transfer the logic into ChannelTabControl. --- ...ontrol.cs => TestCaseChannelTabControl.cs} | 22 +++--- osu.Game/Overlays/Chat/ChatTabControl.cs | 76 ------------------- .../Overlays/Chat/Tabs/ChannelTabControl.cs | 28 +++++++ osu.Game/Overlays/ChatOverlay.cs | 31 ++++---- 4 files changed, 56 insertions(+), 101 deletions(-) rename osu.Game.Tests/Visual/{TestCaseChatTabControl.cs => TestCaseChannelTabControl.cs} (79%) delete mode 100644 osu.Game/Overlays/Chat/ChatTabControl.cs diff --git a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs similarity index 79% rename from osu.Game.Tests/Visual/TestCaseChatTabControl.cs rename to osu.Game.Tests/Visual/TestCaseChannelTabControl.cs index c7d88a4d0e..35f4037ed2 100644 --- a/osu.Game.Tests/Visual/TestCaseChatTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs @@ -14,24 +14,22 @@ using osu.Framework.MathUtils; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; -using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Tabs; using osu.Game.Users; using OpenTK.Graphics; namespace osu.Game.Tests.Visual { - public class TestCaseChatTabControl : OsuTestCase + public class TestCaseChannelTabControl : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { - typeof(ChatTabControl), - typeof(ChannelTabControl) + typeof(ChannelTabControl), }; - private readonly ChatTabControl chatTabControl; + private readonly ChannelTabControl channelTabControl; - public TestCaseChatTabControl() + public TestCaseChannelTabControl() { SpriteText currentText; Add(new Container @@ -41,7 +39,7 @@ namespace osu.Game.Tests.Visual Anchor = Anchor.Centre, Children = new Drawable[] { - chatTabControl = new ChatTabControl + channelTabControl = new ChannelTabControl { RelativeSizeAxes = Axes.X, Origin = Anchor.Centre, @@ -68,13 +66,13 @@ namespace osu.Game.Tests.Visual { currentText = new SpriteText { - Text = "Currently selected chat: " + Text = "Currently selected channel:" } } }); - chatTabControl.OnRequestLeave += chat => chatTabControl.RemoveItem(chat); - chatTabControl.Current.ValueChanged += chat => currentText.Text = "Currently selected chat: " + chat.ToString(); + channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel); + channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.ToString(); AddStep("Add random user", addRandomUser); AddRepeatStep("Add 3 random users", addRandomUser, 3); @@ -88,12 +86,12 @@ namespace osu.Game.Tests.Visual if (users == null || users.Count == 0) return; - chatTabControl.AddItem(new PrivateChannel { User = users[RNG.Next(0, users.Count - 1)] }); + channelTabControl.AddChannel(new PrivateChannel { User = users[RNG.Next(0, users.Count - 1)] }); } private void addChannel(string name) { - chatTabControl.AddItem(new Channel + channelTabControl.AddChannel(new Channel { Name = name }); diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs deleted file mode 100644 index 1df89b668f..0000000000 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ /dev/null @@ -1,76 +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.Linq; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Online.Chat; -using osu.Game.Overlays.Chat.Tabs; - -namespace osu.Game.Overlays.Chat -{ - public class ChatTabControl : Container, IHasCurrentValue - { - public readonly ChannelTabControl ChannelTabControl; - - public Bindable Current { get; } = new Bindable(); - public Action OnRequestLeave; - - public ChatTabControl() - { - Masking = false; - - Children = new Drawable[] - { - ChannelTabControl = new ChannelTabControl - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - OnRequestLeave = channel => OnRequestLeave?.Invoke(channel) - }, - }; - - Current.ValueChanged += currentTabChanged; - ChannelTabControl.Current.ValueChanged += channel => - { - if (channel != null) - Current.Value = channel; - }; - } - - private void currentTabChanged(Channel channel) - { - ChannelTabControl.Current.Value = channel; - } - - /// - /// Adds a channel to the ChatTabControl. - /// The first channel added will automaticly selected. - /// - /// The channel that is going to be added. - public void AddItem(Channel channel) - { - if (!ChannelTabControl.Items.Contains(channel)) - ChannelTabControl.AddItem(channel); - - if (Current.Value == null) - Current.Value = channel; - } - - /// - /// Removes a channel from the ChatTabControl. - /// If the selected channel is the one that is beeing removed, the next available channel will be selected. - /// - /// The channel that is going to be removed. - public void RemoveItem(Channel channel) - { - ChannelTabControl.RemoveItem(channel); - if (Current.Value == channel) - Current.Value = ChannelTabControl.Items.FirstOrDefault(); - } - } -} diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 1eb5f1ac6e..6470963b4f 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -9,6 +9,7 @@ using osu.Game.Online.Chat; using OpenTK; using osu.Framework.Configuration; using System; +using System.Linq; namespace osu.Game.Overlays.Chat.Tabs { @@ -64,6 +65,33 @@ namespace osu.Game.Overlays.Chat.Tabs } } + /// + /// Adds a channel to the ChannelTabControl. + /// The first channel added will automaticly selected. + /// + /// The channel that is going to be added. + public void AddChannel(Channel channel) + { + if (!Items.Contains(channel)) + AddItem(channel); + + if (Current.Value == null) + Current.Value = channel; + } + + /// + /// Removes a channel from the ChannelTabControl. + /// If the selected channel is the one that is beeing removed, the next available channel will be selected. + /// + /// The channel that is going to be removed. + public void RemoveChannel(Channel channel) + { + RemoveItem(channel); + + if (Current.Value == channel) + Current.Value = Items.FirstOrDefault(); + } + protected override void SelectTab(TabItem tab) { if (tab is ChannelSelectorTabItem) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 4a286346d1..99f392a183 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Selection; +using osu.Game.Overlays.Chat.Tabs; namespace osu.Game.Overlays { @@ -43,7 +44,7 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private readonly ChatTabControl chatTabControl; + private readonly ChannelTabControl channelTabControl; private readonly Container chatContainer; private readonly Container tabsArea; @@ -152,22 +153,24 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - chatTabControl = new ChatTabControl + channelTabControl = new ChannelTabControl { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel) - } + }, } }, }, }, }; - chatTabControl.Current.ValueChanged += chat => channelManager.CurrentChannel.Value = chat; - chatTabControl.ChannelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; + channelTabControl.Current.ValueChanged += chat => channelManager.CurrentChannel.Value = chat; + channelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; channelSelection.StateChanged += state => { - chatTabControl.ChannelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible; + channelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible; if (state == Visibility.Visible) { @@ -193,14 +196,16 @@ namespace osu.Game.Overlays case NotifyCollectionChangedAction.Add: foreach (Channel newChannel in args.NewItems) { - chatTabControl.AddItem(newChannel); + channelTabControl.AddChannel(newChannel); + newChannel.Joined.Value = true; } break; case NotifyCollectionChangedAction.Remove: foreach (Channel removedChannel in args.OldItems) { - chatTabControl.RemoveItem(removedChannel); + channelTabControl.RemoveChannel(removedChannel); + loadedChannels.Remove(loadedChannels.Find(c => c.Channel == removedChannel )); removedChannel.Joined.Value = false; } @@ -208,20 +213,20 @@ namespace osu.Game.Overlays } } - private void currentChatChanged(Channel channel) + private void currentChannelChanged(Channel channel) { if (channel == null) { textbox.Current.Disabled = true; currentChannelContainer.Clear(false); - chatTabControl.Current.Value = null; + channelTabControl.Current.Value = null; return; } textbox.Current.Disabled = channel.ReadOnly; - if (chatTabControl.Current.Value != channel) - Scheduler.Add(() => chatTabControl.Current.Value = channel); + if (channelTabControl.Current.Value != channel) + Scheduler.Add(() => channelTabControl.Current.Value = channel); var loaded = loadedChannels.Find(d => d.Channel == channel); if (loaded == null) @@ -329,7 +334,7 @@ namespace osu.Game.Overlays loading.Show(); this.channelManager = channelManager; - channelManager.CurrentChannel.ValueChanged += currentChatChanged; + channelManager.CurrentChannel.ValueChanged += currentChannelChanged; channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; channelManager.AvailableChannels.CollectionChanged += (sender, args) => channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); From 8c0bcb8e3c01e92b554e2251e85e4f3e76ff18d1 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 29 Jul 2018 21:40:43 +0200 Subject: [PATCH 69/88] Fix drawable crash and lload the inital messages of newly joined channels --- osu.Game/Online/Chat/ChannelManager.cs | 37 +++++++++++--------------- osu.Game/Overlays/ChatOverlay.cs | 5 +++- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f34321f597..7d3d21554e 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -180,21 +180,6 @@ namespace osu.Game.Online.Chat privateMessagesHandler.RequestNewMessages(api); } - private void fetchMessages(Func messagesRequest, Action> handler) - { - if (messagesRequest == null) - throw new ArgumentNullException(nameof(messagesRequest)); - if (handler == null) - throw new ArgumentNullException(nameof(handler)); - - var messagesReq = messagesRequest.Invoke(); - - messagesReq.Success += handler.Invoke; - messagesReq.Failure += exception => Logger.Error(exception, "Fetching messages failed."); - - api.Queue(messagesReq); - } - private void handleUserMessages(IEnumerable messages) { var joinedPrivateChannels = JoinedChannels.Where(c => c.Target == TargetType.User).ToList(); @@ -279,12 +264,7 @@ namespace osu.Game.Online.Chat { JoinedChannels.Add(channel); - // TODO: remove this when the API supports returning initial fetch messages for more than one channel. - // right now it caps out at 50 messages and therefore only returns one channel's worth of content. - var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); - fetchInitialMsgReq.Success += handleChannelMessages; - fetchInitialMsgReq.Failure += exception => Logger.Error(exception, "Failed to fetch inital messages."); - api.Queue(fetchInitialMsgReq); + FetchInitalMessages(channel); } } @@ -300,6 +280,21 @@ namespace osu.Game.Online.Chat api.Queue(req); } + /// + /// Fetches inital messages of a channel + /// + /// TODO: remove this when the API supports returning initial fetch messages for more than one channel by specifying the last message id per channel instead of one last message id globally. + /// 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) + { + var fetchInitialMsgReq = new GetMessagesRequest(new[] { channel }, null); + fetchInitialMsgReq.Success += handleChannelMessages; + fetchInitialMsgReq.Failure += exception => Logger.Error(exception, $"Failed to fetch inital messages for the channel {channel.Name}"); + api.Queue(fetchInitialMsgReq); + } + public void APIStateChanged(APIAccess api, APIState state) { switch (state) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 99f392a183..d3277fb0a9 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -184,7 +184,10 @@ namespace osu.Game.Overlays channelSelection.OnRequestJoin = channel => { if (!channelManager.JoinedChannels.Contains(channel)) + { channelManager.JoinedChannels.Add(channel); + channelManager.FetchInitalMessages(channel); + } }; channelSelection.OnRequestLeave = channel => channelManager.JoinedChannels.Remove(channel); } @@ -248,7 +251,7 @@ namespace osu.Game.Overlays else { currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); + Scheduler.Add(() => currentChannelContainer.Add(loaded)); } } From 8bccecc2e58bf48889ba694ddee131ec203d170f Mon Sep 17 00:00:00 2001 From: miterosan Date: Mon, 30 Jul 2018 00:13:32 +0200 Subject: [PATCH 70/88] Add some more testing. --- osu.Game.Tests/Visual/TestCaseChannelTabControl.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs index 35f4037ed2..bdb32e95c9 100644 --- a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs @@ -74,9 +74,13 @@ namespace osu.Game.Tests.Visual channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel); channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.ToString(); - AddStep("Add random user", addRandomUser); - AddRepeatStep("Add 3 random users", addRandomUser, 3); - AddStep("Add random channel", () => addChannel(RNG.Next().ToString())); + AddStep("Add random private channel", addRandomUser); + AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2); + AddRepeatStep("Add 3 random private channels", addRandomUser, 3); + AddAssert("There are four channels", () => channelTabControl.Items.Count() == 5); + AddStep("Add random public channel", () => addChannel(RNG.Next().ToString())); + + AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Count + 1)), 20); } private List users; From 358c3469238e6abf73c43b2e76c3161a5aecdb86 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 4 Aug 2018 00:44:16 +0200 Subject: [PATCH 71/88] Use TabItem.Items.Count --- osu.Game.Tests/Visual/TestCaseChannelTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs index bdb32e95c9..3347b2ad2e 100644 --- a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual AddAssert("There are four channels", () => channelTabControl.Items.Count() == 5); AddStep("Add random public channel", () => addChannel(RNG.Next().ToString())); - AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Count + 1)), 20); + AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Items.Count() + 1)), 20); } private List users; From b414bff86481a537197eaea26e2e06622fc4d0ff Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 4 Aug 2018 01:00:46 +0200 Subject: [PATCH 72/88] Fix the testcase --- .../Visual/TestCaseChannelTabControl.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs index 3347b2ad2e..1c1675a67c 100644 --- a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs @@ -80,17 +80,23 @@ namespace osu.Game.Tests.Visual AddAssert("There are four channels", () => channelTabControl.Items.Count() == 5); AddStep("Add random public channel", () => addChannel(RNG.Next().ToString())); - AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Items.Count() + 1)), 20); + AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Items.Count())), 20); } private List users; private void addRandomUser() { - if (users == null || users.Count == 0) - return; - - channelTabControl.AddChannel(new PrivateChannel { User = users[RNG.Next(0, users.Count - 1)] }); + channelTabControl.AddChannel(new PrivateChannel + { + User = users?.Count > 0 + ? users[RNG.Next(0, users.Count - 1)] + : new User + { + Id = RNG.Next(), + Username = "testuser" + RNG.Next(1000) + } + }); } private void addChannel(string name) From 119f81b86e560004ae106330172b3887768680b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 15:56:04 +0900 Subject: [PATCH 73/88] Fix disposal issues in ChatOverlay testcase --- osu.Game/Overlays/ChatOverlay.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index d3277fb0a9..47262068d1 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -339,13 +339,26 @@ namespace osu.Game.Overlays this.channelManager = channelManager; channelManager.CurrentChannel.ValueChanged += currentChannelChanged; channelManager.JoinedChannels.CollectionChanged += joinedChannelsChanged; - channelManager.AvailableChannels.CollectionChanged += (sender, args) => channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); + channelManager.AvailableChannels.CollectionChanged += availableChannelsChanged; //for the case that channelmanager was faster at fetching the channels than our attachment to CollectionChanged. channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); joinedChannelsChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, channelManager.JoinedChannels)); } + private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + channelSelection.UpdateAvailableChannels(channelManager.AvailableChannels); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + channelManager.CurrentChannel.ValueChanged -= currentChannelChanged; + channelManager.JoinedChannels.CollectionChanged -= joinedChannelsChanged; + channelManager.AvailableChannels.CollectionChanged -= availableChannelsChanged; + } + private void postMessage(TextBox textbox, bool newText) { var text = textbox.Text.Trim(); From 93e2d8f30984d1cdb880a5bb2fcd180cc4a8a35f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 15:56:27 +0900 Subject: [PATCH 74/88] Allow testing of all chat-related classes dynamically --- osu.Game.Tests/Visual/TestCaseChatDisplay.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs index c03b12bdc1..dc47055a87 100644 --- a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs @@ -1,15 +1,31 @@ // 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.ComponentModel; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; +using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.Tabs; namespace osu.Game.Tests.Visual { [Description("Testing chat api and overlay")] public class TestCaseChatDisplay : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ChatOverlay), + typeof(ChatLine), + typeof(DrawableChannel), + typeof(ChannelSelectorTabItem), + typeof(ChannelTabControl), + typeof(ChannelTabItem), + typeof(PrivateChannelTabItem), + typeof(TabCloseButton) + }; + public TestCaseChatDisplay() { Add(new ChatOverlay From 6a668ffe33389acf30ce304bfceb33928f478b97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 16:21:31 +0900 Subject: [PATCH 75/88] Allow PrivateChannelTab to derive from ChannelTab --- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 178 +++++++++------- .../Chat/Tabs/PrivateChannelTabItem.cs | 201 ++++-------------- 2 files changed, 138 insertions(+), 241 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 28a1f91df4..13913baefe 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -20,7 +20,6 @@ namespace osu.Game.Overlays.Chat.Tabs { public class ChannelTabItem : TabItem { - protected Color4 BackgroundInactive; private Color4 backgroundHover; protected Color4 BackgroundActive; @@ -29,81 +28,15 @@ namespace osu.Game.Overlays.Chat.Tabs protected readonly SpriteText Text; protected readonly SpriteText TextBold; - private readonly ClickableContainer closeButton; + protected readonly ClickableContainer CloseButton; private readonly Box box; private readonly Box highlightBox; protected readonly SpriteIcon Icon; public Action OnRequestClose; + private readonly Container content; - private void updateState() - { - if (Active) - fadeActive(); - else - fadeInactive(); - } - - private const float transition_length = 400; - - private void fadeActive() - { - this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); - - box.FadeColour(BackgroundActive, transition_length, Easing.OutQuint); - highlightBox.FadeIn(transition_length, Easing.OutQuint); - - Text.FadeOut(transition_length, Easing.OutQuint); - TextBold.FadeIn(transition_length, Easing.OutQuint); - } - - private void fadeInactive() - { - this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); - - box.FadeColour(BackgroundInactive, transition_length, Easing.OutQuint); - highlightBox.FadeOut(transition_length, Easing.OutQuint); - - Text.FadeIn(transition_length, Easing.OutQuint); - TextBold.FadeOut(transition_length, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - if (IsRemovable) - closeButton.FadeIn(200, Easing.OutQuint); - - if (!Active) - box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - closeButton.FadeOut(200, Easing.OutQuint); - updateState(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundActive = colours.ChatBlue; - BackgroundInactive = colours.Gray4; - backgroundHover = colours.Gray7; - - highlightBox.Colour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); + protected override Container Content => content; public ChannelTabItem(Channel value) : base(value) @@ -118,14 +51,8 @@ namespace osu.Game.Overlays.Chat.Tabs Shear = new Vector2(ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0); Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 10, - Colour = Color4.Black.Opacity(0.2f), - }; - Children = new Drawable[] + InternalChildren = new Drawable[] { box = new Box { @@ -141,7 +68,7 @@ namespace osu.Game.Overlays.Chat.Tabs EdgeSmoothness = new Vector2(1, 0), RelativeSizeAxes = Axes.Y, }, - new Container + content = new Container { Shear = new Vector2(-ChannelTabControl.SHEAR_WIDTH / ChatOverlay.TAB_AREA_HEIGHT, 0), RelativeSizeAxes = Axes.Both, @@ -149,7 +76,7 @@ namespace osu.Game.Overlays.Chat.Tabs { Icon = new SpriteIcon { - Icon = FontAwesome.fa_hashtag, + Icon = DisplayIcon, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Colour = Color4.Black, @@ -175,7 +102,7 @@ namespace osu.Game.Overlays.Chat.Tabs Font = @"Exo2.0-Bold", TextSize = 18, }, - closeButton = new TabCloseButton + CloseButton = new TabCloseButton { Alpha = 0, Margin = new MarginPadding { Right = 20 }, @@ -190,5 +117,96 @@ namespace osu.Game.Overlays.Chat.Tabs }, }; } + + protected virtual FontAwesome DisplayIcon => FontAwesome.fa_hashtag; + + protected virtual bool ShowCloseOnHover => true; + + protected override bool OnHover(InputState state) + { + if (IsRemovable && ShowCloseOnHover) + CloseButton.FadeIn(200, Easing.OutQuint); + + if (!Active) + box.FadeColour(backgroundHover, TRANSITION_LENGTH, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + CloseButton.FadeOut(200, Easing.OutQuint); + updateState(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundActive = colours.ChatBlue; + BackgroundInactive = colours.Gray4; + backgroundHover = colours.Gray7; + + highlightBox.Colour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + FinishTransforms(true); + } + + private void updateState() + { + if (Active) + FadeActive(); + else + FadeInactive(); + } + + protected const float TRANSITION_LENGTH = 400; + + private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 15, + Colour = Color4.Black.Opacity(0.4f), + }; + + private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 10, + Colour = Color4.Black.Opacity(0.2f), + }; + + protected virtual void FadeActive() + { + this.ResizeHeightTo(1.1f, TRANSITION_LENGTH, Easing.OutQuint); + + TweenEdgeEffectTo(activateEdgeEffect, TRANSITION_LENGTH); + + box.FadeColour(BackgroundActive, TRANSITION_LENGTH, Easing.OutQuint); + highlightBox.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); + + Text.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); + TextBold.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); + } + + protected virtual void FadeInactive() + { + this.ResizeHeightTo(1, TRANSITION_LENGTH, Easing.OutQuint); + + TweenEdgeEffectTo(deactivateEdgeEffect, TRANSITION_LENGTH); + + box.FadeColour(BackgroundInactive, TRANSITION_LENGTH, Easing.OutQuint); + highlightBox.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); + + Text.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); + TextBold.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); + } + + protected override void OnActivated() => updateState(); + protected override void OnDeactivated() => updateState(); } } diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 3b837d0a79..14d43d4205 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -7,29 +7,20 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; -using osu.Game.Screens.Menu; using osu.Game.Users; using OpenTK; -using OpenTK.Graphics; namespace osu.Game.Overlays.Chat.Tabs { - public class PrivateChannelTabItem : TabItem + public class PrivateChannelTabItem : ChannelTabItem { - private static readonly Vector2 shear = new Vector2(1f / 5f, 0); - public override bool IsRemovable => true; - - private readonly Box highlightBox; - private readonly Container backgroundContainer; - private readonly Box backgroundBox; private readonly OsuSpriteText username; private readonly Avatar avatarContainer; - private readonly TabCloseButton closeButton; + + protected override FontAwesome DisplayIcon => FontAwesome.fa_at; public PrivateChannelTabItem(Channel value) : base(value) @@ -37,174 +28,61 @@ namespace osu.Game.Overlays.Chat.Tabs if (value.Target != TargetType.User) throw new ArgumentException("Argument value needs to have the targettype user!"); - AutoSizeAxes = Axes.X; - Height = 50; - Origin = Anchor.BottomLeft; - Anchor = Anchor.BottomLeft; - EdgeEffect = activateEdgeEffect; - Masking = true; - Shear = shear; - Children = new Drawable[] + AddRange(new Drawable[] { new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding + { + Horizontal = 3 + }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, Children = new Drawable[] { - backgroundBox = new Box + new CircularContainer { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - EdgeSmoothness = new Vector2(1, 0), + Scale = new Vector2(0.95f), + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Child = new DelayedLoadWrapper(new Avatar(value.JoinedUsers.First()) + { + RelativeSizeAxes = Axes.Both, + OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), + }) + { + RelativeSizeAxes = Axes.Both, + } }, } }, - highlightBox = new Box - { - Width = 5, - BypassAutoSizeAxes = Axes.X, - Alpha = 0, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Y, - Colour = new OsuColour().Yellow - }, - new Container - { - Masking = true, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Child = new FlowContainerWithOrigin - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - X = -5, - Direction = FillDirection.Horizontal, - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Shear = -shear, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Margin = new MarginPadding - { - Horizontal = 5 - }, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_at, - Origin = Anchor.Centre, - Scale = new Vector2(1.2f), - X = -5, - Y = 5, - Anchor = Anchor.Centre, - Colour = new OsuColour().BlueDarker, - RelativeSizeAxes = Axes.Both, - }, - new CircularContainer - { - RelativeSizeAxes = Axes.Y, - Scale = new Vector2(0.95f), - AutoSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - Child = new DelayedLoadWrapper(new Avatar(value.JoinedUsers.First()) - { - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), - }) - { - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - } - }, - } - }, - username = new OsuSpriteText - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.Name, - Margin = new MarginPadding(1), - TextSize = 18, - Alpha = 0, - }, - closeButton = new TabCloseButton - { - Height = 1, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Alpha = 0, - Margin = new MarginPadding - { - Right = 5 - }, - RelativeSizeAxes = Axes.Y, - Action = delegate - { - if (IsRemovable) OnRequestClose?.Invoke(this); - }, - }, - } - } - } - }; + }); - username.ScaleTo(new Vector2(0, 1)); - closeButton.ScaleTo(new Vector2(0, 1)); + Text.X = ChatOverlay.TAB_AREA_HEIGHT; + TextBold.X = ChatOverlay.TAB_AREA_HEIGHT; } - public Action OnRequestClose; + protected override bool ShowCloseOnHover => false; - private readonly EdgeEffectParameters activateEdgeEffect = new EdgeEffectParameters + protected override void FadeActive() { - Type = EdgeEffectType.Shadow, - Radius = 15, - Colour = Color4.Black.Opacity(0.4f), - }; + base.FadeActive(); - protected override void OnActivated() - { - const int activate_length = 1000; - - backgroundBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); - highlightBox.ResizeHeightTo(1.1f, activate_length, Easing.OutQuint); - highlightBox.FadeIn(activate_length, Easing.OutQuint); - username.FadeIn(activate_length, Easing.OutQuint); - username.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); - closeButton.ScaleTo(new Vector2(1, 1), activate_length, Easing.OutQuint); - closeButton.FadeIn(activate_length, Easing.OutQuint); - TweenEdgeEffectTo(activateEdgeEffect, activate_length); + this.ResizeWidthTo(200, TRANSITION_LENGTH * 2, Easing.OutQuint); + CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); } - private readonly EdgeEffectParameters deactivateEdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 10, - Colour = Color4.Black.Opacity(0.2f), - }; - protected override void OnDeactivated() + protected override void FadeInactive() { - const int deactivate_length = 500; + base.FadeInactive(); - backgroundBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); - highlightBox.ResizeHeightTo(1, deactivate_length, Easing.OutQuint); - highlightBox.FadeOut(deactivate_length, Easing.OutQuint); - username.FadeOut(deactivate_length, Easing.OutQuint); - username.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); - closeButton.FadeOut(deactivate_length, Easing.OutQuint); - closeButton.ScaleTo(new Vector2(0, 1), deactivate_length, Easing.OutQuint); - TweenEdgeEffectTo(deactivateEdgeEffect, deactivate_length); + this.ResizeWidthTo(ChatOverlay.TAB_AREA_HEIGHT + 10, TRANSITION_LENGTH, Easing.OutQuint); + CloseButton.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); } [BackgroundDependencyLoader] @@ -212,7 +90,8 @@ namespace osu.Game.Overlays.Chat.Tabs { var user = Value.JoinedUsers.First(); - backgroundBox.Colour = user.Colour != null ? OsuColour.FromHex(user.Colour) : colours.BlueDark; + BackgroundActive = user.Colour != null ? OsuColour.FromHex(user.Colour) : colours.BlueDark; + BackgroundInactive = BackgroundActive.Darken(0.5f); } } } From 016e3957879af6012d0a82cea655ce0eed2f9ae8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 17:11:23 +0900 Subject: [PATCH 76/88] Fix disposal logic --- osu.Game/Overlays/ChatOverlay.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 47262068d1..912734ba0c 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -354,9 +354,13 @@ namespace osu.Game.Overlays protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - channelManager.CurrentChannel.ValueChanged -= currentChannelChanged; - channelManager.JoinedChannels.CollectionChanged -= joinedChannelsChanged; - channelManager.AvailableChannels.CollectionChanged -= availableChannelsChanged; + + if (channelManager != null) + { + channelManager.CurrentChannel.ValueChanged -= currentChannelChanged; + channelManager.JoinedChannels.CollectionChanged -= joinedChannelsChanged; + channelManager.AvailableChannels.CollectionChanged -= availableChannelsChanged; + } } private void postMessage(TextBox textbox, bool newText) From 0cefd89ef78a3b5e558550f0f2f80a5432859bc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 17:22:51 +0900 Subject: [PATCH 77/88] Change transition to match, looks better --- osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 14d43d4205..7492de0123 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -72,7 +72,7 @@ namespace osu.Game.Overlays.Chat.Tabs { base.FadeActive(); - this.ResizeWidthTo(200, TRANSITION_LENGTH * 2, Easing.OutQuint); + this.ResizeWidthTo(200, TRANSITION_LENGTH, Easing.OutQuint); CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); } From be1365fb187f5a11117c6ae7f170615827ce8572 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Sep 2018 11:58:23 +0900 Subject: [PATCH 78/88] Improve exception text --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 8d3c7d1caf..38d82e26ec 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -86,11 +86,11 @@ namespace osu.Game.Graphics.Containers case LinkAction.OpenChannel: try { - channelManager.OpenChannel(linkArgument); + channelManager?.OpenChannel(linkArgument); } catch (ChannelNotFoundException e) { - Logger.Error(e, "It should not be possible that the user is able to click a invalid channel link."); + Logger.Log($"The requested channel \"{linkArgument}\" does not exist"); } break; From 3c8c7a0459e4ce0181418222313027022b896d68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Sep 2018 12:06:04 +0900 Subject: [PATCH 79/88] Move ChannelManager to OsuGame There's no reason for it to exist at OsuGameBase --- osu.Game.Tests/Visual/TestCaseChatDisplay.cs | 16 ++++++++++---- osu.Game/Online/Chat/ChannelManager.cs | 22 ++++++++++---------- osu.Game/OsuGame.cs | 6 ++++++ osu.Game/OsuGameBase.cs | 5 ----- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs index dc47055a87..e3bd4026b3 100644 --- a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs @@ -4,7 +4,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Tabs; @@ -26,12 +29,17 @@ namespace osu.Game.Tests.Visual typeof(TabCloseButton) }; - public TestCaseChatDisplay() + [Cached] + private readonly ChannelManager channelManager = new ChannelManager(); + + [BackgroundDependencyLoader] + private void load() { - Add(new ChatOverlay + Children = new Drawable[] { - State = Visibility.Visible - }); + channelManager, + new ChatOverlay { State = Visibility.Visible } + }; } } } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 7d3d21554e..21e1739c59 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -52,6 +52,17 @@ namespace osu.Game.Online.Chat private IAPIProvider api; private ScheduledDelegate fetchMessagesScheduleder; + public ChannelManager() + { + CurrentChannel.ValueChanged += currentChannelChanged; + + channelMessagesHandler = new IncomingMessagesHandler( + lastId => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastId), handleChannelMessages); + + privateMessagesHandler = new IncomingMessagesHandler( + lastId => new GetPrivateMessagesRequest(lastId),handleUserMessages); + } + /// /// Opens a channel or switches to the channel if already opened. /// @@ -79,17 +90,6 @@ namespace osu.Game.Online.Chat ?? new PrivateChannel { User = user }; } - public ChannelManager() - { - CurrentChannel.ValueChanged += currentChannelChanged; - - channelMessagesHandler = new IncomingMessagesHandler( - lastId => new GetMessagesRequest(JoinedChannels.Where(c => c.Target == TargetType.Channel), lastId), handleChannelMessages); - - privateMessagesHandler = new IncomingMessagesHandler( - lastId => new GetPrivateMessagesRequest(lastId),handleUserMessages); - } - private void currentChannelChanged(Channel channel) { if (!JoinedChannels.Contains(channel)) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e823041c42..f08ee42c40 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -31,6 +31,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; using osu.Game.Input.Bindings; +using osu.Game.Online.Chat; using osu.Game.Rulesets.Mods; using osu.Game.Skinning; using OpenTK.Graphics; @@ -339,6 +340,11 @@ namespace osu.Game //overlay elements loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); + loadComponentSingleFile(new ChannelManager(), channelManager => + { + dependencies.Cache(channelManager); + AddInternal(channelManager); + }); loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(settings = new MainSettings { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 84462bd8c2..9a5dac35b9 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -27,7 +27,6 @@ using osu.Game.Database; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; -using osu.Game.Online.Chat; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -152,10 +151,6 @@ namespace osu.Game dependencies.Cache(api); dependencies.CacheAs(api); - var channelManager = new ChannelManager(); - dependencies.Cache(channelManager); - AddInternal(channelManager); - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); From 4b1b49489368e6f37334ca485f96268a7cd104d4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Nov 2018 16:16:52 +0900 Subject: [PATCH 80/88] Fix selection masks not having the correct size --- .../Blueprints/HoldNoteSelectionBlueprint.cs | 5 +++-- .../Edit/Blueprints/NoteSelectionBlueprint.cs | 6 ++++-- .../Edit/Masks/ManiaSelectionBlueprint.cs | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index 0ede2a7b9d..573c905a51 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; @@ -15,7 +14,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public class HoldNoteSelectionBlueprint : SelectionBlueprint + public class HoldNoteSelectionBlueprint : ManiaSelectionBlueprint { public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject; @@ -26,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public HoldNoteSelectionBlueprint(DrawableHoldNote hold) : base(hold) { + RelativeSizeAxes = Axes.None; + InternalChildren = new Drawable[] { new HoldNoteNoteSelectionBlueprint(hold.Head), diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs index 19726a368d..0ad99f9709 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs @@ -2,18 +2,20 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public class NoteSelectionBlueprint : SelectionBlueprint + public class NoteSelectionBlueprint : ManiaSelectionBlueprint { public NoteSelectionBlueprint(DrawableNote note) : base(note) { + RelativeSizeAxes = Axes.None; + Scale = note.Scale; CornerRadius = 5; diff --git a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs new file mode 100644 index 0000000000..81a2728ad4 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Edit.Masks +{ + public abstract class ManiaSelectionBlueprint : SelectionBlueprint + { + protected ManiaSelectionBlueprint(DrawableHitObject hitObject) + : base(hitObject) + { + RelativeSizeAxes = Axes.None; + } + } +} From 21f8a0a56f86c219c1fdb5eb84fc463b076210a5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Nov 2018 16:19:40 +0900 Subject: [PATCH 81/88] Fix selection box using an incorrect size --- .../Edit/Blueprints/HoldNoteSelectionBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index 573c905a51..2cb0597a40 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; @@ -60,6 +61,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints Y -= HitObject.Tail.DrawHeight; } + public override Quad SelectionQuad => ScreenSpaceDrawQuad; + private class HoldNoteNoteSelectionBlueprint : NoteSelectionBlueprint { public HoldNoteNoteSelectionBlueprint(DrawableNote note) From d0b63e8f8d67463f277c465e805ee379592f7138 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Nov 2018 14:13:57 +0900 Subject: [PATCH 82/88] Fix missing references --- .../Edit/Blueprints/HoldNoteSelectionBlueprint.cs | 1 + .../Edit/Blueprints/NoteSelectionBlueprint.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index 2cb0597a40..b707f88852 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -6,6 +6,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Edit.Masks; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs index 0ad99f9709..dd6adfc230 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Edit.Masks; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; From e3c60c2f965d2d5288ab0df4c49382451fb9c757 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 17:18:58 +0900 Subject: [PATCH 83/88] Cleanups --- .../Edit/Blueprints/HoldNoteSelectionBlueprint.cs | 2 -- .../Edit/Blueprints/ManiaSelectionBlueprint.cs | 2 ++ .../Edit/Blueprints/NoteSelectionBlueprint.cs | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index 2cb0597a40..c41c7addd0 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public HoldNoteSelectionBlueprint(DrawableHoldNote hold) : base(hold) { - RelativeSizeAxes = Axes.None; - InternalChildren = new Drawable[] { new HoldNoteNoteSelectionBlueprint(hold.Head), diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 474b8c662e..53f9dd8752 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; @@ -12,6 +13,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public ManiaSelectionBlueprint(DrawableHitObject hitObject) : base(hitObject) { + RelativeSizeAxes = Axes.None; } public override void AdjustPosition(DragEvent dragEvent) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs index 0ad99f9709..7c0337dc4e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; @@ -14,8 +13,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public NoteSelectionBlueprint(DrawableNote note) : base(note) { - RelativeSizeAxes = Axes.None; - Scale = note.Scale; CornerRadius = 5; From f241fcdba187912afd911c0f0a6d569cdcebead6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Nov 2018 15:20:40 +0900 Subject: [PATCH 84/88] Add back support for new API and private messages --- .../Visual/TestCaseChannelTabControl.cs | 7 +- osu.Game/Online/Chat/Channel.cs | 19 +- osu.Game/Online/Chat/ChannelManager.cs | 199 +++++++++--------- .../Online/Chat/IncomingMessagesHandler.cs | 72 ------- osu.Game/Online/Chat/Message.cs | 9 - osu.Game/Online/Chat/PrivateChannel.cs | 29 --- .../Overlays/Chat/Tabs/ChannelTabControl.cs | 6 +- .../Chat/Tabs/PrivateChannelTabItem.cs | 6 +- osu.Game/Overlays/ChatOverlay.cs | 22 +- 9 files changed, 135 insertions(+), 234 deletions(-) delete mode 100644 osu.Game/Online/Chat/IncomingMessagesHandler.cs delete mode 100644 osu.Game/Online/Chat/PrivateChannel.cs 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; } } From 82ebc74eeeea1b3ecc3901cda7da0850a9b986f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Nov 2018 15:36:36 +0900 Subject: [PATCH 85/88] Fix testcase --- osu.Game.Tests/Visual/TestCaseChannelTabControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs index 1d39cba81d..e6a04bf5c6 100644 --- a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs @@ -106,6 +106,7 @@ namespace osu.Game.Tests.Visual { channelTabControl.AddChannel(new Channel { + Type = ChannelType.Public, Name = name }); } From 72ae22b0c4ab417b47c8a86e2d9bca508c9c3b3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Nov 2018 17:24:11 +0900 Subject: [PATCH 86/88] 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 From c4769f6802eef8d9659a2ddcf0aaaae1af7134b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Nov 2018 13:19:20 +0900 Subject: [PATCH 87/88] Refactors --- .../API/Requests/GetPrivateMessagesRequest.cs | 17 ----------------- osu.Game/Online/Chat/ChannelManager.cs | 11 +++-------- 2 files changed, 3 insertions(+), 25 deletions(-) delete mode 100644 osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs diff --git a/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs b/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs deleted file mode 100644 index dddcbe8939..0000000000 --- a/osu.Game/Online/API/Requests/GetPrivateMessagesRequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Online.API.Requests -{ - public class GetPrivateMessagesRequest : APIMessagesRequest - { - private long? since; - - public GetPrivateMessagesRequest(long? sinceId = null) - : base(sinceId) - { - } - - protected override string Target => @"chat/messages/private"; - } -} diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 71b1d8a80a..1b6a347372 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -66,8 +66,7 @@ namespace osu.Game.Online.Chat if (name == null) throw new ArgumentNullException(nameof(name)); - CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) - ?? throw new ChannelNotFoundException(name); + CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) ?? throw new ChannelNotFoundException(name); } /// @@ -83,11 +82,7 @@ namespace osu.Game.Online.Chat ?? new Channel { Name = user.Username, Users = { user } }; } - private void currentChannelChanged(Channel channel) - { - JoinChannel(channel); - } - + private void currentChannelChanged(Channel channel) => JoinChannel(channel); /// /// Ensure we run post actions in sequence, once at a time. @@ -258,7 +253,7 @@ namespace osu.Game.Online.Chat fetchInitialMsgReq.Success += messages => { handleChannelMessages(messages); - channel.MessagesLoaded = true; // this will mark the channel as having received messages even if tehre were none. + channel.MessagesLoaded = true; // this will mark the channel as having received messages even if there were none. }; api.Queue(fetchInitialMsgReq); From 9a9d5e60af8ab932a96937db6c04235d150cd4c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Nov 2018 13:59:02 +0900 Subject: [PATCH 88/88] More refactors --- osu.Game/Online/Chat/ChannelManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 1b6a347372..9014fce18d 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -46,8 +46,6 @@ namespace osu.Game.Online.Chat /// public ObservableCollection AvailableChannels { get; } = new ObservableCollection(); //todo: should be publicly readonly - /*private readonly IncomingMessagesHandler privateMessagesHandler;*/ - private IAPIProvider api; private ScheduledDelegate fetchMessagesScheduleder; @@ -130,12 +128,14 @@ namespace osu.Game.Online.Chat 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."); @@ -148,17 +148,20 @@ namespace osu.Game.Online.Chat } 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); });