Remove CollectionManager

This commit is contained in:
Dean Herbert 2022-07-27 15:59:36 +09:00
parent 6b73f7c7ec
commit 9c543fef48
19 changed files with 276 additions and 292 deletions

View File

@ -5,12 +5,14 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
@ -30,7 +32,11 @@ namespace osu.Game.Tests.Collections.IO
await importCollectionsFromStream(osu, new MemoryStream()); await importCollectionsFromStream(osu, new MemoryStream());
Assert.That(osu.CollectionManager.Collections.Count, Is.Zero); osu.Realm.Run(realm =>
{
var collections = realm.All<RealmBeatmapCollection>().ToList();
Assert.That(collections.Count, Is.Zero);
});
} }
finally finally
{ {
@ -50,18 +56,22 @@ namespace osu.Game.Tests.Collections.IO
await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db")); await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db"));
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2)); osu.Realm.Run(realm =>
{
var collections = realm.All<RealmBeatmapCollection>().ToList();
Assert.That(collections.Count, Is.EqualTo(2));
// Even with no beatmaps imported, collections are tracking the hashes and will continue to. // Even with no beatmaps imported, collections are tracking the hashes and will continue to.
// In the future this whole mechanism will be replaced with having the collections in realm, // In the future this whole mechanism will be replaced with having the collections in realm,
// but until that happens it makes rough sense that we want to track not-yet-imported beatmaps // but until that happens it makes rough sense that we want to track not-yet-imported beatmaps
// and have them associate with collections if/when they become available. // and have them associate with collections if/when they become available.
Assert.That(osu.CollectionManager.Collections[0].Name.Value, Is.EqualTo("First")); Assert.That(collections[0].Name, Is.EqualTo("First"));
Assert.That(osu.CollectionManager.Collections[0].BeatmapHashes.Count, Is.EqualTo(1)); Assert.That(collections[0].BeatmapMD5Hashes.Count, Is.EqualTo(1));
Assert.That(osu.CollectionManager.Collections[1].Name.Value, Is.EqualTo("Second")); Assert.That(collections[1].Name, Is.EqualTo("Second"));
Assert.That(osu.CollectionManager.Collections[1].BeatmapHashes.Count, Is.EqualTo(12)); Assert.That(collections[1].BeatmapMD5Hashes.Count, Is.EqualTo(12));
});
} }
finally finally
{ {
@ -81,13 +91,18 @@ namespace osu.Game.Tests.Collections.IO
await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db")); await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db"));
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2)); osu.Realm.Run(realm =>
{
var collections = realm.All<RealmBeatmapCollection>().ToList();
Assert.That(osu.CollectionManager.Collections[0].Name.Value, Is.EqualTo("First")); Assert.That(collections.Count, Is.EqualTo(2));
Assert.That(osu.CollectionManager.Collections[0].BeatmapHashes.Count, Is.EqualTo(1));
Assert.That(osu.CollectionManager.Collections[1].Name.Value, Is.EqualTo("Second")); Assert.That(collections[0].Name, Is.EqualTo("First"));
Assert.That(osu.CollectionManager.Collections[1].BeatmapHashes.Count, Is.EqualTo(12)); Assert.That(collections[0].BeatmapMD5Hashes.Count, Is.EqualTo(1));
Assert.That(collections[1].Name, Is.EqualTo("Second"));
Assert.That(collections[1].BeatmapMD5Hashes.Count, Is.EqualTo(12));
});
} }
finally finally
{ {
@ -124,7 +139,11 @@ namespace osu.Game.Tests.Collections.IO
} }
Assert.That(exceptionThrown, Is.False); Assert.That(exceptionThrown, Is.False);
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(0)); osu.Realm.Run(realm =>
{
var collections = realm.All<RealmBeatmapCollection>().ToList();
Assert.That(collections.Count, Is.EqualTo(0));
});
} }
finally finally
{ {
@ -149,12 +168,18 @@ namespace osu.Game.Tests.Collections.IO
await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db")); await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db"));
// Move first beatmap from second collection into the first. // ReSharper disable once MethodHasAsyncOverload
osu.CollectionManager.Collections[0].BeatmapHashes.Add(osu.CollectionManager.Collections[1].BeatmapHashes[0]); osu.Realm.Write(realm =>
osu.CollectionManager.Collections[1].BeatmapHashes.RemoveAt(0); {
var collections = realm.All<RealmBeatmapCollection>().ToList();
// Rename the second collecction. // Move first beatmap from second collection into the first.
osu.CollectionManager.Collections[1].Name.Value = "Another"; collections[0].BeatmapMD5Hashes.Add(collections[1].BeatmapMD5Hashes[0]);
collections[1].BeatmapMD5Hashes.RemoveAt(0);
// Rename the second collecction.
collections[1].Name = "Another";
});
} }
finally finally
{ {
@ -169,13 +194,17 @@ namespace osu.Game.Tests.Collections.IO
{ {
var osu = LoadOsuIntoHost(host, true); var osu = LoadOsuIntoHost(host, true);
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2)); osu.Realm.Run(realm =>
{
var collections = realm.All<RealmBeatmapCollection>().ToList();
Assert.That(collections.Count, Is.EqualTo(2));
Assert.That(osu.CollectionManager.Collections[0].Name.Value, Is.EqualTo("First")); Assert.That(collections[0].Name, Is.EqualTo("First"));
Assert.That(osu.CollectionManager.Collections[0].BeatmapHashes.Count, Is.EqualTo(2)); Assert.That(collections[0].BeatmapMD5Hashes.Count, Is.EqualTo(2));
Assert.That(osu.CollectionManager.Collections[1].Name.Value, Is.EqualTo("Another")); Assert.That(collections[1].Name, Is.EqualTo("Another"));
Assert.That(osu.CollectionManager.Collections[1].BeatmapHashes.Count, Is.EqualTo(11)); Assert.That(collections[1].BeatmapMD5Hashes.Count, Is.EqualTo(11));
});
} }
finally finally
{ {
@ -188,7 +217,7 @@ namespace osu.Game.Tests.Collections.IO
{ {
// intentionally spin this up on a separate task to avoid disposal deadlocks. // intentionally spin this up on a separate task to avoid disposal deadlocks.
// see https://github.com/EventStore/EventStore/issues/1179 // see https://github.com/EventStore/EventStore/issues/1179
await Task.Factory.StartNew(() => new LegacyCollectionImporter(osu.CollectionManager).Import(stream).WaitSafely(), TaskCreationOptions.LongRunning); await Task.Factory.StartNew(() => new LegacyCollectionImporter(osu.Realm).Import(stream).WaitSafely(), TaskCreationOptions.LongRunning);
} }
} }
} }

