From 577d63c0fa3cb0ad105e17ec8560c2335133765a Mon Sep 17 00:00:00 2001 From: Claudio Schaad Date: Sat, 4 Jul 2026 09:39:03 +0200 Subject: [PATCH] PR U-4a: Home refreshes in place instead of forceLoad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SplitBankTransactionAsync ended with NavigateTo(forceLoad: true), which tore down the SignalR circuit and re-ran every repo constructor just to refresh the pending-bookings list. Extract data loading into a LoadDataAsync helper called from OnInitializedAsync, SplitBank TransactionAsync, and OnCompletedAsync — the page refreshes in-circuit, scoped caches survive, and the NavigationManager injection can go. Also flags the plan: U-4 is split into U-4a (this) and U-4b (persist settings + drop MyHeader's forceLoad, touching ~15 pages). Co-Authored-By: Claude Opus 4.7 --- .../Components/Pages/Home.razor.cs | 27 ++++++++++--------- UI_IMPROVEMENT_PLAN.md | 5 +++- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Schaad.Accounting.UI/Components/Pages/Home.razor.cs b/Schaad.Accounting.UI/Components/Pages/Home.razor.cs index 47060ad..cf77dfb 100644 --- a/Schaad.Accounting.UI/Components/Pages/Home.razor.cs +++ b/Schaad.Accounting.UI/Components/Pages/Home.razor.cs @@ -31,9 +31,6 @@ public partial class Home : ComponentBase [Inject] private ITransactionRepository transactionRepository { get; set; } = null!; - [Inject] - private NavigationManager Navigation { get; set; } = null!; - private List? matchingBankTransactions; private IReadOnlyList accounts = []; private IReadOnlyList bookingTexts = []; @@ -45,11 +42,16 @@ public partial class Home : ComponentBase Dictionary Files = new(); protected override async Task OnInitializedAsync() + { + await LoadDataAsync(); + await base.OnInitializedAsync(); + } + + private async Task LoadDataAsync() { matchingBankTransactions = await viewService.MatchOpenBankTransactionsAsync(); accounts = await viewService.GetAccountViewListAsync(); bookingTexts = (await bookingTextRepository.GetBookingTextListAsync()).Select(b => b.Text).ToArray(); - await base.OnInitializedAsync(); } async Task OnProgressChangeAsync(FluentInputFileEventArgs file) @@ -96,7 +98,10 @@ public partial class Home : ComponentBase File.Delete(file.Value); } - matchingBankTransactions = await viewService.MatchOpenBankTransactionsAsync(); + // Imports may update the bank-account balance and add new bank transactions + // — reload everything so the pending list, account balances, and booking-text + // suggestions stay consistent. + await LoadDataAsync(); } private async Task ImportXmlAndShowResultAsync(string fileName) @@ -174,14 +179,10 @@ public partial class Home : ComponentBase var result = await dialog.Result; if (!result.Cancelled && result.Data != null) { - ReloadPage(); - await Task.CompletedTask; + // Re-run the auto-matcher in-place so the newly created split + // transactions disappear from the pending list. No forceLoad — the + // SignalR circuit stays up and the scoped RepositoryCache survives. + await LoadDataAsync(); } } - - private void ReloadPage() - { - var uri = Navigation.Uri; - Navigation.NavigateTo(uri, forceLoad: true); // forceLoad = true does a full reload - } } diff --git a/UI_IMPROVEMENT_PLAN.md b/UI_IMPROVEMENT_PLAN.md index d1f18f2..23e2185 100644 --- a/UI_IMPROVEMENT_PLAN.md +++ b/UI_IMPROVEMENT_PLAN.md @@ -157,7 +157,10 @@ Sequenced by value / risk. Each is one PR unless noted. ### Phase 2 — The forceLoad/singleton trap (biggest single improvement) -- **U-4** Persist year and mandator to a cookie *and* keep them in the URL as query params. `SettingsService` becomes `Scoped`, reading initial state from query params (fallback: cookie, fallback: config default). Header switching becomes an in-place update — no `forceLoad`, no circuit teardown, no state loss. +Split into two PRs on delivery because the header path touches ~15 pages. + +- **U-4a** Fix `Home.ReloadPage()` — the single non-settings `forceLoad` in the app. `SplitBankTransactionAsync` and `OnCompletedAsync` (statement import) now call a shared `LoadDataAsync()` that re-runs the auto-matcher, re-fetches accounts and booking texts in place, and keeps the SignalR circuit + scoped `RepositoryCache` alive. No cross-cutting scaffolding needed. +- **U-4b** Persist year and mandator (cookie + URL query params), make `SettingsService` `Scoped`, add an event or `LocationChanged` subscription so every page refreshes on year/mandator change, and drop `MyHeader`'s `forceLoad`. This is the big one — it touches every routable page. ### Phase 3 — Loading and error UX consistency