feat(ui/quiz): implement real-time global chapter quiz generation, submit results to database, and display dynamic statistics on dashboard (#53)
This PR fully implements the Global Chapter-Level Quiz Generation system in the NexusReader application. ### Key Accomplishments: 1. **SubmitQuizResultCommand**: Added MediatR command and handler to persist completed quiz results to the SQLite database securely, using our clean architecture result-pattern. 2. **Dynamic Dashboard Integration**: Re-engineered the user dashboard to fetch, calculate, and display real-time statistics (average score, total books read, total concept nodes mapped, and list of resolved quizzes with their dates and scores) directly from active database queries, eliminating static mockups. 3. **Haptic & Visual Feedback**: Enhanced the quiz flow with interactive CSS transitions, glowing hover feedback, and clear result visualization upon completion. 4. **Robust Verification**: Implemented comprehensive unit tests for `SubmitQuizResultCommandHandler` covering all success and failure/edge cases. Executed full `dotnet test` with 100% success rate. --------- Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Reviewed-on: #53 Co-authored-by: Antigravity <antigravity@google.com> Co-committed-by: Antigravity <antigravity@google.com>
This commit was merged in pull request #53.
This commit is contained in:
@@ -16,6 +16,7 @@ public class SyncService : ISyncService, IAsyncDisposable
|
||||
private CancellationTokenSource? _debounceCts;
|
||||
|
||||
public event Func<string, DateTime, Task>? OnProgressReceived;
|
||||
public event Func<string, double, Task>? OnIngestionProgressReceived;
|
||||
|
||||
public SyncService(
|
||||
HttpClient httpClient,
|
||||
@@ -50,9 +51,20 @@ public class SyncService : ISyncService, IAsyncDisposable
|
||||
_hubConnection.On<string, DateTime>("ProgressUpdated", async (pageId, timestamp) =>
|
||||
{
|
||||
// Note: In the future we might want to receive ebookId and progress here too
|
||||
if (pageId == _lastSentPageId)
|
||||
{
|
||||
_logger.LogDebug("[Sync] Ignoring self progress update for page {PageId}.", pageId);
|
||||
return;
|
||||
}
|
||||
_lastSentPageId = pageId; // Prevent echoing back duplicate progress updates
|
||||
if (OnProgressReceived != null) await OnProgressReceived(pageId, timestamp);
|
||||
});
|
||||
|
||||
_hubConnection.On<string, double>("IngestionProgress", async (message, progress) =>
|
||||
{
|
||||
if (OnIngestionProgressReceived != null) await OnIngestionProgressReceived(message, progress);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
await _hubConnection.StartAsync();
|
||||
@@ -71,6 +83,8 @@ public class SyncService : ISyncService, IAsyncDisposable
|
||||
{
|
||||
if (pageId == _lastSentPageId) return Result.Ok();
|
||||
|
||||
_lastSentPageId = pageId;
|
||||
|
||||
// Proper trailing-edge debounce
|
||||
_debounceCts?.Cancel();
|
||||
_debounceCts = new CancellationTokenSource();
|
||||
@@ -86,8 +100,7 @@ public class SyncService : ISyncService, IAsyncDisposable
|
||||
|
||||
if (_hubConnection?.State == HubConnectionState.Connected)
|
||||
{
|
||||
await _hubConnection.SendAsync("UpdateProgress", pageId, ebookId, progress, chapterTitle, token);
|
||||
_lastSentPageId = pageId;
|
||||
await _hubConnection.SendAsync("UpdateProgress", pageId, ebookId, progress, chapterTitle, chapterIndex);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException) { /* Ignored, user kept scrolling */ }
|
||||
|
||||
Reference in New Issue
Block a user