fe5ff81c98
## Overview This PR completes the architectural consolidation of the web project and stabilizes the Identity-based authentication flow for the NexusReader application. It also refines the UI aesthetic for the Book Ingestion Modal as requested in #33. ## Key Changes - **Project Consolidation**: Fully merged `NexusReader.Web.New` into `NexusReader.Web`. This includes updating all namespace references, VS Code launch/task configurations, and CI/CD (`Dockerfile`). - **Identity Stabilization**: - Implemented `IIdentityService` on the server using `SignInManager<NexusUser>` and `UserManager<NexusUser>`. - Fixed registration logic to include mandatory fields (`SubscriptionPlanId`, `TenantId`). - Updated `Login.razor` to force a page reload on successful login, ensuring proper synchronization of authentication cookies between SignalR and the browser. - **UI/UX Refinement**: - Updated `BookIngestionModal` styling to follow the **Nexus Neon** design system. - Added premium button styles with hover effects and glows. - Improved modal layout and interaction feedback (shimmer effects, spinner colors). - **Cleanup**: Removed obsolete interfaces and constants that were superseded by newer Application layer implementations. ## Verification - Successfully built the solution: `dotnet build NexusReader.slnx --no-restore` - Verified project structure and file moves. - Validated server-side authentication logic. Fixes #33 --------- Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Reviewed-on: #40 Co-authored-by: Antigravity <antigravity@google.com> Co-committed-by: Antigravity <antigravity@google.com>
59 lines
2.1 KiB
C#
59 lines
2.1 KiB
C#
using System.Net.Http.Json;
|
|
using FluentResults;
|
|
using NexusReader.Application.Abstractions.Services;
|
|
using NexusReader.Application.Queries.Reader;
|
|
|
|
namespace NexusReader.Web.Client.Services;
|
|
|
|
public class WasmEpubReader : IEpubReader
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
|
|
public WasmEpubReader(HttpClient httpClient)
|
|
{
|
|
_httpClient = httpClient;
|
|
}
|
|
|
|
public async Task<Result<ReaderPageViewModel>> GetEpubContentAsync(int chapterIndex, string? userId = null)
|
|
{
|
|
try
|
|
{
|
|
var response = await _httpClient.GetAsync($"/api/epub/{chapterIndex}");
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
var viewModel = await response.Content.ReadFromJsonAsync<ReaderPageViewModel>();
|
|
return viewModel != null ? Result.Ok(viewModel) : Result.Fail("Failed to deserialize response.");
|
|
}
|
|
|
|
// Try to read the error message from the body
|
|
var errorBody = await response.Content.ReadAsStringAsync();
|
|
return Result.Fail($"Server error ({response.StatusCode}): {errorBody}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Fallback for network errors or parsing exceptions
|
|
return Result.Fail(new Error($"Network or parsing error: {ex.Message}").CausedBy(ex));
|
|
}
|
|
}
|
|
// Metadata extraction moved to WasmEpubMetadataExtractor
|
|
}
|
|
|
|
public class WasmEpubMetadataExtractor : IEpubMetadataExtractor
|
|
{
|
|
public async Task<Result<LocalEpubMetadata>> ExtractMetadataAsync(Stream epubStream)
|
|
{
|
|
try
|
|
{
|
|
using var bookRef = await VersOne.Epub.EpubReader.OpenBookAsync(epubStream);
|
|
var title = bookRef.Title ?? "Unknown Title";
|
|
var author = bookRef.Author ?? "Unknown Author";
|
|
byte[]? cover = await bookRef.ReadCoverAsync();
|
|
return Result.Ok(new LocalEpubMetadata(title, author, cover));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail(new Error($"Failed to extract EPUB metadata locally: {ex.Message}").CausedBy(ex));
|
|
}
|
|
}
|
|
}
|