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="card @((city == selectedCity) ? "border-primary" : "")">
<img src=@($"/images/{@city}.png") class="card-img-top" alt="@city">
<div class="card-body @((city == selectedCity) ? "active" : "")">
<button class="btn btn-primary" @onclick="@(() => { SelectCity(city); })">@city</button>
</div>
<div>
@if (city.Equals("Eindhoven", StringComparison.OrdinalIgnoreCase))
{
<text>Online: @serversOnlineEindhoven</text>
}
</div>
</div>
</div>
@code {
private int serversOnlineEindhoven;
[Parameter]
public string? city { get; set; } = "";
@ -21,4 +34,26 @@
{
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
@inject NavigationManager NavigationManager
@inject EindhovenOnlineServersStore EindhovenOnlineServersStore
@if (Server != null)
{
@ -32,14 +33,14 @@
@if (Server.IsOnline)
{
<button type="button" class="btn btn-outline-danger"
@onclick="@(() => { Server.IsOnline = false; })">
@onclick="@(() => { SetServerStatus(false); })">
Turn off
</button>
}
else
{
<button type="button" class="btn btn-outline-success"
@onclick="@(() => { Server.IsOnline = true; })">
@onclick="@(() => { SetServerStatus(true); })">
Turn on
</button>
}
@ -92,4 +93,27 @@
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/back_from/{cityName}"
@using ServerManagement.Components.Controls
@inject NavigationManager NavigationManager
@inject EindhovenOnlineServersStore EindhovenOnlineServersStore
<h3>Servers</h3>
<br/>
@ -60,6 +63,12 @@
selectedCity = CityName;
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
@inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage
@inject ContainerStorage containerStorage
<h3>City Name</h3>
<br/>
@ -38,16 +38,16 @@
base.OnInitialized();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
this.server = await sessionStorage.GetServerAsync();
this.server = containerStorage.GetServer();
StateHasChanged();
}
}
private async Task GoNext()
private void GoNext()
{
if (server != null)
{
@ -57,7 +57,7 @@
}
else
{
await this.sessionStorage.SetServerAsync(server);
containerStorage.SetServer(server);
NavigationManager.NavigateTo($"/serverstatus");
}
}

View File

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

View File

@ -3,7 +3,7 @@
@using ServerManagement.StateStore
@inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage
@inject ContainerStorage containerStorage
<h3>Server Status</h3>
<br/>
@ -34,20 +34,20 @@
base.OnInitialized();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
this.server = await sessionStorage.GetServerAsync();
this.server = containerStorage.GetServer();
StateHasChanged();
}
}
private async Task Save()
private void Save()
{
if (server != null)
{
await sessionStorage.SetServerAsync(null);
containerStorage.SetServer(null);
ServersRepository.AddServer(server);
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>();
// 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();
// Configure the HTTP request pipeline.