From eab2776755694526f8f99c6dc1aec4718216d4b7 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Wed, 21 Nov 2018 17:14:08 +0900
Subject: [PATCH 1/8] Fix a crash on leaving the selected channel

---
 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 e45373c36f..6a5b72c0f3 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -209,7 +209,6 @@ namespace osu.Game.Overlays
             {
                 textbox.Current.Disabled = true;
                 currentChannelContainer.Clear(false);
-                channelTabControl.Current.Value = null;
                 return;
             }
 

From 4cde66240dcba75444ac1693aef3a117498cb122 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Wed, 21 Nov 2018 17:14:48 +0900
Subject: [PATCH 2/8] Fix default channels not being joined

---
 osu.Game/Online/Chat/ChannelManager.cs | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 9014fce18d..9ee6cfa483 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
 
 using System;
@@ -362,12 +362,6 @@ namespace osu.Game.Online.Chat
                             }
                         }
 
-                        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
 
@@ -379,6 +373,13 @@ namespace osu.Game.Online.Chat
                         lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
                     }
 
+                    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();
+                    }
+
                     fetchUpdates();
                 };
 

From 88f82eb722738f323314afa5e51467deecd9b01a Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Wed, 21 Nov 2018 17:15:10 +0900
Subject: [PATCH 3/8] Fix instabilities in channel join logic

---
 osu.Game/Online/Chat/ChannelManager.cs | 89 +++++++++++++++++---------
 1 file changed, 57 insertions(+), 32 deletions(-)

diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 9ee6cfa483..621cdb5737 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
 
 using System;
@@ -223,13 +223,11 @@ namespace osu.Game.Online.Chat
             {
                 foreach (var channel in channels)
                 {
-                    // add as available if not already
-                    if (AvailableChannels.All(c => c.Id != channel.Id))
-                        AvailableChannels.Add(channel);
+                    var ch = getChannel(channel, addToAvailable: true);
 
                     // join any channels classified as "defaults"
                     if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase)))
-                        JoinChannel(channel);
+                        JoinChannel(ch);
                 }
             };
             req.Failure += error =>
@@ -262,38 +260,68 @@ namespace osu.Game.Online.Chat
             api.Queue(fetchInitialMsgReq);
         }
 
-        public void JoinChannel(Channel channel)
+        /// <summary>
+        /// Find an existing channel instance for the provided channel. Lookup is performed basd on ID.
+        /// The provided channel may be used if an existing instance is not found.
+        /// </summary>
+        /// <param name="lookup">A candidate channel to be used for lookup or permanently on lookup failure.</param>
+        /// <param name="addToAvailable">Whether the channel should be added to <see cref="AvailableChannels"/> if not already.</param>
+        /// <param name="addToJoined">Whether the channel should be added to <see cref="JoinedChannels"/> if not already.</param>
+        /// <returns>The found channel.</returns>
+        private Channel getChannel(Channel lookup, bool addToAvailable = false, bool addToJoined = false)
         {
-            if (channel == null) return;
+            Channel found = null;
 
-            // ReSharper disable once AccessToModifiedClosure
-            var existing = JoinedChannels.FirstOrDefault(c => c.Id == channel.Id);
+            var available = AvailableChannels.FirstOrDefault(c => c.Id == lookup.Id);
+            if (available != null)
+                found = available;
 
-            if (existing != null)
+            var joined = JoinedChannels.FirstOrDefault(c => c.Id == lookup.Id);
+            if (found == null && joined != null)
+                found = joined;
+
+            if (found == 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);
+                found = lookup;
+
+                // if we're using a channel object from the server, we want to remove ourselves from the users list.
+                // this is because we check the first user in the channel to display a name/icon on tabs for now.
+                var foundSelf = found.Users.FirstOrDefault(u => u.Id == api.LocalUser.Value.Id);
                 if (foundSelf != null)
-                    channel.Users.Remove(foundSelf);
+                    found.Users.Remove(foundSelf);
+            }
 
-                JoinedChannels.Add(channel);
+            if (joined == null && addToJoined) JoinedChannels.Add(found);
+            if (available == null && addToAvailable) AvailableChannels.Add(found);
 
-                if (channel.Type == ChannelType.Public && !channel.Joined)
+            return found;
+        }
+
+        /// <summary>
+        /// Joins a channel if it has not already been joined.
+        /// </summary>
+        /// <param name="channel">The channel to join.</param>
+        /// <param name="alreadyJoined">Whether the channel has already been joined server-side. Will skip a join request.</param>
+        /// <returns>The joined channel. Note that this may not match the parameter channel as it is a backed object.</returns>
+        public Channel JoinChannel(Channel channel, bool alreadyJoined = false)
+        {
+            if (channel == null) return null;
+
+            channel = getChannel(channel, addToJoined: true);
+
+            // ensure we are joined to the channel
+            if (!channel.Joined.Value)
+            {
+                if (!alreadyJoined && channel.Type == ChannelType.Public)
                 {
                     var req = new JoinChannelRequest(channel, api.LocalUser);
-                    req.Success += () =>
-                    {
-                        channel.Joined.Value = true;
-                        JoinChannel(channel);
-                    };
+                    req.Success += () => JoinChannel(channel, true);
                     req.Failure += ex => LeaveChannel(channel);
                     api.Queue(req);
-                    return;
+                    return channel;
                 }
+
+                channel.Joined.Value = true;
             }
 
             if (CurrentChannel.Value == null)
@@ -304,6 +332,8 @@ namespace osu.Game.Online.Chat
                 // let's fetch a small number of messages to bring us up-to-date with the backlog.
                 fetchInitalMessages(channel);
             }