View File

@ -10,7 +10,7 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Collections; using osu.Game.Database;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
namespace osu.Game.Tests namespace osu.Game.Tests
@ -47,7 +47,7 @@ namespace osu.Game.Tests
public class TestOsuGameBase : OsuGameBase public class TestOsuGameBase : OsuGameBase
{ {
public CollectionManager CollectionManager { get; private set; } public RealmAccess Realm => Dependencies.Get<RealmAccess>();
private readonly bool withBeatmap; private readonly bool withBeatmap;
@ -62,8 +62,6 @@ namespace osu.Game.Tests
// Beatmap must be imported before the collection manager is loaded. // Beatmap must be imported before the collection manager is loaded.
if (withBeatmap) if (withBeatmap)
BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely(); BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely();
AddInternal(CollectionManager = new CollectionManager());
} }
} }
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -27,13 +25,10 @@ namespace osu.Game.Tests.Visual.Collections
{ {
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
private DialogOverlay dialogOverlay; private DialogOverlay dialogOverlay = null!;
private CollectionManager manager; private RulesetStore rulesets = null!;
private BeatmapManager beatmapManager = null!;
private RulesetStore rulesets; private ManageCollectionsDialog dialog = null!;
private BeatmapManager beatmapManager;
private ManageCollectionsDialog dialog;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host) private void load(GameHost host)
@ -46,19 +41,17 @@ namespace osu.Game.Tests.Visual.Collections
base.Content.AddRange(new Drawable[] base.Content.AddRange(new Drawable[]
{ {
manager = new CollectionManager(),
Content, Content,
dialogOverlay = new DialogOverlay(), dialogOverlay = new DialogOverlay(),
}); });
Dependencies.Cache(manager);
Dependencies.CacheAs<IDialogOverlay>(dialogOverlay); Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
} }
[SetUp] [SetUp]
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>
{ {
manager.Collections.Clear(); Realm.Write(r => r.RemoveAll<RealmBeatmapCollection>());
Child = dialog = new ManageCollectionsDialog(); Child = dialog = new ManageCollectionsDialog();
}); });
@ -78,17 +71,17 @@ namespace osu.Game.Tests.Visual.Collections
[Test] [Test]
public void TestLastItemIsPlaceholder() public void TestLastItemIsPlaceholder()
{ {
AddAssert("last item is placeholder", () => !manager.Collections.Contains(dialog.ChildrenOfType<DrawableCollectionListItem>().Last().Model)); AddAssert("last item is placeholder", () => !dialog.ChildrenOfType<DrawableCollectionListItem>().Last().Model.IsManaged);
} }
[Test] [Test]
public void TestAddCollectionExternal() public void TestAddCollectionExternal()
{ {
AddStep("add collection", () => manager.Collections.Add(new BeatmapCollection { Name = { Value = "First collection" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "First collection"))));
assertCollectionCount(1); assertCollectionCount(1);
assertCollectionName(0, "First collection"); assertCollectionName(0, "First collection");
AddStep("add another collection", () => manager.Collections.Add(new BeatmapCollection { Name = { Value = "Second collection" } })); AddStep("add another collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "Second collection"))));
assertCollectionCount(2); assertCollectionCount(2);
assertCollectionName(1, "Second collection"); assertCollectionName(1, "Second collection");
} }
@ -108,7 +101,7 @@ namespace osu.Game.Tests.Visual.Collections
[Test] [Test]
public void TestAddCollectionViaPlaceholder() public void TestAddCollectionViaPlaceholder()
{ {
DrawableCollectionListItem placeholderItem = null; DrawableCollectionListItem placeholderItem = null!;
AddStep("focus placeholder", () => AddStep("focus placeholder", () =>
{ {
@ -117,23 +110,31 @@ namespace osu.Game.Tests.Visual.Collections
}); });
// Done directly via the collection since InputManager methods cannot add text to textbox... // Done directly via the collection since InputManager methods cannot add text to textbox...
AddStep("change collection name", () => placeholderItem.Model.Name.Value = "a"); AddStep("change collection name", () => placeholderItem.Model.Name = "a");
assertCollectionCount(1); assertCollectionCount(1);
AddAssert("collection now exists", () => manager.Collections.Contains(placeholderItem.Model)); AddAssert("collection now exists", () => placeholderItem.Model.IsManaged);
AddAssert("last item is placeholder", () => !manager.Collections.Contains(dialog.ChildrenOfType<DrawableCollectionListItem>().Last().Model)); AddAssert("last item is placeholder", () => !dialog.ChildrenOfType<DrawableCollectionListItem>().Last().Model.IsManaged);
} }
[Test] [Test]
public void TestRemoveCollectionExternal() public void TestRemoveCollectionExternal()
{ {
AddStep("add two collections", () => manager.Collections.AddRange(new[] RealmBeatmapCollection first = null!;
{
new BeatmapCollection { Name = { Value = "1" } },
new BeatmapCollection { Name = { Value = "2" } },
}));
AddStep("remove first collection", () => manager.Collections.RemoveAt(0)); AddStep("add two collections", () =>
{
Realm.Write(r =>
{
r.Add(new[]
{
first = new RealmBeatmapCollection(name: "1"),
new RealmBeatmapCollection(name: "2"),
});
});
});
AddStep("change first collection name", () => Realm.Write(r => r.Remove(first)));
assertCollectionCount(1); assertCollectionCount(1);
assertCollectionName(0, "2"); assertCollectionName(0, "2");
} }
@ -151,21 +152,27 @@ namespace osu.Game.Tests.Visual.Collections
Width = 0.4f, Width = 0.4f,
}); });
}); });
AddStep("add two collections with same name", () => manager.Collections.AddRange(new[] AddStep("add two collections with same name", () => Realm.Write(r => r.Add(new[]
{ {
new BeatmapCollection { Name = { Value = "1" } }, new RealmBeatmapCollection(name: "1"),
new BeatmapCollection { Name = { Value = "1" }, BeatmapHashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash } }, new RealmBeatmapCollection(name: "1")
})); {
BeatmapMD5Hashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash }
},
})));
} }
[Test] [Test]
public void TestRemoveCollectionViaButton() public void TestRemoveCollectionViaButton()
{ {
AddStep("add two collections", () => manager.Collections.AddRange(new[] AddStep("add two collections", () => Realm.Write(r => r.Add(new[]
{ {
new BeatmapCollection { Name = { Value = "1" } }, new RealmBeatmapCollection(name: "1"),
new BeatmapCollection { Name = { Value = "2" }, BeatmapHashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash } }, new RealmBeatmapCollection(name: "2")
})); {
BeatmapMD5Hashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash }
},
})));
assertCollectionCount(2); assertCollectionCount(2);
@ -198,10 +205,13 @@ namespace osu.Game.Tests.Visual.Collections
[Test] [Test]
public void TestCollectionNotRemovedWhenDialogCancelled() public void TestCollectionNotRemovedWhenDialogCancelled()
{ {
AddStep("add two collections", () => manager.Collections.AddRange(new[] AddStep("add collection", () => Realm.Write(r => r.Add(new[]
{ {
new BeatmapCollection { Name = { Value = "1" }, BeatmapHashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash } }, new RealmBeatmapCollection(name: "1")
})); {
BeatmapMD5Hashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash }
},
})));
assertCollectionCount(1); assertCollectionCount(1);
@ -224,13 +234,21 @@ namespace osu.Game.Tests.Visual.Collections
[Test] [Test]
public void TestCollectionRenamedExternal() public void TestCollectionRenamedExternal()
{ {
AddStep("add two collections", () => manager.Collections.AddRange(new[] RealmBeatmapCollection first = null!;
{
new BeatmapCollection { Name = { Value = "1" } },
new BeatmapCollection { Name = { Value = "2" } },
}));
AddStep("change first collection name", () => manager.Collections[0].Name.Value = "First"); AddStep("add two collections", () =>
{
Realm.Write(r =>
{
r.Add(new[]
{
first = new RealmBeatmapCollection(name: "1"),
new RealmBeatmapCollection(name: "2"),
});
});
});
AddStep("change first collection name", () => Realm.Write(_ => first.Name = "First"));
assertCollectionName(0, "First"); assertCollectionName(0, "First");
} }
@ -238,16 +256,24 @@ namespace osu.Game.Tests.Visual.Collections
[Test] [Test]
public void TestCollectionRenamedOnTextChange() public void TestCollectionRenamedOnTextChange()
{ {
AddStep("add two collections", () => manager.Collections.AddRange(new[] RealmBeatmapCollection first = null!;
AddStep("add two collections", () =>
{ {
new BeatmapCollection { Name = { Value = "1" } }, Realm.Write(r =>
new BeatmapCollection { Name = { Value = "2" } }, {
})); r.Add(new[]
{
first = new RealmBeatmapCollection(name: "1"),
new RealmBeatmapCollection(name: "2"),
});
});
});
assertCollectionCount(2); assertCollectionCount(2);
AddStep("change first collection name", () => dialog.ChildrenOfType<TextBox>().First().Text = "First"); AddStep("change first collection name", () => dialog.ChildrenOfType<TextBox>().First().Text = "First");
AddAssert("collection has new name", () => manager.Collections[0].Name.Value == "First"); AddUntilStep("collection has new name", () => first.Name == "First");
} }
private void assertCollectionCount(int count) private void assertCollectionCount(int count)

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
@ -28,12 +26,9 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
private CollectionManager collectionManager; private RulesetStore rulesets = null!;
private BeatmapManager beatmapManager = null!;
private RulesetStore rulesets; private FilterControl control = null!;
private BeatmapManager beatmapManager;
private FilterControl control;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host) private void load(GameHost host)
@ -46,17 +41,14 @@ namespace osu.Game.Tests.Visual.SongSelect
base.Content.AddRange(new Drawable[] base.Content.AddRange(new Drawable[]
{ {
collectionManager = new CollectionManager(),
Content Content
}); });
Dependencies.Cache(collectionManager);
} }
[SetUp] [SetUp]
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>
{ {
collectionManager.Collections.Clear(); Realm.Write(r => r.RemoveAll<RealmBeatmapCollection>());
Child = control = new FilterControl Child = control = new FilterControl
{ {
@ -77,8 +69,8 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestCollectionAddedToDropdown() public void TestCollectionAddedToDropdown()
{ {
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "2" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "2"))));
assertCollectionDropdownContains("1"); assertCollectionDropdownContains("1");
assertCollectionDropdownContains("2"); assertCollectionDropdownContains("2");
} }
@ -86,9 +78,11 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestCollectionRemovedFromDropdown() public void TestCollectionRemovedFromDropdown()
{ {
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); var first = new RealmBeatmapCollection(name: "1");
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "2" } }));
AddStep("remove collection", () => collectionManager.Collections.RemoveAt(0)); AddStep("add collection", () => Realm.Write(r => r.Add(first)));
AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "2"))));
AddStep("remove collection", () => Realm.Write(r => r.Remove(first)));
assertCollectionDropdownContains("1", false); assertCollectionDropdownContains("1", false);
assertCollectionDropdownContains("2"); assertCollectionDropdownContains("2");
@ -97,7 +91,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestCollectionRenamed() public void TestCollectionRenamed()
{ {
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddStep("select collection", () => AddStep("select collection", () =>
{ {
var dropdown = control.ChildrenOfType<CollectionFilterDropdown>().Single(); var dropdown = control.ChildrenOfType<CollectionFilterDropdown>().Single();
@ -106,7 +100,7 @@ namespace osu.Game.Tests.Visual.SongSelect
addExpandHeaderStep(); addExpandHeaderStep();
AddStep("change name", () => collectionManager.Collections[0].Name.Value = "First"); AddStep("change name", () => Realm.Write(_ => getFirstCollection().Name = "First"));
assertCollectionDropdownContains("First"); assertCollectionDropdownContains("First");
assertCollectionHeaderDisplays("First"); assertCollectionHeaderDisplays("First");
@ -124,7 +118,7 @@ namespace osu.Game.Tests.Visual.SongSelect
public void TestCollectionFilterHasAddButton() public void TestCollectionFilterHasAddButton()
{ {
addExpandHeaderStep(); addExpandHeaderStep();
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddStep("hover collection", () => InputManager.MoveMouseTo(getAddOrRemoveButton(1))); AddStep("hover collection", () => InputManager.MoveMouseTo(getAddOrRemoveButton(1)));
AddAssert("collection has add button", () => getAddOrRemoveButton(1).IsPresent); AddAssert("collection has add button", () => getAddOrRemoveButton(1).IsPresent);
} }
@ -134,7 +128,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
addExpandHeaderStep(); addExpandHeaderStep();
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddStep("select available beatmap", () => Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0])); AddStep("select available beatmap", () => Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0]));
AddAssert("button enabled", () => getAddOrRemoveButton(1).Enabled.Value); AddAssert("button enabled", () => getAddOrRemoveButton(1).Enabled.Value);
@ -150,13 +144,13 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select available beatmap", () => Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0])); AddStep("select available beatmap", () => Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0]));
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare)); AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare));
AddStep("add beatmap to collection", () => collectionManager.Collections[0].BeatmapHashes.Add(Beatmap.Value.BeatmapInfo.MD5Hash)); AddStep("add beatmap to collection", () => getFirstCollection().BeatmapMD5Hashes.Add(Beatmap.Value.BeatmapInfo.MD5Hash));
AddAssert("button is minus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.MinusSquare)); AddAssert("button is minus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.MinusSquare));
AddStep("remove beatmap from collection", () => collectionManager.Collections[0].BeatmapHashes.Clear()); AddStep("remove beatmap from collection", () => getFirstCollection().BeatmapMD5Hashes.Clear());
AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare)); AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare));
} }
@ -167,15 +161,15 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select available beatmap", () => Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0])); AddStep("select available beatmap", () => Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0]));
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare)); AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare));
addClickAddOrRemoveButtonStep(1); addClickAddOrRemoveButtonStep(1);
AddAssert("collection contains beatmap", () => collectionManager.Collections[0].BeatmapHashes.Contains(Beatmap.Value.BeatmapInfo.MD5Hash)); AddAssert("collection contains beatmap", () => getFirstCollection().BeatmapMD5Hashes.Contains(Beatmap.Value.BeatmapInfo.MD5Hash));
AddAssert("button is minus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.MinusSquare)); AddAssert("button is minus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.MinusSquare));
addClickAddOrRemoveButtonStep(1); addClickAddOrRemoveButtonStep(1);
AddAssert("collection does not contain beatmap", () => !collectionManager.Collections[0].BeatmapHashes.Contains(Beatmap.Value.BeatmapInfo.MD5Hash)); AddAssert("collection does not contain beatmap", () => !getFirstCollection().BeatmapMD5Hashes.Contains(Beatmap.Value.BeatmapInfo.MD5Hash));
AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare)); AddAssert("button is plus", () => getAddOrRemoveButton(1).Icon.Equals(FontAwesome.Solid.PlusSquare));
} }
@ -184,7 +178,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
addExpandHeaderStep(); addExpandHeaderStep();
AddStep("add collection", () => collectionManager.Collections.Add(new BeatmapCollection { Name = { Value = "1" } })); AddStep("add collection", () => Realm.Write(r => r.Add(new RealmBeatmapCollection(name: "1"))));
AddStep("select collection", () => AddStep("select collection", () =>
{ {
InputManager.MoveMouseTo(getCollectionDropdownItems().ElementAt(1)); InputManager.MoveMouseTo(getCollectionDropdownItems().ElementAt(1));
@ -202,6 +196,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("collection filter still selected", () => control.CreateCriteria().Collection?.Name.Value == "1"); AddAssert("collection filter still selected", () => control.CreateCriteria().Collection?.Name.Value == "1");
} }
private RealmBeatmapCollection getFirstCollection() => Realm.Run(r => r.All<RealmBeatmapCollection>().First());
private void assertCollectionHeaderDisplays(string collectionName, bool shouldDisplay = true) private void assertCollectionHeaderDisplays(string collectionName, bool shouldDisplay = true)
=> AddAssert($"collection dropdown header displays '{collectionName}'", => AddAssert($"collection dropdown header displays '{collectionName}'",
() => shouldDisplay == (control.ChildrenOfType<CollectionFilterDropdown.CollectionDropdownHeader>().Single().ChildrenOfType<SpriteText>().First().Text == collectionName)); () => shouldDisplay == (control.ChildrenOfType<CollectionFilterDropdown.CollectionDropdownHeader>().Single().ChildrenOfType<SpriteText>().First().Text == collectionName));

View File

@ -16,14 +16,14 @@ namespace osu.Game.Beatmaps
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public List<string> BeatmapMD5Hashes { get; set; } = null!; public IList<string> BeatmapMD5Hashes { get; } = null!;
/// <summary> /// <summary>
/// The date when this collection was last modified. /// The date when this collection was last modified.
/// </summary> /// </summary>
public DateTimeOffset LastModified { get; set; } public DateTimeOffset LastModified { get; set; }
public RealmBeatmapCollection(string? name, List<string>? beatmapMD5Hashes) public RealmBeatmapCollection(string? name = null, IList<string>? beatmapMD5Hashes = null)
{ {
ID = Guid.NewGuid(); ID = Guid.NewGuid();
Name = name ?? string.Empty; Name = name ?? string.Empty;

View File

@ -46,9 +46,6 @@ namespace osu.Game.Collections
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private ManageCollectionsDialog manageCollectionsDialog { get; set; } private ManageCollectionsDialog manageCollectionsDialog { get; set; }
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
public CollectionFilterDropdown() public CollectionFilterDropdown()
{ {
ItemSource = filters; ItemSource = filters;
@ -59,8 +56,7 @@ namespace osu.Game.Collections
{ {
base.LoadComplete(); base.LoadComplete();
if (collectionManager != null) // TODO: bind to realm data
collections.BindTo(collectionManager.Collections);
// Dropdown has logic which triggers a change on the bindable with every change to the contained items. // Dropdown has logic which triggers a change on the bindable with every change to the contained items.
// This is not desirable here, as it leads to multiple filter operations running even though nothing has changed. // This is not desirable here, as it leads to multiple filter operations running even though nothing has changed.

View File

@ -1,62 +0,0 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Overlays.Notifications;
using Realms;
namespace osu.Game.Collections
{
/// <summary>
/// Handles user-defined collections of beatmaps.
/// </summary>
public class CollectionManager : Component, IPostNotifications
{
public readonly BindableList<BeatmapCollection> Collections = new BindableList<BeatmapCollection>();
[Resolved]
private RealmAccess realm { get; set; }
[BackgroundDependencyLoader]
private void load()
{
}
protected override void LoadComplete()
{
base.LoadComplete();
realm.RegisterForNotifications(r => r.All<RealmBeatmapCollection>(), collectionsChanged);
}
private void collectionsChanged(IRealmCollection<RealmBeatmapCollection> sender, ChangeSet changes, Exception error)
{
// TODO: hook up with realm changes.
if (changes == null)
{
foreach (var collection in sender)
Collections.Add(new BeatmapCollection
{
Name = { Value = collection.Name },
BeatmapHashes = { Value = collection.BeatmapMD5Hashes },
});
}
}
public Action<Notification> PostNotification { protected get; set; }
public void DeleteAll()
{
Collections.Clear();
PostNotification?.Invoke(new ProgressCompletionNotification { Text = "Deleted all collections!" });
}
}
}

View File

@ -8,16 +8,16 @@ namespace osu.Game.Collections
{ {
public class CollectionToggleMenuItem : ToggleMenuItem public class CollectionToggleMenuItem : ToggleMenuItem
{ {
public CollectionToggleMenuItem(BeatmapCollection collection, IBeatmapInfo beatmap) public CollectionToggleMenuItem(RealmBeatmapCollection collection, IBeatmapInfo beatmap)
: base(collection.Name.Value, MenuItemType.Standard, state => : base(collection.Name, MenuItemType.Standard, state =>
{ {
if (state) if (state)
collection.BeatmapHashes.Add(beatmap.MD5Hash); collection.BeatmapMD5Hashes.Add(beatmap.MD5Hash);
else else
collection.BeatmapHashes.Remove(beatmap.MD5Hash); collection.BeatmapMD5Hashes.Remove(beatmap.MD5Hash);
}) })
{ {
State.Value = collection.BeatmapHashes.Contains(beatmap.MD5Hash); State.Value = collection.BeatmapMD5Hashes.Contains(beatmap.MD5Hash);
} }
} }
} }

View File

@ -6,16 +6,17 @@
using System; using System;
using Humanizer; using Humanizer;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Dialog; using osu.Game.Overlays.Dialog;
namespace osu.Game.Collections namespace osu.Game.Collections
{ {
public class DeleteCollectionDialog : PopupDialog public class DeleteCollectionDialog : PopupDialog
{ {
public DeleteCollectionDialog(BeatmapCollection collection, Action deleteAction) public DeleteCollectionDialog(RealmBeatmapCollection collection, Action deleteAction)
{ {
HeaderText = "Confirm deletion of"; HeaderText = "Confirm deletion of";
BodyText = $"{collection.Name.Value} ({"beatmap".ToQuantity(collection.BeatmapHashes.Count)})"; BodyText = $"{collection.Name} ({"beatmap".ToQuantity(collection.BeatmapMD5Hashes.Count)})";
Icon = FontAwesome.Regular.TrashAlt; Icon = FontAwesome.Regular.TrashAlt;

View File

@ -7,28 +7,31 @@ using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
namespace osu.Game.Collections namespace osu.Game.Collections
{ {
/// <summary> /// <summary>
/// Visualises a list of <see cref="BeatmapCollection"/>s. /// Visualises a list of <see cref="RealmBeatmapCollection"/>s.
/// </summary> /// </summary>
public class DrawableCollectionList : OsuRearrangeableListContainer<BeatmapCollection> public class DrawableCollectionList : OsuRearrangeableListContainer<RealmBeatmapCollection>
{ {
private Scroll scroll; private Scroll scroll;
protected override ScrollContainer<Drawable> CreateScrollContainer() => scroll = new Scroll(); protected override ScrollContainer<Drawable> CreateScrollContainer() => scroll = new Scroll();
protected override FillFlowContainer<RearrangeableListItem<BeatmapCollection>> CreateListFillFlowContainer() => new Flow protected override FillFlowContainer<RearrangeableListItem<RealmBeatmapCollection>> CreateListFillFlowContainer() => new Flow
{ {
DragActive = { BindTarget = DragActive } DragActive = { BindTarget = DragActive }
}; };
protected override OsuRearrangeableListItem<BeatmapCollection> CreateOsuDrawable(BeatmapCollection item) // TODO: source from realm
protected override OsuRearrangeableListItem<RealmBeatmapCollection> CreateOsuDrawable(RealmBeatmapCollection item)
{ {
if (item == scroll.PlaceholderItem.Model) if (item.ID == scroll.PlaceholderItem.Model.ID)
return scroll.ReplacePlaceholder(); return scroll.ReplacePlaceholder();
return new DrawableCollectionListItem(item, true); return new DrawableCollectionListItem(item, true);
@ -95,7 +98,7 @@ namespace osu.Game.Collections
var previous = PlaceholderItem; var previous = PlaceholderItem;
placeholderContainer.Clear(false); placeholderContainer.Clear(false);
placeholderContainer.Add(PlaceholderItem = new DrawableCollectionListItem(new BeatmapCollection(), false)); placeholderContainer.Add(PlaceholderItem = new DrawableCollectionListItem(new RealmBeatmapCollection(), false));
return previous; return previous;
} }
@ -104,7 +107,7 @@ namespace osu.Game.Collections
/// <summary> /// <summary>
/// The flow of <see cref="DrawableCollectionListItem"/>. Disables layout easing unless a drag is in progress. /// The flow of <see cref="DrawableCollectionListItem"/>. Disables layout easing unless a drag is in progress.
/// </summary> /// </summary>
private class Flow : FillFlowContainer<RearrangeableListItem<BeatmapCollection>> private class Flow : FillFlowContainer<RearrangeableListItem<RealmBeatmapCollection>>
{ {
public readonly IBindable<bool> DragActive = new Bindable<bool>(); public readonly IBindable<bool> DragActive = new Bindable<bool>();

View File

@ -12,6 +12,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -22,15 +24,15 @@ using osuTK.Graphics;
namespace osu.Game.Collections namespace osu.Game.Collections
{ {
/// <summary> /// <summary>
/// Visualises a <see cref="BeatmapCollection"/> inside a <see cref="DrawableCollectionList"/>. /// Visualises a <see cref="RealmBeatmapCollection"/> inside a <see cref="DrawableCollectionList"/>.
/// </summary> /// </summary>
public class DrawableCollectionListItem : OsuRearrangeableListItem<BeatmapCollection> public class DrawableCollectionListItem : OsuRearrangeableListItem<RealmBeatmapCollection>
{ {
private const float item_height = 35; private const float item_height = 35;
private const float button_width = item_height * 0.75f; private const float button_width = item_height * 0.75f;
/// <summary> /// <summary>
/// Whether the <see cref="BeatmapCollection"/> currently exists inside the <see cref="CollectionManager"/>. /// Whether the <see cref="RealmBeatmapCollection"/> currently exists inside realm.
/// </summary> /// </summary>
public IBindable<bool> IsCreated => isCreated; public IBindable<bool> IsCreated => isCreated;
@ -39,9 +41,9 @@ namespace osu.Game.Collections
/// <summary> /// <summary>
/// Creates a new <see cref="DrawableCollectionListItem"/>. /// Creates a new <see cref="DrawableCollectionListItem"/>.
/// </summary> /// </summary>
/// <param name="item">The <see cref="BeatmapCollection"/>.</param> /// <param name="item">The <see cref="RealmBeatmapCollection"/>.</param>
/// <param name="isCreated">Whether <paramref name="item"/> currently exists inside the <see cref="CollectionManager"/>.</param> /// <param name="isCreated">Whether <paramref name="item"/> currently exists inside realm.</param>
public DrawableCollectionListItem(BeatmapCollection item, bool isCreated) public DrawableCollectionListItem(RealmBeatmapCollection item, bool isCreated)
: base(item) : base(item)
{ {
this.isCreated.Value = isCreated; this.isCreated.Value = isCreated;
@ -61,24 +63,18 @@ namespace osu.Game.Collections
{ {
public readonly Bindable<bool> IsCreated = new Bindable<bool>(); public readonly Bindable<bool> IsCreated = new Bindable<bool>();
private readonly IBindable<string> collectionName; private readonly RealmBeatmapCollection collection;
private readonly BeatmapCollection collection;
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
private Container textBoxPaddingContainer; private Container textBoxPaddingContainer;
private ItemTextBox textBox; private ItemTextBox textBox;
public ItemContent(BeatmapCollection collection) public ItemContent(RealmBeatmapCollection collection)
{ {
this.collection = collection; this.collection = collection;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = item_height; Height = item_height;
Masking = true; Masking = true;
collectionName = collection.Name.GetBoundCopy();
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -111,14 +107,17 @@ namespace osu.Game.Collections
}; };
} }
[Resolved]
private RealmAccess realm { get; set; }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
// Bind late, as the collection name may change externally while still loading. // Bind late, as the collection name may change externally while still loading.
textBox.Current = collection.Name; textBox.Current.Value = collection.Name;
textBox.Current.BindValueChanged(_ => createNewCollection(), true);
collectionName.BindValueChanged(_ => createNewCollection(), true);
IsCreated.BindValueChanged(created => textBoxPaddingContainer.Padding = new MarginPadding { Right = created.NewValue ? button_width : 0 }, true); IsCreated.BindValueChanged(created => textBoxPaddingContainer.Padding = new MarginPadding { Right = created.NewValue ? button_width : 0 }, true);
} }
@ -127,11 +126,11 @@ namespace osu.Game.Collections
if (IsCreated.Value) if (IsCreated.Value)
return; return;
if (string.IsNullOrEmpty(collectionName.Value)) if (string.IsNullOrEmpty(textBox.Current.Value))
return; return;
// Add the new collection and disable our placeholder. If all text is removed, the placeholder should not show back again. // Add the new collection and disable our placeholder. If all text is removed, the placeholder should not show back again.
collectionManager?.Collections.Add(collection); realm.Write(r => r.Add(collection));
textBox.PlaceholderText = string.Empty; textBox.PlaceholderText = string.Empty;
// When this item changes from placeholder to non-placeholder (via changing containers), its textbox will lose focus, so it needs to be re-focused. // When this item changes from placeholder to non-placeholder (via changing containers), its textbox will lose focus, so it needs to be re-focused.
@ -162,15 +161,15 @@ namespace osu.Game.Collections
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IDialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)] [Resolved]
private CollectionManager collectionManager { get; set; } private RealmAccess realmAccess { get; set; }
private readonly BeatmapCollection collection; private readonly RealmBeatmapCollection collection;
private Drawable fadeContainer; private Drawable fadeContainer;
private Drawable background; private Drawable background;
public DeleteButton(BeatmapCollection collection) public DeleteButton(RealmBeatmapCollection collection)
{ {
this.collection = collection; this.collection = collection;
RelativeSizeAxes = Axes.Y; RelativeSizeAxes = Axes.Y;
@ -227,7 +226,7 @@ namespace osu.Game.Collections
{ {
background.FlashColour(Color4.White, 150); background.FlashColour(Color4.White, 150);
if (collection.BeatmapHashes.Count == 0) if (collection.BeatmapMD5Hashes.Count == 0)
deleteCollection(); deleteCollection();
else else
dialogOverlay?.Push(new DeleteCollectionDialog(collection, deleteCollection)); dialogOverlay?.Push(new DeleteCollectionDialog(collection, deleteCollection));
@ -235,7 +234,7 @@ namespace osu.Game.Collections
return true; return true;
} }
private void deleteCollection() => collectionManager?.Collections.Remove(collection); private void deleteCollection() => realmAccess.Write(r => r.Remove(collection));
} }
} }
} }

View File

@ -5,7 +5,6 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -26,9 +25,6 @@ namespace osu.Game.Collections
private AudioFilter lowPassFilter; private AudioFilter lowPassFilter;
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
public ManageCollectionsDialog() public ManageCollectionsDialog()
{ {
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
@ -107,7 +103,6 @@ namespace osu.Game.Collections
new DrawableCollectionList new DrawableCollectionList
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Items = { BindTarget = collectionManager?.Collections ?? new BindableList<BeatmapCollection>() }
} }
} }
} }

View File

@ -1,14 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Collections; using osu.Game.Beatmaps;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.IO.Legacy; using osu.Game.IO.Legacy;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
@ -17,17 +16,17 @@ namespace osu.Game.Database
{ {
public class LegacyCollectionImporter public class LegacyCollectionImporter
{ {
private readonly CollectionManager collections; public Action<Notification>? PostNotification { protected get; set; }
public LegacyCollectionImporter(CollectionManager collections) private readonly RealmAccess realm;
{
this.collections = collections;
}
public Action<Notification> PostNotification { protected get; set; }
private const string database_name = "collection.db"; private const string database_name = "collection.db";
public LegacyCollectionImporter(RealmAccess realm)
{
this.realm = realm;
}
public Task<int> GetAvailableCount(StableStorage stableStorage) public Task<int> GetAvailableCount(StableStorage stableStorage)
{ {
if (!stableStorage.Exists(database_name)) if (!stableStorage.Exists(database_name))
@ -76,26 +75,30 @@ namespace osu.Game.Database
notification.State = ProgressNotificationState.Completed; notification.State = ProgressNotificationState.Completed;
} }
private Task importCollections(List<BeatmapCollection> newCollections) private Task importCollections(List<RealmBeatmapCollection> newCollections)
{ {
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
// Schedule(() =>
// {
try try
{ {
foreach (var newCol in newCollections) realm.Write(r =>
{ {
var existing = collections.Collections.FirstOrDefault(c => c.Name.Value == newCol.Name.Value); foreach (var collection in newCollections)
if (existing == null)
collections.Collections.Add(existing = new BeatmapCollection { Name = { Value = newCol.Name.Value } });
foreach (string newBeatmap in newCol.BeatmapHashes)
{ {
if (!existing.BeatmapHashes.Contains(newBeatmap)) var existing = r.All<RealmBeatmapCollection>().FirstOrDefault(c => c.Name == collection.Name);
existing.BeatmapHashes.Add(newBeatmap);
if (existing != null)
{
foreach (string newBeatmap in existing.BeatmapMD5Hashes)
{
if (!existing.BeatmapMD5Hashes.Contains(newBeatmap))
existing.BeatmapMD5Hashes.Add(newBeatmap);
}
}
else
r.Add(collection);
} }
} });
tcs.SetResult(true); tcs.SetResult(true);
} }
@ -104,12 +107,11 @@ namespace osu.Game.Database
Logger.Error(e, "Failed to import collection."); Logger.Error(e, "Failed to import collection.");
tcs.SetException(e); tcs.SetException(e);
} }
// });
return tcs.Task; return tcs.Task;
} }
private List<BeatmapCollection> readCollections(Stream stream, ProgressNotification notification = null) private List<RealmBeatmapCollection> readCollections(Stream stream, ProgressNotification? notification = null)
{ {
if (notification != null) if (notification != null)
{ {
@ -117,7 +119,7 @@ namespace osu.Game.Database
notification.Progress = 0; notification.Progress = 0;
} }
var result = new List<BeatmapCollection>(); var result = new List<RealmBeatmapCollection>();
try try
{ {
@ -133,7 +135,7 @@ namespace osu.Game.Database
if (notification?.CancellationToken.IsCancellationRequested == true) if (notification?.CancellationToken.IsCancellationRequested == true)
return result; return result;
var collection = new BeatmapCollection { Name = { Value = sr.ReadString() } }; var collection = new RealmBeatmapCollection(sr.ReadString());
int mapCount = sr.ReadInt32(); int mapCount = sr.ReadInt32();
for (int j = 0; j < mapCount; j++) for (int j = 0; j < mapCount; j++)
@ -143,7 +145,7 @@ namespace osu.Game.Database
string checksum = sr.ReadString(); string checksum = sr.ReadString();
collection.BeatmapHashes.Add(checksum); collection.BeatmapMD5Hashes.Add(checksum);
} }
if (notification != null) if (notification != null)

View File

@ -13,7 +13,6 @@ using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Overlays.Settings.Sections.Maintenance;
@ -36,15 +35,15 @@ namespace osu.Game.Database
[Resolved] [Resolved]
private ScoreManager scores { get; set; } private ScoreManager scores { get; set; }
[Resolved]
private CollectionManager collections { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private OsuGame game { get; set; } private OsuGame game { get; set; }
[Resolved] [Resolved]
private IDialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved]
private RealmAccess realmAccess { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private DesktopGameHost desktopGameHost { get; set; } private DesktopGameHost desktopGameHost { get; set; }
@ -72,7 +71,7 @@ namespace osu.Game.Database
return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage); return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage);
case StableContent.Collections: case StableContent.Collections:
return await new LegacyCollectionImporter(collections).GetAvailableCount(stableStorage); return await new LegacyCollectionImporter(realmAccess).GetAvailableCount(stableStorage);
case StableContent.Scores: case StableContent.Scores:
return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage); return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage);
@ -109,7 +108,7 @@ namespace osu.Game.Database
importTasks.Add(new LegacySkinImporter(skins).ImportFromStableAsync(stableStorage)); importTasks.Add(new LegacySkinImporter(skins).ImportFromStableAsync(stableStorage));
if (content.HasFlagFast(StableContent.Collections)) if (content.HasFlagFast(StableContent.Collections))
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyCollectionImporter(collections).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyCollectionImporter(realmAccess).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));
if (content.HasFlagFast(StableContent.Scores)) if (content.HasFlagFast(StableContent.Scores))
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyScoreImporter(scores).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyScoreImporter(scores).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));

View File

@ -858,11 +858,6 @@ namespace osu.Game
d.Origin = Anchor.TopRight; d.Origin = Anchor.TopRight;
}), rightFloatingOverlayContent.Add, true); }), rightFloatingOverlayContent.Add, true);
loadComponentSingleFile(new CollectionManager
{
PostNotification = n => Notifications.Post(n),
}, Add, true);
loadComponentSingleFile(legacyImportManager, Add); loadComponentSingleFile(legacyImportManager, Add);
loadComponentSingleFile(screenshotManager, Add); loadComponentSingleFile(screenshotManager, Add);

View File

@ -3,9 +3,10 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Collections; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Overlays.Notifications;
namespace osu.Game.Overlays.Settings.Sections.Maintenance namespace osu.Game.Overlays.Settings.Sections.Maintenance
{ {
@ -15,11 +16,15 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
private SettingsButton importCollectionsButton = null!; private SettingsButton importCollectionsButton = null!;
[BackgroundDependencyLoader] [Resolved]
private void load(CollectionManager? collectionManager, LegacyImportManager? legacyImportManager, IDialogOverlay? dialogOverlay) private RealmAccess realm { get; set; } = null!;
{
if (collectionManager == null) return;
[Resolved]
private INotificationOverlay notificationOverlay { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(LegacyImportManager? legacyImportManager, IDialogOverlay? dialogOverlay)
{
if (legacyImportManager?.SupportsImportFromStable == true) if (legacyImportManager?.SupportsImportFromStable == true)
{ {
Add(importCollectionsButton = new SettingsButton Add(importCollectionsButton = new SettingsButton
@ -38,9 +43,15 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
Text = MaintenanceSettingsStrings.DeleteAllCollections, Text = MaintenanceSettingsStrings.DeleteAllCollections,
Action = () => Action = () =>
{ {
dialogOverlay?.Push(new MassDeleteConfirmationDialog(collectionManager.DeleteAll)); dialogOverlay?.Push(new MassDeleteConfirmationDialog(deleteAllCollections));
} }
}); });
} }
private void deleteAllCollections()
{
realm.Write(r => r.RemoveAll<RealmBeatmapCollection>());
notificationOverlay.Post(new ProgressCompletionNotification { Text = "Deleted all collections!" });
}
} }
} }

View File

@ -94,6 +94,9 @@ namespace osu.Game.Screens.OnlinePlay
private PanelBackground panelBackground; private PanelBackground panelBackground;
private FillFlowContainer mainFillFlow; private FillFlowContainer mainFillFlow;
[Resolved]
private RealmAccess realm { get; set; }
[Resolved] [Resolved]
private RulesetStore rulesets { get; set; } private RulesetStore rulesets { get; set; }
@ -112,9 +115,6 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private BeatmapSetOverlay beatmapOverlay { get; set; } private BeatmapSetOverlay beatmapOverlay { get; set; }
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private ManageCollectionsDialog manageCollectionsDialog { get; set; } private ManageCollectionsDialog manageCollectionsDialog { get; set; }
@ -495,11 +495,11 @@ namespace osu.Game.Screens.OnlinePlay
if (beatmapOverlay != null) if (beatmapOverlay != null)
items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(Item.Beatmap.OnlineID))); items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(Item.Beatmap.OnlineID)));
if (collectionManager != null && beatmap != null) if (beatmap != null)
{ {
if (beatmaps.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID) is BeatmapInfo local && !local.BeatmapSet.AsNonNull().DeletePending) if (beatmaps.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID) is BeatmapInfo local && !local.BeatmapSet.AsNonNull().DeletePending)
{ {
var collectionItems = collectionManager.Collections.Select(c => new CollectionToggleMenuItem(c, beatmap)).Cast<OsuMenuItem>().ToList(); var collectionItems = realm.Realm.All<RealmBeatmapCollection>().Select(c => new CollectionToggleMenuItem(c, beatmap)).Cast<OsuMenuItem>().ToList();
if (manageCollectionsDialog != null) if (manageCollectionsDialog != null)
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show)); collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));

