Browse Source

save collection page size (#157)

pull/158/head
Jason Dove 5 years ago committed by GitHub
parent
commit
f04ddd3a40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      ErsatzTV.Application/MediaCollections/PagedMediaCollectionsViewModel.cs
  2. 6
      ErsatzTV.Application/MediaCollections/Queries/GetPagedCollections.cs
  3. 32
      ErsatzTV.Application/MediaCollections/Queries/GetPagedCollectionsHandler.cs
  4. 5
      ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs
  5. 2
      ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs
  6. 26
      ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs
  7. 1
      ErsatzTV/ErsatzTV.csproj
  8. 38
      ErsatzTV/Pages/Collections.razor
  9. 2
      ErsatzTV/Startup.cs

6
ErsatzTV.Application/MediaCollections/PagedMediaCollectionsViewModel.cs

@ -0,0 +1,6 @@
using System.Collections.Generic;
namespace ErsatzTV.Application.MediaCollections
{
public record PagedMediaCollectionsViewModel(int TotalCount, List<MediaCollectionViewModel> Page);
}

6
ErsatzTV.Application/MediaCollections/Queries/GetPagedCollections.cs

@ -0,0 +1,6 @@
using MediatR;
namespace ErsatzTV.Application.MediaCollections.Queries
{
public record GetPagedCollections(int PageNum, int PageSize) : IRequest<PagedMediaCollectionsViewModel>;
}

32
ErsatzTV.Application/MediaCollections/Queries/GetPagedCollectionsHandler.cs

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
using static ErsatzTV.Application.MediaCollections.Mapper;
namespace ErsatzTV.Application.MediaCollections.Queries
{
public class GetPagedCollectionsHandler : IRequestHandler<GetPagedCollections, PagedMediaCollectionsViewModel>
{
private readonly IMediaCollectionRepository _mediaCollectionRepository;
public GetPagedCollectionsHandler(IMediaCollectionRepository mediaCollectionRepository) =>
_mediaCollectionRepository = mediaCollectionRepository;
public async Task<PagedMediaCollectionsViewModel> Handle(
GetPagedCollections request,
CancellationToken cancellationToken)
{
int count = await _mediaCollectionRepository.CountAllCollections();
List<MediaCollectionViewModel> page = await _mediaCollectionRepository
.GetPagedCollections(request.PageNum, request.PageSize)
.Map(list => list.Map(ProjectToViewModel).ToList());
return new PagedMediaCollectionsViewModel(count, page);
}
}
}

5
ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs

@ -26,6 +26,11 @@ namespace ErsatzTV.Core.Tests.Fakes
throw new NotSupportedException(); throw new NotSupportedException();
public Task<List<Collection>> GetAll() => throw new NotSupportedException(); public Task<List<Collection>> GetAll() => throw new NotSupportedException();
public Task<int> CountAllCollections() => throw new NotSupportedException();
public Task<List<Collection>> GetPagedCollections(int pageNumber, int pageSize) =>
throw new NotSupportedException();
public Task<Option<List<MediaItem>>> GetItems(int id) => Some(_data[id].ToList()).AsTask(); public Task<Option<List<MediaItem>>> GetItems(int id) => Some(_data[id].ToList()).AsTask();
Task<bool> IMediaCollectionRepository.Update(Collection collection) => throw new NotSupportedException(); Task<bool> IMediaCollectionRepository.Update(Collection collection) => throw new NotSupportedException();
public Task Delete(int collectionId) => throw new NotSupportedException(); public Task Delete(int collectionId) => throw new NotSupportedException();

2
ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs

@ -15,6 +15,8 @@ namespace ErsatzTV.Core.Interfaces.Repositories
Task<Option<Collection>> GetCollectionWithItemsUntracked(int id); Task<Option<Collection>> GetCollectionWithItemsUntracked(int id);
Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id); Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id);
Task<List<Collection>> GetAll(); Task<List<Collection>> GetAll();
Task<int> CountAllCollections();
Task<List<Collection>> GetPagedCollections(int pageNumber, int pageSize);
Task<Option<List<MediaItem>>> GetItems(int id); Task<Option<List<MediaItem>>> GetItems(int id);
Task<bool> Update(Collection collection); Task<bool> Update(Collection collection);
Task Delete(int collectionId); Task Delete(int collectionId);

26
ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs

