From 93cfe7cd1abab5f4c2f68606567b69fe5ad87124 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Mon, 30 Oct 2023 00:40:59 +0100 Subject: [PATCH 1/2] Started on implementing a better reflection based editor --- Moonlight/Shared/Views/Admin/Settings.razor | 95 +++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Moonlight/Shared/Views/Admin/Settings.razor diff --git a/Moonlight/Shared/Views/Admin/Settings.razor b/Moonlight/Shared/Views/Admin/Settings.razor new file mode 100644 index 00000000..3090b850 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Settings.razor @@ -0,0 +1,95 @@ +@page "/admin/settings" +@using Moonlight.App.Services + +@inject ConfigService ConfigService + +@if (ModelToShow == null) +{ +

Nope

+} +else +{ +
+
+ @{ + string title; + + if (Path.Length == 0) + title = "Configuration"; + else + title = string.Join( + " > ", + Path.Select(x => + x.EndsWith("Data") ? Formatter.ReplaceEnd(x, "Data", "") : x)); + } + +

@(title)

+
+
+
+
+ @{ + var props = ModelToShow + .GetType() + .GetProperties() + .Where(x => x.PropertyType.Assembly.FullName!.Contains("Moonlight") && x.PropertyType.IsClass) + .ToArray(); + } + + @foreach (var prop in props) + { +
+ +
+ } +
+
+ +
+
+
+
+} + +@code +{ + [Parameter] + [SupplyParameterFromQuery] + public string? Section { get; set; } = ""; + + private object? ModelToShow = new(); + private string[] Path = Array.Empty(); + + protected override async Task OnParametersSetAsync() + { + if (Section != null && Section.StartsWith("/")) + Section = Section.TrimStart('/'); + + Path = Section != null ? Section.Split("/") : Array.Empty(); + + ModelToShow = Resolve(ConfigService.Get(), Path, 0); + + await InvokeAsync(StateHasChanged); + } + + 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.PropertyType.IsClass && x.Name == path[index]); + + if (prop == null) + return null; + + return Resolve(prop.GetValue(model)!, path, index + 1); + } +} \ No newline at end of file From 11dace2617a52ac3ea59bf7e14c7b744ed2754bd Mon Sep 17 00:00:00 2001 From: Baumgartner Marcel Date: Tue, 31 Oct 2023 10:48:20 +0100 Subject: [PATCH 2/2] Implemented config editor. Fixed auto property --- Moonlight/App/Helpers/PropBinder.cs | 6 +- Moonlight/App/Services/ConfigService.cs | 7 +- .../Components/Forms/AutoProperty.razor | 5 + .../Navigations/AdminSysNavigation.razor | 22 ++ .../Shared/Components/Partials/Sidebar.razor | 11 + Moonlight/Shared/Views/Admin/Settings.razor | 95 --------- Moonlight/Shared/Views/Admin/Sys/Index.razor | 8 + .../Shared/Views/Admin/Sys/Settings.razor | 190 ++++++++++++++++++ 8 files changed, 245 insertions(+), 99 deletions(-) create mode 100644 Moonlight/Shared/Components/Navigations/AdminSysNavigation.razor delete mode 100644 Moonlight/Shared/Views/Admin/Settings.razor create mode 100644 Moonlight/Shared/Views/Admin/Sys/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Sys/Settings.razor diff --git a/Moonlight/App/Helpers/PropBinder.cs b/Moonlight/App/Helpers/PropBinder.cs index 52c41313..9d3d9865 100644 --- a/Moonlight/App/Helpers/PropBinder.cs +++ b/Moonlight/App/Helpers/PropBinder.cs @@ -24,13 +24,13 @@ public class PropBinder get => (int)PropertyInfo.GetValue(DataObject)!; set => PropertyInfo.SetValue(DataObject, value); } - + public long LongValue { get => (long)PropertyInfo.GetValue(DataObject)!; set => PropertyInfo.SetValue(DataObject, value); } - + public bool BoolValue { get => (bool)PropertyInfo.GetValue(DataObject)!; @@ -48,7 +48,7 @@ public class PropBinder get => (T)PropertyInfo.GetValue(DataObject)!; set => PropertyInfo.SetValue(DataObject, value); } - + public double DoubleValue { get => (double)PropertyInfo.GetValue(DataObject)!; diff --git a/Moonlight/App/Services/ConfigService.cs b/Moonlight/App/Services/ConfigService.cs index 7f74598d..322a4496 100644 --- a/Moonlight/App/Services/ConfigService.cs +++ b/Moonlight/App/Services/ConfigService.cs @@ -21,7 +21,12 @@ public class ConfigService var text = File.ReadAllText(Path); Data = JsonConvert.DeserializeObject(text) ?? new(); - text = JsonConvert.SerializeObject(Data, Formatting.Indented); + Save(); + } + + public void Save() + { + var text = JsonConvert.SerializeObject(Data, Formatting.Indented); File.WriteAllText(Path, text); } diff --git a/Moonlight/Shared/Components/Forms/AutoProperty.razor b/Moonlight/Shared/Components/Forms/AutoProperty.razor index 1bc407b7..ae7616dd 100644 --- a/Moonlight/Shared/Components/Forms/AutoProperty.razor +++ b/Moonlight/Shared/Components/Forms/AutoProperty.razor @@ -133,6 +133,11 @@ Binder = new(Property, Data); } + protected override void OnParametersSet() + { + Binder = new(Property, Data); + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) diff --git a/Moonlight/Shared/Components/Navigations/AdminSysNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSysNavigation.razor new file mode 100644 index 00000000..13b17065 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminSysNavigation.razor @@ -0,0 +1,22 @@ +
+ +
+ +@code +{ + [Parameter] + public int Index { get; set; } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/Sidebar.razor b/Moonlight/Shared/Components/Partials/Sidebar.razor index 65e27cca..c6e02021 100644 --- a/Moonlight/Shared/Components/Partials/Sidebar.razor +++ b/Moonlight/Shared/Components/Partials/Sidebar.razor @@ -105,6 +105,17 @@ + + } diff --git a/Moonlight/Shared/Views/Admin/Settings.razor b/Moonlight/Shared/Views/Admin/Settings.razor deleted file mode 100644 index 3090b850..00000000 --- a/Moonlight/Shared/Views/Admin/Settings.razor +++ /dev/null @@ -1,95 +0,0 @@ -@page "/admin/settings" -@using Moonlight.App.Services - -@inject ConfigService ConfigService - -@if (ModelToShow == null) -{ -

Nope

-} -else -{ -
-
- @{ - string title; - - if (Path.Length == 0) - title = "Configuration"; - else - title = string.Join( - " > ", - Path.Select(x => - x.EndsWith("Data") ? Formatter.ReplaceEnd(x, "Data", "") : x)); - } - -

@(title)

-
-
-
-
- @{ - var props = ModelToShow - .GetType() - .GetProperties() - .Where(x => x.PropertyType.Assembly.FullName!.Contains("Moonlight") && x.PropertyType.IsClass) - .ToArray(); - } - - @foreach (var prop in props) - { -
- -
- } -
-
- -
-
-
-
-} - -@code -{ - [Parameter] - [SupplyParameterFromQuery] - public string? Section { get; set; } = ""; - - private object? ModelToShow = new(); - private string[] Path = Array.Empty(); - - protected override async Task OnParametersSetAsync() - { - if (Section != null && Section.StartsWith("/")) - Section = Section.TrimStart('/'); - - Path = Section != null ? Section.Split("/") : Array.Empty(); - - ModelToShow = Resolve(ConfigService.Get(), Path, 0); - - await InvokeAsync(StateHasChanged); - } - - 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.PropertyType.IsClass && x.Name == path[index]); - - if (prop == null) - return null; - - return Resolve(prop.GetValue(model)!, path, index + 1); - } -} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/Index.razor b/Moonlight/Shared/Views/Admin/Sys/Index.razor new file mode 100644 index 00000000..0559f6cd --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/Index.razor @@ -0,0 +1,8 @@ +@page "/admin/sys" + +@using Moonlight.App.Extensions.Attributes +@using Moonlight.App.Models.Enums + +@attribute [RequirePermission(Permission.AdminRoot)] + + \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/Settings.razor b/Moonlight/Shared/Views/Admin/Sys/Settings.razor new file mode 100644 index 00000000..22f5399d --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/Settings.razor @@ -0,0 +1,190 @@ +@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 + + + +@if (ModelToShow == null) +{ + +} +else +{ +
+
+ @{ + string title; + + if (Path.Length == 0) + title = "Configuration"; + else + { + title = "Configuration - " + string.Join(" - ", Path); + } + } + +

@(title)

+
+ + + + + + +
+
+
+ + + Changes to these settings are live applied. The save button only make the changes persistently saved to disk + + +
+
+
+ @{ + var props = ModelToShow + .GetType() + .GetProperties() + .Where(x => x.PropertyType.Assembly.FullName!.Contains("Moonlight") && x.PropertyType.IsClass) + .ToArray(); + } + + @foreach (var prop in props) + { +
+ +
+ } + + @if (Path.Length != 0) + { +
+
+ Back +
+
+ } +
+
+
+
+
+ + @foreach (var prop in Properties) + { +
+ + + @{ + var typeToCreate = typeof(AutoProperty<>).MakeGenericType(prop.PropertyType); + } + + @ComponentHelper.FromType(typeToCreate) + + +
+ } +
+
+
+
+
+} + +@code +{ + [Parameter] + [SupplyParameterFromQuery] + public string? Section { get; set; } = ""; + + private object? ModelToShow; + private PropertyInfo[] Properties = Array.Empty(); + private string[] Path = Array.Empty(); + + private LazyLoader? LazyLoader; + + protected override async Task OnParametersSetAsync() + { + if (Section != null && Section.StartsWith("/")) + Section = Section.TrimStart('/'); + + Path = Section != null ? Section.Split("/") : Array.Empty(); + + 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(); + } + + 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"); + } +} \ No newline at end of file