Files
Nexus.Reader/src/NexusReader.Application/Queries/Creator/GetBookRevisionsQuery.cs
T
Antigravity c94e8f0acb feat(creator): overhaul Creator flow, editor duplication, and staging setup (#83)
This pull request completely overhauls the Creator editor flow, resolves the editor duplication race condition, aligns layout/styling themes in light and dark mode, and adds Docker staging setups.

### Key Changes
1. **Creator Flow Polish**: Redesigned the editor canvas to prevent double scrolling by delegating overflow to the editor canvas layer, updated styles to a premium aesthetic.
2. **Race Condition Prevention**: Resolved Crepe editor duplication when loading or switching chapters by tracking state via shared window maps (`window.editorCache`, `window.editorStates`) and checking `_lastInitializedEditorId` synchronously in Blazor.
3. **Theme Synchronization**: Integrated explicit theme initialization (`ThemeService.InitializeAsync()`) and anchored CSS isolation selectors to correctly sync with Light (Soft Sepia) and Deep Dark theme preferences.
4. **Staging Automation**: Created staging docker configurations with `--nexus-only` flag to allow iterative development without resetting PG/Neo4j database containers.

---------

Co-authored-by: Marek Jasiński <jasins.marek@gmail.com>
Reviewed-on: #83
Co-authored-by: Antigravity <antigravity@google.com>
Co-committed-by: Antigravity <antigravity@google.com>
2026-06-15 17:15:42 +00:00

64 lines
2.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using NexusReader.Application.Abstractions.Messaging;
using NexusReader.Application.DTOs.Creator;
using NexusReader.Data.Persistence;
using NexusReader.Domain.Exceptions;
namespace NexusReader.Application.Queries.Creator;
/// <summary>
/// Query to load all revisions for a specific Book, checking multi-tenant ownership boundaries.
/// </summary>
/// <param name="BookId">The unique identifier of the target Book.</param>
/// <param name="UserId">The ID of the creator requesting revision data.</param>
/// <param name="TenantId">The tenant ID for multi-tenant isolation.</param>
public record GetBookRevisionsQuery(Guid BookId, string UserId, string TenantId) : IQuery<List<CreatorBookRevisionDto>>;
/// <summary>
/// Handler that lists past revisions of a Book, verifying ownership to prevent cross-tenant leakages.
/// </summary>
public class GetBookRevisionsQueryHandler : IQueryHandler<GetBookRevisionsQuery, List<CreatorBookRevisionDto>>
{
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
public GetBookRevisionsQueryHandler(IDbContextFactory<AppDbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<FluentResults.Result<List<CreatorBookRevisionDto>>> Handle(GetBookRevisionsQuery request, CancellationToken cancellationToken)
{
using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
// Verify the book exists and belongs to this tenant/user to prevent cross-tenant data leaks
var bookExists = await dbContext.Books
.AnyAsync(b => b.Id == request.BookId && b.UserId == request.UserId && b.TenantId == request.TenantId, cancellationToken);
if (!bookExists)
{
return FluentResults.Result.Fail<List<CreatorBookRevisionDto>>(new FluentResults.Error($"Book with ID '{request.BookId}' was not found."));
}
// Fetch all revisions sorted chronologically
var revisions = await dbContext.BookRevisions
.AsNoTracking()
.Where(r => r.BookId == request.BookId)
.OrderByDescending(r => r.CreatedAt)
.Select(r => new CreatorBookRevisionDto(
r.Id,
r.VersionString,
r.IsPublished,
r.CreatedAt,
r.PublishedAt
))
.ToListAsync(cancellationToken);
return FluentResults.Result.Ok(revisions);
}
}