A generic helper that pages through a workshop/storage query one page at a time and accumulates selected items into Items. It creates the initial query via a factory, requests subsequent pages from the last result, tracks loading state and generation to cancel stale requests, and invokes a callback when Items or Loading change.
namespace Sandbox;
/// <summary>
/// Loads a workshop query one page at a time. Restarting it drops pages of the old query that are still on their way.
/// </summary>
public sealed class WorkshopPager<T>
{
readonly Func<Storage.Query> _createQuery;
readonly Func<Storage.QueryItem, T> _select;
readonly Action _changed;
Storage.QueryResult _last;
int _generation;
public List<T> Items { get; } = new();
public bool Loading { get; private set; }
/// <summary>
/// Whether the first page has been asked for yet.
/// </summary>
public bool Started => _generation > 0;
/// <param name="createQuery">Builds the query for the first page.</param>
/// <param name="select">Turns a result into what <see cref="Items"/> holds.</param>
/// <param name="changed">Called when <see cref="Items"/> or <see cref="Loading"/> change.</param>
public WorkshopPager( Func<Storage.Query> createQuery, Func<Storage.QueryItem, T> select, Action changed )
{
_createQuery = createQuery;
_select = select;
_changed = changed;
}
public Task Restart()
{
_generation++;
Items.Clear();
_last = null;
return Next();
}
/// <summary>
/// The next page, one at a time.
/// </summary>
public void LoadMore()
{
if ( Loading ) return;
_ = Next();
}
async Task Next()
{
var generation = _generation;
Loading = true;
Task<Storage.QueryResult> request;
if ( _last is null )
{
_changed?.Invoke();
request = _createQuery().Run();
}
else
{
request = _last.HasMoreResults() ? _last.GetNextResults() : null;
}
var result = request is null ? null : await request;
if ( generation != _generation ) return;
if ( result is not null )
{
_last = result;
if ( result.Items is not null )
Items.AddRange( result.Items.Select( _select ) );
}
Loading = false;
_changed?.Invoke();
}
}