View File

@ -22,6 +22,7 @@ using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Collections; using osu.Game.Collections;
using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -63,12 +64,12 @@ namespace osu.Game.Screens.Select.Carousel
[Resolved] [Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; } private BeatmapDifficultyCache difficultyCache { get; set; }
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private ManageCollectionsDialog manageCollectionsDialog { get; set; } private ManageCollectionsDialog manageCollectionsDialog { get; set; }
[Resolved]
private RealmAccess realm { get; set; }
private IBindable<StarDifficulty?> starDifficultyBindable; private IBindable<StarDifficulty?> starDifficultyBindable;
private CancellationTokenSource starDifficultyCancellationSource; private CancellationTokenSource starDifficultyCancellationSource;
@ -237,14 +238,11 @@ namespace osu.Game.Screens.Select.Carousel
if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null) if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null)
items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID))); items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID)));
if (collectionManager != null) var collectionItems = realm.Realm.All<RealmBeatmapCollection>().Select(c => new CollectionToggleMenuItem(c, beatmapInfo)).Cast<OsuMenuItem>().ToList();
{ if (manageCollectionsDialog != null)
var collectionItems = collectionManager.Collections.Select(c => new CollectionToggleMenuItem(c, beatmapInfo)).Cast<OsuMenuItem>().ToList(); collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
if (manageCollectionsDialog != null)
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
items.Add(new OsuMenuItem("Collections") { Items = collectionItems }); items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
}
if (hideRequested != null) if (hideRequested != null)
items.Add(new OsuMenuItem(CommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo))); items.Add(new OsuMenuItem(CommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo)));

