1
0

Compare commits

..

2 Commits

Author SHA1 Message Date
9e7e7df370 Add observer for online servers.
Only for the servers in the Eindhoven city.
2025-03-30 22:33:08 +02:00
734a59f38b Replace SessionStorage with ContainerStorage.
This is retained on the SignalR connection and is unique to the user.
2025-03-30 22:32:22 +02:00
10 changed files with 149 additions and 15 deletions

View File

@ -1,13 +1,26 @@
@using ServerManagement.StateStore
@implements IDisposable
@inject EindhovenOnlineServersStore EindhovenOnlineServersStore
<div class="col"> <div class="col">
<div class="card @((city == selectedCity) ? "border-primary" : "")"> <div class="card @((city == selectedCity) ? "border-primary" : "")">
<img src=@($"/images/{@city}.png") class="card-img-top" alt="@city"> <img src=@($"/images/{@city}.png") class="card-img-top" alt="@city">
<div class="card-body @((city == selectedCity) ? "active" : "")"> <div class="card-body @((city == selectedCity) ? "active" : "")">
<button class="btn btn-primary" @onclick="@(() => { SelectCity(city); })">@city</button> <button class="btn btn-primary" @onclick="@(() => { SelectCity(city); })">@city</button>
</div> </div>
<div>
@if (city.Equals("Eindhoven", StringComparison.OrdinalIgnoreCase))
{
<text>Online: @serversOnlineEindhoven</text>
}
</div>
</div> </div>
</div> </div>
@code { @code {
private int serversOnlineEindhoven;
[Parameter] [Parameter]
public string? city { get; set; } = ""; public string? city { get; set; } = "";
@ -21,4 +34,26 @@
{ {
SelectCityCallBack.InvokeAsync(cityName); SelectCityCallBack.InvokeAsync(cityName);
} }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
serversOnlineEindhoven = EindhovenOnlineServersStore.GetNumberServersOnline();
EindhovenOnlineServersStore.AddStateChangeListener(OnServersStatusChange);
StateHasChanged();
}
}
private void OnServersStatusChange()
{
serversOnlineEindhoven = EindhovenOnlineServersStore.GetNumberServersOnline();
StateHasChanged();
}
// Remove the listeners, otherwise it can cause memory leaks.
public void Dispose()
{
EindhovenOnlineServersStore.RemoveStateChangeListener(OnServersStatusChange);
}
} }

View File

@ -1,6 +1,7 @@
@namespace ServerManagement.Components.Controls @namespace ServerManagement.Components.Controls
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject EindhovenOnlineServersStore EindhovenOnlineServersStore
@if (Server != null) @if (Server != null)
{ {
@ -32,14 +33,14 @@
@if (Server.IsOnline) @if (Server.IsOnline)
{ {
<button type="button" class="btn btn-outline-danger" <button type="button" class="btn btn-outline-danger"
@onclick="@(() => { Server.IsOnline = false; })"> @onclick="@(() => { SetServerStatus(false); })">
Turn off Turn off
</button> </button>
} }
else else
{ {
<button type="button" class="btn btn-outline-success" <button type="button" class="btn btn-outline-success"
@onclick="@(() => { Server.IsOnline = true; })"> @onclick="@(() => { SetServerStatus(true); })">
Turn on Turn on
</button> </button>
} }
@ -92,4 +93,27 @@
return "white"; return "white";
} }
} }
private void SetServerStatus(bool status)
{
if (this.Server != null)
{
if (this.Server.IsOnline != status)
{
if (this.Server.City.Equals("Eindhoven", StringComparison.OrdinalIgnoreCase))
{
var numberOnline = EindhovenOnlineServersStore.GetNumberServersOnline();
if (status)
{
EindhovenOnlineServersStore.SetNumberServersOnline(numberOnline + 1);
}
else if (numberOnline > 1)
{
EindhovenOnlineServersStore.SetNumberServersOnline(numberOnline - 1);
}
}
this.Server.IsOnline = status;
}
}
}
} }

View File

@ -1,7 +1,10 @@
@page "/servers" @page "/servers"
@page "/servers/back_from/{cityName}" @page "/servers/back_from/{cityName}"
@using ServerManagement.Components.Controls @using ServerManagement.Components.Controls
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject EindhovenOnlineServersStore EindhovenOnlineServersStore
<h3>Servers</h3> <h3>Servers</h3>
<br/> <br/>
@ -60,6 +63,12 @@
selectedCity = CityName; selectedCity = CityName;
StateHasChanged(); StateHasChanged();
} }
var serversEindhoven = ServersRepository.GetServersByCity("Eindhoven");
if (serversEindhoven != null)
{
EindhovenOnlineServersStore.SetNumberServersOnline(serversEindhoven.Count(s => s.IsOnline));
}
} }
} }
} }

