diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs index 0f66122bb5..6e3fafdd6a 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; @@ -42,7 +43,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2 private DialogOverlay dialogOverlay = null!; private LeaderboardManager leaderboardManager = null!; - private RealmPopulatingOnlineLookupSource lookupSource = null!; + + private readonly IBindable onlineLookupResult = new Bindable(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -52,7 +54,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API)); dependencies.Cache(leaderboardManager = new LeaderboardManager()); - dependencies.Cache(lookupSource = new RealmPopulatingOnlineLookupSource()); + dependencies.CacheAs(onlineLookupResult); Dependencies.Cache(Realm); @@ -68,7 +70,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2 }); LoadComponent(leaderboardManager); - LoadComponent(lookupSource); Child = contentContainer = new OsuContextMenuContainer { diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs index ca52e476e2..d4fab55c62 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs @@ -4,13 +4,12 @@ using System; using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Models; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.SelectV2; @@ -18,64 +17,25 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { public partial class TestSceneBeatmapMetadataWedge : SongSelectComponentsTestScene { - private APIBeatmapSet? currentOnlineSet; - private BeatmapMetadataWedge wedge = null!; + [Cached(typeof(IBindable))] + private Bindable onlineLookupResult = new Bindable(); + protected override void LoadComplete() { base.LoadComplete(); - var lookupSource = new RealmPopulatingOnlineLookupSource(); - Child = new DependencyProvidingContainer + Child = wedge = new BeatmapMetadataWedge { - RelativeSizeAxes = Axes.Both, - CachedDependencies = [(typeof(RealmPopulatingOnlineLookupSource), lookupSource)], - Children = - [ - lookupSource, - wedge = new BeatmapMetadataWedge - { - State = { Value = Visibility.Visible }, - } - ] + State = { Value = Visibility.Visible }, }; } - [SetUpSteps] - public override void SetUpSteps() - { - AddStep("register request handling", () => - { - ((DummyAPIAccess)API).HandleRequest = request => - { - switch (request) - { - case GetBeatmapSetRequest set: - if (set.ID == currentOnlineSet?.OnlineID) - { - set.TriggerSuccess(currentOnlineSet); - return true; - } - - return false; - - default: - return false; - } - }; - }); - } - [Test] public void TestShowHide() { - AddStep("all metrics", () => - { - var (working, onlineSet) = createTestBeatmap(); - currentOnlineSet = onlineSet; - Beatmap.Value = working; - }); + AddStep("all metrics", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap()); AddStep("hide wedge", () => wedge.Hide()); AddStep("show wedge", () => wedge.Show()); @@ -84,67 +44,63 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Test] public void TestVariousMetrics() { - AddStep("all metrics", () => - { - var (working, onlineSet) = createTestBeatmap(); - currentOnlineSet = onlineSet; - Beatmap.Value = working; - }); + AddStep("all metrics", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap()); + AddStep("null beatmap", () => Beatmap.SetDefault()); AddStep("no source", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); working.Metadata.Source = string.Empty; - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); AddStep("no success rate", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Beatmaps.Single().PlayCount = 0; - onlineSet.Beatmaps.Single().PassCount = 0; + online.Result!.Beatmaps.Single().PlayCount = 0; + online.Result!.Beatmaps.Single().PassCount = 0; - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); AddStep("no user ratings", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Ratings = Array.Empty(); + online.Result!.Ratings = Array.Empty(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); AddStep("no fail times", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Beatmaps.Single().FailTimes = null; + online.Result!.Beatmaps.Single().FailTimes = null; - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); AddStep("no metrics", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Ratings = Array.Empty(); - onlineSet.Beatmaps.Single().FailTimes = null; + online.Result!.Ratings = Array.Empty(); + online.Result!.Beatmaps.Single().FailTimes = null; - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); AddStep("local beatmap", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, _) = createTestBeatmap(); working.BeatmapInfo.OnlineID = 0; - currentOnlineSet = onlineSet; + onlineLookupResult.Value = null; Beatmap.Value = working; }); } @@ -154,16 +110,16 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { AddStep("long text", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); working.BeatmapInfo.Metadata.Author = new RealmUser { Username = "Verrrrryyyy llooonngggggg author" }; working.BeatmapInfo.Metadata.Source = "Verrrrryyyy llooonngggggg source"; working.BeatmapInfo.Metadata.Tags = string.Join(' ', Enumerable.Repeat(working.BeatmapInfo.Metadata.Tags, 3)); - onlineSet.Genre = new BeatmapSetOnlineGenre { Id = 12, Name = "Verrrrryyyy llooonngggggg genre" }; - onlineSet.Language = new BeatmapSetOnlineLanguage { Id = 12, Name = "Verrrrryyyy llooonngggggg language" }; - onlineSet.Beatmaps.Single().TopTags = Enumerable.Repeat(onlineSet.Beatmaps.Single().TopTags, 3).SelectMany(t => t!).ToArray(); + online.Result!.Genre = new BeatmapSetOnlineGenre { Id = 12, Name = "Verrrrryyyy llooonngggggg genre" }; + online.Result!.Language = new BeatmapSetOnlineLanguage { Id = 12, Name = "Verrrrryyyy llooonngggggg language" }; + online.Result!.Beatmaps.Single().TopTags = Enumerable.Repeat(online.Result!.Beatmaps.Single().TopTags, 3).SelectMany(t => t!).ToArray(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); } @@ -171,22 +127,17 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Test] public void TestOnlineAvailability() { - AddStep("online beatmapset", () => - { - var (working, onlineSet) = createTestBeatmap(); + AddStep("online beatmapset", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap()); - currentOnlineSet = onlineSet; - Beatmap.Value = working; - }); AddUntilStep("rating wedge visible", () => wedge.RatingsVisible); AddUntilStep("fail time wedge visible", () => wedge.FailRetryVisible); AddStep("online beatmapset with local diff", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, lookupResult) = createTestBeatmap(); working.BeatmapInfo.ResetOnlineInfo(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = lookupResult; Beatmap.Value = working; }); AddUntilStep("rating wedge hidden", () => !wedge.RatingsVisible); @@ -195,7 +146,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { var (working, _) = createTestBeatmap(); - currentOnlineSet = null; + onlineLookupResult.Value = null; Beatmap.Value = working; }); AddAssert("rating wedge still hidden", () => !wedge.RatingsVisible); @@ -205,21 +156,17 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Test] public void TestUserTags() { - AddStep("user tags", () => - { - var (working, onlineSet) = createTestBeatmap(); + AddStep("user tags", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap()); - currentOnlineSet = onlineSet; - Beatmap.Value = working; - }); AddStep("no user tags", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Beatmaps.Single().TopTags = null; - onlineSet.RelatedTags = null; + online.Result!.Beatmaps.Single().TopTags = null; + online.Result!.RelatedTags = null; + working.BeatmapSetInfo.Beatmaps.Single().Metadata.UserTags.Clear(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = online; Beatmap.Value = working; }); } @@ -227,72 +174,60 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Test] public void TestLoading() { - AddStep("override request handling", () => - { - currentOnlineSet = null; - - ((DummyAPIAccess)API).HandleRequest = request => - { - switch (request) - { - case GetBeatmapSetRequest set: - Scheduler.AddDelayed(() => set.TriggerSuccess(currentOnlineSet!), 500); - return true; - - default: - return false; - } - }; - }); - AddStep("set beatmap", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress(); + Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500); Beatmap.Value = working; }); AddWaitStep("wait", 5); AddStep("set beatmap", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.RelatedTags![0].Name = "other/tag"; - onlineSet.RelatedTags[1].Name = "another/tag"; - onlineSet.RelatedTags[2].Name = "some/tag"; + online.Result!.RelatedTags![0].Name = "other/tag"; + online.Result!.RelatedTags[1].Name = "another/tag"; + online.Result!.RelatedTags[2].Name = "some/tag"; - currentOnlineSet = onlineSet; + onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress(); + Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500); Beatmap.Value = working; }); AddWaitStep("wait", 5); AddStep("no user tags", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Beatmaps.Single().TopTags = null; - onlineSet.RelatedTags = null; + online.Result!.Beatmaps.Single().TopTags = null; + online.Result!.RelatedTags = null; + working.BeatmapSetInfo.Beatmaps.Single().Metadata.UserTags.Clear(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress(); + Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500); Beatmap.Value = working; }); AddWaitStep("wait", 5); AddStep("no user tags", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, online) = createTestBeatmap(); - onlineSet.Beatmaps.Single().TopTags = null; - onlineSet.RelatedTags = null; + online.Result!.Beatmaps.Single().TopTags = null; + online.Result!.RelatedTags = null; + working.BeatmapSetInfo.Beatmaps.Single().Metadata.UserTags.Clear(); - currentOnlineSet = onlineSet; + onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress(); + Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500); Beatmap.Value = working; }); AddWaitStep("wait", 5); } - private (WorkingBeatmap, APIBeatmapSet) createTestBeatmap() + private (WorkingBeatmap, Screens.SelectV2.SongSelect.BeatmapSetLookupResult) createTestBeatmap() { var working = CreateWorkingBeatmap(Ruleset.Value); var onlineSet = new APIBeatmapSet @@ -346,7 +281,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2 working.BeatmapSetInfo.DateSubmitted = DateTimeOffset.Now; working.BeatmapSetInfo.DateRanked = DateTimeOffset.Now; - return (working, onlineSet); + working.Metadata.UserTags.AddRange(onlineSet.RelatedTags.Select(t => t.Name)); + return (working, Screens.SelectV2.SongSelect.BeatmapSetLookupResult.Completed(onlineSet)); } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs index cc4b38b54c..cbcf16ec51 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs @@ -11,6 +11,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -41,10 +42,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2 private BeatmapTitleWedge titleWedge = null!; private BeatmapTitleWedge.DifficultyDisplay difficultyDisplay => titleWedge.ChildrenOfType().Single(); - private APIBeatmapSet? currentOnlineSet; - - [Cached] - private RealmPopulatingOnlineLookupSource lookupSource = new RealmPopulatingOnlineLookupSource(); + [Cached(typeof(IBindable))] + private Bindable onlineLookupResult = new Bindable(); [BackgroundDependencyLoader] private void load(RulesetStore rulesets) @@ -58,7 +57,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddRange(new Drawable[] { - lookupSource, new Container { RelativeSizeAxes = Axes.Both, @@ -142,44 +140,18 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Test] public void TestOnlineAvailability() { - AddStep("set up request handler", () => - { - ((DummyAPIAccess)API).HandleRequest = request => - { - switch (request) - { - case GetBeatmapSetRequest set: - if (set.ID == currentOnlineSet?.OnlineID) - { - set.TriggerSuccess(currentOnlineSet); - return true; - } + AddStep("online beatmapset", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap()); - return false; - - default: - return false; - } - }; - }); - - AddStep("online beatmapset", () => - { - var (working, onlineSet) = createTestBeatmap(); - - currentOnlineSet = onlineSet; - Beatmap.Value = working; - }); AddUntilStep("play count is 10000", () => this.ChildrenOfType().ElementAt(0).Text.ToString(), () => Is.EqualTo("10,000")); AddUntilStep("favourites count is 2345", () => this.ChildrenOfType().Single().Text.ToString(), () => Is.EqualTo("2,345")); AddStep("online beatmapset with local diff", () => { - var (working, onlineSet) = createTestBeatmap(); + var (working, lookupResult) = createTestBeatmap(); working.BeatmapInfo.ResetOnlineInfo(); - currentOnlineSet = onlineSet; Beatmap.Value = working; + onlineLookupResult.Value = lookupResult; }); AddUntilStep("play count is -", () => this.ChildrenOfType().ElementAt(0).Text.ToString(), () => Is.EqualTo("-")); AddUntilStep("favourites count is 2345", () => this.ChildrenOfType().Single().Text.ToString(), () => Is.EqualTo("2,345")); @@ -187,8 +159,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { var (working, _) = createTestBeatmap(); - currentOnlineSet = null; Beatmap.Value = working; + onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.Completed(null); }); AddUntilStep("play count is -", () => this.ChildrenOfType().ElementAt(0).Text.ToString(), () => Is.EqualTo("-")); AddUntilStep("favourites count is -", () => this.ChildrenOfType().Single().Text.ToString(), () => Is.EqualTo("-")); @@ -205,15 +177,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { switch (request) { - case GetBeatmapSetRequest set: - if (set.ID == currentOnlineSet?.OnlineID) - { - set.TriggerSuccess(currentOnlineSet); - return true; - } - - return false; - case PostBeatmapFavouriteRequest favourite: Task.Run(() => { @@ -228,13 +191,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2 }; }); - AddStep("online beatmapset", () => - { - var (working, onlineSet) = createTestBeatmap(); + AddStep("online beatmapset", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap()); - currentOnlineSet = onlineSet; - Beatmap.Value = working; - }); AddUntilStep("play count is 10000", () => this.ChildrenOfType().ElementAt(0).Text.ToString(), () => Is.EqualTo("10,000")); AddUntilStep("favourites count is 2345", () => this.ChildrenOfType().Single().Text.ToString(), () => Is.EqualTo("2,345")); @@ -251,13 +209,13 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddStep("click favourite button", () => this.ChildrenOfType().Single().TriggerClick()); AddStep("change to another beatmap", () => { - var (working, onlineSet) = createTestBeatmap(); - onlineSet.FavouriteCount = 9999; - onlineSet.HasFavourited = true; - working.BeatmapSetInfo.OnlineID = onlineSet.OnlineID = 99999; + var (working, online) = createTestBeatmap(); + online.Result!.FavouriteCount = 9999; + online.Result!.HasFavourited = true; + working.BeatmapSetInfo.OnlineID = online.Result!.OnlineID = 99999; - currentOnlineSet = onlineSet; Beatmap.Value = working; + onlineLookupResult.Value = online; }); AddStep("allow request to complete", () => resetEvent.Set()); AddUntilStep("favourites count is 9999", () => this.ChildrenOfType().Single().Text.ToString(), () => Is.EqualTo("9,999")); @@ -268,15 +226,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { switch (request) { - case GetBeatmapSetRequest set: - if (set.ID == currentOnlineSet?.OnlineID) - { - set.TriggerSuccess(currentOnlineSet); - return true; - } - - return false; - case PostBeatmapFavouriteRequest favourite: Task.Run(() => { @@ -350,7 +299,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 }); } - private (WorkingBeatmap, APIBeatmapSet) createTestBeatmap() + private (WorkingBeatmap, Screens.SelectV2.SongSelect.BeatmapSetLookupResult) createTestBeatmap() { var working = CreateWorkingBeatmap(Ruleset.Value); var onlineSet = new APIBeatmapSet @@ -371,7 +320,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 working.BeatmapSetInfo.DateSubmitted = DateTimeOffset.Now; working.BeatmapSetInfo.DateRanked = DateTimeOffset.Now; - return (working, onlineSet); + return (working, Screens.SelectV2.SongSelect.BeatmapSetLookupResult.Completed(onlineSet)); } private class TestHitObject : ConvertHitObject; diff --git a/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs b/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs index 37ac4cdb20..818176b3c4 100644 --- a/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs @@ -3,16 +3,12 @@ using System; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; @@ -20,7 +16,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Localisation; using osu.Game.Online; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; using osu.Game.Resources.Localisation.Web; using osuTK; @@ -55,10 +50,10 @@ namespace osu.Game.Screens.SelectV2 private IBindable beatmap { get; set; } = null!; [Resolved] - private IAPIProvider api { get; set; } = null!; + private IBindable onlineLookupResult { get; set; } = null!; [Resolved] - private RealmPopulatingOnlineLookupSource onlineLookupSource { get; set; } = null!; + private IAPIProvider api { get; set; } = null!; [Resolved] private RealmAccess realm { get; set; } = null!; @@ -254,6 +249,7 @@ namespace osu.Game.Screens.SelectV2 { base.LoadComplete(); beatmap.BindValueChanged(_ => updateDisplay()); + onlineLookupResult.BindValueChanged(_ => updateDisplay()); apiState = api.State.GetBoundCopy(); apiState.BindValueChanged(_ => Scheduler.AddOnce(updateDisplay), true); @@ -283,7 +279,7 @@ namespace osu.Game.Screens.SelectV2 // Needs some experimentation on what looks good. var beatmapInfo = beatmap.Value.BeatmapInfo; - var currentOnlineBeatmap = currentOnlineBeatmapSet?.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID); + var currentOnlineBeatmap = onlineLookupResult.Value?.Result?.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID); if (State.Value == Visibility.Visible && currentOnlineBeatmap != null) { @@ -365,41 +361,12 @@ namespace osu.Game.Screens.SelectV2 submitted.Date = beatmapSetInfo.DateSubmitted; ranked.Date = beatmapSetInfo.DateRanked; - if (currentOnlineBeatmapSet == null || currentOnlineBeatmapSet.OnlineID != beatmapSetInfo.OnlineID) - refetchBeatmapSet(); - updateOnlineDisplay(); } - private APIBeatmapSet? currentOnlineBeatmapSet; - private CancellationTokenSource? cancellationTokenSource; - private Task? currentFetchTask; - - private void refetchBeatmapSet() - { - var beatmapSetInfo = beatmap.Value.BeatmapSetInfo; - - cancellationTokenSource?.Cancel(); - currentOnlineBeatmapSet = null; - - if (beatmapSetInfo.OnlineID >= 1) - { - cancellationTokenSource = new CancellationTokenSource(); - currentFetchTask = onlineLookupSource.GetBeatmapSetAsync(beatmapSetInfo.OnlineID); - currentFetchTask.ContinueWith(t => - { - if (t.IsCompletedSuccessfully) - currentOnlineBeatmapSet = t.GetResultSafely(); - if (t.Exception != null) - Logger.Log($"Error when fetching online beatmap set: {t.Exception}", LoggingTarget.Network); - Scheduler.AddOnce(updateOnlineDisplay); - }); - } - } - private void updateOnlineDisplay() { - if (currentFetchTask?.IsCompleted == false) + if (onlineLookupResult.Value?.Status != SongSelect.BeatmapSetLookupStatus.Completed) { genre.Data = null; language.Data = null; @@ -407,7 +374,7 @@ namespace osu.Game.Screens.SelectV2 return; } - if (currentOnlineBeatmapSet == null) + if (onlineLookupResult.Value.Result == null) { genre.Data = ("-", null); language.Data = ("-", null); @@ -416,7 +383,7 @@ namespace osu.Game.Screens.SelectV2 { var beatmapInfo = beatmap.Value.BeatmapInfo; - var onlineBeatmapSet = currentOnlineBeatmapSet; + var onlineBeatmapSet = onlineLookupResult.Value.Result; var onlineBeatmap = onlineBeatmapSet.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID); genre.Data = (onlineBeatmapSet.Genre.Name, () => songSelect?.Search(onlineBeatmapSet.Genre.Name)); diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs index 157e2c2896..21ac04b18a 100644 --- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs @@ -8,11 +8,9 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Configuration; @@ -21,7 +19,6 @@ using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -43,6 +40,9 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private IBindable> mods { get; set; } = null!; + [Resolved] + private IBindable onlineLookupResult { get; set; } = null!; + protected override bool StartHidden => true; private ModSettingChangeTracker? settingChangeTracker; @@ -69,16 +69,9 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private LocalisationManager localisation { get; set; } = null!; - [Resolved] - private RealmPopulatingOnlineLookupSource onlineLookupSource { get; set; } = null!; - [Resolved] private RealmAccess realm { get; set; } = null!; - private APIBeatmapSet? currentOnlineBeatmapSet; - private CancellationTokenSource? cancellationTokenSource; - private Task? currentFetchTask; - private FillFlowContainer statisticsFlow = null!; public BeatmapTitleWedge() @@ -190,6 +183,7 @@ namespace osu.Game.Screens.SelectV2 working.BindValueChanged(_ => updateDisplay()); ruleset.BindValueChanged(_ => updateDisplay()); + onlineLookupResult.BindValueChanged(_ => updateDisplay()); mods.BindValueChanged(m => { @@ -230,7 +224,6 @@ namespace osu.Game.Screens.SelectV2 { var metadata = working.Value.Metadata; var beatmapInfo = working.Value.BeatmapInfo; - var beatmapSetInfo = working.Value.BeatmapSetInfo; statusPill.Status = beatmapInfo.Status; @@ -243,10 +236,6 @@ namespace osu.Game.Screens.SelectV2 artistLink.Action = () => songSelect?.Search(artistText.GetPreferred(localisation.CurrentParameters.Value.PreferOriginalScript)); updateLengthAndBpmStatistics(); - - if (currentOnlineBeatmapSet == null || currentOnlineBeatmapSet.OnlineID != beatmapSetInfo.OnlineID) - refetchBeatmapSet(); - updateOnlineDisplay(); } @@ -289,40 +278,18 @@ namespace osu.Game.Screens.SelectV2 }, token); } - private void refetchBeatmapSet() - { - var beatmapSetInfo = working.Value.BeatmapSetInfo; - - cancellationTokenSource?.Cancel(); - currentOnlineBeatmapSet = null; - - if (beatmapSetInfo.OnlineID >= 1) - { - cancellationTokenSource = new CancellationTokenSource(); - currentFetchTask = onlineLookupSource.GetBeatmapSetAsync(beatmapSetInfo.OnlineID); - currentFetchTask.ContinueWith(t => - { - if (t.IsCompletedSuccessfully) - currentOnlineBeatmapSet = t.GetResultSafely(); - if (t.Exception != null) - Logger.Log($"Error when fetching online beatmap set: {t.Exception}", LoggingTarget.Network); - Scheduler.AddOnce(updateOnlineDisplay); - }); - } - } - private void updateOnlineDisplay() { - if (currentFetchTask?.IsCompleted == false) + if (onlineLookupResult.Value?.Status != SongSelect.BeatmapSetLookupStatus.Completed) { playCount.Value = null; favouriteButton.SetLoading(); } else { - var onlineBeatmap = currentOnlineBeatmapSet?.Beatmaps.SingleOrDefault(b => b.OnlineID == working.Value.BeatmapInfo.OnlineID); + var onlineBeatmap = onlineLookupResult.Value.Result?.Beatmaps.SingleOrDefault(b => b.OnlineID == working.Value.BeatmapInfo.OnlineID); playCount.Value = new StatisticPlayCount.Data(onlineBeatmap?.PlayCount ?? -1, onlineBeatmap?.UserPlayCount ?? -1); - favouriteButton.SetBeatmapSet(currentOnlineBeatmapSet); + favouriteButton.SetBeatmapSet(onlineLookupResult.Value.Result); // the online fetch may have also updated the beatmap's status. // this needs to be checked against the *local* beatmap model rather than the online one, because it's not known here whether the status change has occurred or not diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_FavouriteButton.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_FavouriteButton.cs index 39ef0822d7..2db3ed7613 100644 --- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_FavouriteButton.cs +++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_FavouriteButton.cs @@ -229,7 +229,10 @@ namespace osu.Game.Screens.SelectV2 bool hasFavourited = favouriteRequest.Action == BeatmapFavouriteAction.Favourite; beatmapSet.HasFavourited = hasFavourited; beatmapSet.FavouriteCount += hasFavourited ? 1 : -1; - setBeatmapSet(beatmapSet, withHeartAnimation: hasFavourited); + + // if the beatmap set reference changed under the callback, abort visual updates to avoid showing stale data + if (onlineBeatmapSet == null || ReferenceEquals(beatmapSet, onlineBeatmapSet)) + setBeatmapSet(beatmapSet, withHeartAnimation: hasFavourited); }; favouriteRequest.Failure += e => { @@ -238,7 +241,10 @@ namespace osu.Game.Screens.SelectV2 Text = e.Message, Icon = FontAwesome.Solid.Times, }); - setBeatmapSet(beatmapSet, withHeartAnimation: false); + + // if the beatmap set reference changed under the callback, abort visual updates to avoid showing stale data + if (onlineBeatmapSet == null || ReferenceEquals(beatmapSet, onlineBeatmapSet)) + setBeatmapSet(beatmapSet, withHeartAnimation: false); }; api.Queue(favouriteRequest); setLoading(); diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs index 4ff571a3f8..947b8f9c7c 100644 --- a/osu.Game/Screens/SelectV2/SongSelect.cs +++ b/osu.Game/Screens/SelectV2/SongSelect.cs @@ -5,11 +5,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -34,6 +37,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Volume; @@ -133,8 +137,7 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private IDialogOverlay? dialogOverlay { get; set; } - [Cached] - private RealmPopulatingOnlineLookupSource onlineLookupSource = new RealmPopulatingOnlineLookupSource(); + private readonly RealmPopulatingOnlineLookupSource onlineLookupSource = new RealmPopulatingOnlineLookupSource(); private Bindable configBackgroundBlur = null!; @@ -349,6 +352,7 @@ namespace osu.Game.Screens.SelectV2 ensurePlayingSelected(); updateBackgroundDim(); updateWedgeVisibility(); + fetchOnlineInfo(); }); } @@ -954,6 +958,74 @@ namespace osu.Game.Screens.SelectV2 #endregion + #region Online lookups + + public enum BeatmapSetLookupStatus + { + InProgress, + Completed, + } + + public class BeatmapSetLookupResult + { + public BeatmapSetLookupStatus Status { get; } + public APIBeatmapSet? Result { get; } + + private BeatmapSetLookupResult(BeatmapSetLookupStatus status, APIBeatmapSet? result) + { + Status = status; + Result = result; + } + + public static BeatmapSetLookupResult InProgress() => new BeatmapSetLookupResult(BeatmapSetLookupStatus.InProgress, null); + public static BeatmapSetLookupResult Completed(APIBeatmapSet? beatmapSet) => new BeatmapSetLookupResult(BeatmapSetLookupStatus.Completed, beatmapSet); + } + + /// + /// Result of the latest online beatmap set lookup. + /// Note that this being or is different from + /// being a with a of null. + /// The former indicates a lookup never occurring or being in progress, while the latter indicates a completed lookup with no result. + /// + [Cached(typeof(IBindable))] + private readonly Bindable lastLookupResult = new Bindable(); + + private CancellationTokenSource? onlineLookupCancellation; + private Task? currentOnlineLookup; + + private void fetchOnlineInfo() + { + var beatmapSetInfo = Beatmap.Value.BeatmapSetInfo; + + if (lastLookupResult.Value?.Result?.OnlineID == beatmapSetInfo.OnlineID) + return; + + onlineLookupCancellation?.Cancel(); + + if (beatmapSetInfo.OnlineID < 0) + { + lastLookupResult.Value = BeatmapSetLookupResult.Completed(null); + return; + } + + lastLookupResult.Value = BeatmapSetLookupResult.InProgress(); + onlineLookupCancellation = new CancellationTokenSource(); + currentOnlineLookup = onlineLookupSource.GetBeatmapSetAsync(beatmapSetInfo.OnlineID); + currentOnlineLookup.ContinueWith(t => + { + if (t.IsCompletedSuccessfully) + Schedule(() => lastLookupResult.Value = BeatmapSetLookupResult.Completed(t.GetResultSafely())); + + if (t.Exception != null) + { + Logger.Log($"Error when fetching online beatmap set: {t.Exception}", LoggingTarget.Network); + Schedule(() => lastLookupResult.Value = BeatmapSetLookupResult.Completed(null)); + } + }); + } + + #endregion + #region Implementation of ISongSelect void ISongSelect.Search(string query) => filterControl.Search(query);