@ -15,10 +15,15 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
{ {
private readonly IDbConnection _dbConnection; private readonly IDbConnection _dbConnection;
private readonly TvContext _dbContext; private readonly TvContext _dbContext;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public MediaCollectionRepository(TvContext dbContext, IDbConnection dbConnection) public MediaCollectionRepository(
TvContext dbContext,
IDbContextFactory<TvContext> dbContextFactory,
IDbConnection dbConnection)
{ {
_dbContext = dbContext; _dbContext = dbContext;
_dbContextFactory = dbContextFactory;
_dbConnection = dbConnection; _dbConnection = dbConnection;
} }
@ -29,7 +34,6 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
return collection; return collection;
} }
public async Task<bool> AddMediaItem(int collectionId, int mediaItemId) public async Task<bool> AddMediaItem(int collectionId, int mediaItemId)
{ {
var modified = false; var modified = false;
@ -168,6 +172,22 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
public Task<List<Collection>> GetAll() => public Task<List<Collection>> GetAll() =>
_dbContext.Collections.ToListAsync(); _dbContext.Collections.ToListAsync();
public Task<int> CountAllCollections() =>
_dbConnection.QuerySingleAsync<int>(@"SELECT COUNT (*) FROM Collection");
public async Task<List<Collection>> GetPagedCollections(int pageNumber, int pageSize)
{
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
return await dbContext.Collections.FromSqlRaw(
@"SELECT * FROM Collection
ORDER BY Name
LIMIT {0} OFFSET {1}",
pageSize,
(pageNumber - 1) * pageSize)
.AsNoTracking()
.ToListAsync();
}
public Task<Option<List<MediaItem>>> GetItems(int id) => public Task<Option<List<MediaItem>>> GetItems(int id) =>
Get(id).MapT(GetItemsForCollection).Bind(x => x.Sequence()); Get(id).MapT(GetItemsForCollection).Bind(x => x.Sequence());
@ -227,7 +247,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.Filter(m => ids.Contains(m.Id)) .Filter(m => ids.Contains(m.Id))
.ToListAsync(); .ToListAsync();
} }
private async Task<List<MusicVideo>> GetArtistItems(Collection collection) private async Task<List<MusicVideo>> GetArtistItems(Collection collection)
{ {
IEnumerable<int> ids = await _dbConnection.QueryAsync<int>( IEnumerable<int> ids = await _dbConnection.QueryAsync<int>(

1
ErsatzTV/ErsatzTV.csproj

@ -16,6 +16,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Blazored.LocalStorage" Version="3.0.0" />
<PackageReference Include="FluentValidation" Version="9.5.3" /> <PackageReference Include="FluentValidation" Version="9.5.3" />
<PackageReference Include="FluentValidation.AspNetCore" Version="9.5.3" /> <PackageReference Include="FluentValidation.AspNetCore" Version="9.5.3" />
<PackageReference Include="LanguageExt.Core" Version="3.4.15" /> <PackageReference Include="LanguageExt.Core" Version="3.4.15" />

38
ErsatzTV/Pages/Collections.razor

@ -1,13 +1,20 @@
@page "/media/collections" @page "/media/collections"
@using Microsoft.Extensions.Caching.Memory
@using Blazored.LocalStorage
@using ErsatzTV.Application.MediaCollections @using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands @using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.MediaCollections.Queries @using ErsatzTV.Application.MediaCollections.Queries
@using ErsatzTV.Application.MediaCards @using ErsatzTV.Application.MediaCards
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IMediator Mediator @inject IMediator Mediator
@inject IMemoryCache MemoryCache
@inject ILocalStorageService LocalStorage
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8"> <MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
<MudTable Hover="true" Items="_data" Dense="true"> <MudTable Hover="true"
ServerData="@(new Func<TableState, Task<TableData<MediaCollectionViewModel>>>(ServerReload))"
Dense="true"
@ref=" _table">
<ToolBarContent> <ToolBarContent>
<MudText Typo="Typo.h6">Collections</MudText> <MudText Typo="Typo.h6">Collections</MudText>
</ToolBarContent> </ToolBarContent>
@ -16,11 +23,7 @@
<col style="width: 120px;"/> <col style="width: 120px;"/>
</ColGroup> </ColGroup>
<HeaderContent> <HeaderContent>
<MudTh> <MudTh>Name</MudTh>
<MudTableSortLabel SortBy="new Func<MediaCollectionViewModel, object>(x => x.Name)">
Name
</MudTableSortLabel>
</MudTh>
<MudTh/> <MudTh/>
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>
@ -50,12 +53,14 @@
</MudContainer> </MudContainer>
@code { @code {
private List<MediaCollectionViewModel> _data; private MudTable<MediaCollectionViewModel> _table;
protected override Task OnParametersSetAsync() => RefreshData(); protected override async Task OnAfterRenderAsync(bool firstRender)
{
private async Task RefreshData() => int rowsPerPage = await LocalStorage.GetItemAsync<int?>("pages.collections.rows_per_page") ?? 10;
_data = await Mediator.Send(new GetAllCollections()).Map(list => list.OrderBy(vm => vm.Name).ToList()); _table.RowsPerPage = rowsPerPage;
await _table.ReloadServerData();
}
private async Task DeleteMediaCollection(MediaCardViewModel vm) private async Task DeleteMediaCollection(MediaCardViewModel vm)
{ {
@ -69,9 +74,18 @@
if (!result.Cancelled) if (!result.Cancelled)
{ {
await Mediator.Send(new DeleteCollection(collection.Id)); await Mediator.Send(new DeleteCollection(collection.Id));
await RefreshData(); await _table.ReloadServerData();
} }
} }
} }
private async Task<TableData<MediaCollectionViewModel>> ServerReload(TableState state)
{
await LocalStorage.SetItemAsync<int?>("pages.collections.rows_per_page", state.PageSize);
PagedMediaCollectionsViewModel data = await Mediator.Send(new GetPagedCollections(state.Page, state.PageSize));
return new TableData<MediaCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
} }

2
ErsatzTV/Startup.cs

@ -3,6 +3,7 @@ using System.Data;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading.Channels; using System.Threading.Channels;
using Blazored.LocalStorage;
using Dapper; using Dapper;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Channels.Queries; using ErsatzTV.Application.Channels.Queries;
@ -90,6 +91,7 @@ namespace ErsatzTV
services.AddMudServices(); services.AddMudServices();
services.AddCourier(Assembly.GetAssembly(typeof(LibraryScanProgress))); services.AddCourier(Assembly.GetAssembly(typeof(LibraryScanProgress)));
services.AddBlazoredLocalStorage();
Log.Logger.Information( Log.Logger.Information(
"ErsatzTV version {Version}", "ErsatzTV version {Version}",

Loading…
Cancel
Save