fix(ui): enforce idempotent AI data fetching and JSRuntime guards

This commit is contained in:
2026-05-18 20:07:32 +02:00
parent 541e9e1fb5
commit a2a09b5e91
3 changed files with 46 additions and 3 deletions
@@ -51,9 +51,13 @@
private string _lastFetchedBlockId = string.Empty;
private KnowledgePacket? _packet;
private CancellationTokenSource? _streamCts;
private bool _isInteractive;
protected override async Task OnParametersSetAsync()
{
if (!_isInteractive)
return;
// Only re-fetch when the block context actually changes
if (string.IsNullOrEmpty(ContextBlockId) || ContextBlockId == _lastFetchedBlockId)
return;
@@ -62,6 +66,19 @@
await FetchAndStreamAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_isInteractive = true;
if (!string.IsNullOrEmpty(ContextBlockId))
{
_lastFetchedBlockId = ContextBlockId;
await FetchAndStreamAsync();
}
}
}
private async Task FetchAndStreamAsync()
{
// Cancel any in-progress stream
@@ -26,15 +26,40 @@
private GroundednessResult? _result;
private bool _isChecking;
private bool _isInteractive;
private string _previousAnswer = string.Empty;
private string _previousContext = string.Empty;
protected override void OnParametersSet()
{
if (Answer != _previousAnswer || Context != _previousContext)
{
_result = null;
_previousAnswer = Answer;
_previousContext = Context;
}
}
protected override async Task OnParametersSetAsync()
{
if (!string.IsNullOrEmpty(Answer) && !string.IsNullOrEmpty(Context) && _result == null)
if (_isInteractive && !string.IsNullOrEmpty(Answer) && !string.IsNullOrEmpty(Context) && _result == null)
{
await RunCheck();
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_isInteractive = true;
if (!string.IsNullOrEmpty(Answer) && !string.IsNullOrEmpty(Context) && _result == null)
{
await RunCheck();
}
}
}
private async Task RunCheck()
{
_isChecking = true;
+3 -2
View File
@@ -27,11 +27,10 @@
private IJSObjectReference? _keydownHandler;
private DotNetObjectReference<Home>? _dotNetRef;
protected override async Task OnInitializedAsync()
protected override void OnInitialized()
{
QuizState.OnQuizRequested += HandleQuizRequestedAsync;
FocusMode.OnFocusModeChanged += HandleUpdate;
await FocusMode.InitializeAsync();
}
protected override async Task OnParametersSetAsync()
@@ -65,11 +64,13 @@
{
if (firstRender)
{
await FocusMode.InitializeAsync();
try {
_interopModule = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/NexusReader.UI.Shared/js/focusInterop.js");
_dotNetRef = DotNetObjectReference.Create(this);
_keydownHandler = await _interopModule.InvokeAsync<IJSObjectReference>("attachKeyboardListener", _dotNetRef);
} catch { } /* ignored dynamically */
StateHasChanged();
}
}