From ab462a232f50977dd22a485e2a6296bcd022d320 Mon Sep 17 00:00:00 2001
From: Matthias Coenraerds <matthias.coenraerds@remmicom.be>
Date: Fri, 4 Jan 2019 20:13:32 +0100
Subject: [PATCH 01/15] Implement clear scores on beatmap

---
 osu.Game/Scoring/ScoreManager.cs              |  4 +-
 osu.Game/Screens/Select/ClearScoreDialog.cs   | 46 +++++++++++++++++++
 .../Select/Leaderboards/BeatmapLeaderboard.cs |  2 +-
 osu.Game/Screens/Select/SongSelect.cs         | 23 +++++++---
 4 files changed, 64 insertions(+), 11 deletions(-)
 create mode 100644 osu.Game/Screens/Select/ClearScoreDialog.cs

diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 663f441f2f..fc50178ba8 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -55,9 +55,7 @@ namespace osu.Game.Scoring
 
         public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps, Files.Store);
 
-        public List<ScoreInfo> GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
-
-        public IEnumerable<ScoreInfo> QueryScores(Expression<Func<ScoreInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().Where(query);
+        public IEnumerable<ScoreInfo> GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending);
 
         public ScoreInfo Query(Expression<Func<ScoreInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
     }
diff --git a/osu.Game/Screens/Select/ClearScoreDialog.cs b/osu.Game/Screens/Select/ClearScoreDialog.cs
new file mode 100644
index 0000000000..38577902e9
--- /dev/null
+++ b/osu.Game/Screens/Select/ClearScoreDialog.cs
@@ -0,0 +1,46 @@
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Dialog;
+using osu.Game.Scoring;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace osu.Game.Screens.Select
+{
+    public class ClearScoresDialog : PopupDialog
+    {
+        private ScoreManager manager;
+
+        [BackgroundDependencyLoader]
+        private void load(ScoreManager beatmapManager)
+        {
+            manager = beatmapManager;
+        }
+
+        public ClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
+        {
+            BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
+
+            Icon = FontAwesome.fa_eraser;
+            HeaderText = $@"Clearing {scores.Count()} local score(s). Are you sure?";
+            Buttons = new PopupDialogButton[]
+            {
+                new PopupDialogOkButton
+                {
+                    Text = @"Yes. Please.",
+                    Action = () =>
+                    {
+                        manager.Delete(scores.ToList());
+                        refresh();
+                    }
+                },
+                new PopupDialogCancelButton
+                {
+                    Text = @"No, I'm still attached.",
+                },
+            };
+        }
+    }
+}
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index 9f8726c86a..8d91be9ca1 100644
--- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Leaderboards
         {
             if (Scope == BeatmapLeaderboardScope.Local)
             {
-                Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
+                Scores = scoreManager.GetAllUsableScores().Where(s => s.Beatmap.ID == Beatmap.ID).ToArray();
                 PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
                 return null;
             }
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index f65cc0e49d..e70ff42418 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -1,11 +1,6 @@
 // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using osuTK;
-using osuTK.Input;
 using osu.Framework.Allocation;
 using osu.Framework.Audio;
 using osu.Framework.Audio.Sample;
@@ -13,8 +8,8 @@ using osu.Framework.Audio.Track;
 using osu.Framework.Configuration;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
-using osu.Framework.Logging;
 using osu.Framework.Input.Events;
+using osu.Framework.Logging;
 using osu.Framework.Screens;
 using osu.Framework.Threading;
 using osu.Game.Beatmaps;
@@ -31,6 +26,11 @@ using osu.Game.Screens.Menu;
 using osu.Game.Screens.Play;
 using osu.Game.Screens.Select.Options;
 using osu.Game.Skinning;
+using osuTK;
+using osuTK.Input;
+using System;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace osu.Game.Screens.Select
 {
@@ -227,7 +227,7 @@ namespace osu.Game.Screens.Select
 
                 BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
                 BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
-                BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);
+                BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapSetInfo), Key.Number2);
             }
 
             if (this.beatmaps == null)
@@ -625,6 +625,15 @@ namespace osu.Game.Screens.Select
             dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
         }
 
