Files
Moonlight/Moonlight/Shared/Views/Admin/Sys/Settings.razor
2023-10-31 10:48:20 +01:00

190 lines
5.7 KiB
Plaintext

@page "/admin/sys/settings"
@using Moonlight.App.Services
@using System.Reflection
@using Moonlight.App.Extensions.Attributes
@using Moonlight.App.Models.Enums
@attribute [RequirePermission(Permission.AdminRoot)]
@inject ConfigService ConfigService
@inject ToastService ToastService
<AdminSysNavigation Index="1" />
@if (ModelToShow == null)
{
<NotFoundAlert />
}
else
{
<div class="card mt-5">
<div class="card-header">
@{
string title;
if (Path.Length == 0)
title = "Configuration";
else
{
title = "Configuration - " + string.Join(" - ", Path);
}
}
<h3 class="card-title">@(title)</h3>
<div class="card-toolbar">
<WButton OnClick="Reload" CssClasses="btn btn-icon btn-warning me-3">
<i class="bx bx-sm bx-revision"></i>
</WButton>
<WButton OnClick="Save" CssClasses="btn btn-icon btn-success">
<i class="bx bx-sm bx-save"></i>
</WButton>
</div>
</div>
</div>
<Tooltip>
Changes to these settings are live applied. The save button only make the changes persistently saved to disk
</Tooltip>
<div class="row mt-5">
<div class="col-md-3 col-12 mb-5">
<div class="card card-body">
@{
var props = ModelToShow
.GetType()
.GetProperties()
.Where(x => x.PropertyType.Assembly.FullName!.Contains("Moonlight") && x.PropertyType.IsClass)
.ToArray();
}
@foreach (var prop in props)
{
<div class="d-flex flex-stack">
<div class="d-flex align-items-center flex-row-fluid flex-wrap">
<a href="/admin/sys/settings?section=@(Section + "/" + prop.Name)" class="fs-4 text-primary">@(prop.Name)</a>
</div>
</div>
}
@if (Path.Length != 0)
{
<div class="d-flex flex-stack">
<div class="d-flex align-items-center flex-row-fluid flex-wrap">
<a href="/admin/sys/@(GetBackPath())" class="fs-4 text-primary">Back</a>
</div>
</div>
}
</div>
</div>
<div class="col-md-9 col-12">
<div class="card card-body">
<div class="row">
<LazyLoader @ref="LazyLoader" Load="Load">
@foreach (var prop in Properties)
{
<div class="col-md-6 col-12">
<CascadingValue Name="Property" Value="prop">
<CascadingValue Name="Data" Value="ModelToShow">
@{
var typeToCreate = typeof(AutoProperty<>).MakeGenericType(prop.PropertyType);
}
@ComponentHelper.FromType(typeToCreate)
</CascadingValue>
</CascadingValue>
</div>
}
</LazyLoader>
</div>
</div>
</div>
</div>
}
@code
{
[Parameter]
[SupplyParameterFromQuery]
public string? Section { get; set; } = "";
private object? ModelToShow;
private PropertyInfo[] Properties = Array.Empty<PropertyInfo>();
private string[] Path = Array.Empty<string>();
private LazyLoader? LazyLoader;
protected override async Task OnParametersSetAsync()
{
if (Section != null && Section.StartsWith("/"))
Section = Section.TrimStart('/');
Path = Section != null ? Section.Split("/") : Array.Empty<string>();
ModelToShow = Resolve(ConfigService.Get(), Path, 0);
if (ModelToShow != null)
{
Properties = ModelToShow
.GetType()
.GetProperties()
.Where(x => !x.PropertyType.Assembly.FullName!.Contains("Moonlight"))
.ToArray();
}
else
{
Properties = Array.Empty<PropertyInfo>();
}
await InvokeAsync(StateHasChanged);
if (LazyLoader != null)
await LazyLoader.Reload();
}
private string GetBackPath()
{
if (Path.Length == 1)
return "settings";
else
{
var path = string.Join('/', Path.Take(Path.Length - 1)).TrimEnd('/');
return $"settings?section={path}";
}
}
private object? Resolve(object model, string[] path, int index)
{
if (path.Length == 0)
return model;
if (path.Length == index)
return model;
var prop = model
.GetType()
.GetProperties()
.FirstOrDefault(x => x.PropertyType.Assembly.FullName!.Contains("Moonlight") && x.Name == path[index]);
if (prop == null)
return null;
return Resolve(prop.GetValue(model)!, path, index + 1);
}
private Task Load(LazyLoader arg)
{
return Task.CompletedTask;
}
private async Task Save()
{
ConfigService.Save();
await ToastService.Success("Successfully saved config to disk");
}
private async Task Reload()
{
ConfigService.Reload();
await ToastService.Info("Reloaded configuration from disk");
}
}