@@ -75,10 +76,10 @@
{
if (string.IsNullOrEmpty(nodeId)) return false;
- var nodeSeq = ParseSegmentNumber(nodeId);
+ var nodeSeq = SegmentIdParser.Parse(nodeId);
// Always unlock the very first segment so the user has a starting node
- var minNodeSeq = Nodes.Any() ? Nodes.Min(n => ParseSegmentNumber(n.Id)) : 0;
+ var minNodeSeq = Nodes.Any() ? Nodes.Min(n => SegmentIdParser.Parse(n.Id)) : 0;
if (nodeSeq == minNodeSeq) return true;
if (string.IsNullOrEmpty(LastReadBlockId))
@@ -86,16 +87,11 @@
return false;
}
- var progressSeq = ParseSegmentNumber(LastReadBlockId);
+ var progressSeq = SegmentIdParser.Parse(LastReadBlockId);
return nodeSeq <= progressSeq;
}
- private static int ParseSegmentNumber(string? id)
- {
- if (string.IsNullOrEmpty(id)) return 0;
- var digits = new string(id.Where(char.IsDigit).ToArray());
- return int.TryParse(digits, out var val) ? val : 0;
- }
+
private async Task HandleNodeClick(GraphNodeDto node)
{
diff --git a/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css b/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css
index 94bb76e..47220b5 100644
--- a/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css
+++ b/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css
@@ -65,14 +65,14 @@
}
.timeline-step.unlocked:hover {
- border-color: rgba(0, 243, 255, 0.15);
- box-shadow: 0 4px 20px rgba(0, 243, 255, 0.05);
+ border-color: rgba(0, 255, 153, 0.15);
+ box-shadow: 0 4px 20px rgba(0, 255, 153, 0.05);
}
.timeline-step.selected {
- background: rgba(0, 243, 255, 0.04);
+ background: rgba(0, 255, 153, 0.04);
border-color: var(--nexus-neon);
- box-shadow: 0 0 15px rgba(0, 243, 255, 0.1);
+ box-shadow: 0 0 15px var(--nexus-primary-glow);
}
.node-connector-wrapper {
@@ -100,7 +100,7 @@
.unlocked .node-circle {
border: 2px solid var(--nexus-neon);
color: var(--nexus-neon);
- box-shadow: 0 0 10px rgba(0, 243, 255, 0.3);
+ box-shadow: 0 0 10px var(--nexus-primary-glow);
}
.locked .node-circle {
@@ -136,8 +136,8 @@
}
.track-active {
- background: linear-gradient(180deg, var(--nexus-neon), rgba(0, 243, 255, 0.2));
- box-shadow: 0 0 6px rgba(0, 243, 255, 0.2);
+ background: linear-gradient(180deg, var(--nexus-neon), rgba(0, 255, 153, 0.2));
+ box-shadow: 0 0 6px var(--nexus-primary-glow);
}
.track-inactive {
@@ -159,7 +159,7 @@
.timeline-step.selected .node-content {
background: rgba(255, 255, 255, 0.03);
- border-color: rgba(0, 243, 255, 0.2);
+ border-color: rgba(0, 255, 153, 0.2);
}
.node-header {
@@ -188,9 +188,9 @@
}
.badge-unlocked {
- background: rgba(0, 243, 255, 0.08);
+ background: rgba(0, 255, 153, 0.08);
color: var(--nexus-neon);
- border: 1px solid rgba(0, 243, 255, 0.2);
+ border: 1px solid rgba(0, 255, 153, 0.2);
}
.badge-locked {
diff --git a/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor b/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor
index 1d6f464..e4ae704 100644
--- a/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor
+++ b/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor
@@ -8,12 +8,13 @@
@using NexusReader.Application.Queries.Concepts
@using System.Net.Http.Json
@using NexusReader.Application.Abstractions.Services
+@using NexusReader.Application.Utilities
@inject IConceptsMapService ConceptsMapService
@inject NavigationManager NavigationManager
@inject IIdentityService IdentityService
@inject ISyncService SyncService
@attribute [Authorize]
-@implements IDisposable
+@implements IAsyncDisposable
Mapa Pojęć | Nexus Reader
@@ -235,23 +236,18 @@
private bool IsUnlocked(string nodeId)
{
if (string.IsNullOrEmpty(nodeId)) return false;
- var nodeSeq = ParseSegmentNumber(nodeId);
+ var nodeSeq = SegmentIdParser.Parse(nodeId);
- var minNodeSeq = Nodes.Any() ? Nodes.Min(n => ParseSegmentNumber(n.Id)) : 0;
+ var minNodeSeq = Nodes.Any() ? Nodes.Min(n => SegmentIdParser.Parse(n.Id)) : 0;
if (nodeSeq == minNodeSeq) return true;
if (string.IsNullOrEmpty(LastReadBlockId)) return false;
- var progressSeq = ParseSegmentNumber(LastReadBlockId);
+ var progressSeq = SegmentIdParser.Parse(LastReadBlockId);
return nodeSeq <= progressSeq;
}
- private static int ParseSegmentNumber(string? id)
- {
- if (string.IsNullOrEmpty(id)) return 0;
- var digits = new string(id.Where(char.IsDigit).ToArray());
- return int.TryParse(digits, out var val) ? val : 0;
- }
+
private void HandleNodeSelected(GraphNodeDto node)
{
@@ -276,7 +272,7 @@
{
if (BookId.HasValue && SelectedNode != null)
{
- var chapterIndex = ParseSegmentNumber(SelectedNode.Id);
+ var chapterIndex = SegmentIdParser.Parse(SelectedNode.Id);
NavigationManager.NavigateTo($"/reader/{BookId.Value}?chapter={chapterIndex}");
}
}
@@ -299,9 +295,10 @@
});
}
- public void Dispose()
+ public ValueTask DisposeAsync()
{
IdentityService.OnStateInvalidated -= HandleStateInvalidatedAsync;
SyncService.OnProgressReceived -= HandleProgressReceivedAsync;
+ return ValueTask.CompletedTask;
}
}
diff --git a/src/NexusReader.UI.Shared/Pages/Library.razor.css b/src/NexusReader.UI.Shared/Pages/Library.razor.css
index a6d57b6..3fde1ce 100644
--- a/src/NexusReader.UI.Shared/Pages/Library.razor.css
+++ b/src/NexusReader.UI.Shared/Pages/Library.razor.css
@@ -31,7 +31,7 @@
margin: 0;
}
-::deep .add-book-trigger {
+.add-book-trigger {
background: var(--nexus-neon) !important;
color: #000000 !important;
border: none !important;
@@ -41,7 +41,7 @@
border-radius: var(--radius-md) !important;
}
-::deep .add-book-trigger:hover {
+.add-book-trigger:hover {
transform: translateY(-2px) !important;
box-shadow: 0 8px 20px rgba(0, 255, 153, 0.5) !important;
filter: brightness(1.1);
diff --git a/src/NexusReader.UI.Shared/Pages/SerilogDemo.razor b/src/NexusReader.UI.Shared/Pages/SerilogDemo.razor
index 9de68ef..2c6159c 100644
--- a/src/NexusReader.UI.Shared/Pages/SerilogDemo.razor
+++ b/src/NexusReader.UI.Shared/Pages/SerilogDemo.razor
@@ -2,13 +2,14 @@
@inject ILogger
Logger
@inject IJSRuntime JSRuntime
+#if DEBUG
+#else
+
+
+
Diagnostics Unavailable
+
This page is only available in DEBUG builds.
+
+
+#endif
@code {
+#if DEBUG
private void LogInfo()
{
Logger.LogInformation("Structured native log triggered by user from SerilogDemo. Button: LogInfo");
@@ -121,4 +131,5 @@
{
await JSRuntime.InvokeVoidAsync("eval", "throw new Error('Simulated runtime JS Exception triggered from Blazor UI button click!');");
}
+#endif
}
diff --git a/src/NexusReader.UI.Shared/Pages/Settings.razor b/src/NexusReader.UI.Shared/Pages/Settings.razor
index a08c958..ae288b8 100644
--- a/src/NexusReader.UI.Shared/Pages/Settings.razor
+++ b/src/NexusReader.UI.Shared/Pages/Settings.razor
@@ -5,13 +5,15 @@
Settings
Configure your account and application preferences.
+ #if DEBUG
-
Diagnostics & System Logs
+
Diagnostics & System Logs
Inspect native logging infrastructure, trigger custom logs, and trace WebView errors.
Open Serilog Diagnostics Dashboard
+ #endif
diff --git a/src/NexusReader.UI.Shared/Pages/Settings.razor.css b/src/NexusReader.UI.Shared/Pages/Settings.razor.css
index 16b7053..7df77b0 100644
--- a/src/NexusReader.UI.Shared/Pages/Settings.razor.css
+++ b/src/NexusReader.UI.Shared/Pages/Settings.razor.css
@@ -5,7 +5,7 @@
animation: fadeIn 0.6s ease-out;
}
-h1 {
+.settings-page > h1 {
font-family: var(--nexus-font-serif);
font-size: 2.8rem;
font-weight: 700;
diff --git a/src/NexusReader.UI.Shared/wwwroot/app.css b/src/NexusReader.UI.Shared/wwwroot/app.css
index dc21052..2238d4d 100644
--- a/src/NexusReader.UI.Shared/wwwroot/app.css
+++ b/src/NexusReader.UI.Shared/wwwroot/app.css
@@ -73,8 +73,13 @@
.btn-nexus:hover {
transform: translateY(-2px);
filter: brightness(1.1);
+}
+.btn-nexus-primary:hover {
box-shadow: 0 4px 15px var(--nexus-primary-glow);
}
+.btn-nexus-secondary:hover {
+ box-shadow: 0 4px 15px rgba(255, 255, 255, 0.05);
+}
.theme-light {
diff --git a/src/NexusReader.Web/Services/ServerConceptsMapService.cs b/src/NexusReader.Web/Services/ServerConceptsMapService.cs
index 5dd01e7..04845e4 100644
--- a/src/NexusReader.Web/Services/ServerConceptsMapService.cs
+++ b/src/NexusReader.Web/Services/ServerConceptsMapService.cs
@@ -1,8 +1,6 @@
using System.Security.Claims;
using FluentResults;
using MediatR;
-using Microsoft.AspNetCore.Components.Authorization;
-using Microsoft.AspNetCore.Http;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.Queries.Concepts;
@@ -11,38 +9,30 @@ namespace NexusReader.Web.Services;
public class ServerConceptsMapService : IConceptsMapService
{
private readonly IMediator _mediator;
- private readonly AuthenticationStateProvider _authStateProvider;
- private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly IIdentityService _identityService;
public ServerConceptsMapService(
IMediator mediator,
- AuthenticationStateProvider authStateProvider,
- IHttpContextAccessor httpContextAccessor)
+ IIdentityService identityService)
{
_mediator = mediator;
- _authStateProvider = authStateProvider;
- _httpContextAccessor = httpContextAccessor;
+ _identityService = identityService;
}
public async Task