mirror of
https://github.com/BlossomiShymae/Needlework.Net.git
synced 2025-12-06 18:20:47 +01:00
refactor: code/folder structure, remove hacks
This commit is contained in:
@@ -7,10 +7,6 @@
|
|||||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
RequestedThemeVariant="Dark">
|
RequestedThemeVariant="Dark">
|
||||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
<Application.DataTemplates>
|
|
||||||
<local:ViewLocator/>
|
|
||||||
</Application.DataTemplates>
|
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<sty:FluentAvaloniaTheme PreferSystemTheme="False" PreferUserAccentColor="False" />
|
<sty:FluentAvaloniaTheme PreferSystemTheme="False" PreferUserAccentColor="False" />
|
||||||
<materialIcons:MaterialIconStyles />
|
<materialIcons:MaterialIconStyles />
|
||||||
@@ -21,7 +17,7 @@
|
|||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<converters:EnumerableBoolConverter x:Key="EnumerableBoolConverter"/>
|
<converters:EnumerableToVisibility x:Key="EnumerableToVisibilityConverter"/>
|
||||||
<converters:NullBoolConverter x:Key="NullBoolConverter"/>
|
<converters:NullableToVisibility x:Key="NullableToVisibilityConverter"/>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
@@ -1,18 +1,33 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Needlework.Net.Extensions;
|
||||||
using Needlework.Net.ViewModels.MainWindow;
|
using Needlework.Net.ViewModels.MainWindow;
|
||||||
|
using Needlework.Net.ViewModels.Pages;
|
||||||
using Needlework.Net.Views.MainWindow;
|
using Needlework.Net.Views.MainWindow;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Needlework.Net;
|
namespace Needlework.Net;
|
||||||
|
|
||||||
public partial class App(IServiceProvider serviceProvider) : Application
|
public partial class App : Application, IEnableLogger
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider = serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public App(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
|
||||||
|
this.Log()
|
||||||
|
.Debug("NeedleworkDotNet version: {Version}", Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0");
|
||||||
|
this.Log()
|
||||||
|
.Debug("OS description: {Description}", System.Runtime.InteropServices.RuntimeInformation.OSDescription);
|
||||||
|
}
|
||||||
|
|
||||||
public static JsonSerializerOptions JsonSerializerOptions { get; } = new()
|
public static JsonSerializerOptions JsonSerializerOptions { get; } = new()
|
||||||
{
|
{
|
||||||
@@ -26,11 +41,17 @@ public partial class App(IServiceProvider serviceProvider) : Application
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
DataTemplates.Add(_serviceProvider.GetRequiredService<IDataTemplate>());
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
|
foreach (var page in _serviceProvider.GetServices<PageBase>())
|
||||||
|
{
|
||||||
|
Task.Run(page.InitializeAsync);
|
||||||
|
}
|
||||||
|
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
desktop.MainWindow = new MainWindowView()
|
desktop.MainWindow = new MainWindowView()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Needlework.Net.Converters
|
namespace Needlework.Net.Converters
|
||||||
{
|
{
|
||||||
public class EnumerableBoolConverter : IValueConverter
|
public class EnumerableToVisibility : IValueConverter
|
||||||
{
|
{
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
@@ -4,7 +4,7 @@ using System.Globalization;
|
|||||||
|
|
||||||
namespace Needlework.Net.Converters
|
namespace Needlework.Net.Converters
|
||||||
{
|
{
|
||||||
public class NullBoolConverter : IValueConverter
|
public class NullableToVisibility : IValueConverter
|
||||||
{
|
{
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.OpenApi.Readers;
|
|
||||||
using Needlework.Net.Models;
|
|
||||||
using System;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Needlework.Net
|
|
||||||
{
|
|
||||||
public class DataSource
|
|
||||||
{
|
|
||||||
private readonly ILogger<DataSource> _logger;
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private Document? _lcuSchemaDocument;
|
|
||||||
private Document? _lolClientDocument;
|
|
||||||
private readonly TaskCompletionSource<bool> _taskCompletionSource = new();
|
|
||||||
|
|
||||||
|
|
||||||
public DataSource(HttpClient httpClient, ILogger<DataSource> logger)
|
|
||||||
{
|
|
||||||
_httpClient = httpClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Document> GetLcuSchemaDocumentAsync()
|
|
||||||
{
|
|
||||||
await _taskCompletionSource.Task;
|
|
||||||
return _lcuSchemaDocument ?? throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Document> GetLolClientDocumentAsync()
|
|
||||||
{
|
|
||||||
await _taskCompletionSource.Task;
|
|
||||||
return _lolClientDocument ?? throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var reader = new OpenApiStreamReader();
|
|
||||||
var lcuSchemaStream = await _httpClient.GetStreamAsync("https://raw.githubusercontent.com/dysolix/hasagi-types/main/swagger.json");
|
|
||||||
var lcuSchemaRaw = reader.Read(lcuSchemaStream, out var _);
|
|
||||||
_lcuSchemaDocument = new Document(lcuSchemaRaw);
|
|
||||||
|
|
||||||
var lolClientStream = await _httpClient.GetStreamAsync("https://raw.githubusercontent.com/AlsoSylv/Irelia/refs/heads/master/schemas/game_schema.json");
|
|
||||||
var lolClientRaw = reader.Read(lolClientStream, out var _);
|
|
||||||
_lolClientDocument = new Document(lolClientRaw);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to initialize DataSource");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_taskCompletionSource.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
Needlework.Net/Extensions/EnableLoggerExtensions.cs
Normal file
16
Needlework.Net/Extensions/EnableLoggerExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Extensions
|
||||||
|
{
|
||||||
|
public static class EnableLoggerExtensions
|
||||||
|
{
|
||||||
|
private static readonly ILogger _logger = new LoggerConfiguration()
|
||||||
|
.MinimumLevel.Debug()
|
||||||
|
.WriteTo.File(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}", path: "Logs/debug-.log", rollingInterval: RollingInterval.Day, shared: true)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
public static ILogger Log(this IEnableLogger? context) => _logger.ForContext(context?.GetType() ?? typeof(Program));
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IEnableLogger;
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Extensions
|
|
||||||
{
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddSingletonsFromAssemblies<T>(this ServiceCollection services)
|
|
||||||
{
|
|
||||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
|
||||||
.SelectMany(s => s.GetTypes())
|
|
||||||
.Where(p => !p.IsAbstract && typeof(T).IsAssignableFrom(p));
|
|
||||||
|
|
||||||
foreach (var type in types) services.AddSingleton(typeof(T), type);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Serilog;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Needlework.Net
|
|
||||||
{
|
|
||||||
public static class Logger
|
|
||||||
{
|
|
||||||
public static void Setup(ILoggingBuilder builder)
|
|
||||||
{
|
|
||||||
var logger = new LoggerConfiguration()
|
|
||||||
.MinimumLevel.Debug()
|
|
||||||
.WriteTo.File("Logs/debug-", rollingInterval: RollingInterval.Day, shared: true)
|
|
||||||
.CreateLogger();
|
|
||||||
logger.Debug("NeedleworkDotNet version: {Version}", Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0");
|
|
||||||
logger.Debug("OS description: {Description}", System.Runtime.InteropServices.RuntimeInformation.OSDescription);
|
|
||||||
builder.AddSerilog(logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void LogFatal(UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
File.WriteAllText($"Logs/fatal-{DateTime.Now:HHmmssfff}", e.ExceptionObject.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
|
||||||
using Needlework.Net.ViewModels.MainWindow;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Messages
|
|
||||||
{
|
|
||||||
public class InfoBarUpdateMessage(InfoBarViewModel vm) : ValueChangedMessage<InfoBarViewModel>(vm)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,6 @@ namespace Needlework.Net.Models
|
|||||||
[JsonPropertyName("tag_name")]
|
[JsonPropertyName("tag_name")]
|
||||||
public string TagName { get; set; } = string.Empty;
|
public string TagName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public bool IsLatest(int version) => int.Parse(TagName.Replace(".", "")) > version;
|
public bool IsLatest(string assemblyVersion) => int.Parse(TagName.Replace(".", "")) > int.Parse(assemblyVersion.ToString().Replace(".", ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
namespace Needlework.Net.Models;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Models;
|
||||||
|
|
||||||
public class Library
|
public class Library
|
||||||
{
|
{
|
||||||
|
[JsonPropertyName("repo")]
|
||||||
public required string Repo { get; init; }
|
public required string Repo { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("description")]
|
||||||
public string? Description { get; init; }
|
public string? Description { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("language")]
|
||||||
public required string Language { get; init; }
|
public required string Language { get; init; }
|
||||||
public required string Link { get; init; }
|
|
||||||
|
[JsonPropertyName("owner")]
|
||||||
|
public required string Owner { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("tags")]
|
||||||
|
public List<string> Tags { get; init; } = [];
|
||||||
|
|
||||||
|
public string Link
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Owner.Equals("jellies")) return $"https://github.com/elliejs/{Repo}";
|
||||||
|
return $"https://github.com/{Owner}/{Repo}";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
Needlework.Net/Models/Notification.cs
Normal file
9
Needlework.Net/Models/Notification.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Models
|
||||||
|
{
|
||||||
|
public record Notification(string Title, string Message, InfoBarSeverity InfoBarSeverity, TimeSpan? Duration = null, string? Url = null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="FastCache.Cached" Version="1.8.2" />
|
<PackageReference Include="FastCache.Cached" Version="1.8.2" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="2.3.0" />
|
<PackageReference Include="FluentAvaloniaUI" Version="2.3.0" />
|
||||||
|
<PackageReference Include="Flurl" Version="4.0.0" />
|
||||||
|
<PackageReference Include="Flurl.Http" Version="4.0.2" />
|
||||||
<PackageReference Include="Material.Icons.Avalonia" Version="2.4.1" />
|
<PackageReference Include="Material.Icons.Avalonia" Version="2.4.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||||
@@ -67,11 +69,17 @@
|
|||||||
<Compile Update="Views\MainWindow\MainWindowView.axaml.cs">
|
<Compile Update="Views\MainWindow\MainWindowView.axaml.cs">
|
||||||
<DependentUpon>MainWindowView.axaml</DependentUpon>
|
<DependentUpon>MainWindowView.axaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="Views\Pages\Endpoints\EndpointsNavigationView.axaml.cs">
|
<Compile Update="Views\Pages\Endpoints\EndpointListView.axaml.cs">
|
||||||
<DependentUpon>EndpointsNavigationView.axaml</DependentUpon>
|
<DependentUpon>EndpointListView.axaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="Views\Pages\Endpoints\EndpointView.axaml.cs">
|
<Compile Update="Views\Pages\Endpoints\EndpointsView.axaml.cs">
|
||||||
<DependentUpon>EndpointView.axaml</DependentUpon>
|
<DependentUpon>EndpointsView.axaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="Views\Pages\Endpoints\PluginView.axaml.cs">
|
||||||
|
<DependentUpon>PluginView.axaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="Views\Pages\WebSocket\WebsocketView.axaml.cs">
|
||||||
|
<DependentUpon>WebSocketView.axaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Flurl.Http.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Needlework.Net.Extensions;
|
using Needlework.Net.Extensions;
|
||||||
using Needlework.Net.Services;
|
using Needlework.Net.Services;
|
||||||
using Needlework.Net.ViewModels.MainWindow;
|
using Needlework.Net.ViewModels.MainWindow;
|
||||||
using Needlework.Net.ViewModels.Pages;
|
using Needlework.Net.ViewModels.Pages;
|
||||||
|
using Needlework.Net.ViewModels.Pages.About;
|
||||||
|
using Needlework.Net.ViewModels.Pages.Console;
|
||||||
|
using Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
using Needlework.Net.ViewModels.Pages.Home;
|
||||||
|
using Needlework.Net.ViewModels.Pages.WebSocket;
|
||||||
using Projektanker.Icons.Avalonia;
|
using Projektanker.Icons.Avalonia;
|
||||||
using Projektanker.Icons.Avalonia.FontAwesome;
|
using Projektanker.Icons.Avalonia.FontAwesome;
|
||||||
|
using Serilog;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.IO;
|
||||||
|
|
||||||
namespace Needlework.Net;
|
namespace Needlework.Net;
|
||||||
|
|
||||||
@@ -28,48 +36,52 @@ class Program
|
|||||||
// Avalonia configuration, don't remove; also used by visual designer.
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
public static AppBuilder BuildAvaloniaApp()
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
{
|
{
|
||||||
IconProvider.Current
|
IconProvider.Current.Register<FontAwesomeIconProvider>();
|
||||||
.Register<FontAwesomeIconProvider>();
|
|
||||||
var services = BuildServices();
|
|
||||||
Task.Run(async () => await InitializeDataSourceAsync(services));
|
|
||||||
|
|
||||||
return AppBuilder.Configure(() => new App(services))
|
return AppBuilder.Configure(() => new App(BuildServices()))
|
||||||
.UsePlatformDetect()
|
.UsePlatformDetect()
|
||||||
.WithInterFont()
|
.WithInterFont()
|
||||||
.With(new Win32PlatformOptions
|
.With(new Win32PlatformOptions { CompositionMode = [Win32CompositionMode.WinUIComposition, Win32CompositionMode.DirectComposition] })
|
||||||
{
|
.With(new MacOSPlatformOptions { ShowInDock = true, })
|
||||||
CompositionMode = [Win32CompositionMode.WinUIComposition, Win32CompositionMode.DirectComposition]
|
|
||||||
})
|
|
||||||
.With(new MacOSPlatformOptions
|
|
||||||
{
|
|
||||||
ShowInDock = true,
|
|
||||||
})
|
|
||||||
.LogToTrace();
|
.LogToTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task InitializeDataSourceAsync(IServiceProvider services)
|
|
||||||
{
|
|
||||||
var dataSource = services.GetRequiredService<DataSource>();
|
|
||||||
await dataSource.InitializeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IServiceProvider BuildServices()
|
private static IServiceProvider BuildServices()
|
||||||
{
|
{
|
||||||
var builder = new ServiceCollection();
|
var builder = new ServiceCollection();
|
||||||
|
AddViewModels(builder);
|
||||||
|
AddServices(builder);
|
||||||
|
|
||||||
builder.AddSingleton<MainWindowViewModel>();
|
return builder.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddServices(ServiceCollection builder)
|
||||||
|
{
|
||||||
builder.AddSingleton<DialogService>();
|
builder.AddSingleton<DialogService>();
|
||||||
builder.AddSingleton<DataSource>();
|
builder.AddSingleton<DocumentService>();
|
||||||
builder.AddSingletonsFromAssemblies<PageBase>();
|
builder.AddSingleton<NotificationService>();
|
||||||
builder.AddHttpClient();
|
builder.AddSingleton<IFlurlClientCache>(new FlurlClientCache()
|
||||||
builder.AddLogging(Logger.Setup);
|
.Add("GithubClient", "https://api.github.com")
|
||||||
|
.Add("GithubUserContentClient", "https://raw.githubusercontent.com")
|
||||||
|
.Add("Client"));
|
||||||
|
|
||||||
var services = builder.BuildServiceProvider();
|
builder.AddLogging((builder) => builder.AddSerilog(EnableLoggerExtensions.Log(null)));
|
||||||
return services;
|
builder.AddSingleton<IDataTemplate>(new ViewLocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddViewModels(ServiceCollection builder)
|
||||||
|
{
|
||||||
|
builder.AddSingleton<MainWindowViewModel>();
|
||||||
|
|
||||||
|
builder.AddSingleton<PageBase, HomeViewModel>();
|
||||||
|
builder.AddSingleton<PageBase, ConsoleViewModel>();
|
||||||
|
builder.AddSingleton<PageBase, EndpointsViewModel>();
|
||||||
|
builder.AddSingleton<PageBase, WebSocketViewModel>();
|
||||||
|
builder.AddSingleton<PageBase, AboutViewModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
private static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
Logger.LogFatal(e);
|
File.WriteAllText($"Logs/fatal-{DateTime.Now:yyyyMMdd}.log", e.ExceptionObject.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
53
Needlework.Net/Services/DocumentService.cs
Normal file
53
Needlework.Net/Services/DocumentService.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using FastCache;
|
||||||
|
using Flurl.Http;
|
||||||
|
using Flurl.Http.Configuration;
|
||||||
|
using Microsoft.OpenApi.Readers;
|
||||||
|
using Needlework.Net.Extensions;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Needlework.Net
|
||||||
|
{
|
||||||
|
public class DocumentService : IEnableLogger
|
||||||
|
{
|
||||||
|
private readonly OpenApiStreamReader _reader = new();
|
||||||
|
|
||||||
|
private readonly IFlurlClient _githubUserContentClient;
|
||||||
|
|
||||||
|
public DocumentService(IFlurlClientCache clients)
|
||||||
|
{
|
||||||
|
_githubUserContentClient = clients.Get("GithubUserContentClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Document> GetLcuSchemaDocumentAsync()
|
||||||
|
{
|
||||||
|
if (Cached<Document>.TryGet(nameof(GetLcuSchemaDocumentAsync), out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lcuSchemaStream = await _githubUserContentClient.Request("/dysolix/hasagi-types/main/swagger.json")
|
||||||
|
.GetStreamAsync();
|
||||||
|
var lcuSchemaRaw = _reader.Read(lcuSchemaStream, out var _);
|
||||||
|
var document = new Document(lcuSchemaRaw);
|
||||||
|
|
||||||
|
return cached.Save(document, TimeSpan.FromMinutes(60));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Document> GetLolClientDocumentAsync()
|
||||||
|
{
|
||||||
|
if (Cached<Document>.TryGet(nameof(GetLolClientDocumentAsync), out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lolClientStream = await _githubUserContentClient.Request("/AlsoSylv/Irelia/refs/heads/master/schemas/game_schema.json")
|
||||||
|
.GetStreamAsync();
|
||||||
|
var lolClientRaw = _reader.Read(lolClientStream, out var _);
|
||||||
|
var document = new Document(lolClientRaw);
|
||||||
|
|
||||||
|
return cached.Save(document, TimeSpan.FromMinutes(60));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Needlework.Net/Services/NotificationService.cs
Normal file
20
Needlework.Net/Services/NotificationService.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using System;
|
||||||
|
using System.Reactive.Subjects;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Services
|
||||||
|
{
|
||||||
|
public class NotificationService
|
||||||
|
{
|
||||||
|
private readonly Subject<Notification> _notificationSubject = new();
|
||||||
|
|
||||||
|
public IObservable<Notification> Notifications { get { return _notificationSubject; } }
|
||||||
|
|
||||||
|
public void Notify(string title, string message, InfoBarSeverity severity, TimeSpan? duration = null, string? url = null)
|
||||||
|
{
|
||||||
|
var notification = new Notification(title, message, severity, duration, url);
|
||||||
|
_notificationSubject.OnNext(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,11 +17,11 @@ namespace Needlework.Net
|
|||||||
var name = data?.GetType().Name;
|
var name = data?.GetType().Name;
|
||||||
if (name is null)
|
if (name is null)
|
||||||
{
|
{
|
||||||
return new TextBlock { Text = "Data is null or has no name." };
|
throw new Exception("Data type name is null.");
|
||||||
}
|
}
|
||||||
if (!name.Contains("ViewModel"))
|
if (!name.Contains("ViewModel"))
|
||||||
{
|
{
|
||||||
return new TextBlock { Text = "Data name must end with ViewModel." };
|
throw new Exception("Data type name must end with 'ViewModel'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
name = name.Replace("ViewModel", "View");
|
name = name.Replace("ViewModel", "View");
|
||||||
@@ -32,9 +32,8 @@ namespace Needlework.Net
|
|||||||
|
|
||||||
if (type is null)
|
if (type is null)
|
||||||
{
|
{
|
||||||
return new TextBlock { Text = $"No view for {name}." };
|
throw new Exception("Data type has no view.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_controlCache.TryGetValue(data!, out var res))
|
if (!_controlCache.TryGetValue(data!, out var res))
|
||||||
{
|
{
|
||||||
res ??= (Control)Activator.CreateInstance(type)!;
|
res ??= (Control)Activator.CreateInstance(type)!;
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using FluentAvalonia.UI.Controls;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.MainWindow;
|
|
||||||
|
|
||||||
public partial class InfoBarViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
[ObservableProperty] private string _title;
|
|
||||||
[ObservableProperty] private bool _isOpen;
|
|
||||||
[ObservableProperty] private string _message;
|
|
||||||
[ObservableProperty] private InfoBarSeverity _severity;
|
|
||||||
[ObservableProperty] private TimeSpan _duration;
|
|
||||||
[ObservableProperty] private Control? _actionButton;
|
|
||||||
|
|
||||||
public InfoBarViewModel(string title, bool isOpen, string message, InfoBarSeverity severity, TimeSpan duration, Control? actionButton = null)
|
|
||||||
{
|
|
||||||
_title = title;
|
|
||||||
_isOpen = isOpen;
|
|
||||||
_message = message;
|
|
||||||
_severity = severity;
|
|
||||||
_duration = duration;
|
|
||||||
_actionButton = actionButton;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using Avalonia.Collections;
|
using BlossomiShymae.Briar;
|
||||||
using BlossomiShymae.Briar;
|
|
||||||
using BlossomiShymae.Briar.Utils;
|
using BlossomiShymae.Briar.Utils;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Microsoft.Extensions.Logging;
|
using Flurl.Http;
|
||||||
|
using Flurl.Http.Configuration;
|
||||||
|
using Needlework.Net.Extensions;
|
||||||
using Needlework.Net.Messages;
|
using Needlework.Net.Messages;
|
||||||
using Needlework.Net.Models;
|
using Needlework.Net.Models;
|
||||||
using Needlework.Net.Services;
|
using Needlework.Net.Services;
|
||||||
@@ -16,203 +17,178 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using System.Reactive;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Timers;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.MainWindow;
|
namespace Needlework.Net.ViewModels.MainWindow;
|
||||||
|
|
||||||
public partial class MainWindowViewModel
|
public partial class MainWindowViewModel
|
||||||
: ObservableObject, IRecipient<InfoBarUpdateMessage>, IRecipient<OopsiesDialogRequestedMessage>
|
: ObservableObject, IRecipient<OopsiesDialogRequestedMessage>, IEnableLogger
|
||||||
{
|
{
|
||||||
public IAvaloniaReadOnlyList<NavigationViewItem> MenuItems { get; }
|
private readonly DocumentService _documentService;
|
||||||
[ObservableProperty] private NavigationViewItem _selectedMenuItem;
|
|
||||||
[ObservableProperty] private PageBase _currentPage;
|
|
||||||
|
|
||||||
public string Version { get; } = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0";
|
private readonly IFlurlClient _githubClient;
|
||||||
[ObservableProperty] private bool _isUpdateShown = false;
|
|
||||||
|
|
||||||
[ObservableProperty] private string _schemaVersion = "N/A";
|
private readonly NotificationService _notificationService;
|
||||||
[ObservableProperty] private string _schemaVersionLatest = "N/A";
|
|
||||||
|
|
||||||
public HttpClient HttpClient { get; }
|
private readonly DialogService _dialogService;
|
||||||
public DialogService DialogService { get; }
|
|
||||||
|
|
||||||
private readonly DataSource _dataSource;
|
private readonly IDisposable _checkForUpdatesDisposable;
|
||||||
|
|
||||||
[ObservableProperty] private bool _isBusy = true;
|
private readonly IDisposable _checkForSchemaVersionDisposable;
|
||||||
|
|
||||||
[ObservableProperty] private ObservableCollection<InfoBarViewModel> _infoBarItems = [];
|
public MainWindowViewModel(IEnumerable<PageBase> pages, DialogService dialogService, DocumentService documentService, NotificationService notificationService, IFlurlClientCache clients)
|
||||||
|
|
||||||
private readonly ILogger<MainWindowViewModel> _logger;
|
|
||||||
|
|
||||||
private readonly System.Timers.Timer _latestUpdateTimer = new()
|
|
||||||
{
|
{
|
||||||
Interval = TimeSpan.FromMinutes(10).TotalMilliseconds,
|
_dialogService = dialogService;
|
||||||
Enabled = true
|
_documentService = documentService;
|
||||||
};
|
_notificationService = notificationService;
|
||||||
|
_githubClient = clients.Get("GithubClient");
|
||||||
|
|
||||||
private readonly System.Timers.Timer _schemaVersionTimer = new()
|
NavigationViewItems = pages
|
||||||
{
|
|
||||||
Interval = TimeSpan.FromSeconds(5).TotalMilliseconds,
|
|
||||||
Enabled = true
|
|
||||||
};
|
|
||||||
private bool _isSchemaVersionChecked = false;
|
|
||||||
|
|
||||||
public MainWindowViewModel(IEnumerable<PageBase> pages, HttpClient httpClient, DialogService dialogService, ILogger<MainWindowViewModel> logger, DataSource dataSource)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_dataSource = dataSource;
|
|
||||||
|
|
||||||
MenuItems = new AvaloniaList<NavigationViewItem>(pages
|
|
||||||
.OrderBy(p => p.Index)
|
.OrderBy(p => p.Index)
|
||||||
.ThenBy(p => p.DisplayName)
|
.ThenBy(p => p.DisplayName)
|
||||||
.Select(p => new NavigationViewItem()
|
.Select(ToNavigationViewItem)
|
||||||
{
|
.ToList();
|
||||||
Content = p.DisplayName,
|
SelectedNavigationViewItem = NavigationViewItems.First();
|
||||||
Tag = p,
|
CurrentPage = (PageBase)SelectedNavigationViewItem.Tag!;
|
||||||
IconSource = new BitmapIconSource() { UriSource = new Uri($"avares://NeedleworkDotNet/Assets/Icons/{p.Icon}.png") }
|
|
||||||
}));
|
|
||||||
SelectedMenuItem = MenuItems[0];
|
|
||||||
CurrentPage = (PageBase)MenuItems[0].Tag!;
|
|
||||||
|
|
||||||
HttpClient = httpClient;
|
_notificationService.Notifications.Subscribe(async notification =>
|
||||||
DialogService = dialogService;
|
{
|
||||||
|
var vm = new NotificationViewModel(notification);
|
||||||
|
Notifications.Add(vm);
|
||||||
|
await Task.Delay(notification.Duration ?? TimeSpan.FromSeconds(10));
|
||||||
|
Notifications.Remove(vm);
|
||||||
|
});
|
||||||
|
|
||||||
|
_checkForUpdatesDisposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(10))
|
||||||
|
.Select(time => Unit.Default)
|
||||||
|
.Subscribe(async _ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await CheckForUpdatesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var message = "Failed to check for updates. Please check your internet connection or try again later.";
|
||||||
|
this.Log()
|
||||||
|
.Error(ex, message);
|
||||||
|
_notificationService.Notify("Needlework.Net", message, InfoBarSeverity.Error);
|
||||||
|
_checkForUpdatesDisposable?.Dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_checkForSchemaVersionDisposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(10))
|
||||||
|
.Select(time => Unit.Default)
|
||||||
|
.Subscribe(async _ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await CheckForSchemaVersionAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var message = "Failed to check for schema version. Please check your internet connection or try again later.";
|
||||||
|
this.Log()
|
||||||
|
.Error(ex, message);
|
||||||
|
_notificationService.Notify("Needlework.Net", message, InfoBarSeverity.Error);
|
||||||
|
_checkForSchemaVersionDisposable?.Dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||||
|
|
||||||
_latestUpdateTimer.Elapsed += OnLatestUpdateTimerElapsed;
|
|
||||||
_schemaVersionTimer.Elapsed += OnSchemaVersionTimerElapsed;
|
|
||||||
_latestUpdateTimer.Start();
|
|
||||||
_schemaVersionTimer.Start();
|
|
||||||
OnLatestUpdateTimerElapsed(null, null);
|
|
||||||
OnSchemaVersionTimerElapsed(null, null);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedMenuItemChanged(NavigationViewItem value)
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<NotificationViewModel> _notifications = [];
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private NavigationViewItem _selectedNavigationViewItem;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private PageBase _currentPage;
|
||||||
|
|
||||||
|
public List<NavigationViewItem> NavigationViewItems { get; private set; } = [];
|
||||||
|
|
||||||
|
public bool IsSchemaVersionChecked { get; private set; }
|
||||||
|
|
||||||
|
public string Version { get; } = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0";
|
||||||
|
|
||||||
|
public string Title => $"Needlework.Net {Version}";
|
||||||
|
|
||||||
|
private NavigationViewItem ToNavigationViewItem(PageBase page) => new()
|
||||||
|
{
|
||||||
|
Content = page.DisplayName,
|
||||||
|
Tag = page,
|
||||||
|
IconSource = new BitmapIconSource() { UriSource = new Uri($"avares://NeedleworkDotNet/Assets/Icons/{page.Icon}.png") }
|
||||||
|
};
|
||||||
|
|
||||||
|
partial void OnSelectedNavigationViewItemChanged(NavigationViewItem value)
|
||||||
{
|
{
|
||||||
if (value.Tag is PageBase page)
|
if (value.Tag is PageBase page)
|
||||||
{
|
{
|
||||||
CurrentPage = page;
|
CurrentPage = page;
|
||||||
if (!page.IsInitialized)
|
|
||||||
{
|
|
||||||
Task.Run(page.InitializeAsync);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnSchemaVersionTimerElapsed(object? sender, ElapsedEventArgs? e)
|
private async Task CheckForUpdatesAsync()
|
||||||
|
{
|
||||||
|
var release = await _githubClient
|
||||||
|
.Request("/repos/BlossomiShymae/Needlework.Net/releases/latest")
|
||||||
|
.WithHeader("User-Agent", $"Needlework.Net/{Version}")
|
||||||
|
.GetJsonAsync<GithubRelease>();
|
||||||
|
|
||||||
|
if (release.IsLatest(Version))
|
||||||
|
{
|
||||||
|
this.Log()
|
||||||
|
.Information("New version available: {TagName}", release.TagName);
|
||||||
|
_notificationService.Notify("Needlework.Net", $"New version available: {release.TagName}", InfoBarSeverity.Informational, null, "https://github.com/BlossomiShymae/Needlework.Net/releases/latest");
|
||||||
|
_checkForUpdatesDisposable?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task CheckForSchemaVersionAsync()
|
||||||
{
|
{
|
||||||
if (!ProcessFinder.IsPortOpen()) return;
|
if (!ProcessFinder.IsPortOpen()) return;
|
||||||
var lcuSchemaDocument = await _dataSource.GetLcuSchemaDocumentAsync();
|
|
||||||
|
|
||||||
try
|
var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync();
|
||||||
{
|
|
||||||
var client = Connector.GetLcuHttpClientInstance();
|
var client = Connector.GetLcuHttpClientInstance();
|
||||||
|
|
||||||
var currentSemVer = lcuSchemaDocument.Info.Version.Split('.');
|
var currentSemVer = lcuSchemaDocument.Info.Version.Split('.');
|
||||||
var systemBuild = await client.GetFromJsonAsync<SystemBuild>("/system/v1/builds") ?? throw new NullReferenceException();
|
var systemBuild = await client.GetFromJsonAsync<SystemBuild>("/system/v1/builds") ?? throw new NullReferenceException();
|
||||||
var latestSemVer = systemBuild.Version.Split('.');
|
var latestSemVer = systemBuild.Version.Split('.');
|
||||||
|
|
||||||
if (!_isSchemaVersionChecked)
|
if (!IsSchemaVersionChecked)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("LCU Schema (current): {Version}", lcuSchemaDocument.Info.Version);
|
this.Log()
|
||||||
_logger.LogInformation("LCU Schema (latest): {Version}", systemBuild.Version);
|
.Information("LCU Schema (current): {Version}", lcuSchemaDocument.Info.Version);
|
||||||
_isSchemaVersionChecked = true;
|
this.Log()
|
||||||
|
.Information("LCU Schema (latest): {Version}", systemBuild.Version);
|
||||||
|
IsSchemaVersionChecked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isVersionMatching = currentSemVer[0] == latestSemVer[0] && currentSemVer[1] == latestSemVer[1]; // Compare major and minor versions
|
bool isVersionMatching = currentSemVer[0] == latestSemVer[0] && currentSemVer[1] == latestSemVer[1]; // Compare major and minor versions
|
||||||
if (!isVersionMatching)
|
if (!isVersionMatching)
|
||||||
{
|
{
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Post(async () =>
|
this.Log()
|
||||||
{
|
.Warning("LCU Schema version mismatch: Current {CurrentVersion}, Latest {LatestVersion}", lcuSchemaDocument.Info.Version, systemBuild.Version);
|
||||||
await ShowInfoBarAsync(new("Newer System Build", true, $"LCU Schema is possibly outdated compared to latest system build. Consider submitting a pull request on dysolix/hasagi-types.\nCurrent: {string.Join(".", currentSemVer)}\nLatest: {string.Join(".", latestSemVer)}", InfoBarSeverity.Warning, TimeSpan.FromSeconds(60), new Avalonia.Controls.Button()
|
_notificationService.Notify("Needlework.Net", $"LCU Schema is possibly outdated compared to latest system build. Consider submitting a pull request on dysolix/hasagi-types.\nCurrent: {string.Join(".", currentSemVer)}\nLatest: {string.Join(".", latestSemVer)}", InfoBarSeverity.Warning, null, "https://github.com/dysolix/hasagi-types#updating-the-types");
|
||||||
{
|
_checkForSchemaVersionDisposable?.Dispose();
|
||||||
Command = OpenUrlCommand,
|
|
||||||
CommandParameter = "https://github.com/dysolix/hasagi-types#updating-the-types",
|
|
||||||
Content = "Submit PR"
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
_schemaVersionTimer.Elapsed -= OnSchemaVersionTimerElapsed;
|
|
||||||
_schemaVersionTimer.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Schema version check failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OnLatestUpdateTimerElapsed(object? sender, ElapsedEventArgs? e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/BlossomiShymae/Needlework.Net/releases/latest");
|
|
||||||
request.Headers.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue("Needlework.Net", Version));
|
|
||||||
|
|
||||||
var response = await HttpClient.SendAsync(request);
|
|
||||||
var release = await response.Content.ReadFromJsonAsync<GithubRelease>();
|
|
||||||
if (release == null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Release response is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentVersion = int.Parse(Version.Replace(".", ""));
|
|
||||||
|
|
||||||
if (release.IsLatest(currentVersion))
|
|
||||||
{
|
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ShowInfoBarAsync(new("Needlework.Net Update", true, $"There is a new version available: {release.TagName}.", InfoBarSeverity.Informational, TimeSpan.FromSeconds(30), new Avalonia.Controls.Button()
|
|
||||||
{
|
|
||||||
Command = OpenUrlCommand,
|
|
||||||
CommandParameter = "https://github.com/BlossomiShymae/Needlework.Net/releases",
|
|
||||||
Content = "Download"
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
_latestUpdateTimer.Elapsed -= OnLatestUpdateTimerElapsed;
|
|
||||||
_latestUpdateTimer.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to check for latest version");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenUrl(string url)
|
private void OpenUrl(string url)
|
||||||
{
|
{
|
||||||
var process = new Process()
|
var process = new Process() { StartInfo = new ProcessStartInfo(url) { UseShellExecute = true } };
|
||||||
{
|
|
||||||
StartInfo = new ProcessStartInfo(url)
|
|
||||||
{
|
|
||||||
UseShellExecute = true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
process.Start();
|
process.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(InfoBarUpdateMessage message)
|
|
||||||
{
|
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Post(async () => await ShowInfoBarAsync(message.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ShowInfoBarAsync(InfoBarViewModel vm)
|
|
||||||
{
|
|
||||||
InfoBarItems.Add(vm);
|
|
||||||
await Task.Delay(vm.Duration);
|
|
||||||
InfoBarItems.Remove(vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Receive(OopsiesDialogRequestedMessage message)
|
public void Receive(OopsiesDialogRequestedMessage message)
|
||||||
{
|
{
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Invoke(async () => await DialogService.ShowAsync<OopsiesDialog>(message.Value));
|
Avalonia.Threading.Dispatcher.UIThread.Invoke(async () => await _dialogService.ShowAsync<OopsiesDialog>(message.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.MainWindow
|
||||||
|
{
|
||||||
|
public partial class NotificationViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
public NotificationViewModel(Notification notification)
|
||||||
|
{
|
||||||
|
Notification = notification;
|
||||||
|
IsButtonVisible = !string.IsNullOrEmpty(notification.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsButtonVisible { get; }
|
||||||
|
|
||||||
|
public Notification Notification { get; }
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public void OpenUrl()
|
||||||
|
{
|
||||||
|
var process = new Process() { StartInfo = new() { UseShellExecute = true } };
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Needlework.Net/ViewModels/Pages/About/AboutViewModel.cs
Normal file
24
Needlework.Net/ViewModels/Pages/About/AboutViewModel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.About;
|
||||||
|
|
||||||
|
public partial class AboutViewModel : PageBase
|
||||||
|
{
|
||||||
|
public AboutViewModel() : base("About", "info-circle")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task InitializeAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenUrl(string url)
|
||||||
|
{
|
||||||
|
var process = new Process() { StartInfo = new ProcessStartInfo(url) { UseShellExecute = true } };
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages;
|
|
||||||
|
|
||||||
public partial class AboutViewModel : PageBase
|
|
||||||
{
|
|
||||||
public HttpClient HttpClient { get; }
|
|
||||||
|
|
||||||
public AboutViewModel(HttpClient httpClient) : base("About", "info-circle")
|
|
||||||
{
|
|
||||||
HttpClient = httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task InitializeAsync()
|
|
||||||
{
|
|
||||||
IsInitialized = true;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void OpenUrl(string url)
|
|
||||||
{
|
|
||||||
var process = new Process()
|
|
||||||
{
|
|
||||||
StartInfo = new ProcessStartInfo(url) { UseShellExecute = true }
|
|
||||||
};
|
|
||||||
process.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
Needlework.Net/ViewModels/Pages/Console/ConsoleViewModel.cs
Normal file
45
Needlework.Net/ViewModels/Pages/Console/ConsoleViewModel.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Avalonia.Threading;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Needlework.Net.Services;
|
||||||
|
using Needlework.Net.ViewModels.Shared;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Console;
|
||||||
|
|
||||||
|
public partial class ConsoleViewModel : PageBase
|
||||||
|
{
|
||||||
|
private readonly DocumentService _documentService;
|
||||||
|
|
||||||
|
public ConsoleViewModel(DocumentService documentService, NotificationService notificationService) : base("Console", "terminal", -200)
|
||||||
|
{
|
||||||
|
_request = new(notificationService, Endpoints.Tab.LCU);
|
||||||
|
_documentService = documentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> RequestMethods { get; } = ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"];
|
||||||
|
|
||||||
|
public List<string> RequestPaths { get; } = [];
|
||||||
|
|
||||||
|
[ObservableProperty] private bool _isBusy = true;
|
||||||
|
|
||||||
|
[ObservableProperty] private RequestViewModel _request;
|
||||||
|
|
||||||
|
public override async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
var document = await _documentService.GetLcuSchemaDocumentAsync();
|
||||||
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
|
{
|
||||||
|
RequestPaths.Clear();
|
||||||
|
RequestPaths.AddRange(document.Paths);
|
||||||
|
});
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task SendRequest()
|
||||||
|
{
|
||||||
|
await Request.ExecuteAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using Avalonia.Collections;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Needlework.Net.ViewModels.Shared;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages;
|
|
||||||
|
|
||||||
public partial class ConsoleViewModel : PageBase
|
|
||||||
{
|
|
||||||
public IAvaloniaReadOnlyList<string> RequestMethods { get; } = new AvaloniaList<string>(["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"]);
|
|
||||||
public IAvaloniaList<string> RequestPaths { get; } = new AvaloniaList<string>();
|
|
||||||
|
|
||||||
[ObservableProperty] private bool _isBusy = true;
|
|
||||||
[ObservableProperty] private RequestViewModel _request;
|
|
||||||
|
|
||||||
private readonly DataSource _dataSource;
|
|
||||||
|
|
||||||
public ConsoleViewModel(ILogger<RequestViewModel> requestViewModelLogger, DataSource dataSource) : base("Console", "terminal", -200)
|
|
||||||
{
|
|
||||||
_request = new(requestViewModelLogger, Endpoints.Tab.LCU);
|
|
||||||
_dataSource = dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
var document = await _dataSource.GetLcuSchemaDocumentAsync();
|
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
|
||||||
{
|
|
||||||
RequestPaths.Clear();
|
|
||||||
RequestPaths.AddRange(document.Paths);
|
|
||||||
});
|
|
||||||
IsBusy = false;
|
|
||||||
IsInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task SendRequest()
|
|
||||||
{
|
|
||||||
await Request.ExecuteAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using AvaloniaEdit.Utils;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using Needlework.Net.Services;
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class EndpointListViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly Document _document;
|
||||||
|
|
||||||
|
private readonly Tab _tab;
|
||||||
|
|
||||||
|
private readonly Action<ObservableObject> _onClicked;
|
||||||
|
|
||||||
|
private readonly ObservableCollection<string> _plugins;
|
||||||
|
|
||||||
|
private readonly NotificationService _notificationService;
|
||||||
|
|
||||||
|
public EndpointListViewModel(NotificationService notificationService, ObservableCollection<string> plugins, Action<ObservableObject> onClicked, Models.Document document, Tab tab)
|
||||||
|
{
|
||||||
|
_plugins = new ObservableCollection<string>(plugins);
|
||||||
|
_document = document;
|
||||||
|
_tab = tab;
|
||||||
|
_onClicked = onClicked;
|
||||||
|
_notificationService = notificationService;
|
||||||
|
|
||||||
|
Plugins = EndpointSearchDetails = new ObservableCollection<EndpointSearchDetailsViewModel>(plugins.Select(plugin => new EndpointSearchDetailsViewModel(notificationService, document, tab, onClicked, plugin)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<EndpointSearchDetailsViewModel> Plugins { get; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<EndpointSearchDetailsViewModel> _endpointSearchDetails = [];
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _search = string.Empty;
|
||||||
|
|
||||||
|
partial void OnSearchChanged(string value)
|
||||||
|
{
|
||||||
|
EndpointSearchDetails.Clear();
|
||||||
|
if (!string.IsNullOrEmpty(Search))
|
||||||
|
{
|
||||||
|
EndpointSearchDetails.AddRange(_plugins.Where(plugin => plugin.Contains(value, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Select(plugin => new EndpointSearchDetailsViewModel(_notificationService, _document, _tab, _onClicked, plugin)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EndpointSearchDetails.AddRange(
|
||||||
|
_plugins.Select(plugin => new EndpointSearchDetailsViewModel(_notificationService, _document, _tab, _onClicked, plugin)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenEndpoint(string? value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value)) return;
|
||||||
|
_onClicked.Invoke(new PluginViewModel(_notificationService, value, _document, _tab));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using Needlework.Net.Services;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Endpoints
|
||||||
|
{
|
||||||
|
public partial class EndpointSearchDetailsViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly Document _document;
|
||||||
|
|
||||||
|
private readonly Tab _tab;
|
||||||
|
|
||||||
|
private readonly Action<ObservableObject> _onClicked;
|
||||||
|
|
||||||
|
private readonly NotificationService _notificationService;
|
||||||
|
|
||||||
|
public EndpointSearchDetailsViewModel(Services.NotificationService notificationService, Document document, Tab tab, Action<ObservableObject> onClicked, string? plugin)
|
||||||
|
{
|
||||||
|
_document = document;
|
||||||
|
_tab = tab;
|
||||||
|
_onClicked = onClicked;
|
||||||
|
_plugin = plugin;
|
||||||
|
_notificationService = notificationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _plugin;
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenEndpoint()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Plugin)) return;
|
||||||
|
_onClicked.Invoke(new PluginViewModel(_notificationService, Plugin, _document, _tab));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,36 @@
|
|||||||
using Avalonia.Collections;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Needlework.Net.ViewModels.Shared;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
|
||||||
public partial class EndpointsNavigationViewModel : ObservableObject
|
public partial class EndpointTabItemContentViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public Guid Guid { get; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
[ObservableProperty] private ObservableObject _activeViewModel;
|
|
||||||
[ObservableProperty] private ObservableObject _endpointsViewModel;
|
|
||||||
[ObservableProperty] private string _title;
|
|
||||||
|
|
||||||
private readonly Action<string?, Guid> _onEndpointNavigation;
|
private readonly Action<string?, Guid> _onEndpointNavigation;
|
||||||
|
|
||||||
private readonly Tab _tab;
|
private readonly Tab _tab;
|
||||||
|
|
||||||
public EndpointsNavigationViewModel(IAvaloniaList<string> plugins, Action<string?, Guid> onEndpointNavigation, ILogger<RequestViewModel> requestViewModelLogger, Models.Document document, Tab tab)
|
public EndpointTabItemContentViewModel(Services.NotificationService notificationService, ObservableCollection<string> plugins, Action<string?, Guid> onEndpointNavigation, IAsyncRelayCommand addEndpointCommand, Models.Document document, Tab tab)
|
||||||
{
|
{
|
||||||
_activeViewModel = _endpointsViewModel = new EndpointsViewModel(plugins, OnClicked, requestViewModelLogger, document, tab);
|
_activeViewModel = _endpointsViewModel = new EndpointListViewModel(notificationService, new ObservableCollection<string>(plugins), OnClicked, document, tab);
|
||||||
_onEndpointNavigation = onEndpointNavigation;
|
_onEndpointNavigation = onEndpointNavigation;
|
||||||
_tab = tab;
|
_tab = tab;
|
||||||
_title = GetTitle(tab);
|
_title = GetTitle(tab);
|
||||||
|
|
||||||
|
AddEndpointCommand = addEndpointCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid Guid { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
public IAsyncRelayCommand AddEndpointCommand { get; }
|
||||||
|
|
||||||
|
[ObservableProperty] private ObservableObject _activeViewModel;
|
||||||
|
|
||||||
|
[ObservableProperty] private ObservableObject _endpointsViewModel;
|
||||||
|
|
||||||
|
[ObservableProperty] private string _title;
|
||||||
|
|
||||||
private string GetTitle(Tab tab)
|
private string GetTitle(Tab tab)
|
||||||
{
|
{
|
||||||
return tab switch
|
return tab switch
|
||||||
@@ -39,7 +44,7 @@ public partial class EndpointsNavigationViewModel : ObservableObject
|
|||||||
private void OnClicked(ObservableObject viewModel)
|
private void OnClicked(ObservableObject viewModel)
|
||||||
{
|
{
|
||||||
ActiveViewModel = viewModel;
|
ActiveViewModel = viewModel;
|
||||||
if (viewModel is EndpointViewModel endpoint)
|
if (viewModel is PluginViewModel endpoint)
|
||||||
{
|
{
|
||||||
Title = $"{GetTitle(_tab)} - {endpoint.Title}";
|
Title = $"{GetTitle(_tab)} - {endpoint.Title}";
|
||||||
_onEndpointNavigation.Invoke(endpoint.Title, Guid);
|
_onEndpointNavigation.Invoke(endpoint.Title, Guid);
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class EndpointTabItemViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty] private string _header = string.Empty;
|
||||||
|
public IconSource IconSource { get; set; } = new SymbolIconSource() { Symbol = Symbol.Document, FontSize = 20.0, Foreground = Avalonia.Media.Brushes.White };
|
||||||
|
public bool Selected { get; set; } = false;
|
||||||
|
public required EndpointTabItemContentViewModel Content { get; init; }
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using Avalonia.Collections;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Needlework.Net.ViewModels.Shared;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|
||||||
|
|
||||||
public partial class EndpointViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
public string Endpoint { get; }
|
|
||||||
public string Title => Endpoint;
|
|
||||||
|
|
||||||
|
|
||||||
public IAvaloniaReadOnlyList<PathOperationViewModel> PathOperations { get; }
|
|
||||||
[ObservableProperty] private PathOperationViewModel? _selectedPathOperation;
|
|
||||||
|
|
||||||
[ObservableProperty] private string? _search;
|
|
||||||
public IAvaloniaList<PathOperationViewModel> FilteredPathOperations { get; }
|
|
||||||
|
|
||||||
public event EventHandler<string>? PathOperationSelected;
|
|
||||||
|
|
||||||
public EndpointViewModel(string endpoint, ILogger<RequestViewModel> requestViewModelLogger, Models.Document document, Tab tab)
|
|
||||||
{
|
|
||||||
Endpoint = endpoint;
|
|
||||||
PathOperations = new AvaloniaList<PathOperationViewModel>(document.Plugins[endpoint].Select(x => new PathOperationViewModel(x, requestViewModelLogger, document, tab)));
|
|
||||||
FilteredPathOperations = new AvaloniaList<PathOperationViewModel>(PathOperations);
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnSearchChanged(string? value)
|
|
||||||
{
|
|
||||||
FilteredPathOperations.Clear();
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
|
||||||
{
|
|
||||||
FilteredPathOperations.AddRange(PathOperations);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FilteredPathOperations.AddRange(PathOperations.Where(o => o.Path.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnSelectedPathOperationChanged(PathOperationViewModel? value)
|
|
||||||
{
|
|
||||||
if (value == null) return;
|
|
||||||
PathOperationSelected?.Invoke(this, value.Operation.RequestTemplate ?? string.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
using Avalonia.Collections;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using FluentAvalonia.UI.Controls;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Needlework.Net.Models;
|
|
||||||
using Needlework.Net.ViewModels.Shared;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|
||||||
|
|
||||||
public enum Tab
|
|
||||||
{
|
|
||||||
LCU,
|
|
||||||
GameClient
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class EndpointsTabViewModel : PageBase
|
|
||||||
{
|
|
||||||
public IAvaloniaList<string> Plugins { get; } = new AvaloniaList<string>();
|
|
||||||
public IAvaloniaList<EndpointItem> Endpoints { get; } = new AvaloniaList<EndpointItem>();
|
|
||||||
|
|
||||||
[ObservableProperty] private bool _isBusy = true;
|
|
||||||
|
|
||||||
private readonly ILogger<RequestViewModel> _requestViewModelLogger;
|
|
||||||
private readonly DataSource _dataSource;
|
|
||||||
|
|
||||||
public EndpointsTabViewModel(ILogger<RequestViewModel> requestViewModelLogger, DataSource dataSource) : base("Endpoints", "list-alt", -500)
|
|
||||||
{
|
|
||||||
_requestViewModelLogger = requestViewModelLogger;
|
|
||||||
_dataSource = dataSource;
|
|
||||||
}
|
|
||||||
public override async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
await Dispatcher.UIThread.Invoke(async () => await AddEndpoint(Tab.LCU));
|
|
||||||
IsBusy = false;
|
|
||||||
IsInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task AddEndpoint(Tab tab)
|
|
||||||
{
|
|
||||||
Document document = tab switch
|
|
||||||
{
|
|
||||||
Tab.LCU => await _dataSource.GetLcuSchemaDocumentAsync(),
|
|
||||||
Tab.GameClient => await _dataSource.GetLolClientDocumentAsync(),
|
|
||||||
_ => throw new NotImplementedException(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.Clear();
|
|
||||||
Plugins.AddRange(document.Plugins.Keys);
|
|
||||||
|
|
||||||
var vm = new EndpointsNavigationViewModel(Plugins, OnEndpointNavigation, _requestViewModelLogger, document, tab);
|
|
||||||
Endpoints.Add(new()
|
|
||||||
{
|
|
||||||
Content = vm,
|
|
||||||
Header = vm.Title,
|
|
||||||
Selected = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEndpointNavigation(string? title, Guid guid)
|
|
||||||
{
|
|
||||||
foreach (var endpoint in Endpoints)
|
|
||||||
{
|
|
||||||
if (endpoint.Content.Guid.Equals(guid))
|
|
||||||
{
|
|
||||||
endpoint.Header = endpoint.Content.Title;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class EndpointItem : ObservableObject
|
|
||||||
{
|
|
||||||
[ObservableProperty] private string _header = string.Empty;
|
|
||||||
public IconSource IconSource { get; set; } = new SymbolIconSource() { Symbol = Symbol.Document, FontSize = 20.0, Foreground = Avalonia.Media.Brushes.White };
|
|
||||||
public bool Selected { get; set; } = false;
|
|
||||||
public required EndpointsNavigationViewModel Content { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,52 +1,78 @@
|
|||||||
using Avalonia.Collections;
|
using Avalonia.Threading;
|
||||||
|
using AvaloniaEdit.Utils;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Needlework.Net.Models;
|
using Needlework.Net.Models;
|
||||||
using Needlework.Net.ViewModels.Shared;
|
using Needlework.Net.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
|
||||||
public partial class EndpointsViewModel : ObservableObject
|
public enum Tab
|
||||||
{
|
{
|
||||||
public IAvaloniaList<string> Plugins { get; }
|
LCU,
|
||||||
public IAvaloniaList<string> Query { get; }
|
GameClient
|
||||||
|
|
||||||
[ObservableProperty] private string _search = string.Empty;
|
|
||||||
[ObservableProperty] private string? _selectedQuery = string.Empty;
|
|
||||||
|
|
||||||
public Action<ObservableObject> OnClicked { get; }
|
|
||||||
|
|
||||||
private readonly ILogger<RequestViewModel> _requestViewModelLogger;
|
|
||||||
private readonly Document _document;
|
|
||||||
private readonly Tab _tab;
|
|
||||||
|
|
||||||
public EndpointsViewModel(IAvaloniaList<string> plugins, Action<ObservableObject> onClicked, ILogger<RequestViewModel> requestViewModelLogger, Models.Document document, Tab tab)
|
|
||||||
{
|
|
||||||
Plugins = new AvaloniaList<string>(plugins);
|
|
||||||
Query = new AvaloniaList<string>(plugins);
|
|
||||||
OnClicked = onClicked;
|
|
||||||
_requestViewModelLogger = requestViewModelLogger;
|
|
||||||
_document = document;
|
|
||||||
_tab = tab;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSearchChanged(string value)
|
public partial class EndpointsViewModel : PageBase
|
||||||
{
|
{
|
||||||
Query.Clear();
|
private readonly DocumentService _documentService;
|
||||||
if (!string.IsNullOrEmpty(Search))
|
|
||||||
Query.AddRange(Plugins.Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
|
private readonly NotificationService _notificationService;
|
||||||
else
|
|
||||||
Query.AddRange(Plugins);
|
public EndpointsViewModel(DocumentService documentService, NotificationService notificationService) : base("Endpoints", "list-alt", -500)
|
||||||
|
{
|
||||||
|
_documentService = documentService;
|
||||||
|
_notificationService = notificationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<string> Plugins { get; } = [];
|
||||||
|
|
||||||
|
public ObservableCollection<EndpointTabItemViewModel> Endpoints { get; } = [];
|
||||||
|
|
||||||
|
[ObservableProperty] private bool _isBusy = true;
|
||||||
|
|
||||||
|
public override async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await AddEndpoint(Tab.LCU);
|
||||||
|
IsBusy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenEndpoint(string? value)
|
private async Task AddEndpoint(Tab tab)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value)) return;
|
Document document = tab switch
|
||||||
|
{
|
||||||
|
Tab.LCU => await _documentService.GetLcuSchemaDocumentAsync(),
|
||||||
|
Tab.GameClient => await _documentService.GetLolClientDocumentAsync(),
|
||||||
|
_ => throw new NotImplementedException(),
|
||||||
|
};
|
||||||
|
|
||||||
OnClicked.Invoke(new EndpointViewModel(value, _requestViewModelLogger, _document, _tab));
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
Plugins.Clear();
|
||||||
|
Plugins.AddRange(document.Plugins.Keys);
|
||||||
|
var vm = new EndpointTabItemContentViewModel(_notificationService, Plugins, OnEndpointNavigation, AddEndpointCommand, document, tab);
|
||||||
|
Endpoints.Add(new()
|
||||||
|
{
|
||||||
|
Content = vm,
|
||||||
|
Header = vm.Title,
|
||||||
|
Selected = true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEndpointNavigation(string? title, Guid guid)
|
||||||
|
{
|
||||||
|
foreach (var endpoint in Endpoints)
|
||||||
|
{
|
||||||
|
if (endpoint.Content.Guid.Equals(guid))
|
||||||
|
{
|
||||||
|
endpoint.Header = endpoint.Content.Title;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Avalonia.Collections;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Needlework.Net.Models;
|
using Needlework.Net.Models;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -11,17 +10,6 @@ namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|||||||
|
|
||||||
public partial class OperationViewModel : ObservableObject
|
public partial class OperationViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public string Summary { get; }
|
|
||||||
public string Description { get; }
|
|
||||||
public string ReturnType { get; }
|
|
||||||
public bool IsRequestBody { get; }
|
|
||||||
public string? RequestBodyType { get; }
|
|
||||||
public IAvaloniaReadOnlyList<PropertyClassViewModel> RequestClasses { get; }
|
|
||||||
public IAvaloniaReadOnlyList<PropertyClassViewModel> ResponseClasses { get; }
|
|
||||||
public IAvaloniaReadOnlyList<ParameterViewModel> PathParameters { get; }
|
|
||||||
public IAvaloniaReadOnlyList<ParameterViewModel> QueryParameters { get; }
|
|
||||||
public string? RequestTemplate { get; }
|
|
||||||
|
|
||||||
public OperationViewModel(OpenApiOperation operation, Models.Document document)
|
public OperationViewModel(OpenApiOperation operation, Models.Document document)
|
||||||
{
|
{
|
||||||
Summary = operation.Summary ?? string.Empty;
|
Summary = operation.Summary ?? string.Empty;
|
||||||
@@ -30,12 +18,32 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
ReturnType = GetReturnType(operation.Responses);
|
ReturnType = GetReturnType(operation.Responses);
|
||||||
RequestClasses = GetRequestClasses(operation.RequestBody, document);
|
RequestClasses = GetRequestClasses(operation.RequestBody, document);
|
||||||
ResponseClasses = GetResponseClasses(operation.Responses, document);
|
ResponseClasses = GetResponseClasses(operation.Responses, document);
|
||||||
PathParameters = GetParameters(operation.Parameters, ParameterLocation.Path);
|
PathParameters = GetParameters(operation.Parameters.ToList(), ParameterLocation.Path);
|
||||||
QueryParameters = GetParameters(operation.Parameters, ParameterLocation.Query);
|
QueryParameters = GetParameters(operation.Parameters.ToList(), ParameterLocation.Query);
|
||||||
RequestBodyType = GetRequestBodyType(operation.RequestBody);
|
RequestBodyType = GetRequestBodyType(operation.RequestBody);
|
||||||
RequestTemplate = GetRequestTemplate(operation.RequestBody, document);
|
RequestTemplate = GetRequestTemplate(operation.RequestBody, document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Summary { get; }
|
||||||
|
|
||||||
|
public string Description { get; }
|
||||||
|
|
||||||
|
public string ReturnType { get; }
|
||||||
|
|
||||||
|
public bool IsRequestBody { get; }
|
||||||
|
|
||||||
|
public string? RequestBodyType { get; }
|
||||||
|
|
||||||
|
public List<PropertyClassViewModel> RequestClasses { get; }
|
||||||
|
|
||||||
|
public List<PropertyClassViewModel> ResponseClasses { get; }
|
||||||
|
|
||||||
|
public List<ParameterViewModel> PathParameters { get; }
|
||||||
|
|
||||||
|
public List<ParameterViewModel> QueryParameters { get; }
|
||||||
|
|
||||||
|
public string? RequestTemplate { get; }
|
||||||
|
|
||||||
private string? GetRequestTemplate(OpenApiRequestBody? requestBody, Document document)
|
private string? GetRequestTemplate(OpenApiRequestBody? requestBody, Document document)
|
||||||
{
|
{
|
||||||
var requestClasses = GetRequestClasses(requestBody, document);
|
var requestClasses = GetRequestClasses(requestBody, document);
|
||||||
@@ -50,7 +58,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
return JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(string.Join(string.Empty, template)), App.JsonSerializerOptions);
|
return JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(string.Join(string.Empty, template)), App.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> CreateTemplate(AvaloniaList<PropertyClassViewModel> requestClasses)
|
private List<string> CreateTemplate(List<PropertyClassViewModel> requestClasses)
|
||||||
{
|
{
|
||||||
if (requestClasses.Count == 0) return [];
|
if (requestClasses.Count == 0) return [];
|
||||||
List<string> template = [];
|
List<string> template = [];
|
||||||
@@ -83,7 +91,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AvaloniaList<PropertyClassViewModel> classes = [.. requestClasses];
|
List<PropertyClassViewModel> classes = [.. requestClasses];
|
||||||
classes.Remove(rootClass);
|
classes.Remove(rootClass);
|
||||||
template[i] = string.Join(string.Empty, CreateTemplate(classes));
|
template[i] = string.Join(string.Empty, CreateTemplate(classes));
|
||||||
}
|
}
|
||||||
@@ -121,9 +129,9 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AvaloniaList<ParameterViewModel> GetParameters(IList<OpenApiParameter> parameters, ParameterLocation location)
|
private List<ParameterViewModel> GetParameters(List<OpenApiParameter> parameters, ParameterLocation location)
|
||||||
{
|
{
|
||||||
var pathParameters = new AvaloniaList<ParameterViewModel>();
|
var pathParameters = new List<ParameterViewModel>();
|
||||||
foreach (var parameter in parameters)
|
foreach (var parameter in parameters)
|
||||||
{
|
{
|
||||||
if (parameter.In != location) continue;
|
if (parameter.In != location) continue;
|
||||||
@@ -151,7 +159,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AvaloniaList<PropertyClassViewModel> GetResponseClasses(OpenApiResponses responses, Document document)
|
private List<PropertyClassViewModel> GetResponseClasses(OpenApiResponses responses, Document document)
|
||||||
{
|
{
|
||||||
if (!TryGetResponse(responses, out var response))
|
if (!TryGetResponse(responses, out var response))
|
||||||
return [];
|
return [];
|
||||||
@@ -162,7 +170,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
var schema = media.Schema;
|
var schema = media.Schema;
|
||||||
if (schema == null) return [];
|
if (schema == null) return [];
|
||||||
|
|
||||||
AvaloniaList<PropertyClassViewModel> propertyClasses = [];
|
List<PropertyClassViewModel> propertyClasses = [];
|
||||||
WalkSchema(schema, propertyClasses, rawDocument);
|
WalkSchema(schema, propertyClasses, rawDocument);
|
||||||
return propertyClasses;
|
return propertyClasses;
|
||||||
}
|
}
|
||||||
@@ -170,7 +178,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WalkSchema(OpenApiSchema schema, AvaloniaList<PropertyClassViewModel> propertyClasses, OpenApiDocument document)
|
private void WalkSchema(OpenApiSchema schema, List<PropertyClassViewModel> propertyClasses, OpenApiDocument document)
|
||||||
{
|
{
|
||||||
var type = GetSchemaType(schema);
|
var type = GetSchemaType(schema);
|
||||||
if (IsComponent(type))
|
if (IsComponent(type))
|
||||||
@@ -209,7 +217,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
|| type.Contains("number"));
|
|| type.Contains("number"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AvaloniaList<PropertyClassViewModel> GetRequestClasses(OpenApiRequestBody? requestBody, Document document)
|
private List<PropertyClassViewModel> GetRequestClasses(OpenApiRequestBody? requestBody, Document document)
|
||||||
{
|
{
|
||||||
if (requestBody == null) return [];
|
if (requestBody == null) return [];
|
||||||
if (requestBody.Content.TryGetValue("application/json", out var media))
|
if (requestBody.Content.TryGetValue("application/json", out var media))
|
||||||
@@ -223,7 +231,7 @@ public partial class OperationViewModel : ObservableObject
|
|||||||
{
|
{
|
||||||
var componentId = GetComponentId(schema);
|
var componentId = GetComponentId(schema);
|
||||||
var componentSchema = rawDocument.Components.Schemas[componentId];
|
var componentSchema = rawDocument.Components.Schemas[componentId];
|
||||||
AvaloniaList<PropertyClassViewModel> propertyClasses = [];
|
List<PropertyClassViewModel> propertyClasses = [];
|
||||||
WalkSchema(componentSchema, propertyClasses, rawDocument);
|
WalkSchema(componentSchema, propertyClasses, rawDocument);
|
||||||
return propertyClasses;
|
return propertyClasses;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|||||||
|
|
||||||
public partial class ParameterViewModel : ObservableObject
|
public partial class ParameterViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
|
||||||
public string Type { get; }
|
|
||||||
public bool IsRequired { get; }
|
|
||||||
[ObservableProperty] private string? _value = null;
|
|
||||||
|
|
||||||
public ParameterViewModel(string name, string type, bool isRequired, string? value = null)
|
public ParameterViewModel(string name, string type, bool isRequired, string? value = null)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -16,4 +11,14 @@ public partial class ParameterViewModel : ObservableObject
|
|||||||
IsRequired = isRequired;
|
IsRequired = isRequired;
|
||||||
Value = value;
|
Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public string Type { get; }
|
||||||
|
|
||||||
|
public bool IsRequired { get; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _value = null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Needlework.Net.Models;
|
using Needlework.Net.Models;
|
||||||
using Needlework.Net.ViewModels.Shared;
|
using Needlework.Net.ViewModels.Shared;
|
||||||
using System;
|
using System;
|
||||||
@@ -11,27 +10,31 @@ namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|||||||
|
|
||||||
public partial class PathOperationViewModel : ObservableObject
|
public partial class PathOperationViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public string Path { get; }
|
public PathOperationViewModel(Services.NotificationService notificationService, PathOperation pathOperation, Document document, Tab tab)
|
||||||
public OperationViewModel Operation { get; }
|
|
||||||
|
|
||||||
public string Url { get; }
|
|
||||||
public string Markdown { get; }
|
|
||||||
|
|
||||||
[ObservableProperty] private bool _isBusy;
|
|
||||||
[ObservableProperty] private Lazy<RequestViewModel> _request;
|
|
||||||
|
|
||||||
public PathOperationViewModel(PathOperation pathOperation, ILogger<RequestViewModel> requestViewModelLogger, Document document, Tab tab)
|
|
||||||
{
|
{
|
||||||
Path = pathOperation.Path;
|
Path = pathOperation.Path;
|
||||||
Operation = new OperationViewModel(pathOperation.Operation, document);
|
Operation = new OperationViewModel(pathOperation.Operation, document);
|
||||||
Request = new(() => new RequestViewModel(requestViewModelLogger, tab)
|
Request = new(() => new RequestViewModel(notificationService, tab)
|
||||||
{
|
{
|
||||||
Method = pathOperation.Method.ToUpper()
|
Method = pathOperation.Method.ToUpper(),
|
||||||
|
RequestDocument = new(Operation.RequestTemplate ?? string.Empty)
|
||||||
});
|
});
|
||||||
Url = $"https://swagger.dysolix.dev/lcu/#/{Uri.EscapeDataString(pathOperation.Tag)}/{pathOperation.Operation.OperationId}";
|
Url = $"https://swagger.dysolix.dev/lcu/#/{Uri.EscapeDataString(pathOperation.Tag)}/{pathOperation.Operation.OperationId}";
|
||||||
Markdown = $"[{pathOperation.Method.ToUpper()} {Path}]({Url})";
|
Markdown = $"[{pathOperation.Method.ToUpper()} {Path}]({Url})";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Path { get; }
|
||||||
|
|
||||||
|
public OperationViewModel Operation { get; }
|
||||||
|
|
||||||
|
public string Url { get; }
|
||||||
|
|
||||||
|
public string Markdown { get; }
|
||||||
|
|
||||||
|
[ObservableProperty] private bool _isBusy;
|
||||||
|
|
||||||
|
[ObservableProperty] private Lazy<RequestViewModel> _request;
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task SendRequest()
|
private async Task SendRequest()
|
||||||
{
|
{
|
||||||
|
|||||||
45
Needlework.Net/ViewModels/Pages/Endpoints/PluginViewModel.cs
Normal file
45
Needlework.Net/ViewModels/Pages/Endpoints/PluginViewModel.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using AvaloniaEdit.Utils;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class PluginViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
public PluginViewModel(Services.NotificationService notificationService, string endpoint, Models.Document document, Tab tab)
|
||||||
|
{
|
||||||
|
Endpoint = endpoint;
|
||||||
|
PathOperations = document.Plugins[endpoint].Select(x => new PathOperationViewModel(notificationService, x, document, tab)).ToList();
|
||||||
|
FilteredPathOperations = new ObservableCollection<PathOperationViewModel>(PathOperations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Endpoint { get; }
|
||||||
|
|
||||||
|
public string Title => Endpoint;
|
||||||
|
|
||||||
|
public List<PathOperationViewModel> PathOperations { get; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<PathOperationViewModel> _filteredPathOperations;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private PathOperationViewModel? _selectedPathOperation;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _search;
|
||||||
|
|
||||||
|
partial void OnSearchChanged(string? value)
|
||||||
|
{
|
||||||
|
FilteredPathOperations.Clear();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
FilteredPathOperations.AddRange(PathOperations);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FilteredPathOperations.AddRange(PathOperations.Where(o => o.Path.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Avalonia.Collections;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.OpenApi.Any;
|
using Microsoft.OpenApi.Any;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -9,14 +8,10 @@ namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|||||||
|
|
||||||
public class PropertyClassViewModel : ObservableObject
|
public class PropertyClassViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public string Id { get; }
|
|
||||||
public IAvaloniaReadOnlyList<PropertyFieldViewModel> PropertyFields { get; } = new AvaloniaList<PropertyFieldViewModel>();
|
|
||||||
public IAvaloniaReadOnlyList<PropertyEnumViewModel> PropertyEnums { get; } = new AvaloniaList<PropertyEnumViewModel>();
|
|
||||||
|
|
||||||
public PropertyClassViewModel(string id, IDictionary<string, OpenApiSchema> properties, IList<IOpenApiAny> enumValue)
|
public PropertyClassViewModel(string id, IDictionary<string, OpenApiSchema> properties, IList<IOpenApiAny> enumValue)
|
||||||
{
|
{
|
||||||
AvaloniaList<PropertyFieldViewModel> propertyFields = [];
|
List<PropertyFieldViewModel> propertyFields = [];
|
||||||
AvaloniaList<PropertyEnumViewModel> propertyEnums = [];
|
List<PropertyEnumViewModel> propertyEnums = [];
|
||||||
foreach ((var propertyName, var propertySchema) in properties)
|
foreach ((var propertyName, var propertySchema) in properties)
|
||||||
{
|
{
|
||||||
var type = OperationViewModel.GetSchemaType(propertySchema);
|
var type = OperationViewModel.GetSchemaType(propertySchema);
|
||||||
@@ -32,4 +27,10 @@ public class PropertyClassViewModel : ObservableObject
|
|||||||
PropertyEnums = propertyEnums;
|
PropertyEnums = propertyEnums;
|
||||||
Id = id;
|
Id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Id { get; }
|
||||||
|
|
||||||
|
public List<PropertyFieldViewModel> PropertyFields { get; } = [];
|
||||||
|
|
||||||
|
public List<PropertyEnumViewModel> PropertyEnums { get; } = [];
|
||||||
}
|
}
|
||||||
@@ -6,11 +6,11 @@ namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|||||||
|
|
||||||
public class PropertyEnumViewModel
|
public class PropertyEnumViewModel
|
||||||
{
|
{
|
||||||
public string Type { get; } = "Enum";
|
|
||||||
public string Values { get; }
|
|
||||||
|
|
||||||
public PropertyEnumViewModel(IList<IOpenApiAny> enumValue)
|
public PropertyEnumViewModel(IList<IOpenApiAny> enumValue)
|
||||||
{
|
{
|
||||||
Values = $"[{string.Join(", ", enumValue.Select(x => $"\"{((OpenApiString)x).Value}\"").ToList())}]";
|
Values = $"[{string.Join(", ", enumValue.Select(x => $"\"{((OpenApiString)x).Value}\"").ToList())}]";
|
||||||
}
|
}
|
||||||
|
public string Type { get; } = "Enum";
|
||||||
|
|
||||||
|
public string Values { get; }
|
||||||
}
|
}
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
public class PropertyFieldViewModel
|
public class PropertyFieldViewModel
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
|
||||||
public string Type { get; }
|
|
||||||
|
|
||||||
public PropertyFieldViewModel(string name, string type)
|
public PropertyFieldViewModel(string name, string type)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Type = type;
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public string Type { get; }
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,6 @@ namespace Needlework.Net.ViewModels.Pages.Endpoints;
|
|||||||
|
|
||||||
public partial class ResponseViewModel : ObservableObject
|
public partial class ResponseViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
[ObservableProperty] private string? _path;
|
|
||||||
[ObservableProperty] private string? _status;
|
|
||||||
[ObservableProperty] private string? _authentication;
|
|
||||||
[ObservableProperty] private string? _username;
|
|
||||||
[ObservableProperty] private string? _password;
|
|
||||||
[ObservableProperty] private string? _authorization;
|
|
||||||
|
|
||||||
public ResponseViewModel(string path)
|
public ResponseViewModel(string path)
|
||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
@@ -26,6 +19,24 @@ public partial class ResponseViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _path;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _status;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _authentication;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _username;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _password;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _authorization;
|
||||||
|
|
||||||
private static ProcessInfo? GetProcessInfo()
|
private static ProcessInfo? GetProcessInfo()
|
||||||
{
|
{
|
||||||
if (ProcessFinder.IsActive()) return ProcessFinder.GetProcessInfo();
|
if (ProcessFinder.IsActive()) return ProcessFinder.GetProcessInfo();
|
||||||
|
|||||||
35
Needlework.Net/ViewModels/Pages/Home/HomeViewModel.cs
Normal file
35
Needlework.Net/ViewModels/Pages/Home/HomeViewModel.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Avalonia.Platform;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Home;
|
||||||
|
|
||||||
|
public partial class HomeViewModel : PageBase
|
||||||
|
{
|
||||||
|
public HomeViewModel() : base("Home", "home", int.MinValue) { }
|
||||||
|
|
||||||
|
public List<LibraryViewModel> Libraries { get; } = JsonSerializer.Deserialize<List<Library>>(AssetLoader.Open(new Uri($"avares://NeedleworkDotNet/Assets/libraries.json")))
|
||||||
|
!.Where(library => library.Tags.Contains("lcu") || library.Tags.Contains("ingame"))
|
||||||
|
.Select(library => new LibraryViewModel(library))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public override Task InitializeAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenUrl(string? value)
|
||||||
|
{
|
||||||
|
if (value == null) return;
|
||||||
|
var process = new Process() { StartInfo = new ProcessStartInfo(value) { UseShellExecute = true } };
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
Needlework.Net/ViewModels/Pages/Home/LibraryViewModel.cs
Normal file
24
Needlework.Net/ViewModels/Pages/Home/LibraryViewModel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Needlework.Net.Models;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Needlework.Net.ViewModels.Pages.Home
|
||||||
|
{
|
||||||
|
public partial class LibraryViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
public LibraryViewModel(Library library)
|
||||||
|
{
|
||||||
|
Library = library;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Library Library { get; }
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenUrl()
|
||||||
|
{
|
||||||
|
var process = new Process() { StartInfo = new ProcessStartInfo(Library.Link) { UseShellExecute = true } };
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
using Avalonia.Platform;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using Needlework.Net.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages;
|
|
||||||
|
|
||||||
public partial class HomeViewModel : PageBase
|
|
||||||
{
|
|
||||||
public List<Library> Libraries { get; } = JsonSerializer.Deserialize<List<Library>>(AssetLoader.Open(new Uri($"avares://NeedleworkDotNet/Assets/libraries.json")))!;
|
|
||||||
|
|
||||||
public HomeViewModel() : base("Home", "home", int.MinValue) { }
|
|
||||||
|
|
||||||
public override Task InitializeAsync()
|
|
||||||
{
|
|
||||||
IsInitialized = true;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void OpenUrl(string url)
|
|
||||||
{
|
|
||||||
var process = new Process()
|
|
||||||
{
|
|
||||||
StartInfo = new ProcessStartInfo(url) { UseShellExecute = true }
|
|
||||||
};
|
|
||||||
process.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ public abstract partial class PageBase(string displayName, string icon, int inde
|
|||||||
[ObservableProperty] private string _displayName = displayName;
|
[ObservableProperty] private string _displayName = displayName;
|
||||||
[ObservableProperty] private string _icon = icon;
|
[ObservableProperty] private string _icon = icon;
|
||||||
[ObservableProperty] private int _index = index;
|
[ObservableProperty] private int _index = index;
|
||||||
[ObservableProperty] private bool _isInitialized;
|
|
||||||
|
|
||||||
public abstract Task InitializeAsync();
|
public abstract Task InitializeAsync();
|
||||||
}
|
}
|
||||||
@@ -2,20 +2,22 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages.Websocket;
|
namespace Needlework.Net.ViewModels.Pages.WebSocket;
|
||||||
|
|
||||||
public class EventViewModel : ObservableObject
|
public class EventViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public string Time { get; }
|
|
||||||
public string Type { get; }
|
|
||||||
public string Uri { get; }
|
|
||||||
|
|
||||||
public string Key => $"{Time} {Type} {Uri}";
|
|
||||||
|
|
||||||
public EventViewModel(EventData eventData)
|
public EventViewModel(EventData eventData)
|
||||||
{
|
{
|
||||||
Time = $"{DateTime.Now:HH:mm:ss.fff}";
|
Time = $"{DateTime.Now:HH:mm:ss.fff}";
|
||||||
Type = eventData?.EventType?.ToUpper() ?? string.Empty;
|
Type = eventData?.EventType?.ToUpper() ?? string.Empty;
|
||||||
Uri = eventData?.Uri ?? string.Empty;
|
Uri = eventData?.Uri ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Time { get; }
|
||||||
|
|
||||||
|
public string Type { get; }
|
||||||
|
|
||||||
|
public string Uri { get; }
|
||||||
|
|
||||||
|
public string Key => $"{Time} {Type} {Uri}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
|
using AvaloniaEdit.Document;
|
||||||
using BlossomiShymae.Briar;
|
using BlossomiShymae.Briar;
|
||||||
using BlossomiShymae.Briar.WebSocket.Events;
|
using BlossomiShymae.Briar.WebSocket.Events;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.Extensions.Logging;
|
using Flurl.Http;
|
||||||
|
using Flurl.Http.Configuration;
|
||||||
|
using Needlework.Net.Extensions;
|
||||||
using Needlework.Net.Messages;
|
using Needlework.Net.Messages;
|
||||||
|
using Needlework.Net.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -17,67 +21,81 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Websocket.Client;
|
using Websocket.Client;
|
||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Pages.Websocket;
|
namespace Needlework.Net.ViewModels.Pages.WebSocket;
|
||||||
|
|
||||||
public partial class WebsocketViewModel : PageBase
|
public partial class WebSocketViewModel : PageBase, IEnableLogger
|
||||||
{
|
{
|
||||||
public ObservableCollection<EventViewModel> EventLog { get; } = [];
|
|
||||||
public SemaphoreSlim EventLogLock { get; } = new(1, 1);
|
|
||||||
|
|
||||||
[NotifyPropertyChangedFor(nameof(FilteredEventLog))]
|
|
||||||
[ObservableProperty] private string _search = string.Empty;
|
|
||||||
[ObservableProperty] private bool _isAttach = true;
|
|
||||||
[ObservableProperty] private bool _isTail = false;
|
|
||||||
[ObservableProperty] private EventViewModel? _selectedEventLog = null;
|
|
||||||
|
|
||||||
[ObservableProperty] private IAvaloniaList<string> _eventTypes = new AvaloniaList<string>();
|
|
||||||
[ObservableProperty] private string _eventType = "OnJsonApiEvent";
|
|
||||||
|
|
||||||
private Dictionary<string, EventMessage> _events = [];
|
private Dictionary<string, EventMessage> _events = [];
|
||||||
|
|
||||||
|
private readonly IFlurlClient _githubUserContentClient;
|
||||||
|
|
||||||
|
private readonly NotificationService _notificationService;
|
||||||
|
|
||||||
|
private readonly object _tokenLock = new();
|
||||||
|
|
||||||
|
public WebSocketViewModel(IFlurlClientCache clients, NotificationService notificationService) : base("Event Viewer", "plug", -100)
|
||||||
|
{
|
||||||
|
_githubUserContentClient = clients.Get("GithubUserContentClient");
|
||||||
|
_notificationService = notificationService;
|
||||||
|
|
||||||
|
EventLog.CollectionChanged += (s, e) => OnPropertyChanged(nameof(FilteredEventLog));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<EventViewModel> EventLog { get; } = [];
|
||||||
|
|
||||||
|
public SemaphoreSlim EventLogLock { get; } = new(1, 1);
|
||||||
|
|
||||||
public WebsocketClient? Client { get; set; }
|
public WebsocketClient? Client { get; set; }
|
||||||
|
|
||||||
public List<IDisposable> ClientDisposables = [];
|
public List<IDisposable> ClientDisposables = [];
|
||||||
|
|
||||||
private readonly object _tokenLock = new();
|
|
||||||
public CancellationTokenSource TokenSource { get; set; } = new();
|
public CancellationTokenSource TokenSource { get; set; } = new();
|
||||||
|
|
||||||
public HttpClient HttpClient { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<EventViewModel> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? EventLog : [.. EventLog.Where(x => x.Key.Contains(Search, StringComparison.InvariantCultureIgnoreCase))];
|
public IReadOnlyList<EventViewModel> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? EventLog : [.. EventLog.Where(x => x.Key.Contains(Search, StringComparison.InvariantCultureIgnoreCase))];
|
||||||
|
|
||||||
private readonly ILogger<WebsocketViewModel> _logger;
|
[NotifyPropertyChangedFor(nameof(FilteredEventLog))]
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _search = string.Empty;
|
||||||
|
|
||||||
public WebsocketViewModel(HttpClient httpClient, ILogger<WebsocketViewModel> logger) : base("Event Viewer", "plug", -100)
|
[ObservableProperty]
|
||||||
{
|
private bool _isAttach = true;
|
||||||
_logger = logger;
|
|
||||||
HttpClient = httpClient;
|
[ObservableProperty]
|
||||||
EventLog.CollectionChanged += (s, e) => OnPropertyChanged(nameof(FilteredEventLog));
|
private bool _isTail = false;
|
||||||
Task.Run(async () =>
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private EventViewModel? _selectedEventLog = null;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private IAvaloniaList<string> _eventTypes = new AvaloniaList<string>();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _eventType = "OnJsonApiEvent";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private TextDocument _document = new();
|
||||||
|
|
||||||
|
public override async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
await InitializeEventTypes();
|
await InitializeEventTypes();
|
||||||
InitializeWebsocket();
|
InitializeWebsocket();
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task InitializeAsync()
|
|
||||||
{
|
|
||||||
IsInitialized = true;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeEventTypes()
|
private async Task InitializeEventTypes()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var file = await HttpClient.GetStringAsync("https://raw.githubusercontent.com/dysolix/hasagi-types/refs/heads/main/dist/lcu-events.d.ts");
|
var file = await _githubUserContentClient.Request("/dysolix/hasagi-types/refs/heads/main/dist/lcu-events.d.ts")
|
||||||
|
.GetStringAsync();
|
||||||
var matches = EventTypesRegex().Matches(file);
|
var matches = EventTypesRegex().Matches(file);
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Invoke(() => EventTypes.AddRange(matches.Select(m => m.Groups[1].Value)));
|
Avalonia.Threading.Dispatcher.UIThread.Invoke(() => EventTypes.AddRange(matches.Select(m => m.Groups[1].Value)));
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Failed to get event types");
|
var message = "Failed to get event types from GitHub. Please check your internet connection or try again later.";
|
||||||
WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new("Failed to get event types", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(10))));
|
this.Log()
|
||||||
|
.Error(ex, message);
|
||||||
|
_notificationService.Notify("Needlework.Net", message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +105,8 @@ public partial class WebsocketViewModel : PageBase
|
|||||||
{
|
{
|
||||||
if (Client != null)
|
if (Client != null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Disposing old connection");
|
this.Log()
|
||||||
|
.Debug("Disposing old connection");
|
||||||
foreach (var disposable in ClientDisposables)
|
foreach (var disposable in ClientDisposables)
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
ClientDisposables.Clear();
|
ClientDisposables.Clear();
|
||||||
@@ -117,7 +136,8 @@ public partial class WebsocketViewModel : PageBase
|
|||||||
})
|
})
|
||||||
{ IsBackground = true };
|
{ IsBackground = true };
|
||||||
thread.Start();
|
thread.Start();
|
||||||
_logger.LogDebug("Initialized new connection: {EventType}", EventType);
|
this.Log()
|
||||||
|
.Debug("Initialized new connection: {EventType}", EventType);
|
||||||
TokenSource = tokenSource;
|
TokenSource = tokenSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +149,7 @@ public partial class WebsocketViewModel : PageBase
|
|||||||
{
|
{
|
||||||
var text = JsonSerializer.Serialize(message, App.JsonSerializerOptions);
|
var text = JsonSerializer.Serialize(message, App.JsonSerializerOptions);
|
||||||
if (text.Length >= App.MaxCharacters) WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(text));
|
if (text.Length >= App.MaxCharacters) WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(text));
|
||||||
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(text), nameof(WebsocketViewModel));
|
else Document = new(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,16 +158,19 @@ public partial class WebsocketViewModel : PageBase
|
|||||||
{
|
{
|
||||||
_events.Clear();
|
_events.Clear();
|
||||||
EventLog.Clear();
|
EventLog.Clear();
|
||||||
|
Document = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnReconnection(ReconnectionInfo info)
|
private void OnReconnection(ReconnectionInfo info)
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Reconnected: {Type}", info.Type);
|
this.Log()
|
||||||
|
.Debug("Reconnected: {Type}", info.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisconnection(DisconnectionInfo info)
|
private void OnDisconnection(DisconnectionInfo info)
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Disconnected: {Type}", info.Type);
|
this.Log()
|
||||||
|
.Debug("Disconnected: {Type}", info.Type);
|
||||||
InitializeWebsocket();
|
InitializeWebsocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
using AvaloniaEdit.Document;
|
||||||
using BlossomiShymae.Briar;
|
using BlossomiShymae.Briar;
|
||||||
using BlossomiShymae.Briar.Utils;
|
using BlossomiShymae.Briar.Utils;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.Extensions.Logging;
|
using Needlework.Net.Extensions;
|
||||||
using Needlework.Net.Messages;
|
using Needlework.Net.Messages;
|
||||||
using Needlework.Net.ViewModels.MainWindow;
|
using Needlework.Net.Services;
|
||||||
using Needlework.Net.ViewModels.Pages.Endpoints;
|
using Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -14,35 +15,54 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Needlework.Net.ViewModels.Shared;
|
namespace Needlework.Net.ViewModels.Shared;
|
||||||
|
|
||||||
public partial class RequestViewModel : ObservableObject
|
public partial class RequestViewModel : ObservableObject, IEnableLogger
|
||||||
{
|
{
|
||||||
[ObservableProperty] private string? _method = "GET";
|
private readonly NotificationService _notificationService;
|
||||||
[ObservableProperty] private SolidColorBrush _color = new(GetColor("GET"));
|
|
||||||
|
|
||||||
[ObservableProperty] private bool _isRequestBusy = false;
|
|
||||||
[ObservableProperty] private string? _requestPath = null;
|
|
||||||
[ObservableProperty] private string? _requestBody = null;
|
|
||||||
|
|
||||||
[ObservableProperty] private string? _responsePath = null;
|
|
||||||
[ObservableProperty] private string? _responseStatus = null;
|
|
||||||
[ObservableProperty] private string? _responseAuthentication = null;
|
|
||||||
[ObservableProperty] private string? _responseUsername = null;
|
|
||||||
[ObservableProperty] private string? _responsePassword = null;
|
|
||||||
[ObservableProperty] private string? _responseAuthorization = null;
|
|
||||||
[ObservableProperty] private string? _responseBody = null;
|
|
||||||
|
|
||||||
public event EventHandler<RequestViewModel>? RequestText;
|
|
||||||
public event EventHandler<string>? UpdateText;
|
|
||||||
|
|
||||||
private readonly ILogger<RequestViewModel> _logger;
|
|
||||||
private readonly Tab _tab;
|
private readonly Tab _tab;
|
||||||
|
|
||||||
public RequestViewModel(ILogger<RequestViewModel> logger, Pages.Endpoints.Tab tab)
|
public RequestViewModel(NotificationService notificationService, Tab tab)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
_tab = tab;
|
_tab = tab;
|
||||||
|
_notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _method = "GET";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SolidColorBrush _color = new(GetColor("GET"));
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isRequestBusy;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _requestPath;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private TextDocument _requestDocument = new();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private TextDocument _responseDocument = new();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _responsePath;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _responseStatus;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _responseAuthentication;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _responseUsername;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _responsePassword;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _responseAuthorization;
|
||||||
|
|
||||||
partial void OnMethodChanged(string? oldValue, string? newValue)
|
partial void OnMethodChanged(string? oldValue, string? newValue)
|
||||||
{
|
{
|
||||||
if (newValue == null) return;
|
if (newValue == null) return;
|
||||||
@@ -74,9 +94,10 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
throw new Exception("Path is empty.");
|
throw new Exception("Path is empty.");
|
||||||
var method = GetMethod();
|
var method = GetMethod();
|
||||||
|
|
||||||
_logger.LogDebug("Sending request: {Tuple}", (Method, RequestPath));
|
this.Log()
|
||||||
RequestText?.Invoke(this, this);
|
.Debug("Sending request: {Tuple}", (Method, RequestPath));
|
||||||
var content = new StringContent(RequestBody ?? string.Empty, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
var requestBody = RequestDocument.Text;
|
||||||
|
var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
||||||
var client = Connector.GetGameHttpClientInstance();
|
var client = Connector.GetGameHttpClientInstance();
|
||||||
var response = await client.SendAsync(new HttpRequestMessage(method, RequestPath) { Content = content });
|
var response = await client.SendAsync(new HttpRequestMessage(method, RequestPath) { Content = content });
|
||||||
var responseBody = await response.Content.ReadAsByteArrayAsync();
|
var responseBody = await response.Content.ReadAsByteArrayAsync();
|
||||||
@@ -85,12 +106,11 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
if (body.Length > App.MaxCharacters)
|
if (body.Length > App.MaxCharacters)
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(body));
|
WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(body));
|
||||||
UpdateText?.Invoke(this, string.Empty);
|
ResponseDocument = new();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ResponseBody = body;
|
ResponseDocument = new(body);
|
||||||
UpdateText?.Invoke(this, body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode.ToString()}";
|
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode.ToString()}";
|
||||||
@@ -99,9 +119,9 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Request failed: {Tuple}", (Method, RequestPath));
|
this.Log()
|
||||||
WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new InfoBarViewModel("Request Failed", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(5))));
|
.Error(ex, "Request failed: {Tuple}", (Method, RequestPath));
|
||||||
UpdateText?.Invoke(this, string.Empty);
|
_notificationService.Notify("Request failed", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
|
||||||
|
|
||||||
ResponseStatus = null;
|
ResponseStatus = null;
|
||||||
ResponsePath = null;
|
ResponsePath = null;
|
||||||
@@ -109,7 +129,7 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
ResponseAuthorization = null;
|
ResponseAuthorization = null;
|
||||||
ResponseUsername = null;
|
ResponseUsername = null;
|
||||||
ResponsePassword = null;
|
ResponsePassword = null;
|
||||||
ResponseBody = null;
|
ResponseDocument = new();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -126,11 +146,12 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
throw new Exception("Path is empty.");
|
throw new Exception("Path is empty.");
|
||||||
var method = GetMethod();
|
var method = GetMethod();
|
||||||
|
|
||||||
_logger.LogDebug("Sending request: {Tuple}", (Method, RequestPath));
|
this.Log()
|
||||||
|
.Debug("Sending request: {Tuple}", (Method, RequestPath));
|
||||||
|
|
||||||
var processInfo = ProcessFinder.GetProcessInfo();
|
var processInfo = ProcessFinder.GetProcessInfo();
|
||||||
RequestText?.Invoke(this, this);
|
var requestBody = RequestDocument.Text;
|
||||||
var content = new StringContent(RequestBody ?? string.Empty, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
||||||
var client = Connector.GetLcuHttpClientInstance();
|
var client = Connector.GetLcuHttpClientInstance();
|
||||||
var response = await client.SendAsync(new(method, RequestPath) { Content = content });
|
var response = await client.SendAsync(new(method, RequestPath) { Content = content });
|
||||||
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
|
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
|
||||||
@@ -140,12 +161,11 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
if (body.Length >= App.MaxCharacters)
|
if (body.Length >= App.MaxCharacters)
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(body));
|
WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(body));
|
||||||
UpdateText?.Invoke(this, string.Empty);
|
ResponseDocument = new();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ResponseBody = body;
|
ResponseDocument = new(body);
|
||||||
UpdateText?.Invoke(this, body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode.ToString()}";
|
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode.ToString()}";
|
||||||
@@ -157,9 +177,9 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Request failed: {Tuple}", (Method, RequestPath));
|
this.Log()
|
||||||
WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new InfoBarViewModel("Request Failed", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(5))));
|
.Error(ex, "Request failed: {Tuple}", (Method, RequestPath));
|
||||||
UpdateText?.Invoke(this, string.Empty);
|
_notificationService.Notify("Request failed", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
|
||||||
|
|
||||||
ResponseStatus = null;
|
ResponseStatus = null;
|
||||||
ResponsePath = null;
|
ResponsePath = null;
|
||||||
@@ -167,7 +187,7 @@ public partial class RequestViewModel : ObservableObject
|
|||||||
ResponseAuthorization = null;
|
ResponseAuthorization = null;
|
||||||
ResponseUsername = null;
|
ResponseUsername = null;
|
||||||
ResponsePassword = null;
|
ResponsePassword = null;
|
||||||
ResponseBody = null;
|
ResponseDocument = new();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,11 +28,10 @@
|
|||||||
DockPanel.Dock="Left"
|
DockPanel.Dock="Left"
|
||||||
Grid.Column="0"/>
|
Grid.Column="0"/>
|
||||||
<TextBlock FontSize="12"
|
<TextBlock FontSize="12"
|
||||||
|
Text="{Binding Title}"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Grid.Column="1">
|
Grid.Column="1"/>
|
||||||
Needlework.Net
|
|
||||||
</TextBlock>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<ui:NavigationView AlwaysShowHeader="False"
|
<ui:NavigationView AlwaysShowHeader="False"
|
||||||
PaneDisplayMode="Left"
|
PaneDisplayMode="Left"
|
||||||
@@ -40,8 +39,8 @@
|
|||||||
IsPaneOpen="False"
|
IsPaneOpen="False"
|
||||||
OpenPaneLength="200"
|
OpenPaneLength="200"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
MenuItemsSource="{Binding MenuItems}"
|
MenuItemsSource="{Binding NavigationViewItems}"
|
||||||
SelectedItem="{Binding SelectedMenuItem}">
|
SelectedItem="{Binding SelectedNavigationViewItem}">
|
||||||
<ui:NavigationView.PaneFooter>
|
<ui:NavigationView.PaneFooter>
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
<StackPanel.Styles>
|
<StackPanel.Styles>
|
||||||
@@ -75,27 +74,14 @@
|
|||||||
</ui:NavigationView.PaneFooter>
|
</ui:NavigationView.PaneFooter>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TransitioningContentControl Content="{Binding CurrentPage}"/>
|
<TransitioningContentControl Content="{Binding CurrentPage}"/>
|
||||||
<Button Content="{Binding Version}"
|
<ItemsControl ItemsSource="{Binding Notifications}"
|
||||||
Background="RoyalBlue"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Margin="16"/>
|
|
||||||
<ItemsRepeater ItemsSource="{Binding InfoBarItems}"
|
|
||||||
VerticalAlignment="Bottom">
|
VerticalAlignment="Bottom">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="vm:NotificationViewModel">
|
||||||
<Border Margin="4">
|
<ContentControl Content="{Binding}"/>
|
||||||
<ui:InfoBar
|
|
||||||
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
|
|
||||||
Title="{Binding Title}"
|
|
||||||
IsOpen="{Binding IsOpen}"
|
|
||||||
Severity="{Binding Severity}"
|
|
||||||
Message="{Binding Message}"
|
|
||||||
ActionButton="{Binding ActionButton}"/>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:NavigationView>
|
</ui:NavigationView>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
25
Needlework.Net/Views/MainWindow/NotificationView.axaml
Normal file
25
Needlework.Net/Views/MainWindow/NotificationView.axaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.MainWindow"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.MainWindow.NotificationView"
|
||||||
|
x:DataType="vm:NotificationViewModel">
|
||||||
|
<Border Margin="4">
|
||||||
|
<ui:InfoBar
|
||||||
|
IsOpen="True"
|
||||||
|
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
|
||||||
|
Title="{Binding Notification.Title}"
|
||||||
|
Severity="{Binding Notification.InfoBarSeverity}"
|
||||||
|
Message="{Binding Notification.Message}">
|
||||||
|
<ui:InfoBar.ActionButton>
|
||||||
|
<Button Command="{Binding OpenUrlCommand}"
|
||||||
|
IsVisible="{Binding IsButtonVisible}">
|
||||||
|
Open URL
|
||||||
|
</Button>
|
||||||
|
</ui:InfoBar.ActionButton>
|
||||||
|
</ui:InfoBar>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
11
Needlework.Net/Views/MainWindow/NotificationView.axaml.cs
Normal file
11
Needlework.Net/Views/MainWindow/NotificationView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.MainWindow;
|
||||||
|
|
||||||
|
public partial class NotificationView : UserControl
|
||||||
|
{
|
||||||
|
public NotificationView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,10 @@ namespace Needlework.Net.Views.MainWindow;
|
|||||||
public class OopsiesDialog : IDialog, IDisposable
|
public class OopsiesDialog : IDialog, IDisposable
|
||||||
{
|
{
|
||||||
private bool _isDisposing;
|
private bool _isDisposing;
|
||||||
|
|
||||||
private string? _text;
|
private string? _text;
|
||||||
private ContentDialog _dialog;
|
|
||||||
|
private readonly ContentDialog _dialog;
|
||||||
|
|
||||||
public OopsiesDialog()
|
public OopsiesDialog()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages"
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.About"
|
||||||
xmlns:controls="using:Needlework.Net.Controls"
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Views.Pages.AboutView"
|
x:Class="Needlework.Net.Views.Pages.About.AboutView"
|
||||||
x:DataType="vm:AboutViewModel">
|
x:DataType="vm:AboutViewModel">
|
||||||
<ScrollViewer Margin="8">
|
<ScrollViewer Margin="8">
|
||||||
<WrapPanel HorizontalAlignment="Center">
|
<WrapPanel HorizontalAlignment="Center">
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages;
|
namespace Needlework.Net.Views.Pages.About;
|
||||||
|
|
||||||
public partial class AboutView : UserControl
|
public partial class AboutView : UserControl
|
||||||
{
|
{
|
||||||
@@ -5,10 +5,10 @@
|
|||||||
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
|
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages"
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Console"
|
||||||
xmlns:controls="using:Needlework.Net.Controls"
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Views.Pages.ConsoleView"
|
x:Class="Needlework.Net.Views.Pages.Console.ConsoleView"
|
||||||
x:DataType="vm:ConsoleViewModel">
|
x:DataType="vm:ConsoleViewModel">
|
||||||
<controls:BusyArea IsBusy="{Binding IsBusy}"
|
<controls:BusyArea IsBusy="{Binding IsBusy}"
|
||||||
BusyText="Loading...">
|
BusyText="Loading...">
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
Text="{Binding Request.ResponsePath}"/>
|
Text="{Binding Request.ResponsePath}"/>
|
||||||
<avaloniaEdit:TextEditor
|
<avaloniaEdit:TextEditor
|
||||||
Name="RequestEditor"
|
Name="RequestEditor"
|
||||||
Text=""
|
Document="{Binding Request.RequestDocument}"
|
||||||
ShowLineNumbers="True"
|
ShowLineNumbers="True"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
@@ -77,6 +77,7 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Name="ResponseEditor"
|
Name="ResponseEditor"
|
||||||
|
Document="{Binding Request.ResponseDocument}"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
ShowLineNumbers="True"
|
ShowLineNumbers="True"
|
||||||
25
Needlework.Net/Views/Pages/Console/ConsoleView.axaml.cs
Normal file
25
Needlework.Net/Views/Pages/Console/ConsoleView.axaml.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using Needlework.Net.Extensions;
|
||||||
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Console;
|
||||||
|
|
||||||
|
public partial class ConsoleView : UserControl
|
||||||
|
{
|
||||||
|
public ConsoleView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
ResponseEditor.ApplyJsonEditorSettings();
|
||||||
|
RequestEditor.ApplyJsonEditorSettings();
|
||||||
|
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
||||||
|
{
|
||||||
|
var registryOptions = new RegistryOptions(
|
||||||
|
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Styling;
|
|
||||||
using AvaloniaEdit;
|
|
||||||
using Needlework.Net.Extensions;
|
|
||||||
using Needlework.Net.ViewModels.Pages;
|
|
||||||
using TextMateSharp.Grammars;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages;
|
|
||||||
|
|
||||||
public partial class ConsoleView : UserControl
|
|
||||||
{
|
|
||||||
private TextEditor? _responseEditor;
|
|
||||||
private TextEditor? _requestEditor;
|
|
||||||
|
|
||||||
public ConsoleView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnAttachedToVisualTree(e);
|
|
||||||
|
|
||||||
_responseEditor = this.FindControl<TextEditor>("ResponseEditor");
|
|
||||||
_requestEditor = this.FindControl<TextEditor>("RequestEditor");
|
|
||||||
_responseEditor?.ApplyJsonEditorSettings();
|
|
||||||
_requestEditor?.ApplyJsonEditorSettings();
|
|
||||||
|
|
||||||
var vm = (ConsoleViewModel)DataContext!;
|
|
||||||
vm.Request.RequestText += LcuRequest_RequestText; ;
|
|
||||||
vm.Request.UpdateText += LcuRequest_UpdateText;
|
|
||||||
|
|
||||||
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LcuRequest_RequestText(object? sender, ViewModels.Shared.RequestViewModel e)
|
|
||||||
{
|
|
||||||
e.RequestBody = _requestEditor!.Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LcuRequest_UpdateText(object? sender, string e)
|
|
||||||
{
|
|
||||||
_responseEditor!.Text = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnDetachedFromVisualTree(e);
|
|
||||||
|
|
||||||
var vm = (ConsoleViewModel)DataContext!;
|
|
||||||
vm.Request.RequestText -= LcuRequest_RequestText;
|
|
||||||
vm.Request.UpdateText -= LcuRequest_UpdateText;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
|
||||||
{
|
|
||||||
var registryOptions = new RegistryOptions(
|
|
||||||
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
Needlework.Net/Views/Pages/Endpoints/EndpointListView.axaml
Normal file
23
Needlework.Net/Views/Pages/Endpoints/EndpointListView.axaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
||||||
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
Name="EndpointsControl"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointListView"
|
||||||
|
x:DataType="vm:EndpointListViewModel">
|
||||||
|
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*">
|
||||||
|
<TextBox Watermark="Search" Margin="0 4" Text="{Binding Search}" Grid.Row="1" Grid.Column="0"/>
|
||||||
|
<ScrollViewer Grid.Row="2" Grid.Column="0">
|
||||||
|
<ItemsControl ItemsSource="{Binding EndpointSearchDetails}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ContentControl Content="{Binding}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class EndpointListView : UserControl
|
||||||
|
{
|
||||||
|
public EndpointListView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointSearchDetailsView"
|
||||||
|
x:DataType="vm:EndpointSearchDetailsViewModel">
|
||||||
|
<Button Content="{Binding Plugin}"
|
||||||
|
Command="{Binding OpenEndpointCommand}"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Theme="{StaticResource TransparentButton}"/>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class EndpointSearchDetailsView : UserControl
|
||||||
|
{
|
||||||
|
public EndpointSearchDetailsView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointTabItemContentView"
|
||||||
|
x:DataType="vm:EndpointTabItemContentViewModel">
|
||||||
|
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*">
|
||||||
|
<Menu Grid.Row="0"
|
||||||
|
Grid.Column="0">
|
||||||
|
<MenuItem Header="_New tab">
|
||||||
|
<MenuItem Header="LCU"
|
||||||
|
Command="{Binding AddEndpointCommand}"
|
||||||
|
CommandParameter="{x:Static vm:Tab.LCU}"/>
|
||||||
|
<MenuItem Header="Game Client"
|
||||||
|
Command="{Binding AddEndpointCommand}"
|
||||||
|
CommandParameter="{x:Static vm:Tab.GameClient}"/>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
<Separator Grid.Row="1"
|
||||||
|
Grid.Column="0"/>
|
||||||
|
<Grid Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
RowDefinitions="auto,*"
|
||||||
|
ColumnDefinitions="*"
|
||||||
|
Margin="16">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0 0 0 8">
|
||||||
|
<Button Command="{Binding GoBackCommand}"
|
||||||
|
Theme="{StaticResource TransparentButton}"
|
||||||
|
Margin="0 0 8 0">
|
||||||
|
<i:Icon Value="fa-arrow-left"
|
||||||
|
FontSize="20"/>
|
||||||
|
</Button>
|
||||||
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
||||||
|
Text="{Binding Title}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TransitioningContentControl
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="{Binding ActiveViewModel}"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class EndpointTabItemContentView : UserControl
|
||||||
|
{
|
||||||
|
public EndpointTabItemContentView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Styling;
|
|
||||||
using AvaloniaEdit;
|
|
||||||
using Needlework.Net.Extensions;
|
|
||||||
using Needlework.Net.ViewModels.Pages.Endpoints;
|
|
||||||
using Needlework.Net.ViewModels.Shared;
|
|
||||||
using TextMateSharp.Grammars;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages.Endpoints;
|
|
||||||
|
|
||||||
public partial class EndpointView : UserControl
|
|
||||||
{
|
|
||||||
private TextEditor? _requestEditor;
|
|
||||||
private TextEditor? _responseEditor;
|
|
||||||
private RequestViewModel? _lcuRequestVm;
|
|
||||||
|
|
||||||
public EndpointView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnAttachedToVisualTree(e);
|
|
||||||
|
|
||||||
_requestEditor = this.FindControl<TextEditor>("EndpointRequestEditor");
|
|
||||||
_responseEditor = this.FindControl<TextEditor>("EndpointResponseEditor");
|
|
||||||
_requestEditor?.ApplyJsonEditorSettings();
|
|
||||||
_responseEditor?.ApplyJsonEditorSettings();
|
|
||||||
|
|
||||||
var vm = (EndpointViewModel)DataContext!;
|
|
||||||
vm.PathOperationSelected += Vm_PathOperationSelected;
|
|
||||||
|
|
||||||
if (vm.SelectedPathOperation != null)
|
|
||||||
{
|
|
||||||
_lcuRequestVm = vm.SelectedPathOperation.Request.Value;
|
|
||||||
vm.SelectedPathOperation.Request.Value.RequestText += LcuRequest_RequestText;
|
|
||||||
vm.SelectedPathOperation.Request.Value.UpdateText += LcuRequest_UpdateText;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Vm_PathOperationSelected(object? sender, string e)
|
|
||||||
{
|
|
||||||
var vm = (EndpointViewModel)DataContext!;
|
|
||||||
if (vm.SelectedPathOperation != null)
|
|
||||||
{
|
|
||||||
_requestEditor!.Text = e;
|
|
||||||
if (_lcuRequestVm != null)
|
|
||||||
{
|
|
||||||
_lcuRequestVm.RequestText -= LcuRequest_RequestText;
|
|
||||||
_lcuRequestVm.UpdateText -= LcuRequest_UpdateText;
|
|
||||||
}
|
|
||||||
vm.SelectedPathOperation.Request.Value.RequestText += LcuRequest_RequestText;
|
|
||||||
vm.SelectedPathOperation.Request.Value.UpdateText += LcuRequest_UpdateText;
|
|
||||||
_lcuRequestVm = vm.SelectedPathOperation.Request.Value;
|
|
||||||
_responseEditor!.Text = vm.SelectedPathOperation.Request.Value.ResponseBody ?? string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnDetachedFromVisualTree(e);
|
|
||||||
|
|
||||||
var vm = (EndpointViewModel)DataContext!;
|
|
||||||
vm.PathOperationSelected -= Vm_PathOperationSelected;
|
|
||||||
|
|
||||||
if (_lcuRequestVm != null)
|
|
||||||
{
|
|
||||||
_lcuRequestVm.RequestText -= LcuRequest_RequestText;
|
|
||||||
_lcuRequestVm.UpdateText -= LcuRequest_UpdateText;
|
|
||||||
_lcuRequestVm = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
|
||||||
{
|
|
||||||
var registryOptions = new RegistryOptions(
|
|
||||||
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LcuRequest_RequestText(object? sender, RequestViewModel e)
|
|
||||||
{
|
|
||||||
e.RequestBody = _requestEditor!.Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LcuRequest_UpdateText(object? sender, string e)
|
|
||||||
{
|
|
||||||
_responseEditor!.Text = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
|
||||||
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
|
||||||
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsNavigationView"
|
|
||||||
x:DataType="vm:EndpointsNavigationViewModel">
|
|
||||||
<Grid RowDefinitions="auto,*"
|
|
||||||
ColumnDefinitions="*"
|
|
||||||
Margin="16">
|
|
||||||
<StackPanel Orientation="Horizontal"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="0 0 0 8">
|
|
||||||
<Button Command="{Binding GoBackCommand}"
|
|
||||||
Theme="{StaticResource TransparentButton}"
|
|
||||||
Margin="0 0 8 0">
|
|
||||||
<i:Icon Value="fa-arrow-left"
|
|
||||||
FontSize="20"/>
|
|
||||||
</Button>
|
|
||||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
|
||||||
Text="{Binding Title}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<TransitioningContentControl
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="0"
|
|
||||||
Content="{Binding ActiveViewModel}"/>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
|
||||||
Name="EndpointsTab"
|
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
|
||||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
|
||||||
xmlns:controls="using:Needlework.Net.Controls"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsTabView"
|
|
||||||
x:DataType="vm:EndpointsTabViewModel">
|
|
||||||
<controls:BusyArea IsBusy="{Binding IsBusy}"
|
|
||||||
BusyText="Loading...">
|
|
||||||
<Grid>
|
|
||||||
<ui:TabView TabItems="{Binding Endpoints}"
|
|
||||||
AddTabButtonCommand="{Binding AddEndpointCommand}"
|
|
||||||
AddTabButtonCommandParameter="{x:Static vm:Tab.LCU}"
|
|
||||||
TabCloseRequested="TabView_TabCloseRequested">
|
|
||||||
<!--Need to override Tab header for Mica theme...-->
|
|
||||||
<ui:TabView.Resources>
|
|
||||||
<ResourceDictionary>
|
|
||||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{DynamicResource ControlFillColorTransparent}"/>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</ui:TabView.Resources>
|
|
||||||
<!--We need to hack this style for Mica theme since there is no way to explicity set style priority in Avalonia...-->
|
|
||||||
<ui:TabView.Styles>
|
|
||||||
<Style Selector="Grid > ContentPresenter#TabContentPresenter">
|
|
||||||
<Style.Animations>
|
|
||||||
<Animation IterationCount="1" Duration="0:0:1" FillMode="Both">
|
|
||||||
<KeyFrame Cue="0%">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorTransparent}"/>
|
|
||||||
</KeyFrame>
|
|
||||||
<KeyFrame Cue="100%">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorTransparent}"/>
|
|
||||||
</KeyFrame>
|
|
||||||
</Animation>
|
|
||||||
</Style.Animations>
|
|
||||||
</Style>
|
|
||||||
</ui:TabView.Styles>
|
|
||||||
<ui:TabView.TabItemTemplate>
|
|
||||||
<DataTemplate DataType="vm:EndpointItem">
|
|
||||||
<ui:TabViewItem Header="{Binding Header}"
|
|
||||||
IconSource="{Binding IconSource}"
|
|
||||||
IsSelected="{Binding Selected}"
|
|
||||||
Content="{Binding}">
|
|
||||||
<ui:TabViewItem.ContentTemplate>
|
|
||||||
<DataTemplate DataType="vm:EndpointItem">
|
|
||||||
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*">
|
|
||||||
<Menu Grid.Row="0"
|
|
||||||
Grid.Column="0">
|
|
||||||
<MenuItem Header="_New tab">
|
|
||||||
<MenuItem Header="LCU"
|
|
||||||
Command="{Binding #EndpointsTab.((vm:EndpointsTabViewModel)DataContext).AddEndpointCommand}"
|
|
||||||
CommandParameter="{x:Static vm:Tab.LCU}"/>
|
|
||||||
<MenuItem Header="Game Client"
|
|
||||||
Command="{Binding #EndpointsTab.((vm:EndpointsTabViewModel)DataContext).AddEndpointCommand}"
|
|
||||||
CommandParameter="{x:Static vm:Tab.GameClient}"/>
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
<Separator Grid.Row="1"
|
|
||||||
Grid.Column="0"/>
|
|
||||||
<ContentControl Grid.Row="2"
|
|
||||||
Grid.Column="0"
|
|
||||||
Content="{Binding Content}"/>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ui:TabViewItem.ContentTemplate>
|
|
||||||
</ui:TabViewItem>
|
|
||||||
</DataTemplate>
|
|
||||||
</ui:TabView.TabItemTemplate>
|
|
||||||
</ui:TabView>
|
|
||||||
</Grid>
|
|
||||||
</controls:BusyArea>
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using Needlework.Net.ViewModels.Pages.Endpoints;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages.Endpoints;
|
|
||||||
|
|
||||||
public partial class EndpointsTabView : UserControl
|
|
||||||
{
|
|
||||||
public EndpointsTabView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TabView_TabCloseRequested(FluentAvalonia.UI.Controls.TabView sender, FluentAvalonia.UI.Controls.TabViewTabCloseRequestedEventArgs args)
|
|
||||||
{
|
|
||||||
if (args.Tab.Content is EndpointItem item && sender.TabItems is IList tabItems)
|
|
||||||
{
|
|
||||||
if (tabItems.Count > 1)
|
|
||||||
{
|
|
||||||
tabItems.Remove(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,58 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
|
Name="EndpointsTab"
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
xmlns:controls="using:Needlework.Net.Controls"
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
Name="EndpointsControl"
|
|
||||||
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsView"
|
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsView"
|
||||||
x:DataType="vm:EndpointsViewModel">
|
x:DataType="vm:EndpointsViewModel">
|
||||||
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*">
|
<controls:BusyArea IsBusy="{Binding IsBusy}"
|
||||||
<TextBox Watermark="Search" Margin="0 4" Text="{Binding Search}" Grid.Row="1" Grid.Column="0"/>
|
BusyText="Loading...">
|
||||||
<ScrollViewer Grid.Row="2" Grid.Column="0">
|
<Grid>
|
||||||
<ItemsRepeater ItemsSource="{Binding Query}">
|
<ui:TabView TabItems="{Binding Endpoints}"
|
||||||
<ItemsRepeater.ItemTemplate>
|
AddTabButtonCommand="{Binding AddEndpointCommand}"
|
||||||
<DataTemplate>
|
AddTabButtonCommandParameter="{x:Static vm:Tab.LCU}"
|
||||||
<Button Command="{Binding #EndpointsControl.((vm:EndpointsViewModel)DataContext).OpenEndpointCommand}"
|
TabCloseRequested="TabView_TabCloseRequested">
|
||||||
VerticalAlignment="Stretch"
|
<!--Need to override Tab header for Mica theme...-->
|
||||||
HorizontalAlignment="Stretch"
|
<ui:TabView.Resources>
|
||||||
HorizontalContentAlignment="Left"
|
<ResourceDictionary>
|
||||||
CommandParameter="{Binding}"
|
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{DynamicResource ControlFillColorTransparent}"/>
|
||||||
Content="{Binding}"
|
</ResourceDictionary>
|
||||||
Theme="{StaticResource TransparentButton}"/>
|
</ui:TabView.Resources>
|
||||||
|
<!--We need to hack this style for Mica theme since there is no way to explicity set style priority in Avalonia...-->
|
||||||
|
<ui:TabView.Styles>
|
||||||
|
<Style Selector="Grid > ContentPresenter#TabContentPresenter">
|
||||||
|
<Style.Animations>
|
||||||
|
<Animation IterationCount="1" Duration="0:0:1" FillMode="Both">
|
||||||
|
<KeyFrame Cue="0%">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ControlFillColorTransparentBrush}"/>
|
||||||
|
</KeyFrame>
|
||||||
|
<KeyFrame Cue="100%">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ControlFillColorTransparentBrush}"/>
|
||||||
|
</KeyFrame>
|
||||||
|
</Animation>
|
||||||
|
</Style.Animations>
|
||||||
|
</Style>
|
||||||
|
</ui:TabView.Styles>
|
||||||
|
<ui:TabView.TabItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:EndpointTabItemViewModel">
|
||||||
|
<ui:TabViewItem Header="{Binding Header}"
|
||||||
|
IconSource="{Binding IconSource}"
|
||||||
|
IsSelected="{Binding Selected}"
|
||||||
|
Content="{Binding Content}">
|
||||||
|
<ui:TabViewItem.ContentTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:EndpointTabItemContentViewModel">
|
||||||
|
<ContentControl Content="{Binding}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ui:TabViewItem.ContentTemplate>
|
||||||
</ItemsRepeater>
|
</ui:TabViewItem>
|
||||||
</ScrollViewer>
|
</DataTemplate>
|
||||||
|
</ui:TabView.TabItemTemplate>
|
||||||
|
</ui:TabView>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</controls:BusyArea>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Needlework.Net.ViewModels.Pages.Endpoints;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages.Endpoints;
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
@@ -8,4 +10,15 @@ public partial class EndpointsView : UserControl
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TabView_TabCloseRequested(FluentAvalonia.UI.Controls.TabView sender, FluentAvalonia.UI.Controls.TabViewTabCloseRequestedEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Tab.DataContext is EndpointTabItemViewModel item && sender.TabItems is IList tabItems)
|
||||||
|
{
|
||||||
|
if (tabItems.Count > 1)
|
||||||
|
{
|
||||||
|
tabItems.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
36
Needlework.Net/Views/Pages/Endpoints/PathOperationView.axaml
Normal file
36
Needlework.Net/Views/Pages/Endpoints/PathOperationView.axaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.Endpoints.PathOperationView"
|
||||||
|
x:DataType="vm:PathOperationViewModel">
|
||||||
|
<Grid
|
||||||
|
RowDefinitions="*"
|
||||||
|
ColumnDefinitions="auto,*">
|
||||||
|
<Grid.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuItem Header="Copy Swagger URL" Command="{Binding CopyUrlCommand}"/>
|
||||||
|
<MenuItem Header="Copy Markdown" Command="{Binding CopyMarkdownCommand}"/>
|
||||||
|
</MenuFlyout>
|
||||||
|
</Grid.ContextFlyout>
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
TextAlignment="Center"
|
||||||
|
Margin="0 0 8 0"
|
||||||
|
Text="{Binding Request.Value.Method}"
|
||||||
|
Background="{Binding Request.Value.Color}"
|
||||||
|
FontSize="8"
|
||||||
|
Width="50"
|
||||||
|
Padding="10 2 10 2"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"/>
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding Path}"
|
||||||
|
FontSize="11"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"/>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -2,9 +2,9 @@ using Avalonia.Controls;
|
|||||||
|
|
||||||
namespace Needlework.Net.Views.Pages.Endpoints;
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
public partial class EndpointsNavigationView : UserControl
|
public partial class PathOperationView : UserControl
|
||||||
{
|
{
|
||||||
public EndpointsNavigationView()
|
public PathOperationView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
xmlns:controls="using:Needlework.Net.Controls"
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointView"
|
x:Class="Needlework.Net.Views.Pages.Endpoints.PluginView"
|
||||||
x:DataType="vm:EndpointViewModel">
|
x:DataType="vm:PluginViewModel">
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="DataGrid">
|
<Style Selector="DataGrid">
|
||||||
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ControlElevationBorderBrush}"/>
|
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ControlElevationBorderBrush}"/>
|
||||||
@@ -51,34 +51,8 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0">
|
Grid.Column="0">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="vm:PathOperationViewModel">
|
||||||
<Grid
|
<ContentControl Content="{Binding}"/>
|
||||||
RowDefinitions="*"
|
|
||||||
ColumnDefinitions="auto,*">
|
|
||||||
<Grid.ContextFlyout>
|
|
||||||
<MenuFlyout>
|
|
||||||
<MenuItem Header="Copy Swagger URL" Command="{Binding CopyUrlCommand}"/>
|
|
||||||
<MenuItem Header="Copy Markdown" Command="{Binding CopyMarkdownCommand}"/>
|
|
||||||
</MenuFlyout>
|
|
||||||
</Grid.ContextFlyout>
|
|
||||||
<TextBlock
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
TextAlignment="Center"
|
|
||||||
Margin="0 0 8 0"
|
|
||||||
Text="{Binding Request.Value.Method}"
|
|
||||||
Background="{Binding Request.Value.Color}"
|
|
||||||
FontSize="8"
|
|
||||||
Width="50"
|
|
||||||
Padding="10 2 10 2"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="0"/>
|
|
||||||
<TextBlock
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{Binding Path}"
|
|
||||||
FontSize="11"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="1"/>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
@@ -119,10 +93,10 @@
|
|||||||
<TabControl>
|
<TabControl>
|
||||||
<TabItem Header="Params">
|
<TabItem Header="Params">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel IsVisible="{Binding SelectedPathOperation, Converter={StaticResource NullBoolConverter}}">
|
<StackPanel IsVisible="{Binding SelectedPathOperation, Converter={StaticResource NullableToVisibilityConverter}}">
|
||||||
<controls:Card
|
<controls:Card
|
||||||
Margin="0 4"
|
Margin="0 4"
|
||||||
IsVisible="{Binding SelectedPathOperation.Operation.PathParameters, Converter={StaticResource EnumerableBoolConverter}}">
|
IsVisible="{Binding SelectedPathOperation.Operation.PathParameters, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontSize="14"
|
<TextBlock FontSize="14"
|
||||||
FontWeight="DemiBold">Path Parameters</TextBlock>
|
FontWeight="DemiBold">Path Parameters</TextBlock>
|
||||||
@@ -147,7 +121,7 @@
|
|||||||
</controls:Card>
|
</controls:Card>
|
||||||
<controls:Card
|
<controls:Card
|
||||||
Margin="0 4"
|
Margin="0 4"
|
||||||
IsVisible="{Binding SelectedPathOperation.Operation.QueryParameters, Converter={StaticResource EnumerableBoolConverter}}">
|
IsVisible="{Binding SelectedPathOperation.Operation.QueryParameters, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontSize="14"
|
<TextBlock FontSize="14"
|
||||||
FontWeight="DemiBold">Query Parameters</TextBlock>
|
FontWeight="DemiBold">Query Parameters</TextBlock>
|
||||||
@@ -176,6 +150,7 @@
|
|||||||
<TabItem Header="Body">
|
<TabItem Header="Body">
|
||||||
<avalonEdit:TextEditor
|
<avalonEdit:TextEditor
|
||||||
Name="EndpointRequestEditor"
|
Name="EndpointRequestEditor"
|
||||||
|
Document="{Binding SelectedPathOperation.Request.Value.RequestDocument}"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
Text=""
|
Text=""
|
||||||
@@ -224,40 +199,22 @@
|
|||||||
<TabItem Header="Schemas">
|
<TabItem Header="Schemas">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<controls:Card Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestBodyType, Converter={StaticResource NullBoolConverter}}">
|
<controls:Card Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestBodyType, Converter={StaticResource NullableToVisibilityConverter}}">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<Run Text="Request body: " FontWeight="DemiBold" FontSize="12"/>
|
<Run Text="Request body: " FontWeight="DemiBold" FontSize="12"/>
|
||||||
<Run Text="{Binding SelectedPathOperation.Operation.RequestBodyType}" FontSize="12"/>
|
<Run Text="{Binding SelectedPathOperation.Operation.RequestBodyType}" FontSize="12"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</controls:Card>
|
</controls:Card>
|
||||||
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestClasses, Converter={StaticResource EnumerableBoolConverter}}">
|
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestClasses, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontSize="14" FontWeight="DemiBold" Margin="0 0 0 4">Request Classes</TextBlock>
|
<TextBlock FontSize="14" FontWeight="DemiBold" Margin="0 0 0 4">Request Classes</TextBlock>
|
||||||
<ItemsRepeater ItemsSource="{Binding SelectedPathOperation.Operation.RequestClasses}">
|
<ItemsControl ItemsSource="{Binding SelectedPathOperation.Operation.RequestClasses}">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="vm:PropertyClassViewModel">
|
||||||
<StackPanel Margin="0 4 0 8">
|
<ContentControl Content="{Binding}"/>
|
||||||
<TextBlock FontSize="12" FontWeight="DemiBold" Text="{Binding Id}"/>
|
|
||||||
<controls:Card IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableBoolConverter}}">
|
|
||||||
<DataGrid
|
|
||||||
ItemsSource="{Binding PropertyFields}"
|
|
||||||
AutoGenerateColumns="True"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal">
|
|
||||||
</DataGrid>
|
|
||||||
</controls:Card>
|
|
||||||
<controls:Card Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableBoolConverter}}">
|
|
||||||
<DataGrid
|
|
||||||
ItemsSource="{Binding PropertyEnums}"
|
|
||||||
AutoGenerateColumns="True"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal">
|
|
||||||
</DataGrid>
|
|
||||||
</controls:Card>
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<controls:Card Margin="0 4">
|
<controls:Card Margin="0 4">
|
||||||
@@ -266,34 +223,17 @@
|
|||||||
<Run Text="{Binding SelectedPathOperation.Operation.ReturnType}" FontSize="12"/>
|
<Run Text="{Binding SelectedPathOperation.Operation.ReturnType}" FontSize="12"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</controls:Card>
|
</controls:Card>
|
||||||
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.ResponseClasses, Converter={StaticResource EnumerableBoolConverter}}">
|
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.ResponseClasses, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontSize="14" FontWeight="DemiBold">Response Classes</TextBlock>
|
<TextBlock FontSize="14" FontWeight="DemiBold">Response Classes</TextBlock>
|
||||||
<ItemsRepeater ItemsSource="{Binding SelectedPathOperation.Operation.ResponseClasses}">
|
<ItemsControl ItemsSource="{Binding SelectedPathOperation.Operation.ResponseClasses}">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="vm:PropertyClassViewModel">
|
||||||
<StackPanel Margin="0 4 0 8">
|
<ContentControl Content="{Binding}"
|
||||||
<TextBlock FontSize="12" FontWeight="DemiBold" Text="{Binding Id}" Margin="0 0 0 4"/>
|
Margin="0 4 0 8"/>
|
||||||
<controls:Card IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableBoolConverter}}">
|
|
||||||
<DataGrid
|
|
||||||
ItemsSource="{Binding PropertyFields}"
|
|
||||||
AutoGenerateColumns="True"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal">
|
|
||||||
</DataGrid>
|
|
||||||
</controls:Card>
|
|
||||||
<controls:Card Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableBoolConverter}}">
|
|
||||||
<DataGrid
|
|
||||||
ItemsSource="{Binding PropertyEnums}"
|
|
||||||
AutoGenerateColumns="True"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal">
|
|
||||||
</DataGrid>
|
|
||||||
</controls:Card>
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -320,6 +260,7 @@
|
|||||||
<TabItem Header="Preview">
|
<TabItem Header="Preview">
|
||||||
<avalonEdit:TextEditor
|
<avalonEdit:TextEditor
|
||||||
Name="EndpointResponseEditor"
|
Name="EndpointResponseEditor"
|
||||||
|
Document="{Binding SelectedPathOperation.Request.Value.ResponseDocument}"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
ShowLineNumbers="True"
|
ShowLineNumbers="True"
|
||||||
25
Needlework.Net/Views/Pages/Endpoints/PluginView.axaml.cs
Normal file
25
Needlework.Net/Views/Pages/Endpoints/PluginView.axaml.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using Needlework.Net.Extensions;
|
||||||
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class PluginView : UserControl
|
||||||
|
{
|
||||||
|
public PluginView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
EndpointRequestEditor.ApplyJsonEditorSettings();
|
||||||
|
EndpointResponseEditor.ApplyJsonEditorSettings();
|
||||||
|
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
||||||
|
{
|
||||||
|
var registryOptions = new RegistryOptions(
|
||||||
|
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Needlework.Net/Views/Pages/Endpoints/PropertyClassView.axaml
Normal file
29
Needlework.Net/Views/Pages/Endpoints/PropertyClassView.axaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
|
||||||
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.Endpoints.PropertyClassView"
|
||||||
|
x:DataType="vm:PropertyClassViewModel">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="12" FontWeight="DemiBold" Text="{Binding Id}" Margin="0 0 0 4"/>
|
||||||
|
<controls:Card IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||||
|
<DataGrid
|
||||||
|
ItemsSource="{Binding PropertyFields}"
|
||||||
|
AutoGenerateColumns="True"
|
||||||
|
IsReadOnly="True"
|
||||||
|
GridLinesVisibility="Horizontal">
|
||||||
|
</DataGrid>
|
||||||
|
</controls:Card>
|
||||||
|
<controls:Card Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||||
|
<DataGrid
|
||||||
|
ItemsSource="{Binding PropertyEnums}"
|
||||||
|
AutoGenerateColumns="True"
|
||||||
|
IsReadOnly="True"
|
||||||
|
GridLinesVisibility="Horizontal">
|
||||||
|
</DataGrid>
|
||||||
|
</controls:Card>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Endpoints;
|
||||||
|
|
||||||
|
public partial class PropertyClassView : UserControl
|
||||||
|
{
|
||||||
|
public PropertyClassView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,13 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages"
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Home"
|
||||||
xmlns:controls="using:Needlework.Net.Controls"
|
xmlns:controls="using:Needlework.Net.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
Name="HomeControl"
|
Name="HomeControl"
|
||||||
x:Class="Needlework.Net.Views.Pages.HomeView"
|
x:Class="Needlework.Net.Views.Pages.Home.HomeView"
|
||||||
x:DataType="vm:HomeViewModel">
|
x:DataType="vm:HomeViewModel">
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="Button">
|
|
||||||
<Setter Property="Command" Value="{Binding OpenUrlCommand}"/>
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGrid">
|
<Style Selector="DataGrid">
|
||||||
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ControlElevationBorderBrush}"/>
|
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ControlElevationBorderBrush}"/>
|
||||||
</Style>
|
</Style>
|
||||||
@@ -55,6 +52,11 @@
|
|||||||
Theme="{StaticResource SubtitleTextBlockStyle}"
|
Theme="{StaticResource SubtitleTextBlockStyle}"
|
||||||
Margin="0 0 0 8">Resources</TextBlock>
|
Margin="0 0 0 8">Resources</TextBlock>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||||
|
<StackPanel.Styles>
|
||||||
|
<Style Selector="Button">
|
||||||
|
<Setter Property="Command" Value="{Binding OpenUrlCommand}"/>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Styles>
|
||||||
<Button CommandParameter="https://hextechdocs.dev/tag/lcu/" Margin="4">
|
<Button CommandParameter="https://hextechdocs.dev/tag/lcu/" Margin="4">
|
||||||
Hextech Docs
|
Hextech Docs
|
||||||
</Button>
|
</Button>
|
||||||
@@ -99,26 +101,9 @@
|
|||||||
HorizontalScrollBarVisibility="Disabled">
|
HorizontalScrollBarVisibility="Disabled">
|
||||||
<ItemsRepeater ItemsSource="{Binding Libraries}">
|
<ItemsRepeater ItemsSource="{Binding Libraries}">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsRepeater.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="vm:LibraryViewModel">
|
||||||
<StackPanel Margin="0 12 0 0">
|
<ContentControl Content="{Binding}"
|
||||||
<TextBlock>
|
Margin="0 12 0 0"/>
|
||||||
<Run Text="{Binding Language}"
|
|
||||||
FontWeight="Bold"/>
|
|
||||||
<Bold> - </Bold>
|
|
||||||
<Run Text="{Binding Repo}"
|
|
||||||
FontWeight="Bold"/>
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Text="{Binding Description}"
|
|
||||||
IsVisible="{Binding Description, Converter={StaticResource NullBoolConverter}}"
|
|
||||||
TextAlignment="Left"
|
|
||||||
TextWrapping="WrapWithOverflow"
|
|
||||||
Width="350"/>
|
|
||||||
<Button Command="{Binding #HomeControl.((vm:HomeViewModel)DataContext).OpenUrlCommand}"
|
|
||||||
CommandParameter="{Binding Link}"
|
|
||||||
Margin="0 4 0 0">
|
|
||||||
<TextBlock Text="{Binding Link}"/>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsRepeater.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages;
|
namespace Needlework.Net.Views.Pages.Home;
|
||||||
|
|
||||||
public partial class HomeView : UserControl
|
public partial class HomeView : UserControl
|
||||||
{
|
{
|
||||||
27
Needlework.Net/Views/Pages/Home/LibraryView.axaml
Normal file
27
Needlework.Net/Views/Pages/Home/LibraryView.axaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Home"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.Home.LibraryView"
|
||||||
|
x:DataType="vm:LibraryViewModel">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="{Binding Library.Language}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
<Bold> - </Bold>
|
||||||
|
<Run Text="{Binding Library.Repo}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Text="{Binding Library.Description}"
|
||||||
|
IsVisible="{Binding Library.Description, Converter={StaticResource NullableToVisibilityConverter}}"
|
||||||
|
TextAlignment="Left"
|
||||||
|
TextWrapping="WrapWithOverflow"
|
||||||
|
Width="350"/>
|
||||||
|
<Button Command="{Binding OpenUrlCommand}"
|
||||||
|
Margin="0 4 0 0">
|
||||||
|
<TextBlock Text="{Binding Library.Link}"/>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
11
Needlework.Net/Views/Pages/Home/LibraryView.axaml.cs
Normal file
11
Needlework.Net/Views/Pages/Home/LibraryView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.Home;
|
||||||
|
|
||||||
|
public partial class LibraryView : UserControl
|
||||||
|
{
|
||||||
|
public LibraryView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Needlework.Net/Views/Pages/WebSocket/EventView.axaml
Normal file
22
Needlework.Net/Views/Pages/WebSocket/EventView.axaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.WebSocket"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Views.Pages.WebSocket.EventView"
|
||||||
|
x:DataType="vm:EventViewModel">
|
||||||
|
<Grid ColumnDefinitions="auto,auto,*">
|
||||||
|
<TextBlock Text="{Binding Time}"
|
||||||
|
Margin="0 0 4 0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Foreground="#8be9fd"/>
|
||||||
|
<TextBlock Text="{Binding Type}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0 0 4 0"
|
||||||
|
Foreground="#ffb86c"/>
|
||||||
|
<TextBlock Text="{Binding Uri}"
|
||||||
|
Grid.Column="2"
|
||||||
|
Foreground="#f8f8f2"/>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
11
Needlework.Net/Views/Pages/WebSocket/EventView.axaml.cs
Normal file
11
Needlework.Net/Views/Pages/WebSocket/EventView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Views.Pages.WebSocket;
|
||||||
|
|
||||||
|
public partial class EventView : UserControl
|
||||||
|
{
|
||||||
|
public EventView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,10 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Websocket"
|
xmlns:vm="using:Needlework.Net.ViewModels.Pages.WebSocket"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Views.Pages.WebsocketView"
|
x:Class="Needlework.Net.Views.Pages.WebSocket.WebSocketView"
|
||||||
x:DataType="vm:WebsocketViewModel">
|
x:DataType="vm:WebSocketViewModel">
|
||||||
<Grid RowDefinitions="*,auto,*" Margin="16">
|
<Grid RowDefinitions="*,auto,*" Margin="16">
|
||||||
<Border Grid.Row="0"
|
<Border Grid.Row="0"
|
||||||
Padding="0 0 0 8">
|
Padding="0 0 0 8">
|
||||||
@@ -55,20 +55,8 @@
|
|||||||
SelectedItem="{Binding SelectedEventLog}"
|
SelectedItem="{Binding SelectedEventLog}"
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto">
|
ScrollViewer.HorizontalScrollBarVisibility="Auto">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="vm:EventViewModel">
|
||||||
<Grid ColumnDefinitions="auto,auto,*">
|
<ContentControl Content="{Binding}"/>
|
||||||
<TextBlock Text="{Binding Time}"
|
|
||||||
Margin="0 0 4 0"
|
|
||||||
Grid.Column="0"
|
|
||||||
Foreground="#8be9fd"/>
|
|
||||||
<TextBlock Text="{Binding Type}"
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="0 0 4 0"
|
|
||||||
Foreground="#ffb86c"/>
|
|
||||||
<TextBlock Text="{Binding Uri}"
|
|
||||||
Grid.Column="2"
|
|
||||||
Foreground="#f8f8f2"/>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
@@ -79,6 +67,7 @@
|
|||||||
Padding="0 8 0 0">
|
Padding="0 8 0 0">
|
||||||
<avaloniaEdit:TextEditor
|
<avaloniaEdit:TextEditor
|
||||||
Name="ResponseEditor"
|
Name="ResponseEditor"
|
||||||
|
Document="{Binding Document}"
|
||||||
ShowLineNumbers="True"
|
ShowLineNumbers="True"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
@@ -2,46 +2,33 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using AvaloniaEdit;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
|
||||||
using Needlework.Net.Extensions;
|
using Needlework.Net.Extensions;
|
||||||
using Needlework.Net.Messages;
|
using Needlework.Net.ViewModels.Pages.WebSocket;
|
||||||
using Needlework.Net.ViewModels.Pages.Websocket;
|
|
||||||
using System;
|
using System;
|
||||||
using TextMateSharp.Grammars;
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
namespace Needlework.Net.Views.Pages;
|
namespace Needlework.Net.Views.Pages.WebSocket;
|
||||||
|
|
||||||
public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMessage>
|
public partial class WebSocketView : UserControl
|
||||||
{
|
{
|
||||||
private TextEditor? _responseEditor;
|
public WebSocketViewModel? _viewModel;
|
||||||
public WebsocketViewModel? _viewModel;
|
|
||||||
private ListBox? _viewer;
|
private ListBox? _viewer;
|
||||||
|
|
||||||
public WebsocketView()
|
public WebSocketView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
|
||||||
|
|
||||||
public void Receive(ResponseUpdatedMessage message)
|
ResponseEditor.ApplyJsonEditorSettings();
|
||||||
{
|
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
||||||
_responseEditor!.Text = message.Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|
||||||
_viewModel = (WebsocketViewModel)DataContext!;
|
_viewModel = (WebSocketViewModel)DataContext!;
|
||||||
_viewer = this.FindControl<ListBox>("EventViewer");
|
_viewer = this.FindControl<ListBox>("EventViewer");
|
||||||
_viewModel.EventLog.CollectionChanged += EventLog_CollectionChanged; ;
|
_viewModel.EventLog.CollectionChanged += EventLog_CollectionChanged;
|
||||||
|
|
||||||
_responseEditor = this.FindControl<TextEditor>("ResponseEditor");
|
|
||||||
_responseEditor?.ApplyJsonEditorSettings();
|
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Register(this, nameof(WebsocketViewModel));
|
|
||||||
|
|
||||||
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EventLog_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
private void EventLog_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||||
@@ -66,7 +53,6 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
|
|||||||
|
|
||||||
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
||||||
{
|
{
|
||||||
|
|
||||||
var registryOptions = new RegistryOptions(
|
var registryOptions = new RegistryOptions(
|
||||||
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user