refactor: log silenced UI errors, fix timer leak, narrow exception catch
- EventListShell: detach the Elapsed handler before disposing the refresh timer (both StartTimer and Dispose) to stop a leaked subscription firing on a torn-down component; log the two previously-silent catches. - Insights: log the previously-silent report-load catch. - EventOddsParser: narrow catch(Exception) to catch(ArgumentException) so only the OddsRate/OddsValue/Bet guard-clause throws are swallowed. - AnomalyEvidenceData: make the JSON DTOs init-only per the immutability convention. - Settings: remove a dead DialogParameters block.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
@inject IStringLocalizer<SharedResource> L
|
||||
@inject IAnomalyInsightsService InsightsService
|
||||
@inject NavigationManager Nav
|
||||
@inject ILogger<Insights> Logger
|
||||
|
||||
<PageTitle>@L["App.Title"] · @L["Nav.Insights"]</PageTitle>
|
||||
|
||||
@@ -658,8 +659,9 @@
|
||||
_vm = report;
|
||||
}
|
||||
catch (OperationCanceledException) { /* superseded */ }
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Insights: failed to build the anomaly outcome report.");
|
||||
_errored = true;
|
||||
_vm = null;
|
||||
}
|
||||
|
||||
@@ -282,13 +282,6 @@
|
||||
|
||||
private async Task<bool> ConfirmAsync()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["ContentText"] = L["Settings.Confirm.Body"].Value,
|
||||
["ButtonText"] = L["Settings.Action.Save"].Value,
|
||||
["CancelText"] = L["Common.Cancel"].Value,
|
||||
};
|
||||
|
||||
var result = await Dialogs.ShowMessageBox(
|
||||
title: L["Settings.Confirm.Title"],
|
||||
message: L["Settings.Confirm.Body"],
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
@using DomainEventId = Marathon.Domain.ValueObjects.EventId
|
||||
@implements IDisposable
|
||||
@inject IStringLocalizer<SharedResource> L
|
||||
@inject ILogger<EventListShell> Logger
|
||||
|
||||
<section class="m-shell">
|
||||
<header class="m-rise m-rise-1" style="display: grid; gap: var(--m-space-3); max-width: 880px;">
|
||||
@@ -364,7 +365,11 @@
|
||||
|
||||
private void StartTimer()
|
||||
{
|
||||
_refreshTimer?.Dispose();
|
||||
if (_refreshTimer is not null)
|
||||
{
|
||||
_refreshTimer.Elapsed -= OnRefreshTimerElapsed;
|
||||
_refreshTimer.Dispose();
|
||||
}
|
||||
var interval = Math.Max(5, AutoRefreshSeconds) * 1000.0;
|
||||
_refreshTimer = new System.Timers.Timer(interval) { AutoReset = true };
|
||||
_refreshTimer.Elapsed += OnRefreshTimerElapsed;
|
||||
@@ -383,10 +388,11 @@
|
||||
{
|
||||
await InvokeAsync(LoadAsync);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Swallowed — LoadAsync already handles its own errors; this catch
|
||||
// is the last line of defense for InvokeAsync itself.
|
||||
// Last line of defense for InvokeAsync itself — LoadAsync handles its
|
||||
// own errors. Log rather than silently dropping the failure.
|
||||
Logger.LogError(ex, "EventListShell ({Surface}): auto-refresh tick failed.", Surface);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,9 +420,10 @@
|
||||
{
|
||||
// Swallow — superseded by a newer load.
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Hide errors from the UI; Phase 9 will add a snackbar.
|
||||
// Degrade gracefully (clear the rows) but record the failure for diagnosis.
|
||||
Logger.LogError(ex, "EventListShell ({Surface}): failed to load event rows.", Surface);
|
||||
_rows = new List<EventListItem>();
|
||||
}
|
||||
finally
|
||||
@@ -523,7 +530,11 @@
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_refreshTimer?.Dispose();
|
||||
if (_refreshTimer is not null)
|
||||
{
|
||||
_refreshTimer.Elapsed -= OnRefreshTimerElapsed;
|
||||
_refreshTimer.Dispose();
|
||||
}
|
||||
_searchCts?.Cancel();
|
||||
_searchCts?.Dispose();
|
||||
_loadCts?.Cancel();
|
||||
|
||||
Reference in New Issue
Block a user