+        private void clearScores(BeatmapSetInfo beatmap)
+        {
+            if (beatmap == null || beatmap.ID <= 0) return;
+
+            if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;
+
+            dialogOverlay?.Push(new ClearScoresDialog(beatmap, BeatmapDetails.Leaderboard.Scores, () => BeatmapDetails.Leaderboard.RefreshScores()));
+        }
+
         public override bool OnPressed(GlobalAction action)
         {
             if (!IsCurrentScreen) return false;

From 3879348ee4d4384680371a7b33976dab81462e76 Mon Sep 17 00:00:00 2001
From: Matthias Coenraerds <matthias.coenraerds@remmicom.be>
Date: Fri, 4 Jan 2019 20:13:32 +0100
Subject: [PATCH 02/15] Implement clear scores on beatmap

---
 osu.Game/Scoring/ScoreManager.cs              |  4 +-
 osu.Game/Screens/Select/ClearScoreDialog.cs   | 49 +++++++++++++++++++
 .../Select/Leaderboards/BeatmapLeaderboard.cs |  2 +-
 osu.Game/Screens/Select/SongSelect.cs         | 23 ++++++---
 4 files changed, 67 insertions(+), 11 deletions(-)
 create mode 100644 osu.Game/Screens/Select/ClearScoreDialog.cs

diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 663f441f2f..fc50178ba8 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -55,9 +55,7 @@ namespace osu.Game.Scoring
 
         public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps, Files.Store);
 
-        public List<ScoreInfo> GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
-
-        public IEnumerable<ScoreInfo> QueryScores(Expression<Func<ScoreInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().Where(query);
+        public IEnumerable<ScoreInfo> GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending);
 
         public ScoreInfo Query(Expression<Func<ScoreInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
     }
diff --git a/osu.Game/Screens/Select/ClearScoreDialog.cs b/osu.Game/Screens/Select/ClearScoreDialog.cs
new file mode 100644
index 0000000000..4051d76e99
--- /dev/null
+++ b/osu.Game/Screens/Select/ClearScoreDialog.cs
@@ -0,0 +1,49 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Dialog;
+using osu.Game.Scoring;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace osu.Game.Screens.Select
+{
+    public class ClearScoresDialog : PopupDialog
+    {
+        private ScoreManager manager;
+
+        [BackgroundDependencyLoader]
+        private void load(ScoreManager beatmapManager)
+        {
+            manager = beatmapManager;
+        }
+
+        public ClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
+        {
+            BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
+
+            Icon = FontAwesome.fa_eraser;
+            HeaderText = $@"Clearing {scores.Count()} local score(s). Are you sure?";
+            Buttons = new PopupDialogButton[]
+            {
+                new PopupDialogOkButton
+                {
+                    Text = @"Yes. Please.",
+                    Action = () =>
+                    {
+                        manager.Delete(scores.ToList());
+                        refresh();
+                    }
+                },
+                new PopupDialogCancelButton
+                {
+                    Text = @"No, I'm still attached.",
+                },
+            };
+        }
+    }
+}
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index 9f8726c86a..8d91be9ca1 100644
--- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Leaderboards
         {
             if (Scope == BeatmapLeaderboardScope.Local)
             {
-                Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
+                Scores = scoreManager.GetAllUsableScores().Where(s => s.Beatmap.ID == Beatmap.ID).ToArray();
                 PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
                 return null;
             }
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index f65cc0e49d..e70ff42418 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -1,11 +1,6 @@
 // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using osuTK;
-using osuTK.Input;
 using osu.Framework.Allocation;
 using osu.Framework.Audio;
 using osu.Framework.Audio.Sample;
@@ -13,8 +8,8 @@ using osu.Framework.Audio.Track;
 using osu.Framework.Configuration;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
-using osu.Framework.Logging;
 using osu.Framework.Input.Events;
+using osu.Framework.Logging;
 using osu.Framework.Screens;
 using osu.Framework.Threading;
 using osu.Game.Beatmaps;
@@ -31,6 +26,11 @@ using osu.Game.Screens.Menu;
 using osu.Game.Screens.Play;
 using osu.Game.Screens.Select.Options;
 using osu.Game.Skinning;
+using osuTK;
+using osuTK.Input;
+using System;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace osu.Game.Screens.Select
 {
@@ -227,7 +227,7 @@ namespace osu.Game.Screens.Select
 
                 BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
                 BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
-                BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);
+                BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapSetInfo), Key.Number2);
             }
 
             if (this.beatmaps == null)
