feat: implement real-time reading progress and refactor profile to CQRS

This commit is contained in:
2026-05-10 18:15:29 +02:00
parent 10dc511f2a
commit b456194ea1
20 changed files with 1738 additions and 96 deletions
@@ -35,6 +35,15 @@ public class UpdateReadingProgressCommandHandler : IRequestHandler<UpdateReading
user.LastReadPageId = request.PageId;
user.LastReadAt = now;
// Update specific Ebook progress
var ebook = await context.Ebooks.FirstOrDefaultAsync(e => e.Id == request.EbookId, cancellationToken);
if (ebook != null)
{
ebook.Progress = request.Progress;
ebook.LastChapter = request.ChapterTitle;
ebook.LastReadDate = now;
}
await context.SaveChangesAsync(cancellationToken);
// Broadcast to other devices
@@ -15,12 +15,12 @@ public class SyncHub : Hub
_mediator = mediator;
}
public async Task UpdateProgress(string pageId)
public async Task UpdateProgress(string pageId, Guid ebookId, double progress, string? chapterTitle)
{
var userId = Context.UserIdentifier;
if (!string.IsNullOrEmpty(userId))
{
await _mediator.Send(new UpdateReadingProgressCommand(pageId, userId, Context.ConnectionId));
await _mediator.Send(new UpdateReadingProgressCommand(pageId, userId, ebookId, progress, chapterTitle, Context.ConnectionId));
}
}
@@ -4,14 +4,23 @@ using FluentResults;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.Queries.Reader;
using VersOne.Epub;
using Microsoft.EntityFrameworkCore;
using NexusReader.Data.Persistence;
using NexusReader.Domain.Entities;
namespace NexusReader.Infrastructure.Services;
public class EpubService : IEpubService
{
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
private const string EpubPath = "wwwroot/assets/book.epub";
private const int WordThreshold = 1000;
public EpubService(IDbContextFactory<AppDbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex)
{
try
@@ -100,7 +109,14 @@ public class EpubService : IEpubService
blocks.Add(CreateAiTrigger($"trigger-{blockCounter++}"));
}
return Result.Ok(new ReaderPageViewModel(blocks, chapterIndex, readingOrder.Count, chapterTitle));
// Find the EbookId from DB for this file
using var context = await _dbContextFactory.CreateDbContextAsync();
var ebookId = await context.Ebooks
.Where(e => e.FilePath.Contains("book.epub"))
.Select(e => e.Id)
.FirstOrDefaultAsync();
return Result.Ok(new ReaderPageViewModel(blocks, chapterIndex, readingOrder.Count, chapterTitle, ebookId));
}
catch (Exception ex)
{