feat(ui): Hub Navigation, Profile Dashboard and Auth Stability Fixes (#31)
This PR implements the Hub Navigation system and the Profile Dashboard, while resolving critical session synchronization issues. ### Key Changes - **Hub Navigation**: Introduced `MainHubLayout` with a premium glassmorphism sidebar, providing access to Dashboard, Library, Concepts Map, and Profile. - **Profile Dashboard**: Implemented a high-fidelity Profile page (#27) with learning metrics, AI token usage tracking, and system rank visualization. - **Stability Fixes**: - Resolved an infinite network loop on the `/profile` page by implementing request deduplication and in-memory caching in `IdentityService`. - Added environment-aware guards to prevent illegal JavaScript interop calls during server-side prerendering. - Implemented automatic session invalidation on `401 Unauthorized` responses to handle stale authentication states gracefully. - **Reader Integration**: Added a "Return to Dashboard" option in the reader toolbar (#26). Closes #26 Closes #27 Reviewed-on: #31 Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Co-committed-by: Marek Jasiński <jasins.marek@gmail.com>
This commit was merged in pull request #31.
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
@inject KnowledgeCoordinator Coordinator
|
||||
@inject IReaderInteractionService InteractionService
|
||||
@inject ISyncService SyncService
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
|
||||
<div class="reader-canvas @(ThemeService.IsLightMode ? "theme-light" : "theme-dark")">
|
||||
@if (ViewModel == null)
|
||||
@@ -118,8 +120,19 @@
|
||||
{
|
||||
await Coordinator.OnBlockReachedAsync(blockId, content);
|
||||
|
||||
// Debounce sync update (simple version: every 5 seconds or on a timer)
|
||||
await SyncService.UpdateProgressAsync(blockId);
|
||||
if (ViewModel != null)
|
||||
{
|
||||
// Calculate progress: (CurrentChapter / TotalChapters) * 100
|
||||
// Simple approximation for now: chapter-based
|
||||
double progress = ((double)(ViewModel.CurrentChapterIndex + 1) / ViewModel.TotalChapters) * 100;
|
||||
|
||||
await SyncService.UpdateProgressAsync(
|
||||
blockId,
|
||||
ViewModel.EbookId,
|
||||
progress,
|
||||
ViewModel.ChapterTitle,
|
||||
ViewModel.CurrentChapterIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleSyncProgressReceived(string blockId, DateTime timestamp)
|
||||
@@ -180,7 +193,10 @@
|
||||
ViewModel = null;
|
||||
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)
|
||||
{
|
||||
ViewModel = result.Value;
|
||||
|
||||
Reference in New Issue
Block a user