@@ -625,6 +625,15 @@ namespace osu.Game.Screens.Select
             dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
         }
 
+        private void clearScores(BeatmapSetInfo beatmap)
+        {
+            if (beatmap == null || beatmap.ID <= 0) return;
+
+            if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;
+
+            dialogOverlay?.Push(new ClearScoresDialog(beatmap, BeatmapDetails.Leaderboard.Scores, () => BeatmapDetails.Leaderboard.RefreshScores()));
+        }
+
         public override bool OnPressed(GlobalAction action)
         {
             if (!IsCurrentScreen) return false;

From 6d44672bfa35769d550863f63d2e537c7b94bc5f Mon Sep 17 00:00:00 2001
From: Matthias Coenraerds <matthias.coenraerds@remmicom.be>
Date: Fri, 4 Jan 2019 20:32:52 +0100
Subject: [PATCH 03/15] Filename does not match contained type

---
 .../{ClearScoreDialog.cs => BeatmapClearScoresDialog.cs}      | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
 rename osu.Game/Screens/Select/{ClearScoreDialog.cs => BeatmapClearScoresDialog.cs} (88%)

diff --git a/osu.Game/Screens/Select/ClearScoreDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
similarity index 88%
rename from osu.Game/Screens/Select/ClearScoreDialog.cs
rename to osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index 4051d76e99..e14fae56a5 100644
--- a/osu.Game/Screens/Select/ClearScoreDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -12,7 +12,7 @@ using System.Linq;
 
 namespace osu.Game.Screens.Select
 {
-    public class ClearScoresDialog : PopupDialog
+    public class BeatmapClearScoresDialog : PopupDialog
     {
         private ScoreManager manager;
 
@@ -22,7 +22,7 @@ namespace osu.Game.Screens.Select
             manager = beatmapManager;
         }
 
-        public ClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
+        public BeatmapClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
         {
             BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
 

From a93c26ccfd50d93fdc1cfcbd49f2562f6252ce7a Mon Sep 17 00:00:00 2001
From: Matthias Coenraerds <matthias.coenraerds@remmicom.be>
Date: Fri, 4 Jan 2019 20:32:52 +0100
Subject: [PATCH 04/15] Filename does not match contained type

---
 .../{ClearScoreDialog.cs => BeatmapClearScoresDialog.cs}      | 4 ++--
 osu.Game/Screens/Select/SongSelect.cs                         | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
 rename osu.Game/Screens/Select/{ClearScoreDialog.cs => BeatmapClearScoresDialog.cs} (88%)

diff --git a/osu.Game/Screens/Select/ClearScoreDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
similarity index 88%
rename from osu.Game/Screens/Select/ClearScoreDialog.cs
rename to osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index 4051d76e99..e14fae56a5 100644
--- a/osu.Game/Screens/Select/ClearScoreDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -12,7 +12,7 @@ using System.Linq;
 
 namespace osu.Game.Screens.Select
 {
-    public class ClearScoresDialog : PopupDialog
+    public class BeatmapClearScoresDialog : PopupDialog
     {
         private ScoreManager manager;
 
@@ -22,7 +22,7 @@ namespace osu.Game.Screens.Select
             manager = beatmapManager;
         }
 
-        public ClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
+        public BeatmapClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
         {
             BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
 
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index e70ff42418..c194a191a6 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -631,7 +631,7 @@ namespace osu.Game.Screens.Select
 
             if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;
 
-            dialogOverlay?.Push(new ClearScoresDialog(beatmap, BeatmapDetails.Leaderboard.Scores, () => BeatmapDetails.Leaderboard.RefreshScores()));
+            dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, BeatmapDetails.Leaderboard.Scores, () => BeatmapDetails.Leaderboard.RefreshScores()));
         }
 
         public override bool OnPressed(GlobalAction action)

From 472325b885b4ba54e2b9c4a7383c0d2997298d51 Mon Sep 17 00:00:00 2001
From: Matthias Coenraerds <matthias.coenraerds@remmicom.be>
Date: Sun, 6 Jan 2019 13:37:30 +0100
Subject: [PATCH 05/15] Verify leaderboard scope to be local

---
 osu.Game/Screens/Select/SongSelect.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index c194a191a6..d651243a51 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -24,6 +24,7 @@ using osu.Game.Screens.Backgrounds;
 using osu.Game.Screens.Edit;
 using osu.Game.Screens.Menu;
 using osu.Game.Screens.Play;
+using osu.Game.Screens.Select.Leaderboards;
 using osu.Game.Screens.Select.Options;
 using osu.Game.Skinning;
 using osuTK;
@@ -627,6 +628,8 @@ namespace osu.Game.Screens.Select
 
         private void clearScores(BeatmapSetInfo beatmap)
         {
+            if (BeatmapDetails.Leaderboard.Scope != BeatmapLeaderboardScope.Local) return;
+
             if (beatmap == null || beatmap.ID <= 0) return;
 
             if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;

From 562c9e56fbda6c85dc2ab98402296ec461232eeb Mon Sep 17 00:00:00 2001
From: Matthias Coenraerds <matthias.coenraerds@remmicom.be>
Date: Sun, 6 Jan 2019 13:38:43 +0100
Subject: [PATCH 06/15] Fix naming

---
 osu.Game/Screens/Select/BeatmapClearScoresDialog.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index e14fae56a5..45a192d2ad 100644
--- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -17,9 +17,9 @@ namespace osu.Game.Screens.Select
         private ScoreManager manager;
 
         [BackgroundDependencyLoader]
-        private void load(ScoreManager beatmapManager)
+        private void load(ScoreManager scoreManager)
         {
-            manager = beatmapManager;
+            manager = scoreManager;
         }
 
         public BeatmapClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)

From 097062caf78bd7985fa4d1af9b675743191a6671 Mon Sep 17 00:00:00 2001
From: Microgolf <matthias.coenraerds@remmicom.be>
Date: Tue, 8 Jan 2019 17:57:03 +0100
Subject: [PATCH 07/15] Addresses requested changes

---
 osu.Game/Scoring/ScoreManager.cs                           | 4 +++-
 osu.Game/Screens/Select/BeatmapClearScoresDialog.cs        | 7 +++----
 osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 2 +-
 osu.Game/Screens/Select/SongSelect.cs                      | 6 +++---
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index fc50178ba8..663f441f2f 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -55,7 +55,9 @@ namespace osu.Game.Scoring
 
         public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps, Files.Store);
 
-        public IEnumerable<ScoreInfo> GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending);
+        public List<ScoreInfo> GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
+
+        public IEnumerable<ScoreInfo> QueryScores(Expression<Func<ScoreInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().Where(query);
 
         public ScoreInfo Query(Expression<Func<ScoreInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
     }
diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index 45a192d2ad..c99e9eb428 100644
--- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -22,12 +22,11 @@ namespace osu.Game.Screens.Select
             manager = scoreManager;
         }
 
