I'm working on a Blazor Server app and need to detect when a user actually leaves the application (e.g. closes the browser/tab or loses connection), not just when they navigate between pages.
As you know, each navigation within a Blazor Server app tears down the current circuit and opens a new one — which also triggers OnConnectionDownAsync() and OnConnectionUpAsync() in a custom CircuitHandler. This makes it hard to know whether the user simply navigated or truly disconnected.To solve this, I’m trying the following approach:
Proposed approach:Use a timeout in OnConnectionDownAsync to delay any "user disconnected" logic, and cancel the timeout if a new circuit connects shortly afterward. Here’s the code:
private readonly ConcurrentDictionary<string, Timer> _disconnectTimers = new();private readonly ConcurrentHashSet<string> _activeCircuits = new(); // or use ConcurrentDictionary if neededpublic override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken){ var timer = new Timer(_ => { // Check if user has not reconnected if (!_activeCircuits.Contains(circuit.Id)) { _userTracker.MarkUserAsOffline(circuit.User); } }, null, TimeSpan.FromSeconds(5), Timeout.InfiniteTimeSpan); _disconnectTimers[circuit.Id] = timer; return Task.CompletedTask;}public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken){ _activeCircuits.Add(circuit.Id); if (_disconnectTimers.TryRemove(circuit.Id, out var timer)) { timer.Dispose(); } _userTracker.MarkUserAsOnline(circuit.User); return Task.CompletedTask;}Question:Does this approach make sense for distinguishing between user navigation and actual disconnects in Blazor Server?
Are there any edge cases or race conditions I should watch out for?
Is there a better pattern for handling this kind of circuit tracking?
I’d appreciate feedback or improvements from anyone who’s dealt with similar scenarios.