From 72141935e84f8edd224c94224d1d18f917ff123d Mon Sep 17 00:00:00 2001
From: Jorolf <jorolf@gmx.de>
Date: Thu, 21 Sep 2017 22:07:23 +0200
Subject: [PATCH] make pagination work and remove duplication in RanksSection

---
 osu.Desktop.Tests/Visual/TestCaseUserRanks.cs | 100 --------
 osu.Game/Beatmaps/BeatmapInfo.cs              |   2 +
 .../API/Requests/GetUserScoresRequest.cs      |   6 +-
 .../Profile/Sections/Ranks/DrawableScore.cs   |   5 +-
 .../Overlays/Profile/Sections/RanksSection.cs | 236 ++++++++----------
 osu.Game/Tests/Visual/TestCaseUserRanks.cs    |  75 +-----
 osu.Game/osu.Game.csproj                      |   2 -
 7 files changed, 121 insertions(+), 305 deletions(-)
 delete mode 100644 osu.Desktop.Tests/Visual/TestCaseUserRanks.cs

diff --git a/osu.Desktop.Tests/Visual/TestCaseUserRanks.cs b/osu.Desktop.Tests/Visual/TestCaseUserRanks.cs
deleted file mode 100644
index 41f3a37e94..0000000000
--- a/osu.Desktop.Tests/Visual/TestCaseUserRanks.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Testing;
-using osu.Game.Beatmaps;
-using osu.Game.Graphics;
-using osu.Game.Overlays.Profile.Sections;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Osu.Mods;
-using osu.Game.Rulesets.Scoring;
-using System;
-using System.Collections.Generic;
-
-namespace osu.Desktop.Tests.Visual
-{
-    internal class TestCaseUserRanks : TestCase
-    {
-        public override string Description => "showing your latest achievements";
-
-        protected override void LoadComplete()
-        {
-            base.LoadComplete();
-
-            RanksSection ranks;
-
-            Add(new Container
-            {
-                AutoSizeAxes = Axes.Y,
-                RelativeSizeAxes = Axes.X,
-                Children = new Drawable[]
-                {
-                    new Box
-                    {
-                        RelativeSizeAxes = Axes.Both,
-                        Colour = OsuColour.Gray(0.2f)
-                    },
-                    ranks = new RanksSection(),
-                }
-            });
-
-            AddStep("Add Best Performances", () =>
-            {
-                List<Score> scores = new List<Score>();
-                Mod[] availableMods = { new OsuModHidden(), new OsuModFlashlight(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModPerfect() };
-                List<Mod> selectedMods = new List<Mod>(availableMods);
-                for (int i = 0; i <= availableMods.Length; i++)
-                {
-                    scores.Add(new Score
-                    {
-                        Rank = (ScoreRank) Enum.GetValues(typeof(ScoreRank)).GetValue(Enum.GetValues(typeof(ScoreRank)).Length - 1 - i),
-                        Accuracy = Math.Pow(0.99, i),
-                        PP = Math.Pow(0.5, i) * 800,
-                        Date = DateTimeOffset.UtcNow.AddDays(-Math.Pow(i, 2)),
-                        Mods = selectedMods.ToArray(),
-                        Beatmap = new BeatmapInfo
-                        {
-                            Metadata = new BeatmapMetadata
-                            {
-                                Title = "Highscore",
-                                Artist = "Panda Eyes & Teminite"
-                            },
-                            Version = "Game Over",
-                            OnlineBeatmapID = 736215,
-                        }
-                    });
-                    if(i < availableMods.Length)
-                        selectedMods.Remove(availableMods[i]);
-                }
-                ranks.ScoresBest = scores.ToArray();
-            });
-
-            AddStep("Add First Place", () => ranks.ScoresFirst = new[]
-            {
-                new Score
-                {
-                    Rank = ScoreRank.A,
-                    Accuracy = 0.735,
-                    PP = 666,
-                    Date = DateTimeOffset.UtcNow,
-                    Mods = new Mod[] { new ModAutoplay(), new ModDoubleTime(), new OsuModEasy() },
-                    Beatmap = new BeatmapInfo
-                    {
-                        Metadata = new BeatmapMetadata
-                        {
-                            Title = "FREEDOM DiVE",
-                            Artist = "xi"
-                        },
-                        Version = "FOUR DIMENSIONS",
-                        OnlineBeatmapID = 129891,
-                    }
-                }
-            });
-
-            AddStep("Show More", ((RanksSection.ScoreFlowContainer)ranks.Children[1]).ShowMore);
-        }
-    }
-}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 0776669811..5775299ffb 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -19,8 +19,10 @@ namespace osu.Game.Beatmaps
         //TODO: should be in database
         public int BeatmapVersion;
 
+        [JsonProperty("id")]
         public int? OnlineBeatmapID { get; set; }
 
+        [JsonProperty("beatmapset_id")]
         public int? OnlineBeatmapSetID { get; set; }
 
         [ForeignKey(typeof(BeatmapSetInfo))]
diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
index 20597aecc1..98db234196 100644
--- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
@@ -9,14 +9,16 @@ namespace osu.Game.Online.API.Requests
     {
         private readonly long userId;
         private readonly ScoreType type;
+        private readonly int offset;
 
-        public GetUserScoresRequest(long userId, ScoreType type)
+        public GetUserScoresRequest(long userId, ScoreType type, int offset = 0)
         {
             this.userId = userId;
             this.type = type;
+            this.offset = offset;
         }
 
-        protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLower()}";
+        protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLower()}?offset={offset}";
     }
 
     public enum ScoreType
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs
index d92ebf6dca..27df3fd0fa 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs
@@ -17,6 +17,7 @@ using osu.Framework.Localisation;
 using System.Globalization;
 using osu.Game.Rulesets.Scoring;
 using osu.Framework.Graphics.Cursor;