View File

@ -17,6 +17,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Collections; using osu.Game.Collections;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -32,12 +33,12 @@ namespace osu.Game.Screens.Select.Carousel
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IDialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private ManageCollectionsDialog manageCollectionsDialog { get; set; } private ManageCollectionsDialog manageCollectionsDialog { get; set; }
[Resolved]
private RealmAccess realm { get; set; }
public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty<DrawableCarouselItem>() : beatmapContainer.AliveChildren; public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty<DrawableCarouselItem>() : beatmapContainer.AliveChildren;
[CanBeNull] [CanBeNull]
@ -223,14 +224,11 @@ namespace osu.Game.Screens.Select.Carousel
if (beatmapSet.OnlineID > 0 && viewDetails != null) if (beatmapSet.OnlineID > 0 && viewDetails != null)
items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID))); items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID)));
if (collectionManager != null) var collectionItems = realm.Realm.All<RealmBeatmapCollection>().AsEnumerable().Select(createCollectionMenuItem).ToList();
{ if (manageCollectionsDialog != null)
var collectionItems = collectionManager.Collections.Select(createCollectionMenuItem).ToList(); collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
if (manageCollectionsDialog != null)
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
items.Add(new OsuMenuItem("Collections") { Items = collectionItems }); items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
}
if (beatmapSet.Beatmaps.Any(b => b.Hidden)) if (beatmapSet.Beatmaps.Any(b => b.Hidden))
items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested(beatmapSet))); items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested(beatmapSet)));
@ -241,13 +239,13 @@ namespace osu.Game.Screens.Select.Carousel
} }
} }
private MenuItem createCollectionMenuItem(BeatmapCollection collection) private MenuItem createCollectionMenuItem(RealmBeatmapCollection collection)
{ {
Debug.Assert(beatmapSet != null); Debug.Assert(beatmapSet != null);
TernaryState state; TernaryState state;
int countExisting = beatmapSet.Beatmaps.Count(b => collection.BeatmapHashes.Contains(b.MD5Hash)); int countExisting = beatmapSet.Beatmaps.Count(b => collection.BeatmapMD5Hashes.Contains(b.MD5Hash));
if (countExisting == beatmapSet.Beatmaps.Count) if (countExisting == beatmapSet.Beatmaps.Count)
state = TernaryState.True; state = TernaryState.True;
@ -256,21 +254,21 @@ namespace osu.Game.Screens.Select.Carousel
else else
state = TernaryState.False; state = TernaryState.False;
return new TernaryStateToggleMenuItem(collection.Name.Value, MenuItemType.Standard, s => return new TernaryStateToggleMenuItem(collection.Name, MenuItemType.Standard, s =>
{ {
foreach (var b in beatmapSet.Beatmaps) foreach (var b in beatmapSet.Beatmaps)
{ {
switch (s) switch (s)
{ {
case TernaryState.True: case TernaryState.True:
if (collection.BeatmapHashes.Contains(b.MD5Hash)) if (collection.BeatmapMD5Hashes.Contains(b.MD5Hash))
continue; continue;
collection.BeatmapHashes.Add(b.MD5Hash); collection.BeatmapMD5Hashes.Add(b.MD5Hash);
break; break;
case TernaryState.False: case TernaryState.False:
collection.BeatmapHashes.Remove(b.MD5Hash); collection.BeatmapMD5Hashes.Remove(b.MD5Hash);
break; break;
} }
} }