refactor: reuse AlbumGrid in CosplayerDetail via FetchAlbums delegate
Replace manual album state management (inline AlbumCard loop, own infinite scroll, own sentinel/observer, _ = ReloadAlbums() discard lambdas) with <AlbumGrid> using a new FetchAlbums delegate parameter. - AlbumGrid.razor: add FetchAlbums Func parameter; use it in Reload()/LoadMoreAlbums() when set - CosplayerDetail.razor: replace manual album rendering + state with <AlbumGrid FetchAlbums=@FetchPersonAlbums />; remove all album state management fields (allAlbumsDict, currentAlbumPage, etc.), ReloadAlbums/LoadMoreAlbums/OnAfterRenderAsync methods, and IAsyncDisposable; SortFilterBar now uses clean lambdas - CosplayerDetail.razor.css: remove unused album-grid overrides Closes #120
This commit is contained in:
@@ -4,14 +4,11 @@
|
||||
@using Butter.Types
|
||||
@using Microsoft.Extensions.Options
|
||||
@using System.Globalization
|
||||
@implements IAsyncDisposable
|
||||
|
||||
@inject PersonService personService
|
||||
@inject AlbumService albumService
|
||||
@inject LoginService loginService
|
||||
@inject IOptions<ServiceOptions> serviceOptions
|
||||
@inject NavigationManager navigationManager
|
||||
@inject IJSRuntime jsRuntime
|
||||
|
||||
<PageTitle>@(person?.Name ?? "Cosplayer")</PageTitle>
|
||||
|
||||
@@ -71,15 +68,15 @@
|
||||
</div>
|
||||
|
||||
<SortFilterBar SearchQuery="@albumSearchQuery"
|
||||
SearchQueryChanged="@((string? v) => { albumSearchQuery = v; _ = ReloadAlbums(); })"
|
||||
SearchQueryChanged="@((string? v) => albumSearchQuery = v)"
|
||||
SortBy="@albumSortBy"
|
||||
SortByChanged="@((string? v) => { albumSortBy = v; _ = ReloadAlbums(); })"
|
||||
SortByChanged="@((string? v) => albumSortBy = v)"
|
||||
SortAsc="@albumSortAsc"
|
||||
SortAscChanged="@((bool v) => { albumSortAsc = v; _ = ReloadAlbums(); })"
|
||||
SortAscChanged="@((bool v) => albumSortAsc = v)"
|
||||
SortOptions="@albumSortOptions">
|
||||
<ActionButtons>
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
@if (allAlbumsDict.Count > 0) {
|
||||
@if (person.TotalAlbums > 0) {
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn @(viewMode == "masonry" ? "btn-primary" : "btn-secondary")"
|
||||
@onclick="SetMasonry" title="Masonry">
|
||||
@@ -95,7 +92,7 @@
|
||||
<button class="btn btn-success btn-sm" @onclick="OpenAlbumAssigner">
|
||||
<i class="bi bi-plus-circle"></i> Add Albums
|
||||
</button>
|
||||
@if (allAlbumsDict.Count > 0) {
|
||||
@if (person.TotalAlbums > 0) {
|
||||
if (selectMode) {
|
||||
<button class="btn btn-warning btn-sm" @onclick="RemoveSelectedAlbums" disabled="@(selectedAlbumIds.Count == 0)">
|
||||
<i class="bi bi-link-45deg"></i> Unlink @selectedAlbumIds.Count
|
||||
@@ -117,24 +114,15 @@
|
||||
</ActionButtons>
|
||||
</SortFilterBar>
|
||||
|
||||
@if (allAlbumsDict.Count > 0) {
|
||||
<div class="@("album-grid " + viewMode)">
|
||||
@foreach (var album in allAlbumsDict.Values) {
|
||||
<AlbumCard Album="album"
|
||||
SelectionMode="selectMode"
|
||||
Selected="selectedAlbumIds.Contains(album.Id)"
|
||||
OnToggleSelection="ToggleSelectAlbum" />
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (isLoadingMore) {
|
||||
<LoadSpinner/>
|
||||
}
|
||||
|
||||
<div @ref="sentinelRef" class="masonry-sentinel"></div>
|
||||
} else {
|
||||
<EmptyState Variant="info" Title="No albums for this cosplayer" />
|
||||
}
|
||||
<AlbumGrid SearchQuery="@albumSearchQuery"
|
||||
SortBy="@albumSortBy"
|
||||
SortAsc="@albumSortAsc"
|
||||
ViewMode="@viewMode"
|
||||
SelectionMode="@selectMode"
|
||||
SelectedIds="@selectedAlbumIds"
|
||||
OnToggleSelection="@ToggleSelectAlbum"
|
||||
LoadVersion="@albumLoadVersion"
|
||||
FetchAlbums="@FetchPersonAlbums" />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -212,14 +200,7 @@
|
||||
["Asset count"] = "assets"
|
||||
};
|
||||
|
||||
// Infinite scroll
|
||||
Dictionary<Guid, AlbumPreviewDto> allAlbumsDict = [];
|
||||
int currentAlbumPage;
|
||||
const int AlbumPageSize = 30;
|
||||
bool hasMoreAlbums = true;
|
||||
bool isLoadingMore;
|
||||
ElementReference sentinelRef;
|
||||
DotNetObjectReference<CosplayerDetail>? dotNetRef;
|
||||
int albumLoadVersion;
|
||||
|
||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
||||
|
||||
@@ -241,70 +222,21 @@
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender) {
|
||||
if (allAlbumsDict.Count > 0 && dotNetRef == null) {
|
||||
dotNetRef = DotNetObjectReference.Create(this);
|
||||
await jsRuntime.InvokeVoidAsync(
|
||||
"albumObserver.observeBottom", sentinelRef, dotNetRef
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async Task LoadPerson() {
|
||||
isLoading = true;
|
||||
allAlbumsDict.Clear();
|
||||
currentAlbumPage = 0;
|
||||
hasMoreAlbums = true;
|
||||
|
||||
person = await personService.GetByIdAsync(Id, 0, AlbumPageSize);
|
||||
if (person?.Albums != null) {
|
||||
foreach (var a in person.Albums)
|
||||
allAlbumsDict[a.Id] = a;
|
||||
hasMoreAlbums = person.Albums.Count >= AlbumPageSize;
|
||||
currentAlbumPage = 1;
|
||||
}
|
||||
|
||||
person = await personService.GetByIdAsync(Id, 0, 30);
|
||||
bannerCoverUrls = person?.Albums?
|
||||
.Where(a => a.CoverAssetId.HasValue)
|
||||
.OrderBy(_ => Random.Shared.Next())
|
||||
.Take(3)
|
||||
.Select(a => ThumbUrl(a.CoverAssetId!.Value))
|
||||
.ToList() ?? [];
|
||||
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
async Task ReloadAlbums() {
|
||||
currentAlbumPage = 0;
|
||||
hasMoreAlbums = true;
|
||||
|
||||
person = await personService.GetByIdAsync(Id, 0, AlbumPageSize, albumSearchQuery, albumSortBy, albumSortAsc);
|
||||
allAlbumsDict.Clear();
|
||||
if (person?.Albums != null) {
|
||||
foreach (var a in person.Albums)
|
||||
allAlbumsDict[a.Id] = a;
|
||||
hasMoreAlbums = person.Albums.Count >= AlbumPageSize;
|
||||
currentAlbumPage = 1;
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public async Task LoadMoreAlbums() {
|
||||
if (isLoadingMore || !hasMoreAlbums) return;
|
||||
isLoadingMore = true;
|
||||
StateHasChanged();
|
||||
|
||||
var fresh = await personService.GetByIdAsync(Id, currentAlbumPage, AlbumPageSize, albumSearchQuery, albumSortBy, albumSortAsc);
|
||||
if (fresh?.Albums?.Count > 0) {
|
||||
foreach (var a in fresh.Albums)
|
||||
allAlbumsDict[a.Id] = a;
|
||||
currentAlbumPage++;
|
||||
}
|
||||
hasMoreAlbums = fresh?.Albums?.Count >= AlbumPageSize;
|
||||
isLoadingMore = false;
|
||||
StateHasChanged();
|
||||
async Task<List<AlbumPreviewDto>?> FetchPersonAlbums(int page, int pageSize, string? search, string? sortBy, bool sortAsc) {
|
||||
var p = await personService.GetByIdAsync(Id, page, pageSize, search, sortBy, sortAsc);
|
||||
return p?.Albums;
|
||||
}
|
||||
|
||||
void OpenEditForm() => showForm = true;
|
||||
@@ -347,6 +279,7 @@
|
||||
showAlbumAssigner = false;
|
||||
unassignedAlbums = null;
|
||||
await LoadPerson();
|
||||
albumLoadVersion++;
|
||||
}
|
||||
|
||||
async Task RemoveSelectedAlbums() {
|
||||
@@ -356,6 +289,7 @@
|
||||
selectMode = false;
|
||||
selectedAlbumIds.Clear();
|
||||
await LoadPerson();
|
||||
albumLoadVersion++;
|
||||
}
|
||||
|
||||
async Task ConfirmDelete() {
|
||||
@@ -365,11 +299,4 @@
|
||||
}
|
||||
|
||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
||||
|
||||
public async ValueTask DisposeAsync() {
|
||||
if (dotNetRef != null) {
|
||||
await jsRuntime.InvokeVoidAsync("albumObserver.dispose");
|
||||
dotNetRef.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
[Parameter] public HashSet<Guid> SelectedIds { get; set; } = [];
|
||||
[Parameter] public EventCallback<Guid> OnToggleSelection { get; set; }
|
||||
[Parameter] public int LoadVersion { get; set; }
|
||||
[Parameter] public Func<int, int, string?, string?, bool, Task<List<AlbumPreviewDto>?>>? FetchAlbums { get; set; }
|
||||
|
||||
List<AlbumPreviewDto> albums = [];
|
||||
int currentPage = 0;
|
||||
@@ -87,7 +88,9 @@
|
||||
isLoading = true;
|
||||
StateHasChanged();
|
||||
|
||||
var items = await albumService.GetAlbumsAsync(0, 30, SearchQuery, SortBy, SortAsc);
|
||||
var items = FetchAlbums != null
|
||||
? await FetchAlbums(0, 30, SearchQuery, SortBy, SortAsc)
|
||||
: await albumService.GetAlbumsAsync(0, 30, SearchQuery, SortBy, SortAsc);
|
||||
if (items?.Count > 0) {
|
||||
albums = items;
|
||||
currentPage = 1;
|
||||
@@ -102,7 +105,9 @@
|
||||
isLoadingMore = true;
|
||||
StateHasChanged();
|
||||
|
||||
var items = await albumService.GetAlbumsAsync(currentPage, 30, SearchQuery, SortBy, SortAsc);
|
||||
var items = FetchAlbums != null
|
||||
? await FetchAlbums(currentPage, 30, SearchQuery, SortBy, SortAsc)
|
||||
: await albumService.GetAlbumsAsync(currentPage, 30, SearchQuery, SortBy, SortAsc);
|
||||
if (items?.Count > 0) {
|
||||
albums.AddRange(items);
|
||||
currentPage++;
|
||||
|
||||
Reference in New Issue
Block a user