diff --git a/osu.Game.Tests/Visual/Online/TestSceneOnlineViewContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOnlineViewContainer.cs new file mode 100644 index 0000000000..3c2735ca56 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneOnlineViewContainer.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneOnlineViewContainer : OsuTestScene + { + private readonly TestOnlineViewContainer onlineView; + + public TestSceneOnlineViewContainer() + { + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Child = onlineView = new TestOnlineViewContainer() + }; + } + + [Test] + public void TestOnlineStateVisibility() + { + AddStep("set status to online", () => ((DummyAPIAccess)API).State = APIState.Online); + + AddUntilStep("children are visible", () => onlineView.ViewTarget.IsPresent); + AddUntilStep("loading animation is not visible", () => !onlineView.LoadingAnimation.IsPresent); + } + + [Test] + public void TestOfflineStateVisibility() + { + AddStep("set status to offline", () => ((DummyAPIAccess)API).State = APIState.Offline); + + AddUntilStep("children are not visible", () => !onlineView.ViewTarget.IsPresent); + AddUntilStep("loading animation is not visible", () => !onlineView.LoadingAnimation.IsPresent); + } + + [Test] + public void TestConnectingStateVisibility() + { + AddStep("set status to connecting", () => ((DummyAPIAccess)API).State = APIState.Connecting); + + AddUntilStep("children are not visible", () => !onlineView.ViewTarget.IsPresent); + AddUntilStep("loading animation is visible", () => onlineView.LoadingAnimation.IsPresent); + } + + [Test] + public void TestFailingStateVisibility() + { + AddStep("set status to failing", () => ((DummyAPIAccess)API).State = APIState.Failing); + + AddUntilStep("children are not visible", () => !onlineView.ViewTarget.IsPresent); + AddUntilStep("loading animation is visible", () => onlineView.LoadingAnimation.IsPresent); + } + + private class TestOnlineViewContainer : OnlineViewContainer + { + public new LoadingAnimation LoadingAnimation => base.LoadingAnimation; + + public CompositeDrawable ViewTarget => base.Content; + + public TestOnlineViewContainer() + : base(@"Please sign in to view dummy test content") + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Blue.Opacity(0.8f), + }, + new OsuSpriteText + { + Text = "dummy online content", + Font = OsuFont.Default.With(size: 40), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + } + } +} diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 7f23f9b5d5..a1c3475fd9 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -33,7 +33,7 @@ namespace osu.Game.Online.API public APIState State { get => state; - private set + set { if (state == value) return; diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index bd4fedabd4..87c747675a 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -152,7 +152,7 @@ namespace osu.Game.Online.Leaderboards break; case PlaceholderState.NotLoggedIn: - replacePlaceholder(new LoginPlaceholder()); + replacePlaceholder(new LoginPlaceholder(@"Please sign in to view online leaderboards!")); break; case PlaceholderState.NotSupporter: diff --git a/osu.Game/Online/OnlineViewContainer.cs b/osu.Game/Online/OnlineViewContainer.cs new file mode 100644 index 0000000000..689c1c0afb --- /dev/null +++ b/osu.Game/Online/OnlineViewContainer.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.Placeholders; + +namespace osu.Game.Online +{ + /// + /// A for displaying online content which require a local user to be logged in. + /// Shows its children only when the local user is logged in and supports displaying a placeholder if not. + /// + public abstract class OnlineViewContainer : Container, IOnlineComponent + { + protected LoadingAnimation LoadingAnimation { get; private set; } + + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + + private readonly string placeholderMessage; + + private Placeholder placeholder; + + private const double transform_duration = 300; + + [Resolved] + protected IAPIProvider API { get; private set; } + + protected OnlineViewContainer(string placeholderMessage) + { + this.placeholderMessage = placeholderMessage; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + Content, + placeholder = new LoginPlaceholder(placeholderMessage), + LoadingAnimation = new LoadingAnimation + { + Alpha = 0, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + API.Register(this); + } + + public virtual void APIStateChanged(IAPIProvider api, APIState state) + { + switch (state) + { + case APIState.Offline: + PopContentOut(Content); + placeholder.ScaleTo(0.8f).Then().ScaleTo(1, 3 * transform_duration, Easing.OutQuint); + placeholder.FadeInFromZero(2 * transform_duration, Easing.OutQuint); + LoadingAnimation.Hide(); + break; + + case APIState.Online: + PopContentIn(Content); + placeholder.FadeOut(transform_duration / 2, Easing.OutQuint); + LoadingAnimation.Hide(); + break; + + case APIState.Failing: + case APIState.Connecting: + PopContentOut(Content); + LoadingAnimation.Show(); + placeholder.FadeOut(transform_duration / 2, Easing.OutQuint); + break; + } + } + + /// + /// Applies a transform to the online content to make it hidden. + /// + protected virtual void PopContentOut(Drawable content) => content.FadeOut(transform_duration / 2, Easing.OutQuint); + + /// + /// Applies a transform to the online content to make it visible. + /// + protected virtual void PopContentIn(Drawable content) => content.FadeIn(transform_duration, Easing.OutQuint); + + protected override void Dispose(bool isDisposing) + { + API?.Unregister(this); + base.Dispose(isDisposing); + } + } +} diff --git a/osu.Game/Online/Placeholders/LoginPlaceholder.cs b/osu.Game/Online/Placeholders/LoginPlaceholder.cs index 591eb976e2..73b0fa27c3 100644 --- a/osu.Game/Online/Placeholders/LoginPlaceholder.cs +++ b/osu.Game/Online/Placeholders/LoginPlaceholder.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Placeholders [Resolved(CanBeNull = true)] private LoginOverlay login { get; set; } - public LoginPlaceholder() + public LoginPlaceholder(string actionMessage) { AddIcon(FontAwesome.Solid.UserLock, cp => { @@ -22,7 +22,7 @@ namespace osu.Game.Online.Placeholders cp.Padding = new MarginPadding { Right = 10 }; }); - AddText(@"Please sign in to view online leaderboards!"); + AddText(actionMessage); } protected override bool OnMouseDown(MouseDownEvent e)