diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index c0da605cdb..e708934bc3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -68,9 +68,7 @@ namespace osu.Game.Tests.Visual.Online }; AddStep("Set country", () => countryBindable.Value = country); - AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); - AddAssert("Check country is Null", () => countryBindable.Value == null); AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry); } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs index 849ca2defc..0edf104da0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs @@ -43,11 +43,6 @@ namespace osu.Game.Tests.Visual.Online FullName = "United States" }; - AddStep("Set country", () => countryBindable.Value = countryA); - AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); - AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); - AddAssert("Check country is Null", () => countryBindable.Value == null); - AddStep("Set country 1", () => countryBindable.Value = countryA); AddStep("Set country 2", () => countryBindable.Value = countryB); AddStep("Set null country", () => countryBindable.Value = null); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs new file mode 100644 index 0000000000..568e36df4c --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Overlays.Rankings.Tables; +using osu.Framework.Allocation; +using osu.Game.Overlays; +using NUnit.Framework; +using osu.Game.Users; +using osu.Framework.Bindables; +using osu.Game.Overlays.Rankings; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsOverlay : OsuTestScene + { + protected override bool UseOnlineAPI => true; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PerformanceTable), + typeof(ScoresTable), + typeof(CountriesTable), + typeof(TableRowBackground), + typeof(UserBasedTable), + typeof(RankingsTable<>), + typeof(RankingsOverlay) + }; + + [Cached] + private RankingsOverlay rankingsOverlay; + + private readonly Bindable countryBindable = new Bindable(); + private readonly Bindable scope = new Bindable(); + + public TestSceneRankingsOverlay() + { + Add(rankingsOverlay = new TestRankingsOverlay + { + Country = { BindTarget = countryBindable }, + Scope = { BindTarget = scope }, + }); + } + + [Test] + public void TestShow() + { + AddStep("Show", rankingsOverlay.Show); + } + + [Test] + public void TestFlagScopeDependency() + { + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddAssert("Check country is Null", () => countryBindable.Value == null); + AddStep("Set country", () => countryBindable.Value = us_country); + AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); + } + + [Test] + public void TestShowCountry() + { + AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country)); + } + + [Test] + public void TestHide() + { + AddStep("Hide", rankingsOverlay.Hide); + } + + private static readonly Country us_country = new Country + { + FlagName = "US", + FullName = "United States" + }; + + private class TestRankingsOverlay : RankingsOverlay + { + public new Bindable Country => base.Country; + + public new Bindable Scope => base.Scope; + } + } +} diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs index 9c3eba9fdc..143d21e40d 100644 --- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs @@ -8,13 +8,14 @@ namespace osu.Game.Online.API.Requests { public class GetUserRankingsRequest : GetRankingsRequest { + public readonly UserRankingsType Type; + private readonly string country; - private readonly UserRankingsType type; public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null) : base(ruleset, page) { - this.type = type; + Type = type; this.country = country; } @@ -28,7 +29,7 @@ namespace osu.Game.Online.API.Requests return req; } - protected override string TargetPostfix() => type.ToString().ToLowerInvariant(); + protected override string TargetPostfix() => Type.ToString().ToLowerInvariant(); } public enum UserRankingsType diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs index a1a893fa6b..b08a2a3900 100644 --- a/osu.Game/Overlays/Rankings/HeaderTitle.cs +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -74,13 +74,7 @@ namespace osu.Game.Overlays.Rankings base.LoadComplete(); } - private void onScopeChanged(ValueChangedEvent scope) - { - scopeText.Text = scope.NewValue.ToString(); - - if (scope.NewValue != RankingsScope.Performance) - Country.Value = null; - } + private void onScopeChanged(ValueChangedEvent scope) => scopeText.Text = scope.NewValue.ToString(); private void onCountryChanged(ValueChangedEvent country) { @@ -90,8 +84,6 @@ namespace osu.Game.Overlays.Rankings return; } - Scope.Value = RankingsScope.Performance; - flag.Country = country.NewValue; flag.Show(); } diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs new file mode 100644 index 0000000000..c8874ef891 --- /dev/null +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -0,0 +1,214 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; +using osu.Game.Rulesets; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using System.Threading; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Rankings.Tables; + +namespace osu.Game.Overlays +{ + public class RankingsOverlay : FullscreenOverlay + { + protected readonly Bindable Country = new Bindable(); + protected readonly Bindable Scope = new Bindable(); + private readonly Bindable ruleset = new Bindable(); + + private readonly BasicScrollContainer scrollFlow; + private readonly Box background; + private readonly Container tableContainer; + private readonly DimmedLoadingLayer loading; + + private APIRequest lastRequest; + private CancellationTokenSource cancellationToken; + + [Resolved] + private IAPIProvider api { get; set; } + + public RankingsOverlay() + { + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + scrollFlow = new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new RankingsHeader + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Country = { BindTarget = Country }, + Scope = { BindTarget = Scope }, + Ruleset = { BindTarget = ruleset } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + tableContainer = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Vertical = 10 } + }, + loading = new DimmedLoadingLayer(), + } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + Waves.FirstWaveColour = colour.Green; + Waves.SecondWaveColour = colour.GreenLight; + Waves.ThirdWaveColour = colour.GreenDark; + Waves.FourthWaveColour = colour.GreenDarker; + + background.Colour = OsuColour.Gray(0.1f); + } + + protected override void LoadComplete() + { + Country.BindValueChanged(_ => + { + // if a country is requested, force performance scope. + if (Country.Value != null) + Scope.Value = RankingsScope.Performance; + + Scheduler.AddOnce(loadNewContent); + }, true); + + Scope.BindValueChanged(_ => + { + // country filtering is only valid for performance scope. + if (Scope.Value != RankingsScope.Performance) + Country.Value = null; + + Scheduler.AddOnce(loadNewContent); + }, true); + + ruleset.BindValueChanged(_ => Scheduler.AddOnce(loadNewContent), true); + + base.LoadComplete(); + } + + public void ShowCountry(Country requested) + { + if (requested == null) + return; + + Show(); + + Country.Value = requested; + } + + private void loadNewContent() + { + loading.Show(); + + cancellationToken?.Cancel(); + lastRequest?.Cancel(); + + var request = createScopedRequest(); + lastRequest = request; + + if (request == null) + { + loadTable(null); + return; + } + + request.Success += () => loadTable(createTableFromResponse(request)); + request.Failure += _ => loadTable(null); + + api.Queue(request); + } + + private APIRequest createScopedRequest() + { + switch (Scope.Value) + { + case RankingsScope.Performance: + return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName); + + case RankingsScope.Country: + return new GetCountryRankingsRequest(ruleset.Value); + + case RankingsScope.Score: + return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); + } + + return null; + } + + private Drawable createTableFromResponse(APIRequest request) + { + switch (request) + { + case GetUserRankingsRequest userRequest: + switch (userRequest.Type) + { + case UserRankingsType.Performance: + return new PerformanceTable(1, userRequest.Result.Users); + + case UserRankingsType.Score: + return new ScoresTable(1, userRequest.Result.Users); + } + + return null; + + case GetCountryRankingsRequest countryRequest: + return new CountriesTable(1, countryRequest.Result.Countries); + } + + return null; + } + + private void loadTable(Drawable table) + { + scrollFlow.ScrollToStart(); + + if (table == null) + { + tableContainer.Clear(); + loading.Hide(); + return; + } + + LoadComponentAsync(table, t => + { + loading.Hide(); + tableContainer.Child = table; + }, (cancellationToken = new CancellationTokenSource()).Token); + } + } +} diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs index 1dcce6e870..a9fcd69286 100644 --- a/osu.Game/Users/Country.cs +++ b/osu.Game/Users/Country.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using Newtonsoft.Json; namespace osu.Game.Users { - public class Country + public class Country : IEquatable { /// /// The name of this country. @@ -18,5 +19,7 @@ namespace osu.Game.Users /// [JsonProperty(@"code")] public string FlagName; + + public bool Equals(Country other) => FlagName == other?.FlagName; } } diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index abc16b2390..1d30720889 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -1,8 +1,11 @@ // 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.Framework.Input.Events; +using osu.Game.Overlays; namespace osu.Game.Users.Drawables { @@ -34,5 +37,14 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, }; } + + [Resolved(canBeNull: true)] + private RankingsOverlay rankingsOverlay { get; set; } + + protected override bool OnClick(ClickEvent e) + { + rankingsOverlay?.ShowCountry(Country); + return true; + } } }