+using System;
 
 namespace osu.Game.Overlays.Profile.Sections.Ranks
 {
@@ -81,7 +82,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
         {
             stats.Add(new OsuSpriteText
             {
-                Text = $"{score.PP ?? 0}pp",
+                Text = $"{Math.Round(score.PP ?? 0)}pp",
                 Anchor = Anchor.TopRight,
                 Origin = Anchor.TopRight,
                 TextSize = 18,
@@ -91,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
             {
                 stats.Add(new OsuSpriteText
                 {
-                    Text = $"weighted: {(int)(score?.PP * weight ?? 0)}pp ({weight.ToString("0%", CultureInfo.CurrentCulture)})",
+                    Text = $"weighted: {Math.Round(score.PP * weight ?? 0)}pp ({weight.ToString("0%", CultureInfo.CurrentCulture)})",
                     Anchor = Anchor.TopRight,
                     Origin = Anchor.TopRight,
                     Colour = colour.GrayA,
diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs
index 4f9470bd6e..b6c56f5cb1 100644
--- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs
+++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs
@@ -15,6 +15,8 @@ using osu.Game.Online.API;
 using osu.Game.Online.API.Requests;
 using osu.Game.Rulesets;
 using osu.Game.Users;
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
 
 namespace osu.Game.Overlays.Profile.Sections
 {
@@ -24,8 +26,7 @@ namespace osu.Game.Overlays.Profile.Sections
 
         public override string Identifier => "top_ranks";
 
-        private readonly ScoreFlowContainer best, first;
-        private readonly OsuSpriteText bestMissing, firstMissing;
+        private readonly ScoreContainer best, first;
 
         private APIAccess api;
         private RulesetStore rulesets;
@@ -34,40 +35,8 @@ namespace osu.Game.Overlays.Profile.Sections
         {
             Children = new Drawable[]
             {
-                new OsuSpriteText
-                {
-                    TextSize = 15,
-                    Text = "Best Performance",
-                    Font = "Exo2.0-RegularItalic",
-                    Margin = new MarginPadding { Top = 10, Bottom = 10 },
-                },
-                best = new ScoreFlowContainer
-                {
-                    AutoSizeAxes = Axes.Y,
-                    RelativeSizeAxes = Axes.X,
-                },
-                bestMissing = new OsuSpriteText
-                {
-                    TextSize = 14,
-                    Text = "No awesome performance records yet. :(",
-                },
-                new OsuSpriteText
-                {
-                    TextSize = 15,
-                    Text = "First Place Ranks",
-                    Font = "Exo2.0-RegularItalic",
-                    Margin = new MarginPadding { Top = 20, Bottom = 10 },
-                },
-                first = new ScoreFlowContainer
-                {
-                    AutoSizeAxes = Axes.Y,
-                    RelativeSizeAxes = Axes.X,
-                },
-                firstMissing = new OsuSpriteText
-                {
-                    TextSize = 14,
-                    Text = "No awesome performance records yet. :(",
-                },
+                best = new ScoreContainer(ScoreType.Best, "Best Performance", true),
+                first = new ScoreContainer(ScoreType.Firsts, "First Place Ranks"),
             };
         }
 
@@ -88,119 +57,67 @@ namespace osu.Game.Overlays.Profile.Sections
             set
             {
                 base.User = value;
-
-                // fetch online ranks
-                foreach (ScoreType m in new[] { ScoreType.Best, ScoreType.Firsts })
-                {
-                    ScoreType thisType = m;
-                    var req = new GetUserScoresRequest(User.Id, m);
-                    req.Success += scores =>
-                    {
-                        foreach (var s in scores)
-                            s.ApplyRuleset(rulesets.GetRuleset(s.OnlineRulesetID));
-
-                        switch (thisType)
-                        {
-                            case ScoreType.Best:
-                                ScoresBest = scores;
-                                break;
-                            case ScoreType.Firsts:
-                                ScoresFirst = scores;
-                                break;
-                        }
-                    };
-
-                    Schedule(() => { api.Queue(req); });
-                }
+                best.User = value;
+                first.User = value;
             }
         }
 
-
-        public IEnumerable<Score> ScoresBest
+        private class ScoreContainer : FillFlowContainer
         {
-            set
+            private readonly FillFlowContainer<DrawableScore> scoreContainer;
+            private readonly OsuSpriteText missing;
+            private readonly OsuHoverContainer showMoreButton;
+            private readonly LoadingAnimation showMoreLoading;
+
+            private ScoreType type;
+            private int visiblePages;
+            private User user;
+            private readonly bool includeWeigth;
+
+            private RulesetStore rulesets;
+            private APIAccess api;
+
+            public User User
             {
-                best.Clear();
-                if (!value.Any())
+                set
                 {
-                    bestMissing.Show();
+                    user = value;
+                    visiblePages = 0;
+                    scoreContainer.Clear();
+                    showMoreButton.Hide();
+                    missing.Show();
+                    showMore();
                 }
-                else
-                {
-                    bestMissing.Hide();
-                    int i = 0;
-                    foreach (Score score in value)
-                    {
-                        best.Add(new DrawableScore(score, Math.Pow(0.95, i))
-                        {
-                            RelativeSizeAxes = Axes.X,
-                            Height = 60,
-                            Alpha = 0,
-                        });
-                        i++;
-                    }
-                }
-                best.ShowMore();
             }
-        }
 
-        public IEnumerable<Score> ScoresFirst
-        {
-            set
+            public ScoreContainer(ScoreType type, string header, bool includeWeigth = false)
             {
-                first.Clear();
-                if (!value.Any())
-                {
-                    firstMissing.Show();
-                }
-                else
-                {
-                    firstMissing.Hide();
-                    foreach (Score score in value)
-                        first.Add(new DrawableScore(score)
-                        {
-                            RelativeSizeAxes = Axes.X,
-                            Height = 60,
-                            Alpha = 0,
-                        });
-                }
-                first.ShowMore();
-            }
-        }
+                this.type = type;
+                this.includeWeigth = includeWeigth;
 
-        public class ScoreFlowContainer : Container<DrawableScore>
-        {
-            private FillFlowContainer<DrawableScore> scores;
-            private OsuClickableContainer showMoreText;
+                RelativeSizeAxes = Axes.X;
+                AutoSizeAxes = Axes.Y;
+                Direction = FillDirection.Vertical;
 
-            protected override Container<DrawableScore> Content => scores;
-
-            protected override void LoadComplete()
-            {
-                InternalChild = new FillFlowContainer
+                Children = new Drawable[]
                 {
-                    AutoSizeAxes = Axes.Y,
-                    RelativeSizeAxes = Axes.X,
-                    Direction = FillDirection.Vertical,
-                    Children = new Drawable[]
+                    new OsuSpriteText
                     {
-                        scores = new FillFlowContainer<DrawableScore>
-                        {
-                            AutoSizeAxes = Axes.Y,
-                            RelativeSizeAxes = Axes.X,
-                            Direction = FillDirection.Vertical,
-                        },
+                        TextSize = 15,
+                        Text = header,
+                        Font = "Exo2.0-RegularItalic",
+                        Margin = new MarginPadding { Top = 10, Bottom = 10 },
                     },
-                };
-            }
-
-            public override void Clear(bool disposeChildren)
-            {
-                base.Clear(disposeChildren);
-                if (showMoreText == null)
-                    ((FillFlowContainer)InternalChild).Add(showMoreText = new OsuHoverContainer
+                    scoreContainer = new FillFlowContainer<DrawableScore>
                     {
-                        Action = ShowMore,
+                        AutoSizeAxes = Axes.Y,
+                        RelativeSizeAxes = Axes.X,
+                        Direction = FillDirection.Vertical,
+                    },
+                    showMoreButton = new OsuHoverContainer
+                    {
+                        Alpha = 0,
+                        Action = showMore,
                         AutoSizeAxes = Axes.Both,
                         Anchor = Anchor.TopCentre,
                         Origin = Anchor.TopCentre,
@@ -209,12 +126,57 @@ namespace osu.Game.Overlays.Profile.Sections
                             TextSize = 14,
                             Text = "show more",
                         }
-                    });
-                else
-                    showMoreText.Show();
+                    },
+                    showMoreLoading = new LoadingAnimation
+                    {
+                        Anchor = Anchor.TopCentre,
+                        Origin = Anchor.TopCentre,
+                        Size = new Vector2(14),
+                    },
+                    missing = new OsuSpriteText
+                    {
+                        TextSize = 14,
+                        Text = "No awesome performance records yet. :(",
+                    },
+                };
             }
 
-            public void ShowMore() => showMoreText.Alpha = Children.Where(d => !d.IsPresent).Where((d, i) => (d.Alpha = i < 5 ? 1 : 0) == 0).Any() ? 1 : 0;
+            [BackgroundDependencyLoader]
+            private void load(APIAccess api, RulesetStore rulesets)
+            {
+                this.api = api;
+                this.rulesets = rulesets;
+            }
+
+            private void showMore()
+            {
+                var req = new GetUserScoresRequest(user.Id, type, visiblePages++ * 5);
+
+                showMoreLoading.Show();
+                showMoreButton.Hide();
+
+                req.Success += scores =>
+                {
+                    foreach (var s in scores)
+                        s.ApplyRuleset(rulesets.GetRuleset(s.OnlineRulesetID));
+
+                    showMoreButton.FadeTo(scores.Count == 5 ? 1 : 0);
+                    showMoreLoading.Hide();
+
+                    if (scores.Any())
+                    {
+                        missing.Hide();
+                        foreach (Score score in scores)
+                            scoreContainer.Add(new DrawableScore(score, includeWeigth ? Math.Pow(0.95, scoreContainer.Count) : -1)
+                            {
+                                RelativeSizeAxes = Axes.X,
+                                Height = 60,
+                            });
+                    }
+                };
+
+                Schedule(() => { api.Queue(req); });
+            }
         }
     }
 }
diff --git a/osu.Game/Tests/Visual/TestCaseUserRanks.cs b/osu.Game/Tests/Visual/TestCaseUserRanks.cs
index 41f3a37e94..e164426a4e 100644
--- a/osu.Game/Tests/Visual/TestCaseUserRanks.cs
+++ b/osu.Game/Tests/Visual/TestCaseUserRanks.cs
@@ -8,28 +8,28 @@ using osu.Framework.Testing;
 using osu.Game.Beatmaps;
 using osu.Game.Graphics;
 using osu.Game.Overlays.Profile.Sections;
+using osu.Game.Overlays.Profile.Sections.Ranks;
 using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Osu.Mods;
 using osu.Game.Rulesets.Scoring;
+using osu.Game.Users;
 using System;
 using System.Collections.Generic;
 
-namespace osu.Desktop.Tests.Visual
+namespace osu.Game.Tests.Visual
 {
-    internal class TestCaseUserRanks : TestCase
+    internal class TestCaseUserRanks : OsuTestCase
     {
         public override string Description => "showing your latest achievements";
 
-        protected override void LoadComplete()
-        {
-            base.LoadComplete();
+        public override IReadOnlyList<Type> RequiredTypes => new Type[] { typeof(DrawableScore), typeof(RanksSection) };
 
+        public TestCaseUserRanks()
+        {
             RanksSection ranks;
 
             Add(new Container
             {
-                AutoSizeAxes = Axes.Y,
-                RelativeSizeAxes = Axes.X,
+                RelativeSizeAxes = Axes.Both,
                 Children = new Drawable[]
                 {
                     new Box
@@ -37,64 +37,15 @@ namespace osu.Desktop.Tests.Visual
                         RelativeSizeAxes = Axes.Both,
                         Colour = OsuColour.Gray(0.2f)
                     },
-                    ranks = new RanksSection(),
-                }
-            });
-
-            AddStep("Add Best Performances", () =>
-            {
-                List<Score> scores = new List<Score>();
-                Mod[] availableMods = { new OsuModHidden(), new OsuModFlashlight(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModPerfect() };
-                List<Mod> selectedMods = new List<Mod>(availableMods);
-                for (int i = 0; i <= availableMods.Length; i++)
-                {
-                    scores.Add(new Score
+                    new ScrollContainer
                     {
-                        Rank = (ScoreRank) Enum.GetValues(typeof(ScoreRank)).GetValue(Enum.GetValues(typeof(ScoreRank)).Length - 1 - i),
-                        Accuracy = Math.Pow(0.99, i),
-                        PP = Math.Pow(0.5, i) * 800,
-                        Date = DateTimeOffset.UtcNow.AddDays(-Math.Pow(i, 2)),
-                        Mods = selectedMods.ToArray(),
-                        Beatmap = new BeatmapInfo
-                        {
-                            Metadata = new BeatmapMetadata
-                            {
-                                Title = "Highscore",
-                                Artist = "Panda Eyes & Teminite"
-                            },
-                            Version = "Game Over",
-                            OnlineBeatmapID = 736215,
-                        }
-                    });
-                    if(i < availableMods.Length)
-                        selectedMods.Remove(availableMods[i]);
-                }
-                ranks.ScoresBest = scores.ToArray();
-            });
-
-            AddStep("Add First Place", () => ranks.ScoresFirst = new[]
-            {
-                new Score
-                {
-                    Rank = ScoreRank.A,
-                    Accuracy = 0.735,
-                    PP = 666,
-                    Date = DateTimeOffset.UtcNow,
-                    Mods = new Mod[] { new ModAutoplay(), new ModDoubleTime(), new OsuModEasy() },
-                    Beatmap = new BeatmapInfo
-                    {
-                        Metadata = new BeatmapMetadata
-                        {
-                            Title = "FREEDOM DiVE",
-                            Artist = "xi"
-                        },
-                        Version = "FOUR DIMENSIONS",
-                        OnlineBeatmapID = 129891,
-                    }
+                        RelativeSizeAxes = Axes.Both,
+                        Child = ranks = new RanksSection(),
+                    },
                 }
             });
 
-            AddStep("Show More", ((RanksSection.ScoreFlowContainer)ranks.Children[1]).ShowMore);
+            AddStep("Show cookiezi", () => ranks.User = new User { Id = 124493 });
         }
     }
 }
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 7a78ed8dd4..a20a5ee13f 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -298,7 +298,6 @@
     <Compile Include="Graphics\Containers\OsuFocusedOverlayContainer.cs" />
     <Compile Include="Graphics\Containers\OsuHoverContainer.cs" />
     <Compile Include="Graphics\Containers\OsuScrollContainer.cs" />
-    <Compile Include="Graphics\Containers\SectionsContainer.cs" />
     <Compile Include="Graphics\Containers\OsuTextFlowContainer.cs" />
     <Compile Include="Graphics\Containers\ParallaxContainer.cs" />
     <Compile Include="Graphics\Containers\ReverseChildIDFillFlowContainer.cs" />
@@ -318,7 +317,6 @@
     <Compile Include="Graphics\UserInterface\DialogButton.cs" />
     <Compile Include="Graphics\UserInterface\FocusedTextBox.cs" />
     <Compile Include="Graphics\UserInterface\IconButton.cs" />
-    <Compile Include="Graphics\UserInterface\ProgressBar.cs" />
     <Compile Include="Graphics\UserInterface\LineGraph.cs" />
     <Compile Include="Graphics\UserInterface\LoadingAnimation.cs" />
     <Compile Include="Graphics\UserInterface\MenuItemType.cs" />