1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 17:47:29 +08:00

Merge pull request #18246 from jai-x/new-chat-cache-loaded-channels

Add drawable channel caching to new chat overlay
This commit is contained in:
Dean Herbert 2022-05-19 15:15:47 +09:00 committed by GitHub
commit d187ca2d8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 15 deletions

View File

@ -5,6 +5,8 @@ using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Threading;
using JetBrains.Annotations;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -31,7 +33,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture] [TestFixture]
public class TestSceneChatOverlayV2 : OsuManualInputManagerTestScene public class TestSceneChatOverlayV2 : OsuManualInputManagerTestScene
{ {
private ChatOverlayV2 chatOverlay; private TestChatOverlayV2 chatOverlay;
private ChannelManager channelManager; private ChannelManager channelManager;
private APIUser testUser; private APIUser testUser;
@ -61,7 +63,7 @@ namespace osu.Game.Tests.Visual.Online
Children = new Drawable[] Children = new Drawable[]
{ {
channelManager, channelManager,
chatOverlay = new ChatOverlayV2 { RelativeSizeAxes = Axes.Both }, chatOverlay = new TestChatOverlayV2 { RelativeSizeAxes = Axes.Both },
}, },
}; };
}); });
@ -365,19 +367,19 @@ namespace osu.Game.Tests.Visual.Online
} }
[Test] [Test]
public void TextBoxRetainsFocus() public void TestTextBoxRetainsFocus()
{ {
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel));
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Click selector", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelListSelector>().Single())); AddStep("Click selector", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelListSelector>().Single()));
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelListing>().Single())); AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelListing>().Single()));
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel));
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelList>().Single())); AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelList>().Single()));
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Click top bar", () => clickDrawable(chatOverlay.ChildrenOfType<ChatOverlayTopBar>().Single())); AddStep("Click top bar", () => clickDrawable(chatOverlay.ChildrenOfType<ChatOverlayTopBar>().Single()));
@ -386,6 +388,34 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("TextBox is not focused", () => InputManager.FocusedDrawable == null); AddAssert("TextBox is not focused", () => InputManager.FocusedDrawable == null);
} }
[Test]
public void TestSlowLoadingChannel()
{
AddStep("Show overlay (slow-loading)", () =>
{
chatOverlay.Show();
chatOverlay.SlowLoading = true;
});
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
AddAssert("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading);
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
AddStep("Select channel 2", () => clickDrawable(getChannelListItem(testChannel2)));
AddAssert("Channel 2 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel2).LoadState == LoadState.Loading);
AddStep("Finish channel 1 load", () => chatOverlay.GetSlowLoadingChannel(testChannel1).LoadEvent.Set());
AddAssert("Channel 1 ready", () => chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Ready);
AddAssert("Channel 1 not displayed", () => !channelIsVisible);
AddStep("Finish channel 2 load", () => chatOverlay.GetSlowLoadingChannel(testChannel2).LoadEvent.Set());
AddAssert("Channel 2 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel2).IsLoaded);
AddAssert("Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddAssert("Channel 1 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel1).IsLoaded);
AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
}
private bool listingIsVisible => private bool listingIsVisible =>
chatOverlay.ChildrenOfType<ChannelListing>().Single().State.Value == Visibility.Visible; chatOverlay.ChildrenOfType<ChannelListing>().Single().State.Value == Visibility.Visible;
@ -432,5 +462,35 @@ namespace osu.Game.Tests.Visual.Online
Topic = $"We talk about the number {id} here", Topic = $"We talk about the number {id} here",
Type = ChannelType.Public, Type = ChannelType.Public,
}; };
private class TestChatOverlayV2 : ChatOverlayV2
{
public bool SlowLoading { get; set; }
public SlowLoadingDrawableChannel GetSlowLoadingChannel(Channel channel) => DrawableChannels.OfType<SlowLoadingDrawableChannel>().Single(c => c.Channel == channel);
protected override ChatOverlayDrawableChannel CreateDrawableChannel(Channel newChannel)
{
return SlowLoading
? new SlowLoadingDrawableChannel(newChannel)
: new ChatOverlayDrawableChannel(newChannel);
}
}
private class SlowLoadingDrawableChannel : ChatOverlayDrawableChannel
{
public readonly ManualResetEventSlim LoadEvent = new ManualResetEventSlim();
public SlowLoadingDrawableChannel([NotNull] Channel channel)
: base(channel)
{
}
[BackgroundDependencyLoader]
private void load()
{
LoadEvent.Wait(10000);
}
}
} }
} }