-        public BeatmapClearScoresDialog(BeatmapSetInfo beatmap, IEnumerable<ScoreInfo> scores, Action refresh)
+        public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action refresh)
         {
             BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
-
             Icon = FontAwesome.fa_eraser;
-            HeaderText = $@"Clearing {scores.Count()} local score(s). Are you sure?";
+            HeaderText = $@"Clearing all local scores. Are you sure?";
             Buttons = new PopupDialogButton[]
             {
                 new PopupDialogOkButton
@@ -35,7 +34,7 @@ namespace osu.Game.Screens.Select
                     Text = @"Yes. Please.",
                     Action = () =>
                     {
-                        manager.Delete(scores.ToList());
+                        manager.Delete(manager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList());
                         refresh();
                     }
                 },
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index 8d91be9ca1..0ecf36fddd 100644
--- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Leaderboards
         {
             if (Scope == BeatmapLeaderboardScope.Local)
             {
-                Scores = scoreManager.GetAllUsableScores().Where(s => s.Beatmap.ID == Beatmap.ID).ToArray();
+                Scores = scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID).ToArray();
                 PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
                 return null;
             }
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index d651243a51..9d8e58a496 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -228,7 +228,7 @@ namespace osu.Game.Screens.Select
 
                 BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
                 BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
-                BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapSetInfo), Key.Number2);
+                BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number2);
             }
 
             if (this.beatmaps == null)
@@ -626,7 +626,7 @@ namespace osu.Game.Screens.Select
             dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
         }
 
