34794db209
This PR addresses several UI/UX and architectural refinements for the Knowledge Graph and Intelligence Sidebar. ### Key Changes: - **Knowledge Graph (#21, #22)**: - Implemented \"pill-shaped\" nodes with dynamic label truncation and SVG tooltips. - Added bound-constrained simulation to keep nodes within the viewport. - Integrated `ResizeObserver` for dynamic layout handling. - Implemented Zoom-to-Fit functionality. - Enforced unique concept IDs in AI prompts and hardened JS logic to prevent multi-selection bugs. - **Intelligence Sidebar (#23)**: - Improved visual depth with a radial gradient background for the graph. - Increased sidebar divider contrast for better layering. - Transformed graph controls into a floating glassmorphism panel. - Relocated the \"Logout\" action to the toolbar bottom and rebranded it as \"Exit\". Fixes #21 Fixes #22 Fixes #23 Reviewed-on: #25 Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Co-committed-by: Marek Jasiński <jasins.marek@gmail.com>
57 lines
1.9 KiB
C#
57 lines
1.9 KiB
C#
using FluentResults;
|
|
using MediatR;
|
|
using Microsoft.AspNetCore.SignalR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using NexusReader.Application.Commands.Sync;
|
|
using NexusReader.Domain.Entities;
|
|
using NexusReader.Data.Persistence;
|
|
using NexusReader.Infrastructure.RealTime;
|
|
|
|
namespace NexusReader.Infrastructure.Handlers;
|
|
|
|
public class UpdateReadingProgressCommandHandler : IRequestHandler<UpdateReadingProgressCommand, Result>
|
|
{
|
|
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
|
|
private readonly IHubContext<SyncHub> _hubContext;
|
|
|
|
public UpdateReadingProgressCommandHandler(
|
|
IDbContextFactory<AppDbContext> dbContextFactory,
|
|
IHubContext<SyncHub> hubContext)
|
|
{
|
|
_dbContextFactory = dbContextFactory;
|
|
_hubContext = hubContext;
|
|
}
|
|
|
|
public async Task<Result> Handle(UpdateReadingProgressCommand request, CancellationToken cancellationToken)
|
|
{
|
|
using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
|
var user = await context.Users.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
|
if (user == null)
|
|
{
|
|
return Result.Fail("User not found.");
|
|
}
|
|
|
|
var now = DateTime.UtcNow;
|
|
user.LastReadPageId = request.PageId;
|
|
user.LastReadAt = now;
|
|
|
|
await context.SaveChangesAsync(cancellationToken);
|
|
|
|
// Broadcast to other devices
|
|
var group = _hubContext.Clients.Group($"User_{request.UserId}");
|
|
|
|
if (!string.IsNullOrEmpty(request.ExcludedConnectionId))
|
|
{
|
|
await _hubContext.Clients
|
|
.GroupExcept($"User_{request.UserId}", request.ExcludedConnectionId)
|
|
.SendAsync("ProgressUpdated", request.PageId, now, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
await group.SendAsync("ProgressUpdated", request.PageId, now, cancellationToken);
|
|
}
|
|
|
|
return Result.Ok();
|
|
}
|
|
}
|