fix: ensure user-specific ebook tracking and auto-provisioning for progress sync
This commit is contained in:
@@ -5,5 +5,5 @@ namespace NexusReader.Application.Abstractions.Services;
|
|||||||
|
|
||||||
public interface IEpubService
|
public interface IEpubService
|
||||||
{
|
{
|
||||||
Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex);
|
Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex, string? userId = null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ using NexusReader.Application.Abstractions.Messaging;
|
|||||||
|
|
||||||
namespace NexusReader.Application.Queries.Reader;
|
namespace NexusReader.Application.Queries.Reader;
|
||||||
|
|
||||||
public record GetReaderPageQuery(int ChapterIndex = 0) : IQuery<ReaderPageViewModel>;
|
public record GetReaderPageQuery(int ChapterIndex = 0, string? UserId = null) : IQuery<ReaderPageViewModel>;
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ internal sealed class GetReaderPageQueryHandler : IQueryHandler<GetReaderPageQue
|
|||||||
|
|
||||||
public async Task<Result<ReaderPageViewModel>> Handle(GetReaderPageQuery request, CancellationToken cancellationToken)
|
public async Task<Result<ReaderPageViewModel>> Handle(GetReaderPageQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await _epubService.GetEpubContentAsync(request.ChapterIndex);
|
return await _epubService.GetEpubContentAsync(request.ChapterIndex, request.UserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class EpubService : IEpubService
|
|||||||
_dbContextFactory = dbContextFactory;
|
_dbContextFactory = dbContextFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex)
|
public async Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex, string? userId = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -109,14 +109,29 @@ public class EpubService : IEpubService
|
|||||||
blocks.Add(CreateAiTrigger($"trigger-{blockCounter++}"));
|
blocks.Add(CreateAiTrigger($"trigger-{blockCounter++}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the EbookId from DB for this file
|
// Find the EbookId from DB for this file AND this user
|
||||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
var ebookId = await context.Ebooks
|
var ebook = await context.Ebooks
|
||||||
.Where(e => e.FilePath.Contains("book.epub"))
|
.Where(e => e.FilePath.Contains("book.epub") && (userId == null || e.UserId == userId))
|
||||||
.Select(e => e.Id)
|
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
return Result.Ok(new ReaderPageViewModel(blocks, chapterIndex, readingOrder.Count, chapterTitle, ebookId));
|
// Auto-provision if not found for this user (convenience for dev)
|
||||||
|
if (ebook == null && !string.IsNullOrEmpty(userId))
|
||||||
|
{
|
||||||
|
var author = await context.Authors.FirstOrDefaultAsync() ?? new Author { Name = "Unknown Author" };
|
||||||
|
ebook = new Ebook
|
||||||
|
{
|
||||||
|
Title = "Lives of the Most Excellent Painters, Sculptors, and Architects",
|
||||||
|
FilePath = "wwwroot/assets/book.epub",
|
||||||
|
UserId = userId,
|
||||||
|
Author = author,
|
||||||
|
TenantId = "global"
|
||||||
|
};
|
||||||
|
context.Ebooks.Add(ebook);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok(new ReaderPageViewModel(blocks, chapterIndex, readingOrder.Count, chapterTitle, ebook?.Id ?? Guid.Empty));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
@inject KnowledgeCoordinator Coordinator
|
@inject KnowledgeCoordinator Coordinator
|
||||||
@inject IReaderInteractionService InteractionService
|
@inject IReaderInteractionService InteractionService
|
||||||
@inject ISyncService SyncService
|
@inject ISyncService SyncService
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
|
||||||
<div class="reader-canvas @(ThemeService.IsLightMode ? "theme-light" : "theme-dark")">
|
<div class="reader-canvas @(ThemeService.IsLightMode ? "theme-light" : "theme-dark")">
|
||||||
@if (ViewModel == null)
|
@if (ViewModel == null)
|
||||||
@@ -190,7 +192,10 @@
|
|||||||
ViewModel = null;
|
ViewModel = null;
|
||||||
StatusMessage = "Fetching content...";
|
StatusMessage = "Fetching content...";
|
||||||
|
|
||||||
var result = await Mediator.Send(new GetReaderPageQuery(index));
|
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var userId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
var result = await Mediator.Send(new GetReaderPageQuery(index, userId));
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
ViewModel = result.Value;
|
ViewModel = result.Value;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class WasmEpubService : IEpubService
|
|||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex)
|
public async Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex, string? userId = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -226,9 +226,11 @@ app.MapStaticAssets();
|
|||||||
app.MapHub<NexusReader.Infrastructure.RealTime.SyncHub>("/synchub");
|
app.MapHub<NexusReader.Infrastructure.RealTime.SyncHub>("/synchub");
|
||||||
|
|
||||||
// API endpoint for WASM client to fetch EPUB content
|
// API endpoint for WASM client to fetch EPUB content
|
||||||
app.MapGet("/api/epub/{index}", async (int index, IEpubService epubService) =>
|
app.MapGet("/api/epub/{index}", async (int index, IEpubService epubService, ClaimsPrincipal user) =>
|
||||||
{
|
{
|
||||||
var result = await epubService.GetEpubContentAsync(index);
|
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var result = await epubService.GetEpubContentAsync(index, userId);
|
||||||
|
|
||||||
if (result.IsSuccess) return Results.Ok(result.Value);
|
if (result.IsSuccess) return Results.Ok(result.Value);
|
||||||
|
|
||||||
var errorMsg = result.Errors.Count > 0 ? result.Errors[0].Message : "Unknown server error";
|
var errorMsg = result.Errors.Count > 0 ? result.Errors[0].Message : "Unknown server error";
|
||||||
|
|||||||
Reference in New Issue
Block a user