- Added optional userId parameter to ToAlbumPreviewDto and ToPersonDetailedDto
- AlbumRepository.SearchQuery and PersonMapper now pass userId for visibility-scoped counts
- ToAlbumFullDto unchanged (callers pre-filter assets before mapping)
- Private helper filters out non-visible assets for non-curator users
- Added IPersonRepository injection into AssetController
- Added FindWithAlbums to IAssetRepository/AssetRepository for single-entity checks
- Modified FindBulk to include Albums navigation property
- Single Update/Delete: Maintainers can act on assets in albums of maintained persons
- BulkUpdate/BulkDelete: same scope expansion for Maintainers
- Changed UploadedBy from Guid to Guid? to represent system/scanner uploader
- Controller Create method now handles null UploadedBy (skips user validation)
- Changed column default from 0 (Public) to 1 (Protected) for Assets, People, Albums
- Reordered migration to add Visibility column before dropping IsPubliclyShared
- Assets that were IsPubliclyShared = true are restored to Public (0) via SQL
- Private assets and all existing people/albums default to Protected (1)
Old enum values stored in DB:
- Curator (value 1) → Maintainer after renumbering (incorrect)
- Admin (value 2) → Curator after renumbering (incorrect)
Add SQL UPDATE statements to remap:
1. Old Admin (2) → New Admin (3) — done first to free up value 2
2. Old Curator (1) → New Curator (2) — done second
- GetVisibleAsset: accept EAccessLevel enum instead of bool isCuratorOrAbove
- Admin: sees all assets including deleted
- Curator: sees own deleted + all non-deleted
- Maintainer/User: only non-deleted + visibility filtering
- BulkUpdate: Maintainer can update albums of persons they maintain
- Delete: Maintainer can delete albums of persons they maintain
- BulkDelete: same scoped filtering
Previously Maintainer saw all people like Curator+. Now:
- Curator+: sees all people
- Maintainer: sees Public + Protected + persons they maintain
- User: sees Public + Protected only
Replaces the hardcoded zero with a real query counting people
without a profile picture (ProfileAssetId == null).
Renamed from CosplayersMissingCover to CosplayersMissingProfile
to reflect the actual metric.
DTO, repository, and UI label updated accordingly.
fixes#97
Replace inline AlbumPreviewDto construction in AlbumRepository.SearchQuery
with a call to the new ToAlbumPreviewDto(album, userId, accessLevel) mapper
overload, making the visibility logic reusable and the repository more compact.
- Remove unused using directives across C# and Razor files
- Remove unused IServiceProvider from SettingsRepository
- Simplify null/empty string checks in StatsRepository
- Add null-safe navigation for Albums/Tags in stats queries
- Initialize Asset.Hash default to prevent null refs
- Deduplicate AssetIds in AssetPicker
- Add OnStartedWaiting/OnFinishedWaiting/OnProgressChanged to Job
- Add global SearchDropdown component with keyboard nav
- Fix XML doc param mismatches
Resolved merge conflict in CosplayerDetail.razor:
- Kept SortFilterBar with instant search/sort (from feat/cosplayer-detail-search)
- Adapted allAlbums references to allAlbumsDict (from feature/global-search-dropdown)
- Kept updated PersonController.cs and PersonService.cs from both branches
- Backend: accept PagedSearchParametersDto on GET /api/person/{id}
with in-memory search (case-insensitive title Contains) and sort
(name/created/updated/assets) before pagination
- Frontend service: add search, sortBy, sortAsc params to GetByIdAsync
- Frontend page: replace inline action buttons with reusable SortFilterBar
component, wire search/sort state into initial load and infinite scroll
Per review feedback: only Page being negative is truly invalid.
PageSize validation reverted to original < 0 (allowing 0).
PersonController keeps minimal Page < 0 check since it had none before.
JobsController aligned for consistency.
EF Core cannot translate SharedWith.Any() through the many-to-many
AlbumAsset join table inside a SQL WHERE clause. Moved the visibility
filter to client-side after loading the page's records with Include.
PersonRepository.SearchQuery and AlbumRepository.SearchQuery now:
1. Get paginated IDs without visibility filter (translatable SQL)
2. Load those records with Include chains
3. Filter visibility in C# (SharedWith.Any(), etc.)
4. Project to DTOs
- AssetRepository: all Skip() formulas changed from (page-1)*size to page*size
- JobRecordRepository: same formula change for GetPastRootJobs
- AssetController: removed Page+1 bridge conversion, passes Page directly
- JobsController: GetPast default changed from 1 to 0, added validation
- AlbumController, TagController, PersonController: consistent Page<0/PageSize<1 validation
- IAssetRepository XML doc: 1-based → zero-based
- Home.razor: shifted all internal page state from 1-based to 0-based
Closes#95
Simplify the 'assets'/'albums' sort to use total count instead of
visibility-filtered count, eliminating the expensive correlated COUNT
subquery from ORDER BY (which evaluated 25k times for every album).
Add pg_trgm extension and GIN trigram indexes on People.Name and
Albums.Title for efficient ILike %%query%% searches.
The SELECT projection still computes visibility-filtered counts for
accuracy, but only for the paginated subset (30 rows).
Migration: 20260712162406_AddTrigramIndexes
Create IX_Assets_VisibleForSearch on (IsPubliclyShared, OwnerId)
WHERE "DeletedAt" IS NULL to accelerate the correlated subqueries
used in both AlbumRepository and PersonRepository for filtering,
sorting by asset/album count, and projecting AssetCount/TotalAlbums.
- Fluent API in LactoseDbContext.OnModelCreating
- Generated migration via dotnet ef migrations add
Align AlbumRepository.SearchQuery with the PersonRepository pattern:
return AlbumPreviewDto directly with access-level filtering at the
query level, avoiding loading full entity graphs and filtering
in-memory in the controller.
- Add userId/accessLevel params to SearchQuery
- Push visibility filter into the query for regular users
- Compute AssetCount with same visibility logic in the projection
- Sort by 'assets' uses visible count for regular users
- Remove unused AlbumMapper.ToAlbumPreviewDto extension
- Simplify AlbumController.Search to just return repo results
Replace the TempData-like TotalVisibleAlbums [NotMapped] property on
Person with a direct DTO projection in PersonRepository.SearchQuery.
The repository now returns IEnumerable<PersonPreviewDto>, computing
the visibility-aware album count in the EF Core subquery and projecting
only the needed columns. This avoids polluting the entity model with a
context-dependent property.
- Add Person.TotalVisibleAlbums [NotMapped] populated by repository
- Use visibility-aware album count in PersonMapper
- Restructure PersonRepository.SearchQuery with two-step pagination
for correct ordering and per-user album visibility filtering