-        private void clearScores(BeatmapSetInfo beatmap)
+        private void clearScores(BeatmapInfo beatmap)
         {
             if (BeatmapDetails.Leaderboard.Scope != BeatmapLeaderboardScope.Local) return;
 
@@ -634,7 +634,7 @@ namespace osu.Game.Screens.Select
 
             if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;
 
-            dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, BeatmapDetails.Leaderboard.Scores, () => BeatmapDetails.Leaderboard.RefreshScores()));
+            dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () => BeatmapDetails.Leaderboard.RefreshScores()));
         }
 
         public override bool OnPressed(GlobalAction action)

From 67928ac1fe6e5dc170e1893e1d96e438e91830cf Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 09:49:11 +0900
Subject: [PATCH 08/15] Remove pointless check

---
 osu.Game/Screens/Select/SongSelect.cs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 9d8e58a496..5d63524001 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -628,8 +628,6 @@ namespace osu.Game.Screens.Select
 
         private void clearScores(BeatmapInfo beatmap)
         {
-            if (BeatmapDetails.Leaderboard.Scope != BeatmapLeaderboardScope.Local) return;
-
             if (beatmap == null || beatmap.ID <= 0) return;
 
             if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;

From 80b5f1c5239277c0003c9bbedaf85be7832da620 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 09:49:36 +0900
Subject: [PATCH 09/15] Fix code formatting issues

---
 osu.Game/Screens/Select/BeatmapClearScoresDialog.cs | 3 +--
 osu.Game/Screens/Select/SongSelect.cs               | 1 -
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index c99e9eb428..aec6211076 100644
--- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -7,7 +7,6 @@ using osu.Game.Graphics;
 using osu.Game.Overlays.Dialog;
 using osu.Game.Scoring;
 using System;
-using System.Collections.Generic;
 using System.Linq;
 
 namespace osu.Game.Screens.Select
@@ -26,7 +25,7 @@ namespace osu.Game.Screens.Select
         {
             BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
             Icon = FontAwesome.fa_eraser;
-            HeaderText = $@"Clearing all local scores. Are you sure?";
+            HeaderText = @"Clearing all local scores. Are you sure?";
             Buttons = new PopupDialogButton[]
             {
                 new PopupDialogOkButton
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 5d63524001..6697c1cebc 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -24,7 +24,6 @@ using osu.Game.Screens.Backgrounds;
 using osu.Game.Screens.Edit;
 using osu.Game.Screens.Menu;
 using osu.Game.Screens.Play;
-using osu.Game.Screens.Select.Leaderboards;
 using osu.Game.Screens.Select.Options;
 using osu.Game.Skinning;
 using osuTK;

From acc2113027edb05b31171aca8e2f0d0c7b7a9cbe Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 10:09:03 +0900
Subject: [PATCH 10/15] Make operation run asynchronously

---
 .../Select/BeatmapClearScoresDialog.cs        | 21 ++++++++++---------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index aec6211076..d720a8c69a 100644
--- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -8,20 +8,15 @@ using osu.Game.Overlays.Dialog;
 using osu.Game.Scoring;
 using System;
 using System.Linq;
+using System.Threading.Tasks;
 
 namespace osu.Game.Screens.Select
 {
     public class BeatmapClearScoresDialog : PopupDialog
     {
-        private ScoreManager manager;
+        private ScoreManager scoreManager;
 
-        [BackgroundDependencyLoader]
-        private void load(ScoreManager scoreManager)
-        {
-            manager = scoreManager;
-        }
-
-        public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action refresh)
+        public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action onCompletion)
         {
             BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
             Icon = FontAwesome.fa_eraser;
@@ -33,8 +28,8 @@ namespace osu.Game.Screens.Select
                     Text = @"Yes. Please.",
                     Action = () =>
                     {
-                        manager.Delete(manager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList());
-                        refresh();
+                        Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList()))
+                            .ContinueWith(t => Schedule(onCompletion));
                     }
                 },
                 new PopupDialogCancelButton
@@ -43,5 +38,11 @@ namespace osu.Game.Screens.Select
                 },
             };
         }
+
+        [BackgroundDependencyLoader]
+        private void load(ScoreManager scoreManager)
+        {
+            this.scoreManager = scoreManager;
+        }
     }
 }

From d4041d5d4225c0ec3b1999c93a8545dd540f3db3 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 10:25:21 +0900
Subject: [PATCH 11/15] Automate includes of files in ArchiveModelManager use
 cases