View File

@ -39,8 +39,11 @@ namespace osu.Game.Overlays
private ChatTextBar textBar = null!; private ChatTextBar textBar = null!;
private Container<ChatOverlayDrawableChannel> currentChannelContainer = null!; private Container<ChatOverlayDrawableChannel> currentChannelContainer = null!;
private readonly BindableFloat chatHeight = new BindableFloat(); private readonly Dictionary<Channel, ChatOverlayDrawableChannel> loadedChannels = new Dictionary<Channel, ChatOverlayDrawableChannel>();
protected IEnumerable<DrawableChannel> DrawableChannels => loadedChannels.Values;
private readonly BindableFloat chatHeight = new BindableFloat();
private bool isDraggingTopBar; private bool isDraggingTopBar;
private float dragStartChatHeight; private float dragStartChatHeight;
@ -173,7 +176,7 @@ namespace osu.Game.Overlays
if (currentChannel.Value?.Id != channel.Id) if (currentChannel.Value?.Id != channel.Id)
{ {
if (!channel.Joined.Value) if (!channel.Joined.Value)
channel = channelManager.JoinChannel(channel); channel = channelManager.JoinChannel(channel, false);
channelManager.CurrentChannel.Value = channel; channelManager.CurrentChannel.Value = channel;
} }
@ -240,38 +243,76 @@ namespace osu.Game.Overlays
if (newChannel == null) if (newChannel == null)
{ {
// null channel denotes that we should be showing the listing. // null channel denotes that we should be showing the listing.
channelListing.State.Value = Visibility.Visible; currentChannelContainer.Clear(false);
channelListing.Show();
textBar.ShowSearch.Value = true; textBar.ShowSearch.Value = true;
} }
else else
{ {
channelListing.State.Value = Visibility.Hidden; channelListing.Hide();
textBar.ShowSearch.Value = false; textBar.ShowSearch.Value = false;
loading.Show(); if (loadedChannels.ContainsKey(newChannel))
LoadComponentAsync(new ChatOverlayDrawableChannel(newChannel), loaded =>
{ {
currentChannelContainer.Clear(); currentChannelContainer.Clear(false);
currentChannelContainer.Add(loaded); currentChannelContainer.Add(loadedChannels[newChannel]);
loading.Hide(); }
}); else
{
loading.Show();
// Ensure the drawable channel is stored before async load to prevent double loading
ChatOverlayDrawableChannel drawableChannel = CreateDrawableChannel(newChannel);
loadedChannels.Add(newChannel, drawableChannel);
LoadComponentAsync(drawableChannel, loadedDrawable =>
{
// Ensure the current channel hasn't changed by the time the load completes
if (currentChannel.Value != loadedDrawable.Channel)
return;
// Ensure the cached reference hasn't been removed from leaving the channel
if (!loadedChannels.ContainsKey(loadedDrawable.Channel))
return;
currentChannelContainer.Clear(false);
currentChannelContainer.Add(loadedDrawable);
loading.Hide();
});
}
} }
} }
protected virtual ChatOverlayDrawableChannel CreateDrawableChannel(Channel newChannel) => new ChatOverlayDrawableChannel(newChannel);
private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args)
{ {
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
IEnumerable<Channel> joinedChannels = filterChannels(args.NewItems); IEnumerable<Channel> joinedChannels = filterChannels(args.NewItems);
foreach (var channel in joinedChannels) foreach (var channel in joinedChannels)
channelList.AddChannel(channel); channelList.AddChannel(channel);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
IEnumerable<Channel> leftChannels = filterChannels(args.OldItems); IEnumerable<Channel> leftChannels = filterChannels(args.OldItems);
foreach (var channel in leftChannels) foreach (var channel in leftChannels)
{
channelList.RemoveChannel(channel); channelList.RemoveChannel(channel);
if (loadedChannels.ContainsKey(channel))
{
ChatOverlayDrawableChannel loaded = loadedChannels[channel];
loadedChannels.Remove(channel);
// DrawableChannel removed from cache must be manually disposed
loaded.Dispose();
}
}
break; break;
} }
} }