Merge pull request #189 from Moonlight-Panel/AddCustomLayoutServerList
Added custom layout options for the server list
This commit is contained in:
@@ -24,6 +24,8 @@ public class User
|
|||||||
|
|
||||||
public string Country { get; set; } = "";
|
public string Country { get; set; } = "";
|
||||||
|
|
||||||
|
public string ServerListLayoutJson { get; set; } = "";
|
||||||
|
|
||||||
// States
|
// States
|
||||||
|
|
||||||
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
||||||
|
|||||||
1077
Moonlight/App/Database/Migrations/20230623235512_AddedServerListLayoutToUser.Designer.cs
generated
Normal file
1077
Moonlight/App/Database/Migrations/20230623235512_AddedServerListLayoutToUser.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedServerListLayoutToUser : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ServerListLayoutJson",
|
||||||
|
table: "Users",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ServerListLayoutJson",
|
||||||
|
table: "Users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -780,6 +780,10 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<int>("Rating")
|
b.Property<int>("Rating")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ServerListLayoutJson")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("State")
|
b.Property<string>("State")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|||||||
7
Moonlight/App/Models/Misc/ServerGroup.cs
Normal file
7
Moonlight/App/Models/Misc/ServerGroup.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Moonlight.App.Models.Misc;
|
||||||
|
|
||||||
|
public class ServerGroup
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public List<string> Servers { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -103,6 +103,8 @@
|
|||||||
<script src="/_content/CurrieTechnologies.Razor.SweetAlert2/sweetAlert2.min.js"></script>
|
<script src="/_content/CurrieTechnologies.Razor.SweetAlert2/sweetAlert2.min.js"></script>
|
||||||
<script src="/_content/Blazor.ContextMenu/blazorContextMenu.min.js"></script>
|
<script src="/_content/Blazor.ContextMenu/blazorContextMenu.min.js"></script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@@shopify/draggable@1.0.0-beta.11/lib/draggable.bundle.js"></script>
|
||||||
|
|
||||||
<script src="https://www.google.com/recaptcha/api.js"></script>
|
<script src="https://www.google.com/recaptcha/api.js"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.7.0/lib/xterm-addon-fit.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.7.0/lib/xterm-addon-fit.min.js"></script>
|
||||||
|
|||||||
@@ -1,13 +1,414 @@
|
|||||||
@page "/servers"
|
@page "/servers"
|
||||||
@using Moonlight.App.Repositories.Servers
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Moonlight.App.Database.Entities
|
@using Moonlight.App.Database.Entities
|
||||||
|
@using Moonlight.App.Helpers
|
||||||
|
@using Moonlight.App.Models.Misc
|
||||||
|
@using Moonlight.App.Repositories
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
|
@using Newtonsoft.Json
|
||||||
|
|
||||||
@inject ServerRepository ServerRepository
|
@inject Repository<Server> ServerRepository
|
||||||
|
@inject Repository<User> UserRepository
|
||||||
|
@inject SmartTranslateService SmartTranslateService
|
||||||
@inject IServiceScopeFactory ServiceScopeFactory
|
@inject IServiceScopeFactory ServiceScopeFactory
|
||||||
|
@inject IJSRuntime JsRuntime
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
|
<div class="mx-auto">
|
||||||
|
<div class="card card-body">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<span class="badge badge-primary badge-lg px-5 me-4">Beta</span>
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Finish editing layout"))"
|
||||||
|
CssClasses="btn-secondary"
|
||||||
|
OnClick="async () => await SetEditMode(false)">
|
||||||
|
</WButton>
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("New group"))"
|
||||||
|
WorkingText=""
|
||||||
|
CssClasses="btn-primary"
|
||||||
|
OnClick="AddGroup">
|
||||||
|
</WButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Edit layout"))"
|
||||||
|
CssClasses="btn-secondary"
|
||||||
|
OnClick="async () => await SetEditMode(true)">
|
||||||
|
</WButton>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@foreach (var group in ServerGroups)
|
||||||
|
{
|
||||||
|
@*
|
||||||
|
<div class="card my-2">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title">
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<input @bind="group.Name" class="form-control"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>@(group.Name)</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<div class="card-toolbar">
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Remove group"))"
|
||||||
|
WorkingText=""
|
||||||
|
CssClasses="btn-danger"
|
||||||
|
OnClick="async () => await RemoveGroup(group)">
|
||||||
|
</WButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)">
|
||||||
|
@foreach (var id in group.Servers)
|
||||||
|
{
|
||||||
|
var server = AllServers.First(x => x.Id.ToString() == id);
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)">
|
||||||
|
<div class="card bg-secondary">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title">
|
||||||
|
<h3 class="card-label">@(server.Name)</h3>
|
||||||
|
</div>
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<div class="card-toolbar">
|
||||||
|
<a href="#" class="btn btn-icon btn-sm btn-hover-light-primary draggable-handle">
|
||||||
|
<i class="bx bx-md bx-move"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<TL>Hidden in edit mode</TL>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>*@
|
||||||
|
<div class="accordion my-3" id="serverListGroup@(group.GetHashCode())">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header" id="serverListGroup-header@(group.GetHashCode())">
|
||||||
|
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serverListGroup-body@(group.GetHashCode())" aria-expanded="false" aria-controls="serverListGroup-body@(group.GetHashCode())">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div>
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<input @bind="group.Name" class="form-control"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(group.Name))
|
||||||
|
{
|
||||||
|
<TL>Unsorted servers</TL>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>@(group.Name)</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Remove group"))"
|
||||||
|
WorkingText=""
|
||||||
|
CssClasses="btn-danger"
|
||||||
|
OnClick="async () => await RemoveGroup(group)">
|
||||||
|
</WButton>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="serverListGroup-body@(group.GetHashCode())" class="accordion-collapse collapse" aria-labelledby="serverListGroup-header@(group.GetHashCode())" data-bs-parent="#serverListGroup">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)">
|
||||||
|
@foreach (var id in group.Servers)
|
||||||
|
{
|
||||||
|
var server = AllServers.First(x => x.Id.ToString() == id);
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)">
|
||||||
|
<a class="invisible-a" href="/server/@(server.Uuid)">
|
||||||
|
<div class="card bg-secondary">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title">
|
||||||
|
<span class="card-label">@(server.Name)</span>
|
||||||
|
</div>
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<div class="card-toolbar">
|
||||||
|
<a href="#" class="btn btn-icon btn-sm btn-hover-light-primary draggable-handle">
|
||||||
|
<i class="bx bx-md bx-move"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
@if (EditMode)
|
||||||
|
{
|
||||||
|
<TL>Hidden in edit mode</TL>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="card-text fs-6">
|
||||||
|
@(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
|
||||||
|
</span>
|
||||||
|
<div class="card-text my-1 fs-6 fw-bold">
|
||||||
|
@(server.Node.Fqdn):@(server.MainAllocation.Port)
|
||||||
|
</div>
|
||||||
|
<div class="card-text fs-6">
|
||||||
|
@if (StatusCache.ContainsKey(server))
|
||||||
|
{
|
||||||
|
var status = StatusCache[server];
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case "offline":
|
||||||
|
<span class="text-danger">
|
||||||
|
<TL>Offline</TL>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
case "stopping":
|
||||||
|
<span class="text-warning">
|
||||||
|
<TL>Stopping</TL>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
case "starting":
|
||||||
|
<span class="text-warning">
|
||||||
|
<TL>Starting</TL>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
case "running":
|
||||||
|
<span class="text-success">
|
||||||
|
<TL>Running</TL>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
case "failed":
|
||||||
|
<span class="text-gray-400">
|
||||||
|
<TL>Failed</TL>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
<span class="text-danger">
|
||||||
|
<TL>Offline</TL>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="text-gray-400">
|
||||||
|
<TL>Loading</TL>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</LazyLoader>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
public User User { get; set; }
|
||||||
|
|
||||||
|
private Server[] AllServers;
|
||||||
|
private LazyLoader LazyLoader;
|
||||||
|
private readonly Dictionary<Server, string> StatusCache = new();
|
||||||
|
|
||||||
|
private List<ServerGroup> ServerGroups = new();
|
||||||
|
private bool EditMode = false;
|
||||||
|
|
||||||
|
private async Task Load(LazyLoader arg)
|
||||||
|
{
|
||||||
|
AllServers = ServerRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Owner)
|
||||||
|
.Include(x => x.MainAllocation)
|
||||||
|
.Include(x => x.Node)
|
||||||
|
.Include(x => x.Image)
|
||||||
|
.Where(x => x.Owner.Id == User.Id)
|
||||||
|
.OrderBy(x => x.Name)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(User.ServerListLayoutJson))
|
||||||
|
{
|
||||||
|
ServerGroups.Add(new()
|
||||||
|
{
|
||||||
|
Name = "",
|
||||||
|
Servers = AllServers.Select(x => x.Id.ToString()).ToList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerGroups = (JsonConvert.DeserializeObject<ServerGroup[]>(
|
||||||
|
User.ServerListLayoutJson) ?? Array.Empty<ServerGroup>()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var server in AllServers)
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var serverService = scope.ServiceProvider.GetRequiredService<ServerService>();
|
||||||
|
|
||||||
|
AddStatus(server, (await serverService.GetDetails(server)).State);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
AddStatus(server, "failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddGroup()
|
||||||
|
{
|
||||||
|
ServerGroups.Insert(0, new()
|
||||||
|
{
|
||||||
|
Name = "New group"
|
||||||
|
});
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveGroup(ServerGroup group)
|
||||||
|
{
|
||||||
|
ServerGroups.Remove(group);
|
||||||
|
await EnsureAllServersInGroups();
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetEditMode(bool toggle)
|
||||||
|
{
|
||||||
|
EditMode = toggle;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
if (EditMode)
|
||||||
|
{
|
||||||
|
await EnsureAllServersInGroups();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(await GetGroupsFromClient());
|
||||||
|
User.ServerListLayoutJson = json;
|
||||||
|
UserRepository.Update(User);
|
||||||
|
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ServerGroup[]> GetGroupsFromClient()
|
||||||
|
{
|
||||||
|
var serverGroups = await JsRuntime.InvokeAsync<ServerGroup[]>("moonlight.serverList.getData");
|
||||||
|
|
||||||
|
// Check user data to prevent users from doing stupid stuff
|
||||||
|
foreach (var serverGroup in serverGroups)
|
||||||
|
{
|
||||||
|
if (serverGroup.Name.Length > 30)
|
||||||
|
{
|
||||||
|
Logger.Verbose("Server list group lenght too long");
|
||||||
|
return Array.Empty<ServerGroup>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverGroup.Servers.Any(x => AllServers.All(y => y.Id.ToString() != x)))
|
||||||
|
{
|
||||||
|
Logger.Verbose("User tried to add a server in his server list which he has no access to");
|
||||||
|
return Array.Empty<ServerGroup>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task EnsureAllServersInGroups()
|
||||||
|
{
|
||||||
|
var presentInGroup = new List<Server>();
|
||||||
|
|
||||||
|
foreach (var group in ServerGroups)
|
||||||
|
{
|
||||||
|
foreach (var id in group.Servers)
|
||||||
|
presentInGroup.Add(AllServers.First(x => x.Id.ToString() == id));
|
||||||
|
}
|
||||||
|
|
||||||
|
var serversMissing = new List<Server>();
|
||||||
|
|
||||||
|
foreach (var server in AllServers)
|
||||||
|
{
|
||||||
|
if (presentInGroup.All(x => x.Id != server.Id))
|
||||||
|
serversMissing.Add(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serversMissing.Any())
|
||||||
|
{
|
||||||
|
var defaultGroup = ServerGroups.FirstOrDefault(x => x.Name == "");
|
||||||
|
|
||||||
|
if (defaultGroup == null)
|
||||||
|
{
|
||||||
|
defaultGroup = new ServerGroup()
|
||||||
|
{
|
||||||
|
Name = ""
|
||||||
|
};
|
||||||
|
|
||||||
|
ServerGroups.Add(defaultGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var server in serversMissing)
|
||||||
|
defaultGroup.Servers.Add(server.Id.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddStatus(Server server, string status)
|
||||||
|
{
|
||||||
|
lock (StatusCache)
|
||||||
|
{
|
||||||
|
StatusCache.Add(server, status);
|
||||||
|
InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@*
|
||||||
@if (AllServers.Any())
|
@if (AllServers.Any())
|
||||||
{
|
{
|
||||||
if (UseSortedServerView)
|
if (UseSortedServerView)
|
||||||
@@ -207,57 +608,4 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</LazyLoader>
|
*@
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[CascadingParameter]
|
|
||||||
public User User { get; set; }
|
|
||||||
|
|
||||||
private Server[] AllServers;
|
|
||||||
private readonly Dictionary<Server, string> StatusCache = new();
|
|
||||||
|
|
||||||
private bool UseSortedServerView = false;
|
|
||||||
|
|
||||||
private Task Load(LazyLoader arg)
|
|
||||||
{
|
|
||||||
AllServers = ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.Include(x => x.MainAllocation)
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.Include(x => x.Image)
|
|
||||||
.Where(x => x.Owner.Id == User.Id)
|
|
||||||
.OrderBy(x => x.Name)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var server in AllServers)
|
|
||||||
{
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
|
||||||
var serverService = scope.ServiceProvider.GetRequiredService<ServerService>();
|
|
||||||
|
|
||||||
AddStatus(server, (await serverService.GetDetails(server)).State);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
AddStatus(server, "failed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddStatus(Server server, string status)
|
|
||||||
{
|
|
||||||
lock (StatusCache)
|
|
||||||
{
|
|
||||||
StatusCache.Add(server, status);
|
|
||||||
InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -355,8 +355,6 @@
|
|||||||
{
|
{
|
||||||
// filter here what key events should be sent to moonlight
|
// filter here what key events should be sent to moonlight
|
||||||
|
|
||||||
console.log(event);
|
|
||||||
|
|
||||||
if(event.code === "KeyS" && event.ctrlKey)
|
if(event.code === "KeyS" && event.ctrlKey)
|
||||||
{
|
{
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -370,5 +368,54 @@
|
|||||||
{
|
{
|
||||||
window.removeEventListener('keydown', moonlight.keyListener.listener);
|
window.removeEventListener('keydown', moonlight.keyListener.listener);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
serverList: {
|
||||||
|
init: function ()
|
||||||
|
{
|
||||||
|
if(moonlight.serverList.Swappable)
|
||||||
|
{
|
||||||
|
moonlight.serverList.Swappable.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let containers = document.querySelectorAll(".draggable-zone");
|
||||||
|
|
||||||
|
if (containers.length !== 0)
|
||||||
|
{
|
||||||
|
moonlight.serverList.Swappable = new Draggable.Sortable(containers, {
|
||||||
|
draggable: ".draggable",
|
||||||
|
handle: ".draggable .draggable-handle",
|
||||||
|
mirror: {
|
||||||
|
//appendTo: selector,
|
||||||
|
appendTo: "body",
|
||||||
|
constrainDimensions: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getData: function ()
|
||||||
|
{
|
||||||
|
let groups = new Array();
|
||||||
|
|
||||||
|
let groupElements = document.querySelectorAll('[ml-server-group]');
|
||||||
|
|
||||||
|
groupElements.forEach(groupElement => {
|
||||||
|
let group = new Object();
|
||||||
|
group.name = groupElement.attributes.getNamedItem("ml-server-group").value;
|
||||||
|
|
||||||
|
let servers = new Array();
|
||||||
|
let serverElements = groupElement.querySelectorAll("[ml-server-id]");
|
||||||
|
|
||||||
|
serverElements.forEach(serverElement => {
|
||||||
|
let id = serverElement.attributes.getNamedItem("ml-server-id").value;
|
||||||
|
|
||||||
|
servers.push(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
group.servers = servers;
|
||||||
|
groups.push(group);
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user