+
+            return channel;
         }
 
         public void LeaveChannel(Channel channel)
@@ -353,13 +383,8 @@ namespace osu.Game.Online.Chat
                     {
                         foreach (var channel in updates.Presence)
                         {
-                            if (!channel.Joined.Value)
-                            {
-                                // we received this from the server so should mark the channel already joined.
-                                channel.Joined.Value = true;
-
-                                JoinChannel(channel);
-                            }
+                            // we received this from the server so should mark the channel already joined.
+                            JoinChannel(channel, true);
                         }
 
 

From d67792168029a6d9f2ea491d102d45aa3ce46c0d Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Wed, 21 Nov 2018 18:53:50 +0900
Subject: [PATCH 4/8] Remove excess newline

---
 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 621cdb5737..3ce162ee30 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -387,7 +387,6 @@ namespace osu.Game.Online.Chat
                             JoinChannel(channel, true);
                         }
 
-
                         //todo: handle left channels
 
                         handleChannelMessages(updates.Messages);

From b31efb7bb992256134d581b30a6b603ffbbe77fc Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Wed, 21 Nov 2018 20:31:47 +0900
Subject: [PATCH 5/8] Fix PM channel type

---
 osu.Game/Online/Chat/ChannelManager.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 3ce162ee30..d9098956ec 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -77,7 +77,7 @@ namespace osu.Game.Online.Chat
                 throw new ArgumentNullException(nameof(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 } };
+                                   ?? new Channel { Name = user.Username, Users = { user }, Type = ChannelType.PM };
         }
 
         private void currentChannelChanged(Channel channel) => JoinChannel(channel);

From 2126cf0d9a6f68acf52002a018396fba9cf3ec75 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Wed, 21 Nov 2018 20:44:41 +0900
Subject: [PATCH 6/8] Fix join process for PMs being incorrect

---
 osu.Game/Online/Chat/ChannelManager.cs | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index d9098956ec..a9610e72b1 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -312,16 +312,20 @@ namespace osu.Game.Online.Chat
             // ensure we are joined to the channel
             if (!channel.Joined.Value)
             {
-                if (!alreadyJoined && channel.Type == ChannelType.Public)
+                if (alreadyJoined)
+                    channel.Joined.Value = true;
+                else
                 {
-                    var req = new JoinChannelRequest(channel, api.LocalUser);
-                    req.Success += () => JoinChannel(channel, true);
-                    req.Failure += ex => LeaveChannel(channel);
-                    api.Queue(req);
-                    return channel;
+                    switch (channel.Type)
+                    {
+                        case ChannelType.Public:
+                            var req = new JoinChannelRequest(channel, api.LocalUser);
+                            req.Success += () => JoinChannel(channel, true);
+                            req.Failure += ex => LeaveChannel(channel);
+                            api.Queue(req);
+                            return channel;
+                    }
                 }
-
-                channel.Joined.Value = true;
             }
 
             if (CurrentChannel.Value == null)

From a144e975687d43bde1d36fe330f614758793104d Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Thu, 22 Nov 2018 18:27:22 +0900
Subject: [PATCH 7/8] Fix crash on creating two new PM channels

---
 osu.Game/Online/Chat/ChannelManager.cs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index a9610e72b1..73ac7c9df4 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -272,11 +272,13 @@ namespace osu.Game.Online.Chat
         {
             Channel found = null;
 
-            var available = AvailableChannels.FirstOrDefault(c => c.Id == lookup.Id);
+            bool lookupCondition(Channel ch) => lookup.Id > 0 ? ch.Id == lookup.Id : lookup.Name == ch.Name;
+
+            var available = AvailableChannels.FirstOrDefault(lookupCondition);
             if (available != null)
                 found = available;
 
-            var joined = JoinedChannels.FirstOrDefault(c => c.Id == lookup.Id);
+            var joined = JoinedChannels.FirstOrDefault(lookupCondition);
             if (found == null && joined != null)
                 found = joined;
 

From 6cd69b794d170c8a4c51e8be091166824e1ef5c0 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Thu, 22 Nov 2018 19:08:46 +0900
Subject: [PATCH 8/8] Fix leaderboard accessing drawables in a possibly invalid
 state

---
 osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index 4a677001a0..0748f68dca 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -310,9 +310,9 @@ namespace osu.Game.Screens.Select.Leaderboards
             currentPlaceholder = placeholder;
         }
 
-        protected override void Update()
+        protected override void UpdateAfterChildren()
         {
-            base.Update();
+            base.UpdateAfterChildren();
 
             var fadeStart = scrollContainer.Current + scrollContainer.DrawHeight;