---
 osu.Game/Beatmaps/BeatmapStore.cs             | 11 ++++-----
 osu.Game/Database/ArchiveModelManager.cs      |  2 +-
 ...ableDatabaseBackedStoreWithFileIncludes.cs | 23 +++++++++++++++++++
 osu.Game/Scoring/ScoreStore.cs                |  3 +--
 osu.Game/Skinning/SkinStore.cs                |  8 +------
 5 files changed, 31 insertions(+), 16 deletions(-)
 create mode 100644 osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs

diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs
index 6786a780b6..f4b7b1d74f 100644
--- a/osu.Game/Beatmaps/BeatmapStore.cs
+++ b/osu.Game/Beatmaps/BeatmapStore.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
     /// <summary>
     /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing
     /// </summary>
-    public class BeatmapStore : MutableDatabaseBackedStore<BeatmapSetInfo>
+    public class BeatmapStore : MutableDatabaseBackedStoreWithFileIncludes<BeatmapSetInfo, BeatmapSetFileInfo>
     {
         public event Action<BeatmapInfo> BeatmapHidden;
         public event Action<BeatmapInfo> BeatmapRestored;
@@ -64,18 +64,17 @@ namespace osu.Game.Beatmaps
 
         protected override IQueryable<BeatmapSetInfo> AddIncludesForDeletion(IQueryable<BeatmapSetInfo> query) =>
             base.AddIncludesForDeletion(query)
-                .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
-                .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
                 .Include(s => s.Metadata)
-                .Include(s => s.Beatmaps).ThenInclude(b => b.Scores);
+                .Include(s => s.Beatmaps).ThenInclude(b => b.Scores)
+                .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
+                .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata);
 
         protected override IQueryable<BeatmapSetInfo> AddIncludesForConsumption(IQueryable<BeatmapSetInfo> query) =>
             base.AddIncludesForConsumption(query)
                 .Include(s => s.Metadata)
                 .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
                 .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
-                .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
-                .Include(s => s.Files).ThenInclude(f => f.FileInfo);
+                .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata);
 
         protected override void Purge(List<BeatmapSetInfo> items, OsuDbContext context)
         {
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 86c97df191..5b4a191682 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -108,7 +108,7 @@ namespace osu.Game.Database
                 a.Invoke();
         }
 
-        protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore<TModel> modelStore, IIpcHost importHost = null)
+        protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
         {
             ContextFactory = contextFactory;
 
diff --git a/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
new file mode 100644
index 0000000000..3419a87507
--- /dev/null
+++ b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
@@ -0,0 +1,23 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+using osu.Framework.Platform;
+
+namespace osu.Game.Database
+{
+    public abstract class MutableDatabaseBackedStoreWithFileIncludes<T, U> : MutableDatabaseBackedStore<T>
+        where T : class, IHasPrimaryKey, ISoftDelete, IHasFiles<U>
+        where U : INamedFileInfo
+    {
+        protected MutableDatabaseBackedStoreWithFileIncludes(IDatabaseContextFactory contextFactory, Storage storage = null)
+            : base(contextFactory, storage)
+        {
+        }
+
+        protected override IQueryable<T> AddIncludesForConsumption(IQueryable<T> query) =>
+            base.AddIncludesForConsumption(query)
+                .Include(s => s.Files).ThenInclude(f => f.FileInfo);
+    }
+}
diff --git a/osu.Game/Scoring/ScoreStore.cs b/osu.Game/Scoring/ScoreStore.cs
index 745bb6cc29..9627481f4d 100644
--- a/osu.Game/Scoring/ScoreStore.cs
+++ b/osu.Game/Scoring/ScoreStore.cs
@@ -8,7 +8,7 @@ using osu.Game.Database;
 
 namespace osu.Game.Scoring
 {
-    public class ScoreStore : MutableDatabaseBackedStore<ScoreInfo>
+    public class ScoreStore : MutableDatabaseBackedStoreWithFileIncludes<ScoreInfo, ScoreFileInfo>
     {
         public ScoreStore(IDatabaseContextFactory factory, Storage storage)
             : base(factory, storage)
@@ -17,7 +17,6 @@ namespace osu.Game.Scoring
 
         protected override IQueryable<ScoreInfo> AddIncludesForConsumption(IQueryable<ScoreInfo> query)
             => base.AddIncludesForConsumption(query)
-                   .Include(s => s.Files).ThenInclude(f => f.FileInfo)
                    .Include(s => s.Beatmap)
                    .Include(s => s.Ruleset);
     }
diff --git a/osu.Game/Skinning/SkinStore.cs b/osu.Game/Skinning/SkinStore.cs
index e80b03b935..31cadb0a24 100644
--- a/osu.Game/Skinning/SkinStore.cs
+++ b/osu.Game/Skinning/SkinStore.cs
@@ -1,22 +1,16 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
-using System.Linq;
-using Microsoft.EntityFrameworkCore;
 using osu.Framework.Platform;
 using osu.Game.Database;
 
 namespace osu.Game.Skinning
 {
-    public class SkinStore : MutableDatabaseBackedStore<SkinInfo>
+    public class SkinStore : MutableDatabaseBackedStoreWithFileIncludes<SkinInfo, SkinFileInfo>
     {
         public SkinStore(DatabaseContextFactory contextFactory, Storage storage = null)
             : base(contextFactory, storage)
         {
         }
-
-        protected override IQueryable<SkinInfo> AddIncludesForConsumption(IQueryable<SkinInfo> query) =>
-            base.AddIncludesForConsumption(query)
-                .Include(s => s.Files).ThenInclude(f => f.FileInfo);
     }
 }

From 0300f0d665dc8886f3f7f2f8a10e8df98ffe5914 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 10:47:45 +0900
Subject: [PATCH 12/15] Ensure deletions are correct without relying on FK
 cascade rule

---
 .../Database/MutableDatabaseBackedStoreWithFileIncludes.cs    | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
index 3419a87507..5d6ff6b09b 100644
--- a/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
+++ b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
@@ -19,5 +19,9 @@ namespace osu.Game.Database
         protected override IQueryable<T> AddIncludesForConsumption(IQueryable<T> query) =>
             base.AddIncludesForConsumption(query)
                 .Include(s => s.Files).ThenInclude(f => f.FileInfo);
+
+        protected override IQueryable<T> AddIncludesForDeletion(IQueryable<T> query) =>
+            base.AddIncludesForDeletion(query)
+                .Include(s => s.Files); // don't include FileInfo. these are handled by the FileStore itself.
     }
 }

