mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 18:52:55 +08:00
Combine pagination logic into BeatmapListingFilterControl
This commit is contained in:
parent
04c9973526
commit
c836c9319b
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -12,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -20,9 +22,34 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapListingFilterControl : CompositeDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Fired when a search finishes. Contains only new items in the case of pagination.
|
||||
/// </summary>
|
||||
public Action<List<BeatmapSetInfo>> SearchFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when search criteria change.
|
||||
/// </summary>
|
||||
public Action SearchStarted;
|
||||
private List<BeatmapSetInfo> currentBeatmaps;
|
||||
|
||||
/// <summary>
|
||||
/// True when pagination has reached the end of available results.
|
||||
/// </summary>
|
||||
private bool noMoreResults;
|
||||
|
||||
/// <summary>
|
||||
/// The current page fetched of results (zero index).
|
||||
/// </summary>
|
||||
public int CurrentPage { get; private set; }
|
||||
|
||||
private readonly BeatmapListingSearchControl searchControl;
|
||||
private readonly BeatmapListingSortTabControl sortControl;
|
||||
private readonly Box sortControlBackground;
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
|
||||
private SearchBeatmapSetsRequest getSetsRequest;
|
||||
private SearchBeatmapSetsResponse lastResponse;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
@ -30,19 +57,11 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private readonly BeatmapListingSearchControl searchControl;
|
||||
private readonly BeatmapListingSortTabControl sortControl;
|
||||
private readonly Box sortControlBackground;
|
||||
|
||||
private BeatmapListingPager beatmapListingPager;
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
private ScheduledDelegate queryPagingDebounce;
|
||||
|
||||
public BeatmapListingFilterControl()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -118,69 +137,80 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
public void TakeFocus() => searchControl.TakeFocus();
|
||||
|
||||
public void ShowMore()
|
||||
/// <summary>
|
||||
/// Fetch the next page of results. May result in a no-op if a fetch is already in progress, or if there are no results left.
|
||||
/// </summary>
|
||||
public void FetchNextPage()
|
||||
{
|
||||
if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage)
|
||||
// there may be no results left.
|
||||
if (noMoreResults)
|
||||
return;
|
||||
|
||||
if (queryPagingDebounce != null)
|
||||
// there may already be an active request.
|
||||
if (getSetsRequest != null)
|
||||
return;
|
||||
|
||||
beatmapListingPager.FetchNextPage();
|
||||
if (lastResponse != null)
|
||||
CurrentPage++;
|
||||
|
||||
performRequest();
|
||||
}
|
||||
|
||||
private void queueUpdateSearch(bool queryTextChanged = false)
|
||||
{
|
||||
SearchStarted?.Invoke();
|
||||
|
||||
cancelSearch();
|
||||
resetSearch();
|
||||
|
||||
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
|
||||
queryChangedDebounce = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
resetSearch();
|
||||
FetchNextPage();
|
||||
}, queryTextChanged ? 500 : 100);
|
||||
}
|
||||
|
||||
private void updateSearch()
|
||||
private void performRequest()
|
||||
{
|
||||
cancelSearch();
|
||||
|
||||
beatmapListingPager = new BeatmapListingPager(
|
||||
api,
|
||||
rulesets,
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(
|
||||
searchControl.Query.Value,
|
||||
searchControl.Ruleset.Value,
|
||||
lastResponse?.Cursor,
|
||||
searchControl.Category.Value,
|
||||
sortControl.Current.Value,
|
||||
sortControl.SortDirection.Value
|
||||
);
|
||||
sortControl.SortDirection.Value);
|
||||
|
||||
beatmapListingPager.PageFetched += onSearchFinished;
|
||||
getSetsRequest.Success += response =>
|
||||
{
|
||||
var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList();
|
||||
|
||||
ShowMore();
|
||||
if (sets.Count == 0)
|
||||
noMoreResults = true;
|
||||
|
||||
lastResponse = response;
|
||||
getSetsRequest = null;
|
||||
|
||||
SearchFinished?.Invoke(sets);
|
||||
};
|
||||
|
||||
api.Queue(getSetsRequest);
|
||||
}
|
||||
|
||||
private void cancelSearch()
|
||||
private void resetSearch()
|
||||
{
|
||||
beatmapListingPager?.Reset();
|
||||
noMoreResults = false;
|
||||
CurrentPage = 0;
|
||||
|
||||
lastResponse = null;
|
||||
|
||||
getSetsRequest?.Cancel();
|
||||
getSetsRequest = null;
|
||||
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
queryPagingDebounce?.Cancel();
|
||||
queryPagingDebounce = null;
|
||||
}
|
||||
|
||||
private void onSearchFinished(List<BeatmapSetInfo> beatmaps)
|
||||
{
|
||||
queryPagingDebounce = Scheduler.AddDelayed(() => queryPagingDebounce = null, 1000);
|
||||
|
||||
if (currentBeatmaps == null || !beatmapListingPager.IsPastFirstPage)
|
||||
currentBeatmaps = beatmaps;
|
||||
else
|
||||
currentBeatmaps.AddRange(beatmaps);
|
||||
|
||||
SearchFinished?.Invoke(beatmaps);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
cancelSearch();
|
||||
resetSearch();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
@ -1,88 +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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapListingPager
|
||||
{
|
||||
private readonly IAPIProvider api;
|
||||
private readonly RulesetStore rulesets;
|
||||
private readonly string query;
|
||||
private readonly RulesetInfo ruleset;
|
||||
private readonly SearchCategory searchCategory;
|
||||
private readonly SortCriteria sortCriteria;
|
||||
private readonly SortDirection sortDirection;
|
||||
|
||||
public event Action<List<BeatmapSetInfo>> PageFetched;
|
||||
|
||||
private SearchBeatmapSetsRequest getSetsRequest;
|
||||
private SearchBeatmapSetsResponse lastResponse;
|
||||
|
||||
private bool isLastPageFetched;
|
||||
private bool isFetching => getSetsRequest != null;
|
||||
public bool IsPastFirstPage { get; private set; }
|
||||
public bool CanFetchNextPage => !isLastPageFetched && !isFetching;
|
||||
|
||||
public BeatmapListingPager(IAPIProvider api, RulesetStore rulesets, string query, RulesetInfo ruleset, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending)
|
||||
{
|
||||
this.api = api;
|
||||
this.rulesets = rulesets;
|
||||
this.query = query;
|
||||
this.ruleset = ruleset;
|
||||
this.searchCategory = searchCategory;
|
||||
this.sortCriteria = sortCriteria;
|
||||
this.sortDirection = sortDirection;
|
||||
}
|
||||
|
||||
public void FetchNextPage()
|
||||
{
|
||||
if (isFetching)
|
||||
return;
|
||||
|
||||
if (lastResponse != null)
|
||||
IsPastFirstPage = true;
|
||||
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(
|
||||
query,
|
||||
ruleset,
|
||||
lastResponse?.Cursor,
|
||||
searchCategory,
|
||||
sortCriteria,
|
||||
sortDirection);
|
||||
|
||||
getSetsRequest.Success += response =>
|
||||
{
|
||||
var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList();
|
||||
|
||||
if (sets.Count == 0)
|
||||
isLastPageFetched = true;
|
||||
|
||||
lastResponse = response;
|
||||
getSetsRequest = null;
|
||||
|
||||
PageFetched?.Invoke(sets);
|
||||
};
|
||||
|
||||
api.Queue(getSetsRequest);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
isLastPageFetched = false;
|
||||
IsPastFirstPage = false;
|
||||
|
||||
lastResponse = null;
|
||||
|
||||
getSetsRequest?.Cancel();
|
||||
getSetsRequest = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -34,8 +36,6 @@ namespace osu.Game.Overlays
|
||||
private NotFoundDrawable notFoundContent;
|
||||
|
||||
private OverlayScrollContainer resultScrollContainer;
|
||||
private const int pagination_scroll_distance = 500;
|
||||
private bool shouldAddNextPage => resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance);
|
||||
|
||||
public BeatmapListingOverlay()
|
||||
: base(OverlayColourScheme.Blue)
|
||||
@ -121,51 +121,45 @@ namespace osu.Game.Overlays
|
||||
loadingLayer.Show();
|
||||
}
|
||||
|
||||
private Task panelLoadDelegate;
|
||||
|
||||
private void onSearchFinished(List<BeatmapSetInfo> beatmaps)
|
||||
{
|
||||
//No matches case
|
||||
if (!beatmaps.Any())
|
||||
var newPanels = beatmaps.Select<BeatmapSetInfo, BeatmapPanel>(b => new GridBeatmapPanel(b)
|
||||
{
|
||||
LoadComponentAsync(notFoundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
return;
|
||||
}
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
});
|
||||
|
||||
//New query case
|
||||
if (!shouldAddNextPage)
|
||||
if (filterControl.CurrentPage == 0)
|
||||
{
|
||||
//Spawn new child
|
||||
var newPanels = new FillFlowContainer<BeatmapPanel>
|
||||
//No matches case
|
||||
if (!newPanels.Any())
|
||||
{
|
||||
LoadComponentAsync(notFoundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
return;
|
||||
}
|
||||
|
||||
// spawn new children with the contained so we only clear old content at the last moment.
|
||||
var content = new FillFlowContainer<BeatmapPanel>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(10),
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding { Vertical = 15 },
|
||||
ChildrenEnumerable = beatmaps.Select<BeatmapSetInfo, BeatmapPanel>(b => new GridBeatmapPanel(b)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
})
|
||||
ChildrenEnumerable = newPanels
|
||||
};
|
||||
|
||||
foundContent = newPanels;
|
||||
LoadComponentAsync(foundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
panelLoadDelegate = LoadComponentAsync(foundContent = content, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
//Pagination case
|
||||
else
|
||||
{
|
||||
beatmaps.ForEach(x =>
|
||||
panelLoadDelegate = LoadComponentsAsync(newPanels, loaded =>
|
||||
{
|
||||
LoadComponentAsync(new GridBeatmapPanel(x)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
}, loaded =>
|
||||
{
|
||||
foundContent.Add(loaded);
|
||||
loaded.FadeIn(200, Easing.OutQuint);
|
||||
});
|
||||
lastFetchDisplayedTime = Time.Current;
|
||||
foundContent.AddRange(loaded);
|
||||
loaded.ForEach(p => p.FadeIn(200, Easing.OutQuint));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -173,6 +167,7 @@ namespace osu.Game.Overlays
|
||||
private void addContentToPlaceholder(Drawable content)
|
||||
{
|
||||
loadingLayer.Hide();
|
||||
lastFetchDisplayedTime = Time.Current;
|
||||
|
||||
var lastContent = currentContent;
|
||||
|
||||
@ -242,12 +237,22 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
private const double time_between_fetches = 500;
|
||||
|
||||
private double lastFetchDisplayedTime;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (shouldAddNextPage)
|
||||
filterControl.ShowMore();
|
||||
const int pagination_scroll_distance = 500;
|
||||
|
||||
bool shouldShowMore = panelLoadDelegate?.IsCompleted != false
|
||||
&& Time.Current - lastFetchDisplayedTime > time_between_fetches
|
||||
&& (resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance));
|
||||
|
||||
if (shouldShowMore)
|
||||
filterControl.FetchNextPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user