Reputation: 4256
I have a Singleton service, GameServer
which keeps track of guests to a particular page, a Game.razor
page, and a Scoped service, GameBridge
, between them. Gamebridge
is injected into Game.razor
, and GameServer
is injected into Gamebridge
.
GameServer
sends out a ping event, which is relayed by GameBridge
to the page. The page then calls a method in Gamebridge
, which is relayed to GameServer
, and sets a LastSeen
DateTime to DateTime.Now for the user.
The idea was that if the user closes their browser, the Scoped service would be disposed, and the GameServer
would no longer get update pings from the user, i.e. the user has timed out, and games can be forfeited, game rooms abandoned and so on.
The problem is that even if the browser is closed, Game.razor
and GameBridge
keep chugging along like everything's fine-- which I can see because the time value for the missing user keeps updating. As of right now, I cannot detect the user disconnection even when they close the browser in blazor server side.
Upvotes: 1
Views: 2634
Reputation: 30410
My solution hooks into the browser window beforeunload
event, and uses a Guid to track SPA sessions.
This is my demo system which you an adapt to fit your code.
Add a Singleton Service and register it.
using System;
namespace Blazor.App.Services
{
public class SessionTrackerService
{
// Session List, blah, blah, ..
public void NotifyNewSession(Guid ID)
{
// Do whatever you want
Debug.WriteLine($"Session {ID} Registered");
}
public void NotifySessionClosed(Guid ID)
{
var x = 0;
Debug.WriteLine($"Session {ID} Closed");
// Do re-registering
}
}
}
site.js registered in base page.
window.blazor_setExitEvent = function (dotNetHelper) {
blazor_dotNetHelper = dotNetHelper;
window.addEventListener("beforeunload", blazor_SetSPAClosed);
}
var blazor_dotNetHelper;
window.blazor_SetSPAClosed = function() {
blazor_dotNetHelper.invokeMethodAsync('SPASessionClosed')
blazor_dotNetHelper.dispose();
}
Add some code to App.razor
. Cascade the SessionID
if you need to use it elsewhere. Call window.blazor_setExitEvent
to set the window beforeunload
event and pass it a reference to App
.
<CascadingValue Name="SessionID" Value="this.ID">
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
.....
</Router>
</CascadingValue>
@code {
[Inject] private IJSRuntime _js { get; set; }
[Inject] private SessionTrackerService sessionTrackerService { get; set; }
public Guid ID { get; set; } = Guid.NewGuid();
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
sessionTrackerService.NotifyNewSession(ID);
var dotNetReference = DotNetObjectReference.Create(this);
await _js.InvokeVoidAsync("window.blazor_setExitEvent", dotNetReference);
}
}
[JSInvokable("SPASessionClosed")]
public void SPASessionClosed()
{
sessionTrackerService.NotifySessionClosed(ID);
}
I tested in on Edge, Chrome and Firefox.
Upvotes: 3