View File

@ -3,7 +3,7 @@
@using ServerManagement.StateStore @using ServerManagement.StateStore
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage @inject ContainerStorage containerStorage
<h3>City Name</h3> <h3>City Name</h3>
<br/> <br/>
@ -38,16 +38,16 @@
base.OnInitialized(); base.OnInitialized();
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override void OnAfterRender(bool firstRender)
{ {
if (firstRender) if (firstRender)
{ {
this.server = await sessionStorage.GetServerAsync(); this.server = containerStorage.GetServer();
StateHasChanged(); StateHasChanged();
} }
} }
private async Task GoNext() private void GoNext()
{ {
if (server != null) if (server != null)
{ {
@ -57,7 +57,7 @@
} }
else else
{ {
await this.sessionStorage.SetServerAsync(server); containerStorage.SetServer(server);
NavigationManager.NavigateTo($"/serverstatus"); NavigationManager.NavigateTo($"/serverstatus");
} }
} }

View File

@ -3,7 +3,7 @@
@using ServerManagement.StateStore @using ServerManagement.StateStore
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage @inject ContainerStorage containerStorage
<h3>Server Name</h3> <h3>Server Name</h3>
<br/> <br/>
@ -44,7 +44,7 @@
} }
} }
private async Task GoNext() private void GoNext()
{ {
if (string.IsNullOrWhiteSpace(server?.Name)) if (string.IsNullOrWhiteSpace(server?.Name))
{ {
@ -52,7 +52,7 @@
} }
else else
{ {
await sessionStorage.SetServerAsync(server); containerStorage.SetServer(server);
NavigationManager.NavigateTo($"/cityname"); NavigationManager.NavigateTo($"/cityname");
} }
} }

View File

@ -3,7 +3,7 @@
@using ServerManagement.StateStore @using ServerManagement.StateStore
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage @inject ContainerStorage containerStorage
<h3>Server Status</h3> <h3>Server Status</h3>
<br/> <br/>
@ -34,20 +34,20 @@
base.OnInitialized(); base.OnInitialized();
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override void OnAfterRender(bool firstRender)
{ {
if (firstRender) if (firstRender)
{ {
this.server = await sessionStorage.GetServerAsync(); this.server = containerStorage.GetServer();
StateHasChanged(); StateHasChanged();
} }
} }
private async Task Save() private void Save()
{ {
if (server != null) if (server != null)
{ {
await sessionStorage.SetServerAsync(null); containerStorage.SetServer(null);
ServersRepository.AddServer(server); ServersRepository.AddServer(server);
NavigationManager.NavigateTo($"/servers/back_from/{server?.City}"); NavigationManager.NavigateTo($"/servers/back_from/{server?.City}");
} }

View File

@ -0,0 +1,12 @@
using ServerManagement.Models;
namespace ServerManagement.StateStore
{
public class ContainerStorage
{
private Server _server = new Server();
public Server GetServer() { return _server; }
public void SetServer(Server? server) { _server = server; }
}
}

View File

@ -0,0 +1,18 @@
namespace ServerManagement.StateStore
{
public class EindhovenOnlineServersStore : Observer
{
private int _numberServersOnline;
public int GetNumberServersOnline()
{
return _numberServersOnline;
}
public void SetNumberServersOnline(int numbersOnline)
{
_numberServersOnline = numbersOnline;
base.BroadcastStateChange();
}
}
}

View File

@ -0,0 +1,28 @@
namespace ServerManagement.StateStore
{
public class Observer
{
protected Action? _listeners;
public void AddStateChangeListener(Action? listener)
{
if (listener is not null)
{
_listeners += listener;
}
}
public void RemoveStateChangeListener(Action? listener)
{
if (listener is not null)
{
_listeners -= listener;
}
}
public void BroadcastStateChange()
{
_listeners?.Invoke();
}
}
}

View File

@ -9,6 +9,14 @@ builder.Services.AddRazorComponents()
builder.Services.AddTransient<SessionStorage>(); builder.Services.AddTransient<SessionStorage>();
// Scoped lifespan is the same as the SignalR lifespan.
// Everything stored in the SignalR channel will be lost if that connection is broken.
// This means that the data in there, can only be used for the current user.
// For a WebAssembly, you want to use AddSingleton, so that everything is locally downloaded in the browser.
builder.Services.AddScoped<ContainerStorage>();
builder.Services.AddScoped<EindhovenOnlineServersStore>();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.