@page "/" @attribute [Authorize] @using NexusReader.UI.Shared.Services @implements IAsyncDisposable @inject IQuizStateService QuizState @inject IFocusModeService FocusMode @inject IJSRuntime JS Nexus E-Reader
@code { private ReaderCanvas? readerCanvas; private string? _activeQuizBlockId; private IJSObjectReference? _interopModule; private IJSObjectReference? _keydownHandler; private DotNetObjectReference? _dotNetRef; protected override async Task OnInitializedAsync() { QuizState.OnQuizRequested += HandleQuizRequestedAsync; FocusMode.OnFocusModeChanged += HandleUpdate; await FocusMode.InitializeAsync(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { try { _interopModule = await JS.InvokeAsync("import", "./_content/NexusReader.UI.Shared/js/focusInterop.js"); _dotNetRef = DotNetObjectReference.Create(this); _keydownHandler = await _interopModule.InvokeAsync("attachKeyboardListener", _dotNetRef); } catch { } /* ignored dynamically */ } } [JSInvokable] public async Task OnFocusKeypressed() { await FocusMode.ToggleAsync(); StateHasChanged(); } private async Task HandleNodeSelected(string nodeId) { if (readerCanvas != null) { await readerCanvas.ScrollToNodeAsync(nodeId); } } private async Task HandleQuizRequestedAsync(string blockId) { _activeQuizBlockId = blockId; await InvokeAsync(StateHasChanged); } private Task HandleUpdate() => InvokeAsync(StateHasChanged); public async ValueTask DisposeAsync() { QuizState.OnQuizRequested -= HandleQuizRequestedAsync; FocusMode.OnFocusModeChanged -= HandleUpdate; if (_interopModule != null && _keydownHandler != null) { try { await _interopModule.InvokeVoidAsync("detachKeyboardListener", _keydownHandler); await _interopModule.DisposeAsync(); await _keydownHandler.DisposeAsync(); } catch { } // Circuit disconnected catch explicitly } _dotNetRef?.Dispose(); } }