LiveMatch.ReloadAsync (LiveMatch.razor.cs:281) treats every falsy ReportFailure as a reason to leave the page:
var result = await Live.GetLiveAsync(GameId, Cancellation);
if (!Snackbar.ReportFailure(L, result))
{
Trail.Redirect(AppRoutes.Games);
return false;
}
ReportFailure returns false for a cancellation as well as a real failure, and deliberately says nothing when it does (UiFeedback.cs:58) — a cancelled read means the visitor has gone and there is nobody to show a snackbar to. So the redirect fires on a page the visitor already left, and Trail.Redirect replaces the entry rather than pushing one, so the back button does not undo it.
CancellableComponent states the convention this misses, in as many words:
A caller whose failure branch does something the visitor would notice checks IsCancelled first.
Every other page that redirects on a failed load does:
| Page |
|
MatchResult.razor.cs:106 |
if (gameResult.IsCancelled) return; |
FormationOverview.razor.cs:39 |
if (result.IsCancelled) return; |
FormationBuilder.razor.cs:42 |
if (gameResult.IsCancelled) return; |
PlayerStats.razor.cs:33 |
if (playerResult.IsCancelled) return; |
The live screen is the one that doesn't, and it is also the likeliest to reach it. Cancellation is cancelled on dispose, and ReloadAsync runs on every LiveMatchNotifier fan-out as well as at init (OnLiveChanged, LiveMatch.razor.cs:270) — so a visitor who navigates off the live screen just as someone logs a goal disposes the page mid-reload, and the cancelled read then navigates them to /games instead of where they were going.
What to do
Check IsCancelled before the redirect, the way the four pages above do. ReloadAsync returns bool and OnInitializedAsync uses it to decide whether to carry on loading (LiveMatch.razor.cs:242), so a cancelled read should return false without redirecting: the rest of the load is skipped and the visitor is left where they went.
Scope
LiveMatch.razor.cs alone. No service change, nothing an anonymous visitor sees differently on a page that loads.
- Awkward to cover with a UI test, since it needs a navigation to land inside a reload. The real check is that the five pages now read the same way.
LiveMatch.ReloadAsync(LiveMatch.razor.cs:281) treats every falsyReportFailureas a reason to leave the page:ReportFailurereturns false for a cancellation as well as a real failure, and deliberately says nothing when it does (UiFeedback.cs:58) — a cancelled read means the visitor has gone and there is nobody to show a snackbar to. So the redirect fires on a page the visitor already left, andTrail.Redirectreplaces the entry rather than pushing one, so the back button does not undo it.CancellableComponentstates the convention this misses, in as many words:Every other page that redirects on a failed load does:
MatchResult.razor.cs:106if (gameResult.IsCancelled) return;FormationOverview.razor.cs:39if (result.IsCancelled) return;FormationBuilder.razor.cs:42if (gameResult.IsCancelled) return;PlayerStats.razor.cs:33if (playerResult.IsCancelled) return;The live screen is the one that doesn't, and it is also the likeliest to reach it.
Cancellationis cancelled on dispose, andReloadAsyncruns on everyLiveMatchNotifierfan-out as well as at init (OnLiveChanged,LiveMatch.razor.cs:270) — so a visitor who navigates off the live screen just as someone logs a goal disposes the page mid-reload, and the cancelled read then navigates them to/gamesinstead of where they were going.What to do
Check
IsCancelledbefore the redirect, the way the four pages above do.ReloadAsyncreturnsboolandOnInitializedAsyncuses it to decide whether to carry on loading (LiveMatch.razor.cs:242), so a cancelled read should return false without redirecting: the rest of the load is skipped and the visitor is left where they went.Scope
LiveMatch.razor.csalone. No service change, nothing an anonymous visitor sees differently on a page that loads.