Files
Nexus.Reader/src/.documentation/cache/summaries/NexusReader.UI.Shared__Pages__Home.razor.json
T
2026-05-25 14:02:56 +02:00

1 line
18 KiB
JSON

{"path":"NexusReader.UI.Shared/Pages/Home.razor","purpose":"Razor UI page for the Nexus E-Reader reader experience: mounts the ReaderCanvas, handles navigation/book selection from route/query or user profile, wires UI events (quiz, focus mode), initializes JS focus interop, and cleans up resources on disposal.","classification":{"role":"ui-page","layer":"frontend","confidence":0.9,"evidence":["Middleware pattern","Frontend path heuristic","Contains @page directives (lines 1-2)","Renders <ReaderCanvas> and injects UI services (lines 15-16, 5-12)","Implements IAsyncDisposable and lifecycle methods OnInitialized / OnParametersSetAsync / OnAfterRenderAsync (lines 6, 30, 36, 63)"]},"className":"Home","methods":[{"name":"OnInitialized","line":30,"endLine":34,"signature":"() -> void","purpose":"Subscribe to UI-level events for quiz requests and focus-mode changes when the component is initialized.","calls":[],"actions":[{"id":"event-subscribe_32","kind":"mapping","label":"Subscribe to QuizState.OnQuizRequested","line":32,"detail":"QuizState.OnQuizRequested += HandleQuizRequestedAsync","visibility":"detail-only","confidence":0.7},{"id":"event-subscribe_33","kind":"mapping","label":"Subscribe to FocusMode.OnFocusModeChanged","line":33,"detail":"FocusMode.OnFocusModeChanged += HandleUpdate","visibility":"detail-only","confidence":0.7}]},{"name":"OnParametersSetAsync","line":36,"endLine":61,"signature":"() -> Task","purpose":"React to route/query changes: parse chapter query, determine BookId from route or fallback to last-read book from profile, and instruct navigation service to load the correct book/chapter.","calls":[{"targetFile":"NexusReader.UI.Shared/Services/IReaderNavigationService.cs","targetMethod":"SetBook","callLine":49,"paramSummary":"BookId.Value, chapterIndex (from query)"},{"targetFile":"NexusReader.UI.Shared/Services/IIdentityService.cs","targetMethod":"GetProfileAsync","callLine":55,"paramSummary":"none"},{"targetFile":"NexusReader.UI.Shared/Services/IReaderNavigationService.cs","targetMethod":"SetBook","callLine":58,"paramSummary":"profileResult.Value.LastReadBook.Id and computed chapter index"}],"actions":[{"id":"onparameterssetasync_branch_40_0","kind":"branch","label":"Evaluates branch condition","line":40,"detail":"if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue(\"chapter\", out var chapterValue))","conditionSummary":"Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue(\"chapter\", out var chapterValue)","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"parse_40","kind":"mapping","label":"Query parsing","line":40,"detail":"Parses `chapter` from URL query and tries to convert to int","visibility":"detail-only","confidence":0.7},{"id":"onparameterssetasync_branch_45_1","kind":"branch","label":"Evaluates branch condition","line":45,"detail":"if (BookId.HasValue && BookId.Value != Guid.Empty)","conditionSummary":"BookId.HasValue && BookId.Value != Guid.Empty","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"guard-clause_45","kind":"guard-clause","label":"If BookId provided route","line":45,"detail":"If NavService differs from requested book/chapter, call SetBook (line 47-50)","conditionSummary":"BookId.HasValue && BookId != Guid.Empty","outcomeLabels":["maybe SetBook","skip"],"visibility":"detail-only","confidence":0.7},{"id":"onparameterssetasync_branch_47_2","kind":"branch","label":"Evaluates branch condition","line":47,"detail":"if (NavService.CurrentEbookId != BookId.Value || NavService.CurrentChapterIndex != chapterIndex)","conditionSummary":"NavService.CurrentEbookId != BookId.Value || NavService.CurrentChapterIndex != chapterIndex","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"onparameterssetasync_branch_52_3","kind":"branch","label":"Evaluates branch condition","line":52,"detail":"else if (NavService.CurrentEbookId == Guid.Empty)","conditionSummary":"NavService.CurrentEbookId == Guid.Empty","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"branch_52","kind":"branch","label":"Fallback to last-read profile book","line":52,"detail":"Calls IdentityService.GetProfileAsync and if profile has LastReadBook calls NavService.SetBook (lines 55-59)","conditionSummary":"NavService.CurrentEbookId == Guid.Empty and no BookId in URL","outcomeLabels":["load last read","do nothing"],"visibility":"detail-only","confidence":0.7},{"id":"onparameterssetasync_fallback_52_4","kind":"fallback","label":"Falls back to alternate path","line":52,"detail":"else if (NavService.CurrentEbookId == Guid.Empty)","outcomeLabels":["fallback"],"visibility":"primary-visible","confidence":0.84},{"id":"onparameterssetasync_await_55_5","kind":"await","label":"Waits for async work","line":55,"detail":"var profileResult = await IdentityService.GetProfileAsync();","visibility":"secondary-visible","confidence":0.81},{"id":"onparameterssetasync_branch_56_6","kind":"branch","label":"Evaluates branch condition","line":56,"detail":"if (profileResult.IsSuccess && profileResult.Value.LastReadBook != null)","conditionSummary":"profileResult.IsSuccess && profileResult.Value.LastReadBook != null","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78}]},{"name":"OnAfterRenderAsync","line":63,"endLine":75,"signature":"(firstRender: bool) -> Task","purpose":"On first render initialize focus-mode service and attempt to attach a JS keyboard listener, updating state afterwards.","calls":[{"targetFile":"NexusReader.UI.Shared/Services/IFocusModeService.cs","targetMethod":"InitializeAsync","callLine":67,"paramSummary":"none"}],"actions":[{"id":"onafterrenderasync_branch_65_0","kind":"branch","label":"Evaluates branch condition","line":65,"detail":"if (firstRender)","conditionSummary":"firstRender","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"guard-clause_65","kind":"guard-clause","label":"First render check","line":65,"detail":"Only runs initialization and JS interop on first render","conditionSummary":"firstRender == true","outcomeLabels":["initialize","skip"],"visibility":"detail-only","confidence":0.7},{"id":"onafterrenderasync_await_67_1","kind":"await","label":"Waits for async work","line":67,"detail":"await FocusMode.InitializeAsync();","visibility":"secondary-visible","confidence":0.81},{"id":"onafterrenderasync_try_68_2","kind":"try","label":"Begins protected execution","line":68,"detail":"try {","visibility":"primary-visible","confidence":0.84},{"id":"try-catch_68","kind":"mapping","label":"Dynamic JS interop load","line":68,"detail":"Attempts to import JS module and attach keyboard listener; exceptions are swallowed (line 68-72)","visibility":"detail-only","confidence":0.7},{"id":"onafterrenderasync_await_69_3","kind":"await","label":"Waits for async work","line":69,"detail":"_interopModule = await JS.InvokeAsync<IJSObjectReference>(\"import\", \"./_content/NexusReader.UI.Shared/js/focusInterop.js\");","visibility":"secondary-visible","confidence":0.81},{"id":"onafterrenderasync_await_71_4","kind":"await","label":"Waits for async work","line":71,"detail":"_keydownHandler = await _interopModule.InvokeAsync<IJSObjectReference>(\"attachKeyboardListener\", _dotNetRef);","visibility":"secondary-visible","confidence":0.81},{"id":"onafterrenderasync_catch_72_5","kind":"catch","label":"Handles exception path","line":72,"detail":"} catch { } /* ignored dynamically */","outcomeLabels":["handled exception"],"visibility":"primary-visible","confidence":0.86},{"id":"state-change_73","kind":"mapping","label":"Force UI refresh","line":73,"detail":"StateHasChanged() invoked after initialization","visibility":"detail-only","confidence":0.7}]},{"name":"OnFocusKeypressed","line":77,"endLine":82,"signature":"() -> Task","purpose":"JS-invokable handler called when the focus-mode key is pressed: toggles focus mode and refreshes UI.","calls":[{"targetFile":"NexusReader.UI.Shared/Services/IFocusModeService.cs","targetMethod":"ToggleAsync","callLine":80,"paramSummary":"none"}],"actions":[{"id":"js-invokable_77","kind":"mapping","label":"Exposed to JS","line":77,"detail":"[JSInvokable] attribute","visibility":"detail-only","confidence":0.7},{"id":"onfocuskeypressed_await_80_0","kind":"await","label":"Waits for async work","line":80,"detail":"await FocusMode.ToggleAsync();","visibility":"secondary-visible","confidence":0.81},{"id":"state-change_81","kind":"mapping","label":"Force UI refresh after toggle","line":81,"detail":"StateHasChanged() called","visibility":"detail-only","confidence":0.7}]},{"name":"HandleNodeSelected","line":84,"endLine":90,"signature":"(nodeId: string) -> Task","purpose":"Scroll the embedded ReaderCanvas to a particular node when a node selection event occurs.","calls":[{"targetFile":"NexusReader.UI.Shared/Components/ReaderCanvas.razor","targetMethod":"ScrollToNodeAsync","callLine":88,"paramSummary":"nodeId"}],"actions":[{"id":"handlenodeselected_branch_86_0","kind":"branch","label":"Evaluates branch condition","line":86,"detail":"if (readerCanvas != null)","conditionSummary":"readerCanvas != null","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"guard-clause_86","kind":"guard-clause","label":"Null-check ReaderCanvas reference","line":86,"detail":"Only calls ScrollToNodeAsync when component ref exists","conditionSummary":"readerCanvas != null","outcomeLabels":["scroll","no-op"],"visibility":"detail-only","confidence":0.7},{"id":"handlenodeselected_await_88_1","kind":"await","label":"Waits for async work","line":88,"detail":"await readerCanvas.ScrollToNodeAsync(nodeId);","visibility":"secondary-visible","confidence":0.81}]},{"name":"HandleQuizRequestedAsync","line":92,"endLine":96,"signature":"(blockId: string) -> Task","purpose":"Receive quiz request events, set active quiz block id and request UI update.","calls":[],"actions":[{"id":"state-mutation_94","kind":"mapping","label":"Set active quiz block id","line":94,"detail":"_activeQuizBlockId = blockId","visibility":"detail-only","confidence":0.7},{"id":"state-change_95","kind":"mapping","label":"Invoke UI update","line":95,"detail":"InvokeAsync(StateHasChanged)","visibility":"detail-only","confidence":0.7},{"id":"handlequizrequestedasync_await_95_0","kind":"await","label":"Waits for async work","line":95,"detail":"await InvokeAsync(StateHasChanged);","visibility":"secondary-visible","confidence":0.81}]},{"name":"HandleUpdate","line":98,"endLine":98,"signature":"() -> Task","purpose":"Request a UI update from other subscribers (used as an event handler).","calls":[],"actions":[{"id":"state-change_98","kind":"mapping","label":"Invoke UI update","line":98,"detail":"Returns InvokeAsync(StateHasChanged)","visibility":"detail-only","confidence":0.7}]},{"name":"DisposeAsync","line":100,"endLine":115,"signature":"() -> ValueTask","purpose":"Unsubscribe event handlers, detach JS keyboard listener if present, dispose JS interop objects and DotNetObjectReference when the component is disposed.","calls":[{"targetFile":"NexusReader.UI.Shared/Services/IQuizStateService.cs","targetMethod":"OnQuizRequested - event unsubscription","callLine":102,"paramSummary":"HandleQuizRequestedAsync"},{"targetFile":"NexusReader.UI.Shared/Services/IFocusModeService.cs","targetMethod":"OnFocusModeChanged - event unsubscription","callLine":103,"paramSummary":"HandleUpdate"},{"targetFile":"NexusReader.UI.Shared/Components/ReaderCanvas.razor","targetMethod":"Dispose/interop cleanup (via JS module)","callLine":108,"paramSummary":"detachKeyboardListener then dispose interop references"}],"actions":[{"id":"event-unsubscribe_102","kind":"mapping","label":"Unsubscribe QuizState.OnQuizRequested","line":102,"detail":"QuizState.OnQuizRequested -= HandleQuizRequestedAsync","visibility":"detail-only","confidence":0.7},{"id":"event-unsubscribe_103","kind":"mapping","label":"Unsubscribe FocusMode.OnFocusModeChanged","line":103,"detail":"FocusMode.OnFocusModeChanged -= HandleUpdate","visibility":"detail-only","confidence":0.7},{"id":"guard-clause_105","kind":"guard-clause","label":"Conditional JS interop cleanup","line":105,"detail":"Calls JS detach and disposes JS object references inside try/catch (lines 107-111); exceptions swallowed","conditionSummary":"_interopModule != null && _keydownHandler != null","outcomeLabels":["try detach and dispose","skip"],"visibility":"detail-only","confidence":0.7},{"id":"disposeasync_branch_105_0","kind":"branch","label":"Evaluates branch condition","line":105,"detail":"if (_interopModule != null && _keydownHandler != null)","conditionSummary":"_interopModule != null && _keydownHandler != null","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"disposeasync_try_107_1","kind":"try","label":"Begins protected execution","line":107,"detail":"try {","visibility":"primary-visible","confidence":0.84},{"id":"disposeasync_await_108_2","kind":"await","label":"Waits for async work","line":108,"detail":"await _interopModule.InvokeVoidAsync(\"detachKeyboardListener\", _keydownHandler);","visibility":"secondary-visible","confidence":0.81},{"id":"disposeasync_await_109_3","kind":"await","label":"Waits for async work","line":109,"detail":"await _interopModule.DisposeAsync();","visibility":"secondary-visible","confidence":0.81},{"id":"disposeasync_await_110_4","kind":"await","label":"Waits for async work","line":110,"detail":"await _keydownHandler.DisposeAsync();","visibility":"secondary-visible","confidence":0.81},{"id":"disposeasync_catch_111_5","kind":"catch","label":"Handles exception path","line":111,"detail":"} catch { } // Circuit disconnected catch explicitly","outcomeLabels":["handled exception"],"visibility":"primary-visible","confidence":0.86},{"id":"dispose_114","kind":"mapping","label":"Dispose DotNetObjectReference","line":114,"detail":"_dotNetRef?.Dispose()","visibility":"detail-only","confidence":0.7}]}],"types":[{"name":"BookId","kind":"type-alias","line":21,"purpose":"Component Parameter holding optional route BookId (Guid?).","fields":[{"name":"value","type":"Guid?","required":false,"line":21,"description":"Optional BookId provided by route"}]}],"serviceRegistrations":[],"startupActions":[],"dependencies":["NexusReader.UI.Shared/Services/IQuizStateService.cs","NexusReader.UI.Shared/Services/IFocusModeService.cs","NexusReader.UI.Shared/Services/IReaderNavigationService.cs","NexusReader.UI.Shared/Services/IIdentityService.cs","NexusReader.UI.Shared/Components/ReaderCanvas.razor"],"patterns":["Event-driven UI (subscribe/unsubscribe)","JS interop with DotNetObjectReference","Navigation state orchestration (NavService SetBook)"],"domainConcepts":["Book","Reader","Quiz","FocusMode","Navigation"],"keyDetails":"Wires UI-level services and events to coordinate which book/chapter is active (route or profile fallback), initializes focus-mode and optional JS keyboard listener on first render, and ensures proper event unsubscription and JS interop disposal in DisposeAsync to avoid leaks.","orchestrationMethods":[{"name":"OnParametersSetAsync","line":36,"confidence":0.98,"reason":"Coordinates 3 downstream calls with 7 architectural actions.","actionKinds":["branch","mapping","guard-clause","fallback","await"],"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Services/IReaderNavigationService.cs","NexusReader.UI.Shared/Services/IIdentityService.cs","NexusReader.UI.Shared/Services/IReaderNavigationService.cs"]},{"name":"DisposeAsync","line":100,"confidence":0.81,"reason":"Coordinates 3 downstream calls with 1 architectural actions.","actionKinds":["mapping","guard-clause","branch","try","await","catch"],"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Services/IQuizStateService.cs","NexusReader.UI.Shared/Services/IFocusModeService.cs","NexusReader.UI.Shared/Components/ReaderCanvas.razor"]},{"name":"OnAfterRenderAsync","line":63,"confidence":0.65,"reason":"Contains 1 architectural actions relevant to business execution.","actionKinds":["branch","guard-clause","await","try","mapping","catch"],"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Services/IFocusModeService.cs"]},{"name":"HandleNodeSelected","line":84,"confidence":0.65,"reason":"Contains 1 architectural actions relevant to business execution.","actionKinds":["branch","guard-clause","await"],"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Components/ReaderCanvas.razor"]}],"typedContracts":[],"persistenceInteractions":[],"externalInteractions":[],"evidenceAnchors":[{"kind":"orchestration-method","label":"OnParametersSetAsync","line":36,"summary":"Coordinates 3 downstream calls with 7 architectural actions.","confidence":0.98,"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Services/IReaderNavigationService.cs","NexusReader.UI.Shared/Services/IIdentityService.cs","NexusReader.UI.Shared/Services/IReaderNavigationService.cs"]},{"kind":"orchestration-method","label":"DisposeAsync","line":100,"summary":"Coordinates 3 downstream calls with 1 architectural actions.","confidence":0.81,"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Services/IQuizStateService.cs","NexusReader.UI.Shared/Services/IFocusModeService.cs","NexusReader.UI.Shared/Components/ReaderCanvas.razor"]},{"kind":"orchestration-method","label":"OnAfterRenderAsync","line":63,"summary":"Contains 1 architectural actions relevant to business execution.","confidence":0.65,"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Services/IFocusModeService.cs"]},{"kind":"orchestration-method","label":"HandleNodeSelected","line":84,"summary":"Contains 1 architectural actions relevant to business execution.","confidence":0.65,"evidencePaths":["NexusReader.UI.Shared/Pages/Home.razor","NexusReader.UI.Shared/Components/ReaderCanvas.razor"]}],"cacheMetadata":{"schemaVersion":2,"analysisVersion":"2026-05-23.cache-v1","contentChecksum":"bf4258ee2016ca2bdaac73b19f4b7df0707b1ecbf7864e6ca596c23bc687d477","sourceByteSize":3989,"analyzedAt":"2026-05-23T16:29:32.382Z","technology":"dotnet"}}