diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index fb1fa09253..f12de8562d 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -6,9 +6,9 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Models; using osu.Game.Online.API; using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; @@ -30,72 +30,45 @@ namespace osu.Game.Screens.Select.Carousel [Resolved] private IAPIProvider api { get; set; } + private IDisposable scoreSubscription; + + private bool rankUpdatePending; + public TopLocalRank(BeatmapInfo beatmapInfo) : base(null) { this.beatmapInfo = beatmapInfo; } - [BackgroundDependencyLoader] - private void load() - { - ruleset.ValueChanged += _ => fetchAndLoadTopScore(); - - fetchAndLoadTopScore(); - } - protected override void LoadComplete() { base.LoadComplete(); - scoreSubscription = realmFactory.Context.All() - .Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} = $0", beatmapInfo.ID) - .QueryAsyncWithNotifications((_, changes, ___) => - { - if (changes == null) - return; - - fetchTopScoreRank(); - }); - } - - private IDisposable scoreSubscription; - - private ScheduledDelegate scheduledRankUpdate; - - private void fetchAndLoadTopScore() - { - // TODO: this lookup likely isn't required, we can use the results of the subscription directly. - var rank = fetchTopScoreRank(); - - scheduledRankUpdate = Scheduler.Add(() => + ruleset.BindValueChanged(_ => { - Rank = rank; - + rankUpdatePending = true; // Required since presence is changed via IsPresent override Invalidate(Invalidation.Presence); - }); + + scoreSubscription?.Dispose(); + scoreSubscription = realmFactory.Context.All() + .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0" + + $"&& {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1" + + $"&& {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2" + + $"&& {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName) + .OrderByDescending(s => s.TotalScore) + .QueryAsyncWithNotifications((items, changes, ___) => + { + if (changes == null) + rankUpdatePending = false; + + Rank = items.FirstOrDefault()?.Rank; + }); + }, true); } // We're present if a rank is set, or if there is a pending rank update (IsPresent = true is required for the scheduler to run). - public override bool IsPresent => base.IsPresent && (Rank != null || scheduledRankUpdate?.Completed == false); - - private ScoreRank? fetchTopScoreRank() - { - if (realmFactory == null || beatmapInfo == null || ruleset?.Value == null || api?.LocalUser.Value == null) - return null; - - using (var realm = realmFactory.CreateContext()) - { - return realm.All() - .AsEnumerable() - // TODO: update to use a realm filter directly (or at least figure out the beatmap part to reduce scope). - .Where(s => s.UserID == api.LocalUser.Value.Id && s.BeatmapInfoID == beatmapInfo.ID && s.RulesetID == ruleset.Value.ID && !s.DeletePending) - .OrderByDescending(s => s.TotalScore) - .FirstOrDefault() - ?.Rank; - } - } + public override bool IsPresent => base.IsPresent && (Rank != null || rankUpdatePending); protected override void Dispose(bool isDisposing) {