almost done
This commit is contained in:
@@ -2,5 +2,4 @@
|
||||
|
||||
namespace TorchRemote.Models.Responses;
|
||||
|
||||
public record SettingInfoResponse(string Name, JsonSchema Schema, ICollection<SettingPropertyInfo> PropertyInfos);
|
||||
public record SettingPropertyInfo(string Name, string PropertyName, string? Description, int? Order);
|
||||
public record SettingInfoResponse(string Name, JsonSchema Schema);
|
@@ -8,6 +8,7 @@ namespace TorchRemote.Models.Shared.Settings;
|
||||
[JsonDerivedType(typeof(BooleanProperty), "boolean")]
|
||||
[JsonDerivedType(typeof(NumberProperty), "number")]
|
||||
[JsonDerivedType(typeof(ObjectProperty), "object")]
|
||||
[JsonDerivedType(typeof(ArrayProperty), "array")]
|
||||
[JsonDerivedType(typeof(EnumProperty), "enum")]
|
||||
[JsonDerivedType(typeof(DateTimeProperty), "date-time")]
|
||||
[JsonDerivedType(typeof(DurationProperty), "duration")]
|
||||
@@ -21,6 +22,7 @@ public record StringProperty(string Name, string? Value) : PropertyBase(Name);
|
||||
public record EnumProperty(string Name, string Value) : PropertyBase(Name);
|
||||
public record BooleanProperty(string Name, bool? Value) : PropertyBase(Name);
|
||||
public record ObjectProperty(string Name, JsonElement Value) : PropertyBase(Name);
|
||||
public record ArrayProperty(string Name, JsonElement Value) : PropertyBase(Name);
|
||||
public record DateTimeProperty(string Name, DateTime? Value) : PropertyBase(Name);
|
||||
public record DurationProperty(string Name, TimeSpan? Value) : PropertyBase(Name);
|
||||
public record UriProperty(string Name, Uri? Value) : PropertyBase(Name);
|
||||
|
@@ -1,10 +1,10 @@
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
using Humanizer;
|
||||
using Torch.Views;
|
||||
using TorchRemote.Models.Responses;
|
||||
using TorchRemote.Models.Shared.Settings;
|
||||
using TorchRemote.Plugin.Abstractions.Controllers;
|
||||
@@ -17,27 +17,19 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
{
|
||||
private const string RootPath = "/settings";
|
||||
|
||||
[Route(HttpVerbs.Get, RootPath)]
|
||||
public IEnumerable<string> GetAllSettings()
|
||||
{
|
||||
return Statics.SettingManager.Settings.Keys;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, $"{RootPath}/{{fullName}}")]
|
||||
public SettingInfoResponse Get(string fullName)
|
||||
{
|
||||
if (!Statics.SettingManager.Settings.TryGetValue(fullName, out var setting))
|
||||
throw HttpException.NotFound($"Setting with fullName {fullName} not found", fullName);
|
||||
|
||||
return new(StringExtensions.Humanize(setting.Name), setting.Schema, setting
|
||||
.Type.GetProperties(
|
||||
BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(b => setting.IncludeDisplayOnly &&
|
||||
b.HasAttribute<DisplayAttribute>())
|
||||
.Select(b =>
|
||||
{
|
||||
var attr = b.GetCustomAttribute<DisplayAttribute>();
|
||||
return new SettingPropertyInfo(
|
||||
attr?.Name ?? StringExtensions.Humanize(b.Name),
|
||||
Statics.SerializerOptions
|
||||
.PropertyNamingPolicy!.ConvertName(b.Name),
|
||||
attr?.Description,
|
||||
attr?.Order is 0 or null ? null : attr.Order);
|
||||
}).ToArray());
|
||||
return new(StringExtensions.Humanize(setting.Name), setting.Schema);
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, $"{RootPath}/{{fullName}}/values")]
|
||||
@@ -59,9 +51,12 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
|
||||
return type switch
|
||||
{
|
||||
_ when type == typeof(int) || type == typeof(int?) => (PropertyBase)new IntegerProperty(name, (int?)value),
|
||||
_ when type == typeof(int) || type == typeof(int?) => (PropertyBase)new IntegerProperty(
|
||||
name, (int?)value),
|
||||
_ when type == typeof(bool) || type == typeof(bool?) => new BooleanProperty(name, (bool?)value),
|
||||
_ when type == typeof(string) => new StringProperty(name, (string?)value),
|
||||
_ when type.IsPrimitive => new NumberProperty(name, value is null ? null : (double?)Convert.ChangeType(value, typeof(double))),
|
||||
_ when type.IsPrimitive => new NumberProperty(
|
||||
name, value is null ? null : (double?)Convert.ChangeType(value, typeof(double))),
|
||||
_ when type == typeof(DateTime) || type == typeof(DateTime?) => new DateTimeProperty(
|
||||
name, (DateTime?)value),
|
||||
_ when type == typeof(TimeSpan) || type == typeof(TimeSpan?) => new DurationProperty(
|
||||
@@ -71,6 +66,8 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
_ when type == typeof(Guid) || type == typeof(Guid?) => new UuidProperty(
|
||||
name, (Guid?)value),
|
||||
_ when type == typeof(Uri) => new UriProperty(name, (Uri?)value),
|
||||
_ when typeof(ICollection).IsAssignableFrom(type) =>
|
||||
new ArrayProperty(name, JsonSerializer.SerializeToElement(value, type, Statics.SerializerOptions)),
|
||||
_ when type.IsClass => new ObjectProperty(
|
||||
name, JsonSerializer.SerializeToElement(value, type, Statics.SerializerOptions)),
|
||||
_ => throw HttpException.NotFound("Property type not found", name),
|
||||
@@ -147,6 +144,9 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
case ObjectProperty objectProperty when type.IsClass:
|
||||
propInfo.SetValue(instance, objectProperty.Value.Deserialize(type, Statics.SerializerOptions));
|
||||
break;
|
||||
case ArrayProperty arrayProperty when typeof(ICollection).IsAssignableFrom(type):
|
||||
propInfo.SetValue(instance, arrayProperty.Value.Deserialize(type, Statics.SerializerOptions));
|
||||
break;
|
||||
case StringProperty stringProperty when type == typeof(string):
|
||||
propInfo.SetValue(instance, stringProperty.Value);
|
||||
break;
|
||||
|
@@ -1,12 +1,16 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Json.Schema;
|
||||
using Json.Schema.Generation;
|
||||
using NLog;
|
||||
using Torch;
|
||||
using Torch.API;
|
||||
using Torch.Managers;
|
||||
using Torch.Server;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
using TorchRemote.Plugin.Refiners;
|
||||
using TorchRemote.Plugin.Utils;
|
||||
|
||||
namespace TorchRemote.Plugin.Managers;
|
||||
@@ -17,6 +21,8 @@ public class SettingManager : Manager
|
||||
|
||||
[Dependency]
|
||||
private readonly InstanceManager _instanceManager = null!;
|
||||
[Dependency]
|
||||
private readonly PluginManager _pluginManager = null!;
|
||||
|
||||
public SettingManager(ITorchBase torchInstance) : base(torchInstance)
|
||||
{
|
||||
@@ -26,25 +32,66 @@ public class SettingManager : Manager
|
||||
{
|
||||
base.Attach();
|
||||
_instanceManager.InstanceLoaded += InstanceManagerOnInstanceLoaded;
|
||||
|
||||
RegisterSetting("Torch Config", Torch.Config, typeof(TorchConfig));
|
||||
|
||||
foreach (var plugin in _pluginManager.Plugins.Values)
|
||||
{
|
||||
var type = plugin.GetType();
|
||||
object persistentInstance;
|
||||
|
||||
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
|
||||
bool IsSuitable(MemberInfo m, Type t) =>
|
||||
t.IsGenericType && typeof(Persistent<>).IsAssignableFrom(t.GetGenericTypeDefinition()) &&
|
||||
(m.Name.Contains(
|
||||
"config", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
m.Name.Contains(
|
||||
"cfg", StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
var fields = type.GetFields(flags).Where(b => IsSuitable(b, b.FieldType)).ToArray();
|
||||
var props = type.GetProperties(flags).Where(b => IsSuitable(b, b.PropertyType)).ToArray();
|
||||
|
||||
if (fields.FirstOrDefault() is { } field)
|
||||
{
|
||||
persistentInstance = field.GetValue(plugin);
|
||||
}
|
||||
else if (props.FirstOrDefault() is { } prop)
|
||||
{
|
||||
persistentInstance = prop.GetValue(plugin);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (persistentInstance is null)
|
||||
continue;
|
||||
|
||||
var persistentType = persistentInstance.GetType();
|
||||
var getter = persistentType.GetProperty("Data")!;
|
||||
|
||||
RegisterSetting(plugin.Name, getter.GetValue(persistentInstance), persistentType.GenericTypeArguments[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void InstanceManagerOnInstanceLoaded(ConfigDedicatedViewModel config)
|
||||
{
|
||||
RegisterSetting(config.SessionSettings, typeof(SessionSettingsViewModel));
|
||||
RegisterSetting("Session Settings", config.SessionSettings, typeof(SessionSettingsViewModel));
|
||||
}
|
||||
|
||||
public void RegisterSetting(object value, Type type, bool includeOnlyDisplay = true)
|
||||
public void RegisterSetting(string name, object value, Type type)
|
||||
{
|
||||
var builder = new JsonSchemaBuilder().FromType(type, new()
|
||||
{
|
||||
PropertyNamingMethod = input => Statics.SerializerOptions.PropertyNamingPolicy!.ConvertName(input)
|
||||
PropertyNamingMethod = input => Statics.SerializerOptions.PropertyNamingPolicy!.ConvertName(input),
|
||||
Refiners = { new DisplayAttributeRefiner() }
|
||||
});
|
||||
|
||||
Settings[type.FullName!] = new(type.Name, type, builder.Build(), value, includeOnlyDisplay);
|
||||
Log.Info("Registered {0} type", type.FullName);
|
||||
Settings[type.FullName!] = new(name, type, builder.Build(), value);
|
||||
Log.Info("Registered {0} type with name {1}", type.FullName, name);
|
||||
}
|
||||
|
||||
public IDictionary<string, Setting> Settings { get; } = new ConcurrentDictionary<string, Setting>();
|
||||
}
|
||||
|
||||
public record Setting(string Name, Type Type, JsonSchema Schema, object Value, bool IncludeDisplayOnly);
|
||||
public record Setting(string Name, Type Type, JsonSchema Schema, object Value);
|
28
TorchRemote.Plugin/Refiners/DisplayAttributeRefiner.cs
Normal file
28
TorchRemote.Plugin/Refiners/DisplayAttributeRefiner.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Json.Schema.Generation;
|
||||
using Json.Schema.Generation.Intents;
|
||||
using Torch.Views;
|
||||
|
||||
namespace TorchRemote.Plugin.Refiners;
|
||||
|
||||
public class DisplayAttributeRefiner : ISchemaRefiner
|
||||
{
|
||||
public bool ShouldRun(SchemaGenerationContextBase context)
|
||||
{
|
||||
return context.GetAttributes().OfType<DisplayAttribute>().Any();
|
||||
}
|
||||
|
||||
public void Run(SchemaGenerationContextBase context)
|
||||
{
|
||||
foreach (var displayAttribute in context.GetAttributes().OfType<DisplayAttribute>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(displayAttribute.Name))
|
||||
context.Intents.Add(new TitleIntent(displayAttribute.Name));
|
||||
|
||||
if (!string.IsNullOrEmpty(displayAttribute.Description))
|
||||
context.Intents.Add(new DescriptionIntent(displayAttribute.Description));
|
||||
|
||||
if (displayAttribute.ReadOnly)
|
||||
context.Intents.Add(new ReadOnlyIntent(displayAttribute.ReadOnly));
|
||||
}
|
||||
}
|
||||
}
|
@@ -75,7 +75,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EmbedIO" Version="3.5.0" />
|
||||
<PackageReference Include="EmbedIO" Version="3.4.3" />
|
||||
<PackageReference Include="EmbedIO.BearerToken" Version="3.4.2" />
|
||||
<PackageReference Include="Json.More.Net" Version="1.7.1" />
|
||||
<PackageReference Include="JsonPointer.Net" Version="2.2.2" />
|
||||
|
@@ -1 +0,0 @@
|
||||
global using JsonData = TorchRemote.Plugin.Utils.JsonDataAttribute;
|
@@ -2,5 +2,5 @@
|
||||
<PluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Name>TorchRemote.Plugin</Name>
|
||||
<Guid>284017F3-9682-4841-A544-EB04DB8CB9BA</Guid>
|
||||
<Version>v1.0.1</Version>
|
||||
<Version>v1.0.2</Version>
|
||||
</PluginManifest>
|
@@ -4,11 +4,16 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.WebSockets;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Refit;
|
||||
using TorchRemote.Models.Responses;
|
||||
using Websocket.Client;
|
||||
using Websocket.Client.Models;
|
||||
|
||||
namespace TorchRemote.Services;
|
||||
|
||||
public class ApiClientService : IDisposable
|
||||
@@ -23,7 +28,7 @@ public class ApiClientService : IDisposable
|
||||
private readonly CancellationTokenSource _tokenSource = new();
|
||||
public string BaseUrl
|
||||
{
|
||||
get => _client.BaseAddress?.ToString() ?? "http://localhost";
|
||||
get => _client.BaseAddress?.ToString() ?? $"http://localhost/api/{Version}/";
|
||||
set => _client.BaseAddress = new($"{value}/api/{Version}/");
|
||||
}
|
||||
|
||||
@@ -48,9 +53,11 @@ public class ApiClientService : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public Task<WebsocketClient> WatchChatAsync() => StartWebsocketConnectionAsync("live/chat");
|
||||
public async Task<WebsocketFeed<ChatResponseBase>> WatchChatAsync() =>
|
||||
new(await StartWebsocketConnectionAsync("live/chat"));
|
||||
|
||||
public Task<WebsocketClient> WatchLogLinesAsync() => StartWebsocketConnectionAsync("live/logs");
|
||||
public async Task<WebsocketFeed<LogLineResponse>> WatchLogLinesAsync() =>
|
||||
new(await StartWebsocketConnectionAsync("live/logs"));
|
||||
|
||||
private async Task<WebsocketClient> StartWebsocketConnectionAsync(string url)
|
||||
{
|
||||
@@ -77,4 +84,32 @@ public class ApiClientService : IDisposable
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
}
|
||||
|
||||
public static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
|
||||
}
|
||||
|
||||
public sealed class WebsocketFeed<TMessage> : IDisposable where TMessage : class
|
||||
{
|
||||
private readonly WebsocketClient _client;
|
||||
|
||||
public WebsocketFeed(WebsocketClient client)
|
||||
{
|
||||
_client = client;
|
||||
Disconnected = client.DisconnectionHappened;
|
||||
Connected = client.ReconnectionHappened;
|
||||
Messages = client.MessageReceived.Where(b => b.MessageType is WebSocketMessageType.Text)
|
||||
.Select(b => JsonSerializer.Deserialize<TMessage>(b.Text, ApiClientService.SerializerOptions))
|
||||
.Where(b => b is not null)!;
|
||||
}
|
||||
|
||||
public IObservable<DisconnectionInfo> Disconnected { get; }
|
||||
|
||||
public IObservable<ReconnectionInfo> Connected { get; }
|
||||
|
||||
public IObservable<TMessage> Messages { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ using Refit;
|
||||
using TorchRemote.Models.Requests;
|
||||
using TorchRemote.Models.Responses;
|
||||
using TorchRemote.Models.Shared;
|
||||
using TorchRemote.Models.Shared.Settings;
|
||||
|
||||
namespace TorchRemote.Services;
|
||||
|
||||
public interface IRemoteApi
|
||||
@@ -41,7 +43,12 @@ public interface IRemoteApi
|
||||
#endregion
|
||||
|
||||
#region Settings
|
||||
[Get("/settings/{id}")]
|
||||
Task<IApiResponse<SettingInfoResponse>> GetSetting(Guid id);
|
||||
[Get("/settings/{fullName}")]
|
||||
Task<IApiResponse<SettingInfoResponse>> GetSetting(string fullName);
|
||||
[Get("/settings/{fullName}/values")]
|
||||
Task<IApiResponse<IEnumerable<PropertyBase>>> GetSettingValues(string fullName, [Body] IEnumerable<string> propertyNames);
|
||||
[Patch("/settings/{fullName}/values")]
|
||||
Task<IApiResponse<int>> PatchSettingValues(string fullName, [Body] IEnumerable<PropertyBase> properties);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
<TrimMode>copyused</TrimMode>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<AvaloniaVersion>0.10.17</AvaloniaVersion>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<EnablePreviewFeatures>True</EnablePreviewFeatures>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
@@ -28,7 +30,7 @@
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="18.3.1" />
|
||||
<PackageReference Include="Refit" Version="6.3.2" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="System.Text.Json" Version="7.0.0-preview.6.22324.4" />
|
||||
<PackageReference Include="System.Text.Json" Version="7.0.0-rc.1.22426.10" />
|
||||
<PackageReference Include="Websocket.Client" Version="4.4.43" />
|
||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
|
||||
</ItemGroup>
|
||||
|
@@ -15,15 +15,13 @@ public class ChatViewModel : ViewModelBase
|
||||
{
|
||||
public ChatViewModel(ApiClientService clientService)
|
||||
{
|
||||
Observable.FromEventPattern(clientService, nameof(clientService.Connected))
|
||||
clientService.Connected
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
||||
Observable.FromAsync(clientService.WatchChatAsync)
|
||||
.Select(b => b.MessageReceived)
|
||||
.Select(b => b.Messages)
|
||||
.Concat()
|
||||
.Select(b => JsonSerializer.Deserialize<ChatResponseBase>(b.Text, options))
|
||||
.Select(b => b switch
|
||||
{
|
||||
ChatMessageResponse msg => $"[{msg.Channel}] {msg.AuthorName}: {msg.Message}",
|
||||
|
@@ -18,7 +18,7 @@ public class DashboardViewModel : ViewModelBase
|
||||
{
|
||||
_clientService = clientService;
|
||||
|
||||
Observable.FromEventPattern(_clientService, nameof(_clientService.Connected))
|
||||
_clientService.Connected
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
@@ -35,11 +35,9 @@ public class DashboardViewModel : ViewModelBase
|
||||
MemberCount = online;
|
||||
});
|
||||
|
||||
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
||||
Observable.FromAsync(() => _clientService.WatchLogLinesAsync())
|
||||
.Select(b => b.MessageReceived)
|
||||
.Select(b => b.Messages)
|
||||
.Concat()
|
||||
.Select(b => JsonSerializer.Deserialize<LogLineResponse>(b.Text, options))
|
||||
.Select(b => $"{b.Time:hh:mm:ss} [{b.Level}] {(b.Logger.Contains('.') ? b.Logger[(b.Logger.LastIndexOf('.') + 1)..] : b.Logger)}: {b.Message}")
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(s =>
|
||||
|
@@ -11,7 +11,7 @@ public class ServerConfigViewModel : ViewModelBase
|
||||
{
|
||||
public ServerConfigViewModel(ApiClientService clientService)
|
||||
{
|
||||
Observable.FromEventPattern(clientService, nameof(clientService.Connected))
|
||||
clientService.Connected
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Select(_ => Observable.FromAsync(clientService.Api.GetServerSettings))
|
||||
.Concat()
|
||||
@@ -35,7 +35,7 @@ public class ServerConfigViewModel : ViewModelBase
|
||||
new(Ip, Port)
|
||||
)));
|
||||
|
||||
Worlds = Observable.FromEventPattern(clientService, nameof(clientService.Connected))
|
||||
Worlds = clientService.Connected
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Select(_ => Observable.FromAsync(clientService.Api.GetWorlds))
|
||||
.Concat()
|
||||
@@ -46,7 +46,7 @@ public class ServerConfigViewModel : ViewModelBase
|
||||
.Select(b => new World(id, b.Name, b.SizeKb)))
|
||||
.Concat();
|
||||
|
||||
Observable.FromEventPattern(clientService, nameof(clientService.Connected))
|
||||
clientService.Connected
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Select(_ => Observable.FromAsync(clientService.Api.GetSelectedWorld))
|
||||
.Concat()
|
||||
|
177
TorchRemote/ViewModels/Server/SettingViewModel.cs
Normal file
177
TorchRemote/ViewModels/Server/SettingViewModel.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using DynamicData.Binding;
|
||||
using Json.Schema;
|
||||
using ReactiveUI;
|
||||
using TorchRemote.Models.Responses;
|
||||
using TorchRemote.Models.Shared.Settings;
|
||||
using TorchRemote.Services;
|
||||
using Api = TorchRemote.Models.Shared.Settings;
|
||||
|
||||
namespace TorchRemote.ViewModels.Server;
|
||||
|
||||
public class SettingViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ApiClientService _service;
|
||||
public ObjectProperty Setting { get; set; } = null!;
|
||||
public string FullName { get; set; } = string.Empty;
|
||||
|
||||
public SettingViewModel(ApiClientService service)
|
||||
{
|
||||
_service = service;
|
||||
|
||||
/*this.WhenValueChanged(x => x.FullName, false)
|
||||
.Select(b => Observable.FromAsync(() => _service.Api.GetSetting(b!)))
|
||||
.Concat()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Where(b => b.Content is not null)
|
||||
.Select(b =>
|
||||
{
|
||||
var value = b.Content!;
|
||||
|
||||
|
||||
})*/
|
||||
}
|
||||
}
|
||||
|
||||
public interface IProperty<in TProperty> where TProperty : PropertyBase
|
||||
{
|
||||
public static abstract Property FromProperty(TProperty property);
|
||||
}
|
||||
|
||||
public abstract class Property : ViewModelBase
|
||||
{
|
||||
public abstract string TypeName { get; }
|
||||
}
|
||||
|
||||
public class IntegerProperty : Property, IProperty<Api.IntegerProperty>
|
||||
{
|
||||
public int Value { get; set; }
|
||||
public override string TypeName => "integer";
|
||||
|
||||
public static Property FromProperty(Api.IntegerProperty property)
|
||||
{
|
||||
return new IntegerProperty
|
||||
{
|
||||
Value = property.Value.GetValueOrDefault()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class StringProperty : Property, IProperty<Api.StringProperty>
|
||||
{
|
||||
public string Value { get; set; } = "value";
|
||||
public override string TypeName => "string";
|
||||
public static Property FromProperty(Api.StringProperty property)
|
||||
{
|
||||
return new StringProperty
|
||||
{
|
||||
Value = property.Value ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class BooleanProperty : Property, IProperty<Api.BooleanProperty>
|
||||
{
|
||||
public bool Value { get; set; }
|
||||
public override string TypeName => "boolean";
|
||||
public static Property FromProperty(Api.BooleanProperty property)
|
||||
{
|
||||
return new BooleanProperty
|
||||
{
|
||||
Value = property.Value.GetValueOrDefault()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class NumberProperty : Property, IProperty<Api.NumberProperty>
|
||||
{
|
||||
public double Value { get; set; }
|
||||
public override string TypeName => "number";
|
||||
public static Property FromProperty(Api.NumberProperty property)
|
||||
{
|
||||
return new NumberProperty
|
||||
{
|
||||
Value = property.Value.GetValueOrDefault()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class DateTimeProperty : Property, IProperty<Api.DateTimeProperty>
|
||||
{
|
||||
public DateTime Value { get; set; }
|
||||
public override string TypeName => "date-time";
|
||||
public static Property FromProperty(Api.DateTimeProperty property)
|
||||
{
|
||||
return new DateTimeProperty
|
||||
{
|
||||
Value = property.Value ?? DateTime.Now
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class DurationProperty : Property, IProperty<Api.DurationProperty>
|
||||
{
|
||||
public TimeSpan Value { get; set; }
|
||||
public override string TypeName => "duration";
|
||||
public static Property FromProperty(Api.DurationProperty property)
|
||||
{
|
||||
return new DurationProperty
|
||||
{
|
||||
Value = property.Value ?? TimeSpan.Zero
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class UuidProperty : Property, IProperty<Api.UuidProperty>
|
||||
{
|
||||
public Guid Value { get; set; }
|
||||
public override string TypeName => "uuid";
|
||||
public static Property FromProperty(Api.UuidProperty property)
|
||||
{
|
||||
return new UuidProperty
|
||||
{
|
||||
Value = property.Value ?? Guid.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class UriProperty : Property, IProperty<Api.UriProperty>
|
||||
{
|
||||
public string Value { get; set; } = null!;
|
||||
public override string TypeName => "uri";
|
||||
public static Property FromProperty(Api.UriProperty property)
|
||||
{
|
||||
return new UriProperty
|
||||
{
|
||||
Value = property.Value?.ToString() ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumProperty : Property, IProperty<Api.EnumProperty>
|
||||
{
|
||||
public string Value { get; set; } = "value";
|
||||
public IEnumerable<(string Name, string Value)> EnumValues { get; set; } = ArraySegment<(string Name, string Value)>.Empty;
|
||||
public override string TypeName => "enum";
|
||||
public static Property FromProperty(Api.EnumProperty property)
|
||||
{
|
||||
return new EnumProperty
|
||||
{
|
||||
Value = property.Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class ObjectProperty : Property, IProperty<Api.ObjectProperty>
|
||||
{
|
||||
public ObservableCollection<Property> Properties { get; init; } = new();
|
||||
public override string TypeName => "object";
|
||||
public static Property FromProperty(Api.ObjectProperty property)
|
||||
{
|
||||
return new ObjectProperty();
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ApiClientService _clientService;
|
||||
[Reactive]
|
||||
public string BearerToken { get; set; } = "WcdYT5qHjSt5Uzjs54xu8vE9Oq4a5MD2edLxywtJHtc=";
|
||||
public string BearerToken { get; set; } = "NSN9qSbvKO6PtvoUg+fV5CrSpLqz+F2ssXvzbFbgOpE=";
|
||||
[Reactive]
|
||||
public string RemoteUrl { get; set; } = "http://localhost";
|
||||
|
||||
|
@@ -5,6 +5,6 @@ namespace TorchRemote.ViewModels
|
||||
public class ViewModelBase : ReactiveObject, IRoutableViewModel
|
||||
{
|
||||
public string? UrlPathSegment { get; set; }
|
||||
public IScreen HostScreen { get; set; }
|
||||
public IScreen HostScreen { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
|
@@ -31,9 +31,7 @@
|
||||
<Panel>
|
||||
<Grid Margin="3" RowDefinitions="Auto,*">
|
||||
<TextBlock Text="{Binding CurrentNavItem.Title}" FontSize="24" FontWeight="Bold" />
|
||||
<ScrollViewer Grid.Row="1" Margin="0,10" HorizontalScrollBarVisibility="Auto">
|
||||
<reactiveUi:RoutedViewHost Router="{Binding Router}" />
|
||||
</ScrollViewer>
|
||||
<reactiveUi:RoutedViewHost Grid.Row="1" Margin="0,10" Router="{Binding Router}" />
|
||||
</Grid>
|
||||
<TextBlock Text="Loading..."
|
||||
IsVisible="{Binding !Connected}"
|
||||
|
Reference in New Issue
Block a user