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
///
/// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing
///
- public class BeatmapStore : MutableDatabaseBackedStore
+ public class BeatmapStore : MutableDatabaseBackedStoreWithFileIncludes
{
public event Action BeatmapHidden;
public event Action BeatmapRestored;
@@ -64,18 +64,17 @@ namespace osu.Game.Beatmaps
protected override IQueryable AddIncludesForDeletion(IQueryable 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 AddIncludesForConsumption(IQueryable 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 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 modelStore, IIpcHost importHost = null)
+ protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes 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..5d6ff6b09b
--- /dev/null
+++ b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
@@ -0,0 +1,27 @@
+// Copyright (c) ppy Pty Ltd . 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 : MutableDatabaseBackedStore
+ where T : class, IHasPrimaryKey, ISoftDelete, IHasFiles
+ where U : INamedFileInfo
+ {
+ protected MutableDatabaseBackedStoreWithFileIncludes(IDatabaseContextFactory contextFactory, Storage storage = null)
+ : base(contextFactory, storage)
+ {
+ }
+
+ protected override IQueryable AddIncludesForConsumption(IQueryable query) =>
+ base.AddIncludesForConsumption(query)
+ .Include(s => s.Files).ThenInclude(f => f.FileInfo);
+
+ protected override IQueryable AddIncludesForDeletion(IQueryable query) =>
+ base.AddIncludesForDeletion(query)
+ .Include(s => s.Files); // don't include FileInfo. these are handled by the FileStore itself.
+ }
+}
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
+ public class ScoreStore : MutableDatabaseBackedStoreWithFileIncludes
{
public ScoreStore(IDatabaseContextFactory factory, Storage storage)
: base(factory, storage)
@@ -17,7 +17,6 @@ namespace osu.Game.Scoring
protected override IQueryable AddIncludesForConsumption(IQueryable 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/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
new file mode 100644
index 0000000000..a37327f2c3
--- /dev/null
+++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs
@@ -0,0 +1,48 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+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 scoreManager;
+
+ public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action onCompletion)
+ {
+ BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
+ Icon = FontAwesome.fa_eraser;
+ HeaderText = @"Clearing all local scores. Are you sure?";
+ Buttons = new PopupDialogButton[]
+ {
+ new PopupDialogOkButton
+ {
+ Text = @"Yes. Please.",
+ Action = () =>
+ {
+ Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList()))
+ .ContinueWith(_ => onCompletion);
+ }
+ },
+ new PopupDialogCancelButton
+ {
+ Text = @"No, I'm still attached.",
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ScoreManager scoreManager)
+ {
+ this.scoreManager = scoreManager;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index adf989ee62..61370f7ce3 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.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 0c5871f483..866c7e0926 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -1,11 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
-using System.Collections.Generic;
-using 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.Bindables;
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,7 +26,12 @@ using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Options;
using osu.Game.Skinning;
+using osuTK;
using osuTK.Graphics;
+using osuTK.Input;
+using System;
+using System.Collections.Generic;
+using System.Linq;
namespace osu.Game.Screens.Select
{
@@ -226,7 +226,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.BeatmapInfo), Key.Number2);
}
if (this.beatmaps == null)
@@ -621,6 +621,15 @@ namespace osu.Game.Screens.Select
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
}
+ private void clearScores(BeatmapInfo beatmap)
+ {
+ if (beatmap == null || beatmap.ID <= 0) return;
+
+ 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)
{
if (!this.IsCurrentScreen()) return false;
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 . 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
+ public class SkinStore : MutableDatabaseBackedStoreWithFileIncludes
{
public SkinStore(DatabaseContextFactory contextFactory, Storage storage = null)
: base(contextFactory, storage)
{
}
-
- protected override IQueryable AddIncludesForConsumption(IQueryable query) =>
- base.AddIncludesForConsumption(query)
- .Include(s => s.Files).ThenInclude(f => f.FileInfo);
}
}