From fe559f4b62245d66e2f6b6b0ae9a4ffa2f60fb7b Mon Sep 17 00:00:00 2001 From: naoey Date: Mon, 20 Nov 2017 10:36:26 +0530 Subject: [PATCH 01/34] Add respective query params to GetScoreRequest based on selected tab. --- .../Online/API/Requests/GetScoresRequest.cs | 25 +++++++++++++++-- osu.Game/Screens/Select/BeatmapDetailArea.cs | 1 + .../Select/Leaderboards/Leaderboard.cs | 28 ++++++++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 3777e10a31..a52db4496a 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -10,19 +10,22 @@ using osu.Game.Rulesets; using osu.Game.Users; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Select.Leaderboards; namespace osu.Game.Online.API.Requests { public class GetScoresRequest : APIRequest { private readonly BeatmapInfo beatmap; + private readonly LeaderboardScope scope; - public GetScoresRequest(BeatmapInfo beatmap) + public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope = LeaderboardScope.Global) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); this.beatmap = beatmap; + this.scope = scope; Success += onSuccess; } @@ -33,7 +36,25 @@ namespace osu.Game.Online.API.Requests score.ApplyBeatmap(beatmap); } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; + private string mapScopeToQuery() + { + switch(scope) + { + case LeaderboardScope.Global: + return @"?type=global"; + + case LeaderboardScope.Friends: + return @"?type=friend"; + + case LeaderboardScope.Country: + return @"?type=country"; + + default: + return String.Empty; + } + } + + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{mapScopeToQuery()}"; } public class GetScoresResponse diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index a676516300..790a8421a2 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -52,6 +52,7 @@ namespace osu.Game.Screens.Select default: Details.Hide(); + Leaderboard.Scope = (LeaderboardScope) tab - 1; Leaderboard.Show(); break; } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index d896da5319..20ab09e83e 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -74,6 +74,19 @@ namespace osu.Game.Screens.Select.Leaderboards } } + private LeaderboardScope scope = LeaderboardScope.Global; + public LeaderboardScope Scope + { + get { return scope; } + set + { + if (value == scope) return; + + scope = value; + updateScores(); + } + } + public Leaderboard() { Children = new Drawable[] @@ -120,6 +133,11 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + if (Scope == LeaderboardScope.Local) + { + // TODO: get local scores from wherever here. + } + Scores = null; getScoresRequest?.Cancel(); @@ -127,7 +145,7 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap); + getScoresRequest = new GetScoresRequest(Beatmap, Scope); getScoresRequest.Success += r => { Scores = r.Scores; @@ -165,4 +183,12 @@ namespace osu.Game.Screens.Select.Leaderboards } } } + + public enum LeaderboardScope + { + Local, + Country, + Global, + Friends, + } } From a58bd72c6ede87d84a2c2d2c22d05e69d34e37ad Mon Sep 17 00:00:00 2001 From: naoey Date: Mon, 20 Nov 2017 17:07:41 +0530 Subject: [PATCH 02/34] Add placeholder when there are no scores. --- .../Select/Leaderboards/Leaderboard.cs | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 20ab09e83e..b5cd729739 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -18,13 +18,18 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using System.Linq; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics; namespace osu.Game.Screens.Select.Leaderboards { public class Leaderboard : Container { + private const double fade_duration = 200; + private readonly ScrollContainer scrollContainer; private FillFlowContainer scrollFlow; + private Container placeholderContainer; public Action ScoreSelected; @@ -40,13 +45,19 @@ namespace osu.Game.Screens.Select.Leaderboards scores = value; getScoresRequest?.Cancel(); - scrollFlow?.FadeOut(200); - scrollFlow?.Expire(); + placeholderContainer.FadeOut(fade_duration); + scrollFlow?.FadeOut(fade_duration).Expire(); scrollFlow = null; if (scores == null) return; + if (scores.Count() == 0) + { + placeholderContainer.FadeIn(fade_duration); + return; + } + // schedule because we may not be loaded yet (LoadComponentAsync complains). Schedule(() => { @@ -74,7 +85,7 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private LeaderboardScope scope = LeaderboardScope.Global; + private LeaderboardScope scope; public LeaderboardScope Scope { get { return scope; } @@ -96,7 +107,36 @@ namespace osu.Game.Screens.Select.Leaderboards RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, }, - loading = new LoadingAnimation() + loading = new LoadingAnimation(), + placeholderContainer = new Container + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_exclamation_circle, + Size = new Vector2(26), + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Text = @"No records yet!", + TextSize = 22, + }, + } + }, + }, + }, }; } @@ -133,14 +173,16 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + Scores = null; + getScoresRequest?.Cancel(); + if (Scope == LeaderboardScope.Local) { // TODO: get local scores from wherever here. + Scores = Enumerable.Empty(); + return; } - Scores = null; - getScoresRequest?.Cancel(); - if (api == null || Beatmap?.OnlineBeatmapID == null) return; loading.Show(); From 487483eaddf0833aa39c3f4c305fcb75404a78bb Mon Sep 17 00:00:00 2001 From: naoey Date: Mon, 20 Nov 2017 18:53:50 +0530 Subject: [PATCH 03/34] Move loader hiding to a better place. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index b5cd729739..2567110ef6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -52,6 +52,8 @@ namespace osu.Game.Screens.Select.Leaderboards if (scores == null) return; + loading.Hide(); + if (scores.Count() == 0) { placeholderContainer.FadeIn(fade_duration); @@ -191,7 +193,6 @@ namespace osu.Game.Screens.Select.Leaderboards getScoresRequest.Success += r => { Scores = r.Scores; - loading.Hide(); }; api.Queue(getScoresRequest); } From 096e98b5d3aaeebb67c528b65c1aad44756d0597 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Nov 2017 19:44:38 +0530 Subject: [PATCH 04/34] Add game mode query to request. - Also update scores when game mode is changed --- .../Online/API/Requests/GetScoresRequest.cs | 44 ++++++++++++++++--- .../Select/Leaderboards/Leaderboard.cs | 14 +++--- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index a52db4496a..37b3cc55f1 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -11,6 +11,7 @@ using osu.Game.Users; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; +using System.Collections.Specialized; namespace osu.Game.Online.API.Requests { @@ -18,14 +19,26 @@ namespace osu.Game.Online.API.Requests { private readonly BeatmapInfo beatmap; private readonly LeaderboardScope scope; + private readonly RulesetInfo ruleset; - public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope = LeaderboardScope.Global) + public GetScoresRequest(BeatmapInfo beatmap) + { + if (!beatmap.OnlineBeatmapID.HasValue) + throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); + + this.beatmap = beatmap; + + Success += onSuccess; + } + + public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope, RulesetInfo ruleset) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); this.beatmap = beatmap; this.scope = scope; + this.ruleset = ruleset; Success += onSuccess; } @@ -41,20 +54,41 @@ namespace osu.Game.Online.API.Requests switch(scope) { case LeaderboardScope.Global: - return @"?type=global"; + return @"type=global"; case LeaderboardScope.Friends: - return @"?type=friend"; + return @"type=friend"; case LeaderboardScope.Country: - return @"?type=country"; + return @"type=country"; default: return String.Empty; } } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{mapScopeToQuery()}"; + private string mapRulesetToQuery() + { + switch(ruleset.Name) + { + case @"osu!": + return @"mode=osu"; + + case @"osu!taiko": + return @"mode=taiko"; + + case @"osu!catch": + return @"mode=catch"; + + case @"osu!mania": + return @"mode=mania"; + + default: + return String.Empty; + } + } + + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores?{mapScopeToQuery()}&{mapRulesetToQuery()}"; } public class GetScoresResponse diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 2567110ef6..3bc520e8e6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -146,6 +146,8 @@ namespace osu.Game.Screens.Select.Leaderboards private BeatmapInfo beatmap; + private OsuGame osuGame; + private ScheduledDelegate pendingBeatmapSwitch; public BeatmapInfo Beatmap @@ -164,9 +166,12 @@ namespace osu.Game.Screens.Select.Leaderboards } [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api) + private void load(APIAccess api, OsuGame osuGame) { this.api = api; + this.osuGame = osuGame; + + osuGame.Ruleset.ValueChanged += r => updateScores(); } private GetScoresRequest getScoresRequest; @@ -176,7 +181,8 @@ namespace osu.Game.Screens.Select.Leaderboards if (!IsLoaded) return; Scores = null; - getScoresRequest?.Cancel(); + + if (api == null || Beatmap?.OnlineBeatmapID == null) return; if (Scope == LeaderboardScope.Local) { @@ -185,11 +191,9 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - if (api == null || Beatmap?.OnlineBeatmapID == null) return; - loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, Scope); + getScoresRequest = new GetScoresRequest(Beatmap, Scope, osuGame.Ruleset.Value); getScoresRequest.Success += r => { Scores = r.Scores; From b6de1ce5b649011c6d60d8050215cfcc0a0c7517 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 22 Nov 2017 09:56:01 +0530 Subject: [PATCH 05/34] Handle query params better. --- .../Online/API/Requests/GetScoresRequest.cs | 58 +++++++++++-------- .../Select/Leaderboards/Leaderboard.cs | 2 +- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 37b3cc55f1..534a209609 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; using System.Collections.Specialized; +using osu.Framework.IO.Network; namespace osu.Game.Online.API.Requests { @@ -31,7 +32,7 @@ namespace osu.Game.Online.API.Requests Success += onSuccess; } - public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope, RulesetInfo ruleset) + public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); @@ -49,46 +50,53 @@ namespace osu.Game.Online.API.Requests score.ApplyBeatmap(beatmap); } - private string mapScopeToQuery() + protected override WebRequest CreateWebRequest() { + var req = base.CreateWebRequest(); + switch(scope) { + default: case LeaderboardScope.Global: - return @"type=global"; + req.AddParameter(@"type", @"global"); + break; case LeaderboardScope.Friends: - return @"type=friend"; + req.AddParameter(@"type", @"friend"); + break; case LeaderboardScope.Country: - return @"type=country"; - - default: - return String.Empty; + req.AddParameter(@"type", @"country"); + break; } - } - private string mapRulesetToQuery() - { - switch(ruleset.Name) + if (ruleset != null) { - case @"osu!": - return @"mode=osu"; + switch (ruleset.Name) + { + default: + case @"osu!": + req.AddParameter(@"mode", @"osu"); + break; - case @"osu!taiko": - return @"mode=taiko"; - - case @"osu!catch": - return @"mode=catch"; - - case @"osu!mania": - return @"mode=mania"; + case @"osu!taiko": + req.AddParameter(@"mode", @"taiko"); + break; - default: - return String.Empty; + case @"osu!catch": + req.AddParameter(@"mode", @"catch"); + break; + + case @"osu!mania": + req.AddParameter(@"mode", @"mania"); + break; + } } + + return req; } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores?{mapScopeToQuery()}&{mapRulesetToQuery()}"; + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; } public class GetScoresResponse diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3bc520e8e6..3b7e30dafe 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, Scope, osuGame.Ruleset.Value); + getScoresRequest = new GetScoresRequest(Beatmap, osuGame.Ruleset.Value, Scope); getScoresRequest.Success += r => { Scores = r.Scores; From c5a78e54e97f29dac7f471c5bb87de89782e0629 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 24 Nov 2017 18:40:52 +0530 Subject: [PATCH 06/34] Add a retry button for when scores request fails. --- .../Select/Leaderboards/Leaderboard.cs | 103 +++++++++++++++++- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3b7e30dafe..b25479704c 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -20,6 +20,8 @@ using osu.Game.Online.API.Requests; using System.Linq; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Framework.Logging; +using System.Net; namespace osu.Game.Screens.Select.Leaderboards { @@ -29,7 +31,8 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly ScrollContainer scrollContainer; private FillFlowContainer scrollFlow; - private Container placeholderContainer; + private Container noResultsPlaceholder; + private Container retryPlaceholder; public Action ScoreSelected; @@ -44,19 +47,20 @@ namespace osu.Game.Screens.Select.Leaderboards { scores = value; getScoresRequest?.Cancel(); + getScoresRequest = null; - placeholderContainer.FadeOut(fade_duration); + noResultsPlaceholder.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); scrollFlow = null; + loading.Hide(); + if (scores == null) return; - loading.Hide(); - if (scores.Count() == 0) { - placeholderContainer.FadeIn(fade_duration); + noResultsPlaceholder.FadeIn(fade_duration); return; } @@ -110,7 +114,7 @@ namespace osu.Game.Screens.Select.Leaderboards ScrollbarVisible = false, }, loading = new LoadingAnimation(), - placeholderContainer = new Container + noResultsPlaceholder = new Container { Alpha = 0, RelativeSizeAxes = Axes.Both, @@ -139,6 +143,35 @@ namespace osu.Game.Screens.Select.Leaderboards }, }, }, + retryPlaceholder = new Container + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new RetryButton + { + Action = updateScores, + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"An error occurred!", + TextSize = 22, + }, + } + }, + }, + }, }; } @@ -180,6 +213,8 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + retryPlaceholder.FadeOut(fade_duration); + Scores = null; if (api == null || Beatmap?.OnlineBeatmapID == null) return; @@ -198,6 +233,17 @@ namespace osu.Game.Screens.Select.Leaderboards { Scores = r.Scores; }; + getScoresRequest.Failure += e => + { + // TODO: check why failure is repeatedly invoked even on successful requests + if (e is WebException) + { + Scores = null; + retryPlaceholder.FadeIn(fade_duration); + Logger.Error(e, @"Couldn't fetch beatmap scores!"); + } + }; + api.Queue(getScoresRequest); } @@ -229,6 +275,51 @@ namespace osu.Game.Screens.Select.Leaderboards } } } + + private class RetryButton : ClickableContainer + { + private SpriteIcon icon; + + public RetryButton() + { + Height = 26; + Width = 26; + Children = new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_refresh, + Size = new Vector2(26), + } + }; + } + + protected override bool OnHover(Framework.Input.InputState state) + { + icon.ScaleTo(1.4f, 400, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(Framework.Input.InputState state) + { + icon.ScaleTo(1f, 400, Easing.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnMouseDown(Framework.Input.InputState state, Framework.Input.MouseDownEventArgs args) + { + icon.ScaleTo(0.8f, 200, Easing.InElastic); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(Framework.Input.InputState state, Framework.Input.MouseUpEventArgs args) + { + icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + } } public enum LeaderboardScope From 0b3f75505ef530a9960fec3f816b7f40cbaaf474 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 20:59:03 +0530 Subject: [PATCH 07/34] Don't break VisualTests and add a real beatmap step. --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 65 ++++++++++++++++--- .../Select/Leaderboards/Leaderboard.cs | 6 +- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index 9d6fb3a4ec..ad4aa63aa8 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -6,15 +6,43 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; +using osu.Framework.Allocation; using OpenTK; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Game.Tests.Visual { [Description("PlaySongSelect leaderboard")] internal class TestCaseLeaderboard : OsuTestCase { + private RulesetStore rulesets; + private readonly Leaderboard leaderboard; + public TestCaseLeaderboard() + { + Add(leaderboard = new Leaderboard + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(550f, 450f), + Scope = LeaderboardScope.Global, + }); + + AddStep(@"New Scores", newScores); + AddStep(@"Empty Scores", () => leaderboard.Scores = Enumerable.Empty()); + AddStep(@"Real beatmap", realBeatmap); + newScores(); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + private void newScores() { var scores = new[] @@ -204,17 +232,36 @@ namespace osu.Game.Tests.Visual leaderboard.Scores = scores; } - public TestCaseLeaderboard() + private void realBeatmap() { - Add(leaderboard = new Leaderboard + leaderboard.Beatmap = new BeatmapInfo { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(550f, 450f), - }); - - AddStep(@"New Scores", newScores); - newScores(); + StarDifficulty = 1.36, + Version = @"BASIC", + OnlineBeatmapID = 1113057, + Ruleset = rulesets.GetRuleset(0), + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 6.5f, + OverallDifficulty = 6.5f, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 115000, + CircleCount = 265, + SliderCount = 71, + PlayCount = 47906, + PassCount = 19899, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }; } } } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index b25479704c..824a54d372 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -51,6 +51,7 @@ namespace osu.Game.Screens.Select.Leaderboards noResultsPlaceholder.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); + scrollContainer.Clear(true); // scores stick around in scrollFlow in VisualTests without this for some reason scrollFlow = null; loading.Hide(); @@ -204,7 +205,8 @@ namespace osu.Game.Screens.Select.Leaderboards this.api = api; this.osuGame = osuGame; - osuGame.Ruleset.ValueChanged += r => updateScores(); + if (osuGame != null) + osuGame.Ruleset.ValueChanged += r => updateScores(); } private GetScoresRequest getScoresRequest; @@ -228,7 +230,7 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, osuGame.Ruleset.Value, Scope); + getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value, Scope); getScoresRequest.Success += r => { Scores = r.Scores; From ae9ce2f122fa29a65dee21db065a532201141f7e Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 21:23:36 +0530 Subject: [PATCH 08/34] Unbind ruleset event from leaderboard. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 824a54d372..3bea1d4bfd 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -22,6 +22,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics; using osu.Framework.Logging; using System.Net; +using osu.Game.Rulesets; namespace osu.Game.Screens.Select.Leaderboards { @@ -206,11 +207,21 @@ namespace osu.Game.Screens.Select.Leaderboards this.osuGame = osuGame; if (osuGame != null) - osuGame.Ruleset.ValueChanged += r => updateScores(); + osuGame.Ruleset.ValueChanged += handleRulesetChange; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (osuGame != null) + osuGame.Ruleset.ValueChanged -= handleRulesetChange; } private GetScoresRequest getScoresRequest; + private void handleRulesetChange(RulesetInfo ruleset) => updateScores(); + private void updateScores() { if (!IsLoaded) return; From f4f732ca4381768d5f54508b90fe06683f2bad0c Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 11:19:42 +0530 Subject: [PATCH 09/34] Remove unnecessary null check and tweak transform a bit. --- .../Online/API/Requests/GetScoresRequest.cs | 31 +++++++++---------- .../Select/Leaderboards/Leaderboard.cs | 8 ++--- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 534a209609..b9db836898 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -70,27 +70,24 @@ namespace osu.Game.Online.API.Requests break; } - if (ruleset != null) + switch (ruleset?.Name) { - switch (ruleset.Name) - { - default: - case @"osu!": - req.AddParameter(@"mode", @"osu"); - break; + default: + case @"osu!": + req.AddParameter(@"mode", @"osu"); + break; - case @"osu!taiko": - req.AddParameter(@"mode", @"taiko"); - break; + case @"osu!taiko": + req.AddParameter(@"mode", @"taiko"); + break; - case @"osu!catch": - req.AddParameter(@"mode", @"catch"); - break; + case @"osu!catch": + req.AddParameter(@"mode", @"catch"); + break; - case @"osu!mania": - req.AddParameter(@"mode", @"mania"); - break; - } + case @"osu!mania": + req.AddParameter(@"mode", @"mania"); + break; } return req; diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3bea1d4bfd..ef6ba01393 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -232,6 +232,8 @@ namespace osu.Game.Screens.Select.Leaderboards if (api == null || Beatmap?.OnlineBeatmapID == null) return; + loading.Show(); + if (Scope == LeaderboardScope.Local) { // TODO: get local scores from wherever here. @@ -239,8 +241,6 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value, Scope); getScoresRequest.Success += r => { @@ -323,13 +323,13 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(Framework.Input.InputState state, Framework.Input.MouseDownEventArgs args) { - icon.ScaleTo(0.8f, 200, Easing.InElastic); + icon.ScaleTo(0.8f, 800, Easing.InElastic); return base.OnMouseDown(state, args); } protected override bool OnMouseUp(Framework.Input.InputState state, Framework.Input.MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); + icon.ScaleTo(1.2f, 800, Easing.OutElastic).Then().ScaleTo(1f, 800, Easing.OutElastic); return base.OnMouseUp(state, args); } } From b261d32588887474cd439200a1d371523b680e70 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 12:25:48 +0530 Subject: [PATCH 10/34] Put retry button in a BeatSyncedContainer and change error message. --- .../Select/Leaderboards/Leaderboard.cs | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index ef6ba01393..8ea577d8c0 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -23,6 +23,9 @@ using osu.Game.Graphics; using osu.Framework.Logging; using System.Net; using osu.Game.Rulesets; +using osu.Framework.Input; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio.Track; namespace osu.Game.Screens.Select.Leaderboards { @@ -167,7 +170,7 @@ namespace osu.Game.Screens.Select.Leaderboards new OsuSpriteText { Anchor = Anchor.TopLeft, - Text = @"An error occurred!", + Text = @"Couldn't retrieve scores!", TextSize = 22, }, } @@ -289,47 +292,71 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private class RetryButton : ClickableContainer + private class RetryButton : BeatSyncedContainer { private SpriteIcon icon; + public Action Action; + public RetryButton() { Height = 26; Width = 26; - Children = new Drawable[] + Child = new ClickableContainer { - icon = new SpriteIcon + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => Action?.Invoke(), + Child = icon = new SpriteIcon { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Icon = FontAwesome.fa_refresh, Size = new Vector2(26), - } + }, }; } - protected override bool OnHover(Framework.Input.InputState state) + private bool rightWard; + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + var duration = timingPoint.BeatLength / 2; + + icon.RotateTo(rightWard ? 3 : -3, duration * 2, Easing.OutCubic); + icon.Animate( + i => i.MoveToY(-3, duration, Easing.Out), + i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) + ).Then( + i => i.MoveToY(0, duration, Easing.In), + i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) + ); + + rightWard = !rightWard; + } + + protected override bool OnHover(InputState state) { icon.ScaleTo(1.4f, 400, Easing.OutQuint); return base.OnHover(state); } - protected override void OnHoverLost(Framework.Input.InputState state) + protected override void OnHoverLost(InputState state) { + icon.ClearTransforms(); icon.ScaleTo(1f, 400, Easing.OutQuint); base.OnHoverLost(state); } - protected override bool OnMouseDown(Framework.Input.InputState state, Framework.Input.MouseDownEventArgs args) + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - icon.ScaleTo(0.8f, 800, Easing.InElastic); + icon.ClearTransforms(); + icon.ScaleTo(0.8f, 400, Easing.InElastic); return base.OnMouseDown(state, args); } - protected override bool OnMouseUp(Framework.Input.InputState state, Framework.Input.MouseUpEventArgs args) + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 800, Easing.OutElastic).Then().ScaleTo(1f, 800, Easing.OutElastic); + icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); return base.OnMouseUp(state, args); } } From 421231550496456baebdbc345f0c8fe97a0bf61d Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 12:50:20 +0530 Subject: [PATCH 11/34] Use a single placeholder container for empty and retry. --- .../Select/Leaderboards/Leaderboard.cs | 97 +++++++++---------- 1 file changed, 45 insertions(+), 52 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 8ea577d8c0..31e79022c3 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly ScrollContainer scrollContainer; private FillFlowContainer scrollFlow; - private Container noResultsPlaceholder; - private Container retryPlaceholder; + private Container placeholderContainer; + private FillFlowContainer placeholderFlow; public Action ScoreSelected; @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Select.Leaderboards getScoresRequest?.Cancel(); getScoresRequest = null; - noResultsPlaceholder.FadeOut(fade_duration); + placeholderContainer.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); scrollContainer.Clear(true); // scores stick around in scrollFlow in VisualTests without this for some reason scrollFlow = null; @@ -65,7 +65,22 @@ namespace osu.Game.Screens.Select.Leaderboards if (scores.Count() == 0) { - noResultsPlaceholder.FadeIn(fade_duration); + placeholderFlow.Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_exclamation_circle, + Size = new Vector2(26), + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Text = @"No records yet!", + TextSize = 22, + }, + }; + + placeholderContainer.FadeIn(fade_duration); return; } @@ -119,61 +134,18 @@ namespace osu.Game.Screens.Select.Leaderboards ScrollbarVisible = false, }, loading = new LoadingAnimation(), - noResultsPlaceholder = new Container + placeholderContainer = new Container { Alpha = 0, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new FillFlowContainer + placeholderFlow = new FillFlowContainer { Direction = FillDirection.Horizontal, Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_exclamation_circle, - Size = new Vector2(26), - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Text = @"No records yet!", - TextSize = 22, - }, - } - }, - }, - }, - retryPlaceholder = new Container - { - Alpha = 0, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new RetryButton - { - Action = updateScores, - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopLeft, - Text = @"Couldn't retrieve scores!", - TextSize = 22, - }, - } }, }, }, @@ -229,8 +201,6 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; - retryPlaceholder.FadeOut(fade_duration); - Scores = null; if (api == null || Beatmap?.OnlineBeatmapID == null) return; @@ -255,7 +225,21 @@ namespace osu.Game.Screens.Select.Leaderboards if (e is WebException) { Scores = null; - retryPlaceholder.FadeIn(fade_duration); + placeholderFlow.Children = new Drawable[] + { + new RetryButton + { + Action = updateScores, + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"Couldn't retrieve scores!", + TextSize = 22, + }, + }; + placeholderContainer.FadeIn(fade_duration); Logger.Error(e, @"Couldn't fetch beatmap scores!"); } }; @@ -298,6 +282,8 @@ namespace osu.Game.Screens.Select.Leaderboards public Action Action; + private OsuColour colours; + public RetryButton() { Height = 26; @@ -316,6 +302,12 @@ namespace osu.Game.Screens.Select.Leaderboards }; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + this.colours = colours; + } + private bool rightWard; protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) @@ -350,6 +342,7 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { icon.ClearTransforms(); + icon.FlashColour(colours.Yellow, 400); icon.ScaleTo(0.8f, 400, Easing.InElastic); return base.OnMouseDown(state, args); } From ae201f0ef5dcdf1f4bbae80082ee214739ad7862 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 15:03:49 +0530 Subject: [PATCH 12/34] R# --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 1 - osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index b9db836898..54d656eeca 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -11,7 +11,6 @@ using osu.Game.Users; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; -using System.Collections.Specialized; using osu.Framework.IO.Network; namespace osu.Game.Online.API.Requests diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 31e79022c3..62bd292710 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -34,9 +34,10 @@ namespace osu.Game.Screens.Select.Leaderboards private const double fade_duration = 200; private readonly ScrollContainer scrollContainer; + private readonly Container placeholderContainer; + private readonly FillFlowContainer placeholderFlow; + private FillFlowContainer scrollFlow; - private Container placeholderContainer; - private FillFlowContainer placeholderFlow; public Action ScoreSelected; @@ -63,7 +64,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (scores == null) return; - if (scores.Count() == 0) + if (!scores.Any()) { placeholderFlow.Children = new Drawable[] { @@ -278,7 +279,7 @@ namespace osu.Game.Screens.Select.Leaderboards private class RetryButton : BeatSyncedContainer { - private SpriteIcon icon; + private readonly SpriteIcon icon; public Action Action; From ac1fb5118c6b075de0d333a6f49665b6396b8d53 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 28 Nov 2017 11:35:39 +0530 Subject: [PATCH 13/34] Fix line endings and derp that was causing request failures. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 62bd292710..5574dd69a1 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -24,7 +24,7 @@ using osu.Framework.Logging; using System.Net; using osu.Game.Rulesets; using osu.Framework.Input; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; namespace osu.Game.Screens.Select.Leaderboards @@ -51,7 +51,6 @@ namespace osu.Game.Screens.Select.Leaderboards set { scores = value; - getScoresRequest?.Cancel(); getScoresRequest = null; placeholderContainer.FadeOut(fade_duration); @@ -202,6 +201,8 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + getScoresRequest?.Cancel(); + Scores = null; if (api == null || Beatmap?.OnlineBeatmapID == null) return; @@ -318,7 +319,7 @@ namespace osu.Game.Screens.Select.Leaderboards icon.RotateTo(rightWard ? 3 : -3, duration * 2, Easing.OutCubic); icon.Animate( i => i.MoveToY(-3, duration, Easing.Out), - i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) + i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) ).Then( i => i.MoveToY(0, duration, Easing.In), i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) @@ -350,7 +351,7 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); + icon.ScaleTo(1.2f, 400, Easing.OutElastic); return base.OnMouseUp(state, args); } } From e832f163e7174873ae2d68058e6e51ecc1218cf8 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 28 Nov 2017 11:57:29 +0530 Subject: [PATCH 14/34] Add failure test case. - Only show failure if request wasn't cancelled --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 23 +++++++- .../Select/Leaderboards/Leaderboard.cs | 59 +++++++++---------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index ad4aa63aa8..52daf95810 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -9,6 +9,7 @@ using osu.Game.Users; using osu.Framework.Allocation; using OpenTK; using System.Linq; +using System.Net; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -19,11 +20,11 @@ namespace osu.Game.Tests.Visual { private RulesetStore rulesets; - private readonly Leaderboard leaderboard; + private readonly FailableLeaderboard leaderboard; public TestCaseLeaderboard() { - Add(leaderboard = new Leaderboard + Add(leaderboard = new FailableLeaderboard { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -33,8 +34,8 @@ namespace osu.Game.Tests.Visual AddStep(@"New Scores", newScores); AddStep(@"Empty Scores", () => leaderboard.Scores = Enumerable.Empty()); + AddStep(@"Network failure", networkFailure); AddStep(@"Real beatmap", realBeatmap); - newScores(); } [BackgroundDependencyLoader] @@ -263,5 +264,21 @@ namespace osu.Game.Tests.Visual }, }; } + + private void networkFailure() + { + leaderboard.Beatmap = new BeatmapInfo(); + } + + private class FailableLeaderboard : Leaderboard + { + protected override void UpdateScores() + { + if (Beatmap?.OnlineBeatmapID == null) + OnUpdateFailed(new WebException()); + else + base.UpdateScores(); + } + } } } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 5574dd69a1..8fd04e2c25 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -21,7 +21,6 @@ using System.Linq; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; using osu.Framework.Logging; -using System.Net; using osu.Game.Rulesets; using osu.Framework.Input; using osu.Game.Beatmaps.ControlPoints; @@ -51,7 +50,6 @@ namespace osu.Game.Screens.Select.Leaderboards set { scores = value; - getScoresRequest = null; placeholderContainer.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); @@ -120,7 +118,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (value == scope) return; scope = value; - updateScores(); + UpdateScores(); } } @@ -171,7 +169,7 @@ namespace osu.Game.Screens.Select.Leaderboards Scores = null; pendingBeatmapSwitch?.Cancel(); - pendingBeatmapSwitch = Schedule(updateScores); + pendingBeatmapSwitch = Schedule(UpdateScores); } } @@ -195,13 +193,14 @@ namespace osu.Game.Screens.Select.Leaderboards private GetScoresRequest getScoresRequest; - private void handleRulesetChange(RulesetInfo ruleset) => updateScores(); + private void handleRulesetChange(RulesetInfo ruleset) => UpdateScores(); - private void updateScores() + protected virtual void UpdateScores() { if (!IsLoaded) return; getScoresRequest?.Cancel(); + getScoresRequest = null; Scores = null; @@ -221,34 +220,34 @@ namespace osu.Game.Screens.Select.Leaderboards { Scores = r.Scores; }; - getScoresRequest.Failure += e => - { - // TODO: check why failure is repeatedly invoked even on successful requests - if (e is WebException) - { - Scores = null; - placeholderFlow.Children = new Drawable[] - { - new RetryButton - { - Action = updateScores, - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopLeft, - Text = @"Couldn't retrieve scores!", - TextSize = 22, - }, - }; - placeholderContainer.FadeIn(fade_duration); - Logger.Error(e, @"Couldn't fetch beatmap scores!"); - } - }; + getScoresRequest.Failure += OnUpdateFailed; api.Queue(getScoresRequest); } + protected void OnUpdateFailed(Exception e) + { + if (e is OperationCanceledException) return; + + Scores = null; + placeholderFlow.Children = new Drawable[] + { + new RetryButton + { + Action = UpdateScores, + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"Couldn't retrieve scores!", + TextSize = 22, + }, + }; + placeholderContainer.FadeIn(fade_duration); + Logger.Error(e, @"Couldn't fetch beatmap scores!"); + } + protected override void Update() { base.Update(); From a30cd42ba2cb43b7535a60c4345bd6c77d26b464 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 28 Nov 2017 14:38:35 +0530 Subject: [PATCH 15/34] Make retry button not look drunk. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 8fd04e2c25..e80f502e73 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -299,6 +299,7 @@ namespace osu.Game.Screens.Select.Leaderboards { Icon = FontAwesome.fa_refresh, Size = new Vector2(26), + Shadow = true, }, }; } @@ -309,13 +310,10 @@ namespace osu.Game.Screens.Select.Leaderboards this.colours = colours; } - private bool rightWard; - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { var duration = timingPoint.BeatLength / 2; - icon.RotateTo(rightWard ? 3 : -3, duration * 2, Easing.OutCubic); icon.Animate( i => i.MoveToY(-3, duration, Easing.Out), i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) @@ -323,8 +321,6 @@ namespace osu.Game.Screens.Select.Leaderboards i => i.MoveToY(0, duration, Easing.In), i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) ); - - rightWard = !rightWard; } protected override bool OnHover(InputState state) @@ -342,15 +338,13 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - icon.ClearTransforms(); - icon.FlashColour(colours.Yellow, 400); - icon.ScaleTo(0.8f, 400, Easing.InElastic); + icon.FadeColour(colours.Yellow, 400); return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 400, Easing.OutElastic); + icon.FadeColour(Color4.White, 400); return base.OnMouseUp(state, args); } } From 47bd97363e625ccdf63bc337d158c00149d997d6 Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 11 Dec 2017 15:05:12 +0530 Subject: [PATCH 16/34] Use ShortName for mode. - Also set the ruleset when fetching scores in BeatmapSetOverlay --- .../Online/API/Requests/GetScoresRequest.cs | 20 +------------------ osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- .../Select/Leaderboards/Leaderboard.cs | 2 +- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 54d656eeca..4379daa47b 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -69,25 +69,7 @@ namespace osu.Game.Online.API.Requests break; } - switch (ruleset?.Name) - { - default: - case @"osu!": - req.AddParameter(@"mode", @"osu"); - break; - - case @"osu!taiko": - req.AddParameter(@"mode", @"taiko"); - break; - - case @"osu!catch": - req.AddParameter(@"mode", @"catch"); - break; - - case @"osu!mania": - req.AddParameter(@"mode", @"mania"); - break; - } + req.AddParameter(@"mode", ruleset?.ShortName ?? @"osu"); return req; } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 0d658b346e..0a88f586b5 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -104,7 +104,7 @@ namespace osu.Game.Overlays scores.IsLoading = true; - getScoresRequest = new GetScoresRequest(beatmap); + getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); getScoresRequest.Success += r => { scores.Scores = r.Scores; diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index e80f502e73..36dc254792 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.Select.Leaderboards { Height = 26; Width = 26; - Child = new ClickableContainer + Child = new OsuClickableContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, From 63dce59c8c6f2ac2273d385cc0a20f981ceb2e44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2017 20:25:57 +0900 Subject: [PATCH 17/34] Throw an exception if we try and retrieve local scores online --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 4379daa47b..c32d8045ab 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -36,6 +36,9 @@ namespace osu.Game.Online.API.Requests if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); + if (scope == LeaderboardScope.Local) + throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); + this.beatmap = beatmap; this.scope = scope; this.ruleset = ruleset; From 1b91f24044807d7be61d80a39f4e67e9cb061fb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2017 20:33:16 +0900 Subject: [PATCH 18/34] Simplify scope logic --- .../Online/API/Requests/GetScoresRequest.cs | 17 +---------------- .../Screens/Select/Leaderboards/Leaderboard.cs | 2 +- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index c32d8045ab..0d9812374a 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -56,22 +56,7 @@ namespace osu.Game.Online.API.Requests { var req = base.CreateWebRequest(); - switch(scope) - { - default: - case LeaderboardScope.Global: - req.AddParameter(@"type", @"global"); - break; - - case LeaderboardScope.Friends: - req.AddParameter(@"type", @"friend"); - break; - - case LeaderboardScope.Country: - req.AddParameter(@"type", @"country"); - break; - } - + req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); req.AddParameter(@"mode", ruleset?.ShortName ?? @"osu"); return req; diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 36dc254792..decd966fa1 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -355,6 +355,6 @@ namespace osu.Game.Screens.Select.Leaderboards Local, Country, Global, - Friends, + Friend, } } From c871a25dfa53dbd1a6f6814efba1c4e4bcb2d4f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2017 20:41:01 +0900 Subject: [PATCH 19/34] Remove unnecessary constructure and make ruleset required --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 14 ++------------ osu.Game/Tests/Visual/TestCasePerformancePoints.cs | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 0d9812374a..ebd00e8505 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -21,16 +21,6 @@ namespace osu.Game.Online.API.Requests private readonly LeaderboardScope scope; private readonly RulesetInfo ruleset; - public GetScoresRequest(BeatmapInfo beatmap) - { - if (!beatmap.OnlineBeatmapID.HasValue) - throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); - - this.beatmap = beatmap; - - Success += onSuccess; - } - public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) { if (!beatmap.OnlineBeatmapID.HasValue) @@ -41,7 +31,7 @@ namespace osu.Game.Online.API.Requests this.beatmap = beatmap; this.scope = scope; - this.ruleset = ruleset; + this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); Success += onSuccess; } @@ -57,7 +47,7 @@ namespace osu.Game.Online.API.Requests var req = base.CreateWebRequest(); req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); - req.AddParameter(@"mode", ruleset?.ShortName ?? @"osu"); + req.AddParameter(@"mode", ruleset.ShortName); return req; } diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 6da14e9b12..f71bece279 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual if (!api.IsLoggedIn) return; - lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo); + lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo, newBeatmap.BeatmapInfo.Ruleset); lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap))); api.Queue(lastRequest); } From 3292ef33fd72f3b3ae81bf8bd4487a9cc951efc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2017 21:06:33 +0900 Subject: [PATCH 20/34] Fix test edge cases --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 2 +- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index 52daf95810..62aa5a79b3 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual private void networkFailure() { - leaderboard.Beatmap = new BeatmapInfo(); + leaderboard.Beatmap = new BeatmapInfo { Ruleset = rulesets.GetRuleset(0) }; } private class FailableLeaderboard : Leaderboard diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index decd966fa1..2d9cec4cc0 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -215,7 +215,7 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value, Scope); + getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest.Success += r => { Scores = r.Scores; From 6d471da459e63376f212aa3a17c1b3f408936775 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2017 21:50:45 +0900 Subject: [PATCH 21/34] Remove unnecessary workaround --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 2d9cec4cc0..68e135e5a4 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -52,8 +52,8 @@ namespace osu.Game.Screens.Select.Leaderboards scores = value; placeholderContainer.FadeOut(fade_duration); + scrollFlow?.FadeOut(fade_duration).Expire(); - scrollContainer.Clear(true); // scores stick around in scrollFlow in VisualTests without this for some reason scrollFlow = null; loading.Hide(); From 23f479984029ee5f1f2f91dcd486c833753edf29 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 20 Dec 2017 20:00:52 +0530 Subject: [PATCH 22/34] Create placeholder classes instead of changing Children. - Add MessagePlaceholder - Use MessagePlacholder for when API is offline/user isn't a supporter - Remove unnecessary placeholderFlow field - Hook into API state changes --- .../Select/Leaderboards/Leaderboard.cs | 139 ++++++++++++------ 1 file changed, 97 insertions(+), 42 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 68e135e5a4..44d4d2ff52 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -34,7 +34,6 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly ScrollContainer scrollContainer; private readonly Container placeholderContainer; - private readonly FillFlowContainer placeholderFlow; private FillFlowContainer scrollFlow; @@ -63,22 +62,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (!scores.Any()) { - placeholderFlow.Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_exclamation_circle, - Size = new Vector2(26), - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Text = @"No records yet!", - TextSize = 22, - }, - }; - - placeholderContainer.FadeIn(fade_duration); + replacePlaceholder(new MessagePlaceholder(@"No records yet!")); return; } @@ -135,17 +119,9 @@ namespace osu.Game.Screens.Select.Leaderboards placeholderContainer = new Container { Alpha = 0, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - placeholderFlow = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - }, - }, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, }; } @@ -181,6 +157,9 @@ namespace osu.Game.Screens.Select.Leaderboards if (osuGame != null) osuGame.Ruleset.ValueChanged += handleRulesetChange; + + if (api != null) + api.OnStateChange += handleApiStateChange; } protected override void Dispose(bool isDisposing) @@ -195,6 +174,23 @@ namespace osu.Game.Screens.Select.Leaderboards private void handleRulesetChange(RulesetInfo ruleset) => UpdateScores(); + private void handleApiStateChange(APIState oldState, APIState newState) + { + if (Scope == LeaderboardScope.Local) + // No need to respond to API state change while current scope is local + return; + + if (newState == APIState.Offline) + { + Scores = null; + replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); + return; + } + + if (newState == APIState.Online) + UpdateScores(); + } + protected virtual void UpdateScores() { if (!IsLoaded) return; @@ -215,6 +211,20 @@ namespace osu.Game.Screens.Select.Leaderboards return; } + if (!api.IsLoggedIn) + { + loading.Hide(); + replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); + return; + } + + if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) + { + loading.Hide(); + replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!")); + return; + } + getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest.Success += r => { @@ -230,24 +240,21 @@ namespace osu.Game.Screens.Select.Leaderboards if (e is OperationCanceledException) return; Scores = null; - placeholderFlow.Children = new Drawable[] + replacePlaceholder(new RetrievalFailurePlaceholder { - new RetryButton - { - Action = UpdateScores, - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopLeft, - Text = @"Couldn't retrieve scores!", - TextSize = 22, - }, - }; - placeholderContainer.FadeIn(fade_duration); + OnRetry = UpdateScores, + }); Logger.Error(e, @"Couldn't fetch beatmap scores!"); } + private void replacePlaceholder(Drawable placeholder) + { + placeholderContainer.FadeOutFromOne(fade_duration, Easing.OutQuint); + placeholderContainer.Clear(true); + placeholderContainer.Child = placeholder; + placeholderContainer.FadeInFromZero(fade_duration, Easing.OutQuint); + } + protected override void Update() { base.Update(); @@ -277,6 +284,54 @@ namespace osu.Game.Screens.Select.Leaderboards } } + private class MessagePlaceholder : FillFlowContainer + { + public MessagePlaceholder(string message) + { + Direction = FillDirection.Horizontal; + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_exclamation_circle, + Size = new Vector2(26), + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Text = message, + TextSize = 22, + }, + }; + } + } + + private class RetrievalFailurePlaceholder : FillFlowContainer + { + public Action OnRetry; + + public RetrievalFailurePlaceholder() + { + Direction = FillDirection.Horizontal; + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new RetryButton + { + Action = () => OnRetry?.Invoke(), + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"Couldn't retrieve scores!", + TextSize = 22, + }, + }; + } + } + private class RetryButton : BeatSyncedContainer { private readonly SpriteIcon icon; From c8c8b6810adc3fc7b1feb5435e1364a31bede4a7 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 20 Dec 2017 20:11:48 +0530 Subject: [PATCH 23/34] Kill ugly retry button bounce. --- .../Select/Leaderboards/Leaderboard.cs | 40 ++----------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 44d4d2ff52..97e694a4b1 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -332,14 +332,12 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private class RetryButton : BeatSyncedContainer + private class RetryButton : OsuHoverContainer { private readonly SpriteIcon icon; public Action Action; - private OsuColour colours; - public RetryButton() { Height = 26; @@ -359,47 +357,15 @@ namespace osu.Game.Screens.Select.Leaderboards }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - this.colours = colours; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - var duration = timingPoint.BeatLength / 2; - - icon.Animate( - i => i.MoveToY(-3, duration, Easing.Out), - i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) - ).Then( - i => i.MoveToY(0, duration, Easing.In), - i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) - ); - } - - protected override bool OnHover(InputState state) - { - icon.ScaleTo(1.4f, 400, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - icon.ClearTransforms(); - icon.ScaleTo(1f, 400, Easing.OutQuint); - base.OnHoverLost(state); - } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - icon.FadeColour(colours.Yellow, 400); + icon.ScaleTo(0.8f, 400, Easing.OutQuint); return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.FadeColour(Color4.White, 400); + icon.ScaleTo(1, 400, Easing.OutElastic); return base.OnMouseUp(state, args); } } From 8d24a046410bfdf1d8003d61e3a04f86ad7e34db Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 20 Dec 2017 20:26:59 +0530 Subject: [PATCH 24/34] Remove unused usings. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 97e694a4b1..73b6cb0c53 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -23,8 +23,6 @@ using osu.Game.Graphics; using osu.Framework.Logging; using osu.Game.Rulesets; using osu.Framework.Input; -using osu.Game.Beatmaps.ControlPoints; -using osu.Framework.Audio.Track; namespace osu.Game.Screens.Select.Leaderboards { From 85dee3abac6b563ee647edb7bc0ddbfb85d6784d Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 20 Dec 2017 20:48:30 +0530 Subject: [PATCH 25/34] Increase GetScoresRequest timeout & leave existing scores when API dies. --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 1 + osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index ebd00e8505..065c770738 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -46,6 +46,7 @@ namespace osu.Game.Online.API.Requests { var req = base.CreateWebRequest(); + req.Timeout = 30000; req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); req.AddParameter(@"mode", ruleset.ShortName); diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 73b6cb0c53..1c14d1ad33 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -178,13 +178,6 @@ namespace osu.Game.Screens.Select.Leaderboards // No need to respond to API state change while current scope is local return; - if (newState == APIState.Offline) - { - Scores = null; - replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); - return; - } - if (newState == APIState.Online) UpdateScores(); } From 2ff351c6cb3f050a7d3c76000193eb350faa1c89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2017 03:10:09 +0900 Subject: [PATCH 26/34] Show retrieval failure when OnlineBeatmapID is missing --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 1c14d1ad33..792ee8211a 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -191,7 +191,14 @@ namespace osu.Game.Screens.Select.Leaderboards Scores = null; - if (api == null || Beatmap?.OnlineBeatmapID == null) return; + if (api == null || Beatmap?.OnlineBeatmapID == null) + { + replacePlaceholder(new RetrievalFailurePlaceholder + { + OnRetry = UpdateScores, + }); + return; + } loading.Show(); From afcb9912e4fe28cbe85ceac71770587447434524 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2017 03:11:44 +0900 Subject: [PATCH 27/34] Reorder API / logged in checks to make more sense --- .../Select/Leaderboards/Leaderboard.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 792ee8211a..9a6e88b0a6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -191,7 +191,20 @@ namespace osu.Game.Screens.Select.Leaderboards Scores = null; - if (api == null || Beatmap?.OnlineBeatmapID == null) + if (Scope == LeaderboardScope.Local) + { + // TODO: get local scores from wherever here. + Scores = Enumerable.Empty(); + return; + } + + if (api?.IsLoggedIn != true) + { + replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); + return; + } + + if (Beatmap?.OnlineBeatmapID == null) { replacePlaceholder(new RetrievalFailurePlaceholder { @@ -202,20 +215,6 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - if (Scope == LeaderboardScope.Local) - { - // TODO: get local scores from wherever here. - Scores = Enumerable.Empty(); - return; - } - - if (!api.IsLoggedIn) - { - loading.Hide(); - replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); - return; - } - if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) { loading.Hide(); From 01c4b1e544577462ebd708d56173ace5df8f2bd1 Mon Sep 17 00:00:00 2001 From: naoey Date: Thu, 21 Dec 2017 15:17:37 +0530 Subject: [PATCH 28/34] Maintain a placeholder state and add tests showing all the states. - Also don't replace placeholder if new one is same as old --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 19 +- .../Select/Leaderboards/Leaderboard.cs | 185 ++++++++++++------ 2 files changed, 127 insertions(+), 77 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index 62aa5a79b3..d1d030ced8 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -9,7 +9,6 @@ using osu.Game.Users; using osu.Framework.Allocation; using OpenTK; using System.Linq; -using System.Net; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -33,8 +32,10 @@ namespace osu.Game.Tests.Visual }); AddStep(@"New Scores", newScores); - AddStep(@"Empty Scores", () => leaderboard.Scores = Enumerable.Empty()); - AddStep(@"Network failure", networkFailure); + AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores)); + AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure)); + AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); + AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); AddStep(@"Real beatmap", realBeatmap); } @@ -265,19 +266,11 @@ namespace osu.Game.Tests.Visual }; } - private void networkFailure() - { - leaderboard.Beatmap = new BeatmapInfo { Ruleset = rulesets.GetRuleset(0) }; - } - private class FailableLeaderboard : Leaderboard { - protected override void UpdateScores() + public void SetRetrievalState(PlaceholderState state) { - if (Beatmap?.OnlineBeatmapID == null) - OnUpdateFailed(new WebException()); - else - base.UpdateScores(); + PlaceholderState = state; } } } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 9a6e88b0a6..3636115227 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -48,21 +48,16 @@ namespace osu.Game.Screens.Select.Leaderboards { scores = value; - placeholderContainer.FadeOut(fade_duration); - scrollFlow?.FadeOut(fade_duration).Expire(); scrollFlow = null; loading.Hide(); - if (scores == null) + if (scores == null || !scores.Any()) return; - if (!scores.Any()) - { - replacePlaceholder(new MessagePlaceholder(@"No records yet!")); - return; - } + // ensure placeholder is hidden when displaying scores + PlaceholderState = PlaceholderState.Successful; // schedule because we may not be loaded yet (LoadComponentAsync complains). Schedule(() => @@ -100,7 +95,43 @@ namespace osu.Game.Screens.Select.Leaderboards if (value == scope) return; scope = value; - UpdateScores(); + updateScores(); + } + } + + private PlaceholderState placeholderState; + protected PlaceholderState PlaceholderState + { + get { return placeholderState; } + set + { + if (value == placeholderState) return; + + switch (placeholderState = value) + { + case PlaceholderState.NetworkFailure: + replacePlaceholder(new RetrievalFailurePlaceholder + { + OnRetry = updateScores, + }); + break; + + case PlaceholderState.NoScores: + replacePlaceholder(new MessagePlaceholder(@"No records yet!")); + break; + + case PlaceholderState.NotLoggedIn: + replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); + break; + + case PlaceholderState.NotSupporter: + replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!")); + break; + + default: + replacePlaceholder(null); + break; + } } } @@ -143,7 +174,7 @@ namespace osu.Game.Screens.Select.Leaderboards Scores = null; pendingBeatmapSwitch?.Cancel(); - pendingBeatmapSwitch = Schedule(UpdateScores); + pendingBeatmapSwitch = Schedule(updateScores); } } @@ -170,7 +201,7 @@ namespace osu.Game.Screens.Select.Leaderboards private GetScoresRequest getScoresRequest; - private void handleRulesetChange(RulesetInfo ruleset) => UpdateScores(); + private void handleRulesetChange(RulesetInfo ruleset) => updateScores(); private void handleApiStateChange(APIState oldState, APIState newState) { @@ -179,46 +210,43 @@ namespace osu.Game.Screens.Select.Leaderboards return; if (newState == APIState.Online) - UpdateScores(); + updateScores(); } - protected virtual void UpdateScores() + private void updateScores() { if (!IsLoaded) return; getScoresRequest?.Cancel(); getScoresRequest = null; - Scores = null; if (Scope == LeaderboardScope.Local) { // TODO: get local scores from wherever here. - Scores = Enumerable.Empty(); + PlaceholderState = PlaceholderState.NoScores; return; } if (api?.IsLoggedIn != true) { - replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); + PlaceholderState = PlaceholderState.NotLoggedIn; return; } if (Beatmap?.OnlineBeatmapID == null) { - replacePlaceholder(new RetrievalFailurePlaceholder - { - OnRetry = UpdateScores, - }); + PlaceholderState = PlaceholderState.NetworkFailure; return; } + PlaceholderState = PlaceholderState.Retrieving; loading.Show(); if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) { loading.Hide(); - replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!")); + PlaceholderState = PlaceholderState.NotSupporter; return; } @@ -226,27 +254,37 @@ namespace osu.Game.Screens.Select.Leaderboards getScoresRequest.Success += r => { Scores = r.Scores; + PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; }; - getScoresRequest.Failure += OnUpdateFailed; + getScoresRequest.Failure += onUpdateFailed; api.Queue(getScoresRequest); } - protected void OnUpdateFailed(Exception e) + private void onUpdateFailed(Exception e) { if (e is OperationCanceledException) return; - Scores = null; - replacePlaceholder(new RetrievalFailurePlaceholder - { - OnRetry = UpdateScores, - }); + PlaceholderState = PlaceholderState.NetworkFailure; Logger.Error(e, @"Couldn't fetch beatmap scores!"); } - private void replacePlaceholder(Drawable placeholder) + private void replacePlaceholder(Placeholder placeholder) { - placeholderContainer.FadeOutFromOne(fade_duration, Easing.OutQuint); + if (placeholder == null) + { + placeholderContainer.FadeOutFromOne(fade_duration, Easing.OutQuint); + placeholderContainer.Clear(true); + return; + } + + var existingPlaceholder = placeholderContainer.Children.FirstOrDefault() as Placeholder; + + if (placeholder.Equals(existingPlaceholder)) + return; + + Scores = null; + placeholderContainer.Clear(true); placeholderContainer.Child = placeholder; placeholderContainer.FadeInFromZero(fade_duration, Easing.OutQuint); @@ -281,8 +319,15 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private class MessagePlaceholder : FillFlowContainer + private abstract class Placeholder : FillFlowContainer, IEquatable { + public virtual bool Equals(Placeholder other) => GetType() == other?.GetType(); + } + + private class MessagePlaceholder : Placeholder + { + private readonly string message; + public MessagePlaceholder(string message) { Direction = FillDirection.Horizontal; @@ -297,14 +342,16 @@ namespace osu.Game.Screens.Select.Leaderboards }, new OsuSpriteText { - Text = message, + Text = this.message = message, TextSize = 22, }, }; } + + public override bool Equals(Placeholder other) => (other as MessagePlaceholder)?.message == message; } - private class RetrievalFailurePlaceholder : FillFlowContainer + private class RetrievalFailurePlaceholder : Placeholder { public Action OnRetry; @@ -327,43 +374,43 @@ namespace osu.Game.Screens.Select.Leaderboards }, }; } - } - private class RetryButton : OsuHoverContainer - { - private readonly SpriteIcon icon; - - public Action Action; - - public RetryButton() + private class RetryButton : OsuHoverContainer { - Height = 26; - Width = 26; - Child = new OsuClickableContainer + private readonly SpriteIcon icon; + + public Action Action; + + public RetryButton() { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => Action?.Invoke(), - Child = icon = new SpriteIcon + Height = 26; + Width = 26; + Child = new OsuClickableContainer { - Icon = FontAwesome.fa_refresh, - Size = new Vector2(26), - Shadow = true, - }, - }; - } + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => Action?.Invoke(), + Child = icon = new SpriteIcon + { + Icon = FontAwesome.fa_refresh, + Size = new Vector2(26), + Shadow = true, + }, + }; + } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - icon.ScaleTo(0.8f, 400, Easing.OutQuint); - return base.OnMouseDown(state, args); - } + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.8f, 400, Easing.OutQuint); + return base.OnMouseDown(state, args); + } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - icon.ScaleTo(1, 400, Easing.OutElastic); - return base.OnMouseUp(state, args); + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(1, 400, Easing.OutElastic); + return base.OnMouseUp(state, args); + } } } } @@ -375,4 +422,14 @@ namespace osu.Game.Screens.Select.Leaderboards Global, Friend, } + + public enum PlaceholderState + { + Successful, + Retrieving, + NetworkFailure, + NoScores, + NotLoggedIn, + NotSupporter, + } } From 644aaa81679d00b0292386898835f1356976185b Mon Sep 17 00:00:00 2001 From: naoey Date: Thu, 21 Dec 2017 18:00:10 +0530 Subject: [PATCH 29/34] Unsubscribe from API state change event. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3636115227..52b152b42f 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -197,6 +197,9 @@ namespace osu.Game.Screens.Select.Leaderboards if (osuGame != null) osuGame.Ruleset.ValueChanged -= handleRulesetChange; + + if (api != null) + api.OnStateChange -= handleApiStateChange; } private GetScoresRequest getScoresRequest; From 4bd2c7e95fe17544c1ac29f90f87a4d3f4ec26d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2017 21:54:46 +0900 Subject: [PATCH 30/34] Fix minor formatting issue --- osu.Game/Screens/Select/BeatmapDetailArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index 790a8421a2..4403d412fc 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select default: Details.Hide(); - Leaderboard.Scope = (LeaderboardScope) tab - 1; + Leaderboard.Scope = (LeaderboardScope)tab - 1; Leaderboard.Show(); break; } From 65e6206d06858c6de53182ffb51aefd662217dba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2017 23:48:35 +0900 Subject: [PATCH 31/34] Use local bindable --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 52b152b42f..50c16ed04d 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using System.Linq; +using osu.Framework.Configuration; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; using osu.Framework.Logging; @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Select.Leaderboards private FillFlowContainer scrollFlow; + private readonly Bindable ruleset = new Bindable(); + public Action ScoreSelected; private readonly LoadingAnimation loading; @@ -185,7 +188,9 @@ namespace osu.Game.Screens.Select.Leaderboards this.osuGame = osuGame; if (osuGame != null) - osuGame.Ruleset.ValueChanged += handleRulesetChange; + ruleset.BindTo(osuGame.Ruleset); + + ruleset.ValueChanged += r => updateScores(); if (api != null) api.OnStateChange += handleApiStateChange; @@ -195,17 +200,12 @@ namespace osu.Game.Screens.Select.Leaderboards { base.Dispose(isDisposing); - if (osuGame != null) - osuGame.Ruleset.ValueChanged -= handleRulesetChange; - if (api != null) api.OnStateChange -= handleApiStateChange; } private GetScoresRequest getScoresRequest; - private void handleRulesetChange(RulesetInfo ruleset) => updateScores(); - private void handleApiStateChange(APIState oldState, APIState newState) { if (Scope == LeaderboardScope.Local) From 57fdbda16d82dc4f03d167f55a7f1db7dfd8e282 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2017 23:48:48 +0900 Subject: [PATCH 32/34] Remove unnecessary IsLoaded check We are always loaded at this point. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 50c16ed04d..3cf4166e2d 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -218,8 +218,6 @@ namespace osu.Game.Screens.Select.Leaderboards private void updateScores() { - if (!IsLoaded) return; - getScoresRequest?.Cancel(); getScoresRequest = null; Scores = null; From 5c9d4843957ddd83d6ee42ab096097f4fc7bb73b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Dec 2017 00:50:32 +0900 Subject: [PATCH 33/34] Adjust debounce a bit --- osu.Game/Screens/Select/SongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4d5101447a..68ee08e721 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -275,7 +275,7 @@ namespace osu.Game.Screens.Select if (beatmap == Beatmap.Value.BeatmapInfo) performLoad(); else - selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 100); + selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 200); } } From aa388885b7e0f2dcc7500466014dab50700572c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Dec 2017 00:53:34 +0900 Subject: [PATCH 34/34] Adjust animation slightly --- 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 3cf4166e2d..b3f2649ab6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -403,13 +403,13 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - icon.ScaleTo(0.8f, 400, Easing.OutQuint); + icon.ScaleTo(0.8f, 4000, Easing.OutQuint); return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1, 400, Easing.OutElastic); + icon.ScaleTo(1, 1000, Easing.OutElastic); return base.OnMouseUp(state, args); } }