From 49cbaecf4c8f046c5f4058878ac0e4f41206a7dd Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 10:52:53 +0900
Subject: [PATCH 13/15] Update licence header

---
 osu.Game/Screens/Select/BeatmapClearScoresDialog.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index d720a8c69a..5fa50e00b9 100644
--- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -1,5 +1,5 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
 
 using osu.Framework.Allocation;
 using osu.Game.Beatmaps;

From 19ce1f28696616e224467a5e450d103151fa86c0 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 13:43:16 +0900
Subject: [PATCH 14/15] Remove second conditional

---
 osu.Game/Screens/Select/SongSelect.cs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index fd3c4e003e..c794d32d49 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -625,8 +625,6 @@ namespace osu.Game.Screens.Select
         {
             if (beatmap == null || beatmap.ID <= 0) return;
 
-            if (BeatmapDetails.Leaderboard.Scores == null || !BeatmapDetails.Leaderboard.Scores.Any()) return;
-
             dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () => BeatmapDetails.Leaderboard.RefreshScores()));
         }
 

From c01990d00587ba3f652ae4623c8a2d13de562860 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 1 Mar 2019 20:52:34 +0900
Subject: [PATCH 15/15] Fix callback potentially not getting fired

---
 osu.Game/Screens/Select/BeatmapClearScoresDialog.cs | 2 +-
 osu.Game/Screens/Select/SongSelect.cs               | 4 +++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
index 5fa50e00b9..a37327f2c3 100644
--- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Screens.Select
                     Action = () =>
                     {
                         Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList()))
-                            .ContinueWith(t => Schedule(onCompletion));
+                            .ContinueWith(_ => onCompletion);
                     }
                 },
                 new PopupDialogCancelButton
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index c794d32d49..866c7e0926 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -625,7 +625,9 @@ namespace osu.Game.Screens.Select
         {
             if (beatmap == null || beatmap.ID <= 0) return;
 
-            dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () => BeatmapDetails.Leaderboard.RefreshScores()));
+            dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () =>
+                // schedule done here rather than inside the dialog as the dialog may fade out and never callback.
+                Schedule(() => BeatmapDetails.Leaderboard.RefreshScores())));
         }
 
         public override bool OnPressed(GlobalAction action)