18 Commits

Author SHA1 Message Date
BlossomiShymae
7288c471a4 Bump version, update GrrrLCU 2024-08-20 06:09:19 -05:00
BlossomiShymae
7faedcf039 Update app preview 2024-08-19 06:45:37 -05:00
BlossomiShymae
641d230647 Bump version 2024-08-19 05:15:40 -05:00
BlossomiShymae
d53c24c57f Update event viewer to have colored text 2024-08-19 05:15:14 -05:00
BlossomiShymae
a24a72b3b2 Update about page 2024-08-19 04:40:36 -05:00
BlossomiShymae
2c88ae44a2 Add unhandled exception logging 2024-08-19 02:58:36 -05:00
BlossomiShymae
f0294b3042 Use dialog instead of window for oopsies 2024-08-18 22:10:04 -05:00
BlossomiShymae
d26180dce5 Refactor folder stucture 2024-08-18 20:08:25 -05:00
BlossomiShymae
baf189e6a9 Refactor workspace name 2024-08-18 19:03:47 -05:00
BlossomiShymae
88149d1458 Bump version 2024-08-17 16:26:52 -05:00
BlossomiShymae
79fd79c01d Fix bug where event viewer may crash from a race condition, resolves #4 2024-08-17 16:25:43 -05:00
BlossomiShymae
7550102406 Fix bug where body template can be incorrect, resolves #5 2024-08-17 16:15:06 -05:00
BlossomiShymae
98996609a3 Bump version, fix error, complete TODOs 2024-08-16 15:04:42 -05:00
Blossomi Shymae
65464d22e3 Merge pull request #2 from AoshiW/perf
bug fix in WebsocketView(Model), optimization and others
2024-08-16 14:49:34 -05:00
AoshiW
0ca7f7869d add missing changes/feedback 2024-08-16 21:14:57 +02:00
AoshiW
af47e7c763 Merge branch 'main' into perf 2024-08-16 08:49:47 +02:00
BlossomiShymae
04058f12c1 Bump version, fix bug where whitespace in request body is removed 2024-08-16 01:30:39 -05:00
AoshiW
3a7d39971a bug fix in WebsocketView, optimization and others 2024-08-16 08:17:43 +02:00
92 changed files with 659 additions and 573 deletions

View File

@@ -20,14 +20,14 @@ jobs:
fetch-depth: 0 fetch-depth: 0
ref: release ref: release
- name: Build - name: Build
run: dotnet build Needlework.Net.Desktop -c Release run: dotnet build Needlework.Net -c Release
- name: Publish - name: Publish
run: dotnet publish Needlework.Net.Desktop -c Release -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o publish -r win-x64 --self-contained=false run: dotnet publish Needlework.Net -c Release -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o publish -r win-x64 --self-contained=false
- name: Get Version - name: Get Version
id: version id: version
shell: powershell shell: powershell
run: | run: |
$xml=[xml](Get-Content .\Needlework.Net.Desktop\Needlework.Net.Desktop.csproj) $xml=[xml](Get-Content .\Needlework.Net\Needlework.Net.csproj)
$ver=($xml.Project.PropertyGroup).AssemblyVersion $ver=($xml.Project.PropertyGroup).AssemblyVersion
$ver="VERSION=$ver" $ver="VERSION=$ver"
$ver=$ver -replace '\s','' $ver=$ver -replace '\s',''

View File

@@ -1,27 +0,0 @@
using Xunit.Abstractions;
namespace Needlework.Net.Core.Tests;
public class LcuSchemaHandlerTest
{
private readonly ITestOutputHelper _output;
internal HttpClient HttpClient { get; } = new();
public LcuSchemaHandlerTest(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public async Task PluginsTestAsync()
{
var reader = new LcuSchemaHandler(await Resources.GetOpenApiDocumentAsync(HttpClient));
var plugins = reader.Plugins.Keys.ToList();
foreach (var plugin in plugins)
_output.WriteLine($"Plugin: {plugin}");
Assert.True(plugins.Count > 0);
}
}

View File

@@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Needlework.Net.Core\Needlework.Net.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,23 +0,0 @@
using Xunit.Abstractions;
namespace Needlework.Net.Core.Tests;
public class ResourcesTest
{
private readonly ITestOutputHelper _output;
internal HttpClient HttpClient { get; } = new();
public ResourcesTest(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public async Task DocumentTestAsync()
{
var document = await Resources.GetOpenApiDocumentAsync(HttpClient);
Assert.True(document.Info.Title == "LCU SCHEMA");
}
}

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OpenApi" Version="1.6.16" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.16" />
</ItemGroup>
</Project>

View File

@@ -1,9 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Core;
namespace Needlework.Net.Desktop.Messages
{
public class DataReadyMessage(LcuSchemaHandler handler) : ValueChangedMessage<LcuSchemaHandler>(handler)
{
}
}

View File

@@ -1,9 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Core;
namespace Needlework.Net.Desktop.Messages
{
public class DataRequestMessage : RequestMessage<LcuSchemaHandler>
{
}
}

View File

@@ -1,8 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
{
public class OopsiesWindowCanceledMessage(object? data) : ValueChangedMessage<object?>(data)
{
}
}

View File

@@ -1,8 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
{
public class OopsiesWindowRequestedMessage(string text) : ValueChangedMessage<string>(text)
{
}
}

View File

@@ -1,48 +0,0 @@
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.ViewModels;
using Needlework.Net.Desktop.Views;
using System;
namespace Needlework.Net.Desktop.Services
{
public class WindowService : IRecipient<OopsiesWindowCanceledMessage>
{
public IServiceProvider ServiceProvider { get; }
public OopsiesWindow? OopsiesWindow { get; set; }
public WindowService(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
WeakReferenceMessenger.Default.Register<OopsiesWindowCanceledMessage>(this);
}
public void ShowOopsiesWindow(string text)
{
if (OopsiesWindow != null) OopsiesWindow!.Close();
var window = new OopsiesWindow();
window.DataContext = new OopsiesWindowViewModel(text);
window.Show(App.MainWindow!);
window.Closed += OnOopsiesWindowClosed;
OopsiesWindow = window;
}
public void OnOopsiesWindowClosed(object? sender, EventArgs e)
{
if (sender == null) return;
var window = (OopsiesWindow)sender;
window.DataContext = null;
window.Closed -= OnOopsiesWindowClosed;
OopsiesWindow = null;
}
public void Receive(OopsiesWindowCanceledMessage message)
{
if (OopsiesWindow is OopsiesWindow window) window.Close();
}
}
}

View File

@@ -1,9 +0,0 @@
namespace Needlework.Net.Desktop.ViewModels
{
public class AboutViewModel : PageBase
{
public AboutViewModel() : base("About", "info-circle")
{
}
}
}

View File

@@ -1,29 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages;
using System.Diagnostics;
using System.IO;
namespace Needlework.Net.Desktop.ViewModels
{
public partial class OopsiesWindowViewModel(string text) : ObservableObject
{
public string Text { get; } = text;
[RelayCommand]
private void OpenDefaultEditor()
{
var temp = Path.GetTempFileName().Replace(".tmp", ".json");
File.WriteAllText(temp, Text);
Process.Start("explorer", "\"" + temp + "\"");
CloseDialog();
}
[RelayCommand]
private void CloseDialog()
{
WeakReferenceMessenger.Default.Send(new OopsiesWindowCanceledMessage(null));
}
}
}

View File

@@ -1,39 +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.Desktop.ViewModels"
xmlns:controls="using:Needlework.Net.Desktop.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Desktop.Views.AboutView"
x:DataType="vm:AboutViewModel">
<Grid Margin="8"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<WrapPanel Orientation="Horizontal">
<controls:Card Margin="8">
<Image Source="/Assets/about.png"
RenderOptions.BitmapInterpolationMode="MediumQuality"
Width="200"
Height="200"/>
</controls:Card>
<StackPanel Margin="8 0 0 0">
<controls:Card Width="400" Margin="8">
<StackPanel>
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Blossomi Shymae</TextBlock>
</StackPanel>
</controls:Card>
<controls:Card Width="400" Margin="8">
<StackPanel >
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">About</TextBlock>
<TextBlock TextWrapping="Wrap">
Needlework.Net is the .NET rewrite of Needlework. This tool was made to help others with LCU development. Feel free to ask any questions
or help contribute to the project! Made with love. 💜
</TextBlock>
</StackPanel>
</controls:Card>
</StackPanel>
</WrapPanel>
</Grid>
</UserControl>

View File

@@ -1,69 +0,0 @@
<Window
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.Desktop.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Desktop.Views.OopsiesWindow"
x:DataType="vm:OopsiesWindowViewModel"
Title="Needlework.Net - Oopsies"
WindowStartupLocation="CenterOwner"
Width="560"
Height="200">
<Grid RowDefinitions="auto,*">
<Grid ColumnDefinitions="auto,auto,*,auto"
Background="Transparent"
Height="40"
Grid.Row="0">
<Image Margin="12 4"
IsHitTestVisible="False"
Source="/Assets/app.png"
Width="18"
Height="18"
DockPanel.Dock="Left"
Grid.Column="0"/>
<TextBlock FontSize="12"
IsHitTestVisible="False"
VerticalAlignment="Center"
Grid.Column="1">
Needlework.Net - Oopsies
</TextBlock>
</Grid>
<Grid RowDefinitions="auto,auto,auto"
ColumnDefinitions="auto,auto"
Grid.Row="1"
Grid.Column="0"
Margin="8"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2">
This response is too large for Needlework.Net to handle for performance reasons.
</TextBlock>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0 0 0 12">
It can be viewed in an external editor or viewer.
</TextBlock>
<Button Command="{Binding OpenDefaultEditorCommand}"
HorizontalAlignment="Stretch"
Grid.Row="2"
Grid.Column="0"
Margin="0 0 8 0">
Open
</Button>
<Button Command="{Binding CloseDialogCommand}"
HorizontalAlignment="Stretch"
Grid.Row="2"
Grid.Column="1"
Margin="8 0 0 0">
Cancel
</Button>
</Grid>
</Grid>
</Window>

View File

@@ -1,13 +0,0 @@
using FluentAvalonia.UI.Windowing;
namespace Needlework.Net.Desktop.Views;
public partial class OopsiesWindow : AppWindow
{
public OopsiesWindow()
{
InitializeComponent();
TitleBar.ExtendsContentIntoTitleBar = true;
}
}

View File

@@ -3,11 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59 VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Needlework.Net.Core", "Needlework.Net.Core\Needlework.Net.Core.csproj", "{B14E1B39-3C5A-400F-8148-CC3A4833CBC4}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Needlework.Net", "Needlework.Net\Needlework.Net.csproj", "{7388B579-2DC0-46D6-957A-6683D0FCF5D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Needlework.Net.Desktop", "Needlework.Net.Desktop\Needlework.Net.Desktop.csproj", "{7388B579-2DC0-46D6-957A-6683D0FCF5D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Needlework.Net.Core.Tests", "Needlework.Net.Core.Tests\Needlework.Net.Core.Tests.csproj", "{0E08542E-6E3F-4825-9F9C-7D6275D6AEC5}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -18,17 +14,9 @@ Global
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B14E1B39-3C5A-400F-8148-CC3A4833CBC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B14E1B39-3C5A-400F-8148-CC3A4833CBC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B14E1B39-3C5A-400F-8148-CC3A4833CBC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B14E1B39-3C5A-400F-8148-CC3A4833CBC4}.Release|Any CPU.Build.0 = Release|Any CPU
{7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Release|Any CPU.Build.0 = Release|Any CPU {7388B579-2DC0-46D6-957A-6683D0FCF5D3}.Release|Any CPU.Build.0 = Release|Any CPU
{0E08542E-6E3F-4825-9F9C-7D6275D6AEC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E08542E-6E3F-4825-9F9C-7D6275D6AEC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E08542E-6E3F-4825-9F9C-7D6275D6AEC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E08542E-6E3F-4825-9F9C-7D6275D6AEC5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@@ -1,8 +1,8 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Needlework.Net.Desktop.App" x:Class="Needlework.Net.App"
xmlns:local="using:Needlework.Net.Desktop" xmlns:local="using:Needlework.Net"
xmlns:converters="using:Needlework.Net.Desktop.Converters" xmlns:converters="using:Needlework.Net.Converters"
xmlns:sty="using:FluentAvalonia.Styling" xmlns:sty="using:FluentAvalonia.Styling"
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">

View File

@@ -3,12 +3,12 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Needlework.Net.Desktop.ViewModels; using Needlework.Net.ViewModels;
using Needlework.Net.Desktop.Views; using Needlework.Net.Views;
using System; using System;
using System.Text.Json; using System.Text.Json;
namespace Needlework.Net.Desktop; namespace Needlework.Net;
public partial class App(IServiceProvider serviceProvider) : Application public partial class App(IServiceProvider serviceProvider) : Application
{ {

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 938 B

After

Width:  |  Height:  |  Size: 938 B

View File

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 221 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -2,9 +2,9 @@
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:controls="using:Needlework.Net.Desktop.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.Desktop.Controls.BusyArea"> x:Class="Needlework.Net.Controls.BusyArea">
<UserControl.Styles> <UserControl.Styles>
<Style Selector="controls|BusyArea"> <Style Selector="controls|BusyArea">
<Setter Property="Template"> <Setter Property="Template">

View File

@@ -1,7 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
namespace Needlework.Net.Desktop.Controls; namespace Needlework.Net.Controls;
public partial class BusyArea : UserControl public partial class BusyArea : UserControl
{ {

View File

@@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui" <Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Needlework.Net.Desktop.Controls"> xmlns:controls="using:Needlework.Net.Controls">
<Design.PreviewWith> <Design.PreviewWith>
<controls:Card /> <controls:Card />
</Design.PreviewWith> </Design.PreviewWith>

View File

@@ -1,6 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace Needlework.Net.Desktop.Controls; namespace Needlework.Net.Controls;
public class Card : ContentControl public class Card : ContentControl
{ {

View File

@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
namespace Needlework.Net.Desktop.Converters namespace Needlework.Net.Converters
{ {
public class EnumerableBoolConverter : IValueConverter public class EnumerableBoolConverter : IValueConverter
{ {

View File

@@ -2,7 +2,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
namespace Needlework.Net.Desktop.Converters namespace Needlework.Net.Converters
{ {
public class NullBoolConverter : IValueConverter public class NullBoolConverter : IValueConverter
{ {

View File

@@ -0,0 +1,22 @@
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;
}
}
}

View File

@@ -3,7 +3,7 @@ using AvaloniaEdit;
using AvaloniaEdit.Highlighting; using AvaloniaEdit.Highlighting;
using AvaloniaEdit.Indentation.CSharp; using AvaloniaEdit.Indentation.CSharp;
namespace Needlework.Net.Desktop.Extensions namespace Needlework.Net.Extensions
{ {
public static class TextEditorExtensions public static class TextEditorExtensions
{ {

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages namespace Needlework.Net.Messages
{ {
public class ContentRequestMessage : RequestMessage<string> public class ContentRequestMessage : RequestMessage<string>
{ {

View File

@@ -0,0 +1,9 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Models;
namespace Needlework.Net.Messages
{
public class DataReadyMessage(OpenApiDocumentWrapper wrapper) : ValueChangedMessage<OpenApiDocumentWrapper>(wrapper)
{
}
}

View File

@@ -0,0 +1,9 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Models;
namespace Needlework.Net.Messages
{
public class DataRequestMessage : RequestMessage<OpenApiDocumentWrapper>
{
}
}

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages namespace Needlework.Net.Messages
{ {
public class EditorUpdateMessage(EditorUpdate editorUpdate) : ValueChangedMessage<EditorUpdate>(editorUpdate) public class EditorUpdateMessage(EditorUpdate editorUpdate) : ValueChangedMessage<EditorUpdate>(editorUpdate)
{ {

View File

@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace Needlework.Net.Desktop.Messages namespace Needlework.Net.Messages
{ {
public class HostDocumentRequestMessage : RequestMessage<OpenApiDocument> public class HostDocumentRequestMessage : RequestMessage<OpenApiDocument>
{ {

View File

@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Desktop.ViewModels; using Needlework.Net.ViewModels;
namespace Needlework.Net.Desktop.Messages namespace Needlework.Net.Messages
{ {
public class InfoBarUpdateMessage(InfoBarViewModel vm) : ValueChangedMessage<InfoBarViewModel>(vm) public class InfoBarUpdateMessage(InfoBarViewModel vm) : ValueChangedMessage<InfoBarViewModel>(vm)
{ {

View File

@@ -0,0 +1,8 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Messages
{
public class OopsiesDialogRequestedMessage(string text) : ValueChangedMessage<string>(text)
{
}
}

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages namespace Needlework.Net.Messages
{ {
public class ResponseUpdatedMessage(string data) : ValueChangedMessage<string>(data) public class ResponseUpdatedMessage(string data) : ValueChangedMessage<string>(data)
{ {

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Needlework.Net.Desktop namespace Needlework.Net.Models
{ {
public class GithubRelease public class GithubRelease
{ {

View File

@@ -1,8 +1,9 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace Needlework.Net.Core; namespace Needlework.Net.Models;
public class LcuSchemaHandler public class OpenApiDocumentWrapper
{ {
internal OpenApiDocument OpenApiDocument { get; } internal OpenApiDocument OpenApiDocument { get; }
@@ -12,7 +13,7 @@ public class LcuSchemaHandler
public List<string> Paths => [.. OpenApiDocument.Paths.Keys]; public List<string> Paths => [.. OpenApiDocument.Paths.Keys];
public LcuSchemaHandler(OpenApiDocument openApiDocument) public OpenApiDocumentWrapper(OpenApiDocument openApiDocument)
{ {
OpenApiDocument = openApiDocument; OpenApiDocument = openApiDocument;
var plugins = new SortedDictionary<string, List<PathOperation>>(); var plugins = new SortedDictionary<string, List<PathOperation>>();
@@ -68,5 +69,3 @@ public class LcuSchemaHandler
Plugins = plugins; Plugins = plugins;
} }
} }
public record PathOperation(string Method, string Path, OpenApiOperation Operation);

View File

@@ -0,0 +1,5 @@
using Microsoft.OpenApi.Models;
namespace Needlework.Net.Models;
public record PathOperation(string Method, string Path, OpenApiOperation Operation);

View File

@@ -1,7 +1,9 @@
using Microsoft.OpenApi.Models; using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Readers;
namespace Needlework.Net.Core; namespace Needlework.Net.Models;
public static class Resources public static class Resources
{ {

View File

@@ -11,8 +11,8 @@
<AvaloniaXamlIlDebuggerLaunch>False</AvaloniaXamlIlDebuggerLaunch> <AvaloniaXamlIlDebuggerLaunch>False</AvaloniaXamlIlDebuggerLaunch>
<ApplicationIcon>app.ico</ApplicationIcon> <ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyName>NeedleworkDotNet</AssemblyName> <AssemblyName>NeedleworkDotNet</AssemblyName>
<AssemblyVersion>0.4.1.0</AssemblyVersion> <AssemblyVersion>0.6.1.0</AssemblyVersion>
<FileVersion>0.4.1.0</FileVersion> <FileVersion>$(AssemblyVersion)</FileVersion>
<AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions> <AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions>
</PropertyGroup> </PropertyGroup>
@@ -27,12 +27,14 @@
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" /> <PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
<PackageReference Include="BlossomiShymae.GrrrLCU" Version="0.9.0" /> <PackageReference Include="BlossomiShymae.GrrrLCU" Version="0.11.1" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" /> <PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.10" /> <PackageReference Include="Material.Icons.Avalonia" Version="2.1.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.17" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.17" />
<PackageReference Include="Projektanker.Icons.Avalonia" Version="9.4.0" /> <PackageReference Include="Projektanker.Icons.Avalonia" Version="9.4.0" />
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0" /> <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0" />
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.62" /> <PackageReference Include="TextMateSharp.Grammars" Version="1.0.62" />
@@ -42,10 +44,6 @@
<AvaloniaResource Include="Assets\**" /> <AvaloniaResource Include="Assets\**" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Needlework.Net.Core\Needlework.Net.Core.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<UpToDateCheckInput Remove="Views\AboutView.axaml" /> <UpToDateCheckInput Remove="Views\AboutView.axaml" />
</ItemGroup> </ItemGroup>
@@ -57,12 +55,10 @@
<Compile Update="Views\EndpointView.axaml.cs"> <Compile Update="Views\EndpointView.axaml.cs">
<DependentUpon>EndpointView.axaml</DependentUpon> <DependentUpon>EndpointView.axaml</DependentUpon>
</Compile> </Compile>
<Compile Update="Views\OopsiesWindow.axaml.cs">
<DependentUpon>OopsiesWindow.axaml</DependentUpon>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Assets\Users\" />
<Folder Include="Utilities\" /> <Folder Include="Utilities\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,13 +1,14 @@
using Avalonia; using Avalonia;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Needlework.Net.Desktop.Services; using Needlework.Net.Extensions;
using Needlework.Net.Desktop.ViewModels; using Needlework.Net.Services;
using Needlework.Net.ViewModels;
using Projektanker.Icons.Avalonia; using Projektanker.Icons.Avalonia;
using Projektanker.Icons.Avalonia.FontAwesome; using Projektanker.Icons.Avalonia.FontAwesome;
using System; using System;
using System.Linq; using System.IO;
namespace Needlework.Net.Desktop; namespace Needlework.Net;
class Program class Program
{ {
@@ -15,8 +16,13 @@ class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break. // yet and stuff might break.
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp() public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += Program_UnhandledException;
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args); .StartWithClassicDesktopLifetime(args);
}
// 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()
@@ -35,17 +41,17 @@ class Program
var builder = new ServiceCollection(); var builder = new ServiceCollection();
builder.AddSingleton<MainWindowViewModel>(); builder.AddSingleton<MainWindowViewModel>();
builder.AddSingleton<WindowService>(); builder.AddSingleton<DialogService>();
// Dynamically add ViewModels builder.AddSingletonsFromAssemblies<PageBase>();
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => !p.IsAbstract && typeof(PageBase).IsAssignableFrom(p));
foreach (var type in types)
builder.AddSingleton(typeof(PageBase), type);
builder.AddHttpClient(); builder.AddHttpClient();
var services = builder.BuildServiceProvider(); var services = builder.BuildServiceProvider();
return services; return services;
} }
private static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
File.WriteAllText($"errorlog-{DateTime.Now:HHmmssfff}", e.ExceptionObject.ToString());
}
} }

View File

@@ -0,0 +1,20 @@
using FluentAvalonia.UI.Controls;
using System;
using System.Threading.Tasks;
namespace Needlework.Net.Services
{
public class DialogService
{
public async Task<ContentDialogResult> ShowAsync<T>(object data)
where T : IDialog, IDisposable
{
T dialog = Activator.CreateInstance<T>();
var result = await dialog.ShowAsync(data);
dialog.Dispose();
return result;
}
}
}

View File

@@ -0,0 +1,10 @@
using FluentAvalonia.UI.Controls;
using System.Threading.Tasks;
namespace Needlework.Net.Services
{
public interface IDialog
{
public Task<ContentDialogResult> ShowAsync(object data);
}
}

View File

@@ -3,7 +3,7 @@ using Avalonia.Controls.Templates;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
namespace Needlework.Net.Desktop namespace Needlework.Net
{ {
public class ViewLocator : IDataTemplate public class ViewLocator : IDataTemplate
{ {

View File

@@ -0,0 +1,26 @@
using CommunityToolkit.Mvvm.Input;
using System.Diagnostics;
using System.Net.Http;
namespace Needlework.Net.ViewModels
{
public partial class AboutViewModel : PageBase
{
public HttpClient HttpClient { get; }
public AboutViewModel(HttpClient httpClient) : base("About", "info-circle")
{
HttpClient = httpClient;
}
[RelayCommand]
private void OpenUrl(string url)
{
var process = new Process()
{
StartInfo = new ProcessStartInfo(url) { UseShellExecute = true }
};
process.Start();
}
}
}

View File

@@ -3,23 +3,21 @@ using BlossomiShymae.GrrrLCU;
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 Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using Needlework.Net.Desktop.Services;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class ConsoleViewModel : PageBase, IRecipient<DataReadyMessage> public partial class ConsoleViewModel : PageBase, IRecipient<DataReadyMessage>
{ {
public IAvaloniaReadOnlyList<string> RequestMethods { get; } = new AvaloniaList<string>(["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"]); 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 bool _isBusy = true;
[ObservableProperty] private bool _isRequestBusy = false; [ObservableProperty] private bool _isRequestBusy = false;
[ObservableProperty] private IAvaloniaReadOnlyList<string> _requestPaths = new AvaloniaList<string>();
[ObservableProperty] private string? _requestMethodSelected = "GET"; [ObservableProperty] private string? _requestMethodSelected = "GET";
[ObservableProperty] private string? _requestPath = null; [ObservableProperty] private string? _requestPath = null;
[ObservableProperty] private string? _requestBody = null; [ObservableProperty] private string? _requestBody = null;
@@ -27,12 +25,8 @@ namespace Needlework.Net.Desktop.ViewModels
[ObservableProperty] private string? _responseStatus = null; [ObservableProperty] private string? _responseStatus = null;
[ObservableProperty] private string? _responseAuthorization = null; [ObservableProperty] private string? _responseAuthorization = null;
public WindowService WindowService { get; } public ConsoleViewModel() : base("Console", "terminal", -200)
public ConsoleViewModel(WindowService windowService) : base("Console", "terminal", -200)
{ {
WindowService = windowService;
WeakReferenceMessenger.Default.Register<DataReadyMessage>(this); WeakReferenceMessenger.Default.Register<DataReadyMessage>(this);
} }
@@ -59,15 +53,15 @@ namespace Needlework.Net.Desktop.ViewModels
var processInfo = Connector.GetProcessInfo(); var processInfo = Connector.GetProcessInfo();
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "ConsoleRequestEditor").Response; var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "ConsoleRequestEditor").Response;
var content = new StringContent(Regex.Replace(requestBody, @"\s+", ""), new System.Net.Http.Headers.MediaTypeHeaderValue("application/json")); var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
var response = await Connector.SendAsync(method, RequestPath, content) ?? throw new Exception("Response is null."); var response = await Connector.SendAsync(method, RequestPath, content);
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken); var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
var body = await response.Content.ReadAsStringAsync(); var responseBody = await response.Content.ReadAsByteArrayAsync();
body = !string.IsNullOrEmpty(body) ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(body), App.JsonSerializerOptions) : string.Empty; var body = responseBody.Length > 0 ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), App.JsonSerializerOptions) : string.Empty;
if (body.Length >= App.MaxCharacters) if (body.Length >= App.MaxCharacters)
{ {
WindowService.ShowOopsiesWindow(body); WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(body));
WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(string.Empty), nameof(ConsoleViewModel)); WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(string.Empty), nameof(ConsoleViewModel));
} }
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(body), nameof(ConsoleViewModel)); else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(body), nameof(ConsoleViewModel));
@@ -94,7 +88,8 @@ namespace Needlework.Net.Desktop.ViewModels
{ {
Avalonia.Threading.Dispatcher.UIThread.Invoke(() => Avalonia.Threading.Dispatcher.UIThread.Invoke(() =>
{ {
RequestPaths = new AvaloniaList<string>([.. message.Value.Paths]); RequestPaths.Clear();
RequestPaths.AddRange(message.Value.Paths);
IsBusy = false; IsBusy = false;
}); });
} }

View File

@@ -1,10 +1,11 @@
using Avalonia.Collections; using Avalonia.Collections;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using System;
using System.Linq; using System.Linq;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class EndpointViewModel : ObservableObject public partial class EndpointViewModel : ObservableObject
{ {
@@ -12,11 +13,11 @@ namespace Needlework.Net.Desktop.ViewModels
public string Title => Endpoint; public string Title => Endpoint;
[ObservableProperty] private IAvaloniaReadOnlyList<PathOperationViewModel> _pathOperations; public IAvaloniaReadOnlyList<PathOperationViewModel> PathOperations { get; }
[ObservableProperty] private PathOperationViewModel? _selectedPathOperation; [ObservableProperty] private PathOperationViewModel? _selectedPathOperation;
[ObservableProperty] private string? _search; [ObservableProperty] private string? _search;
[ObservableProperty] private IAvaloniaReadOnlyList<PathOperationViewModel> _filteredPathOperations; public IAvaloniaList<PathOperationViewModel> FilteredPathOperations { get; }
public EndpointViewModel(string endpoint) public EndpointViewModel(string endpoint)
{ {
@@ -29,13 +30,14 @@ namespace Needlework.Net.Desktop.ViewModels
partial void OnSearchChanged(string? value) partial void OnSearchChanged(string? value)
{ {
FilteredPathOperations.Clear();
if (string.IsNullOrWhiteSpace(value)) if (string.IsNullOrWhiteSpace(value))
{ {
FilteredPathOperations = new AvaloniaList<PathOperationViewModel>(PathOperations); FilteredPathOperations.AddRange(PathOperations);
return; return;
} }
FilteredPathOperations.AddRange(PathOperations.Where(o => o.Path.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
FilteredPathOperations = new AvaloniaList<PathOperationViewModel>(PathOperations.Where(o => o.Path.ToLower().Contains(value.ToLower())));
} }
partial void OnSelectedPathOperationChanged(PathOperationViewModel? value) partial void OnSelectedPathOperationChanged(PathOperationViewModel? value)

View File

@@ -2,7 +2,7 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using System.Net.Http; using System.Net.Http;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class EndpointsContainerViewModel : PageBase public partial class EndpointsContainerViewModel : PageBase
{ {

View File

@@ -2,12 +2,12 @@
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 Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class EndpointsViewModel : ObservableObject, IRecipient<DataReadyMessage> public partial class EndpointsViewModel : ObservableObject, IRecipient<DataReadyMessage>
{ {
@@ -15,11 +15,11 @@ namespace Needlework.Net.Desktop.ViewModels
public string Title => "Endpoints"; public string Title => "Endpoints";
public Action<ObservableObject> OnClicked; public Action<ObservableObject> OnClicked;
public IAvaloniaList<string> Plugins { get; } = new AvaloniaList<string>();
public IAvaloniaList<string> Query { get; } = new AvaloniaList<string>();
[ObservableProperty] private IAvaloniaReadOnlyList<string> _plugins = new AvaloniaList<string>();
[ObservableProperty] private bool _isBusy = true; [ObservableProperty] private bool _isBusy = true;
[ObservableProperty] private string _search = string.Empty; [ObservableProperty] private string _search = string.Empty;
[ObservableProperty] private IAvaloniaReadOnlyList<string> _query = new AvaloniaList<string>();
[ObservableProperty] private string? _selectedQuery = string.Empty; [ObservableProperty] private string? _selectedQuery = string.Empty;
public EndpointsViewModel(HttpClient httpClient, Action<ObservableObject> onClicked) public EndpointsViewModel(HttpClient httpClient, Action<ObservableObject> onClicked)
@@ -33,16 +33,19 @@ namespace Needlework.Net.Desktop.ViewModels
public void Receive(DataReadyMessage message) public void Receive(DataReadyMessage message)
{ {
IsBusy = false; IsBusy = false;
Plugins = new AvaloniaList<string>([.. message.Value.Plugins.Keys]); Plugins.Clear();
Query = new AvaloniaList<string>([.. Plugins]); Plugins.AddRange(message.Value.Plugins.Keys);
Query.Clear();
Query.AddRange(Plugins);
} }
partial void OnSearchChanged(string value) partial void OnSearchChanged(string value)
{ {
Query.Clear();
if (!string.IsNullOrEmpty(Search)) if (!string.IsNullOrEmpty(Search))
Query = new AvaloniaList<string>(Plugins.Where(x => x.Contains(value))); Query.AddRange(Plugins.Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
else else
Query = Plugins; Query.AddRange(Plugins);
} }
[RelayCommand] [RelayCommand]

View File

@@ -0,0 +1,22 @@
using BlossomiShymae.GrrrLCU;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
namespace Needlework.Net.ViewModels
{
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)
{
Time = $"{DateTime.Now:HH:mm:ss.fff}";
Type = eventData?.EventType.ToUpper() ?? string.Empty;
Uri = eventData?.Uri ?? string.Empty;
}
}
}

View File

@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using System.Diagnostics; using System.Diagnostics;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class HomeViewModel : PageBase public partial class HomeViewModel : PageBase
{ {

View File

@@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using System; using System;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class InfoBarViewModel : ObservableObject public partial class InfoBarViewModel : ObservableObject
{ {

View File

@@ -4,9 +4,10 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Needlework.Net.Core; using Needlework.Net.Messages;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Models;
using Needlework.Net.Desktop.Services; using Needlework.Net.Services;
using Needlework.Net.Views;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -18,9 +19,10 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class MainWindowViewModel : ObservableObject, IRecipient<DataRequestMessage>, IRecipient<HostDocumentRequestMessage>, IRecipient<OopsiesWindowRequestedMessage>, IRecipient<InfoBarUpdateMessage> public partial class MainWindowViewModel
: ObservableObject, IRecipient<DataRequestMessage>, IRecipient<HostDocumentRequestMessage>, IRecipient<InfoBarUpdateMessage>, IRecipient<OopsiesDialogRequestedMessage>
{ {
public IAvaloniaReadOnlyList<NavigationViewItem> MenuItems { get; } public IAvaloniaReadOnlyList<NavigationViewItem> MenuItems { get; }
[NotifyPropertyChangedFor(nameof(CurrentPage))] [NotifyPropertyChangedFor(nameof(CurrentPage))]
@@ -31,15 +33,15 @@ namespace Needlework.Net.Desktop.ViewModels
[ObservableProperty] private bool _isUpdateShown = false; [ObservableProperty] private bool _isUpdateShown = false;
public HttpClient HttpClient { get; } public HttpClient HttpClient { get; }
public WindowService WindowService { get; } public DialogService DialogService { get; }
public LcuSchemaHandler? LcuSchemaHandler { get; set; } public OpenApiDocumentWrapper? OpenApiDocumentWrapper { get; set; }
public OpenApiDocument? HostDocument { get; set; } public OpenApiDocument? HostDocument { get; set; }
[ObservableProperty] private bool _isBusy = true; [ObservableProperty] private bool _isBusy = true;
[ObservableProperty] private ObservableCollection<InfoBarViewModel> _infoBarItems = []; [ObservableProperty] private ObservableCollection<InfoBarViewModel> _infoBarItems = [];
public MainWindowViewModel(IEnumerable<PageBase> pages, HttpClient httpClient, WindowService windowService) public MainWindowViewModel(IEnumerable<PageBase> pages, HttpClient httpClient, DialogService dialogService)
{ {
MenuItems = new AvaloniaList<NavigationViewItem>(pages MenuItems = new AvaloniaList<NavigationViewItem>(pages
.OrderBy(p => p.Index) .OrderBy(p => p.Index)
@@ -53,7 +55,7 @@ namespace Needlework.Net.Desktop.ViewModels
SelectedMenuItem = MenuItems[0]; SelectedMenuItem = MenuItems[0];
HttpClient = httpClient; HttpClient = httpClient;
WindowService = windowService; DialogService = dialogService;
WeakReferenceMessenger.Default.RegisterAll(this); WeakReferenceMessenger.Default.RegisterAll(this);
@@ -63,7 +65,7 @@ namespace Needlework.Net.Desktop.ViewModels
private void ProcessEvents(object? obj) private void ProcessEvents(object? obj)
{ {
while (true) while (!IsUpdateShown)
{ {
Task.Run(CheckLatestVersionAsync); Task.Run(CheckLatestVersionAsync);
@@ -84,7 +86,7 @@ namespace Needlework.Net.Desktop.ViewModels
var currentVersion = int.Parse(Version.Replace(".", "")); var currentVersion = int.Parse(Version.Replace(".", ""));
if (release.IsLatest(currentVersion) && !IsUpdateShown) if (release.IsLatest(currentVersion))
{ {
Avalonia.Threading.Dispatcher.UIThread.Post(async () => Avalonia.Threading.Dispatcher.UIThread.Post(async () =>
{ {
@@ -105,8 +107,8 @@ namespace Needlework.Net.Desktop.ViewModels
{ {
var document = await Resources.GetOpenApiDocumentAsync(HttpClient); var document = await Resources.GetOpenApiDocumentAsync(HttpClient);
HostDocument = document; HostDocument = document;
var handler = new LcuSchemaHandler(document); var handler = new OpenApiDocumentWrapper(document);
LcuSchemaHandler = handler; OpenApiDocumentWrapper = handler;
WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler)); WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler));
IsBusy = false; IsBusy = false;
@@ -114,7 +116,7 @@ namespace Needlework.Net.Desktop.ViewModels
public void Receive(DataRequestMessage message) public void Receive(DataRequestMessage message)
{ {
message.Reply(LcuSchemaHandler!); message.Reply(OpenApiDocumentWrapper!);
} }
public void Receive(HostDocumentRequestMessage message) public void Receive(HostDocumentRequestMessage message)
@@ -135,11 +137,6 @@ namespace Needlework.Net.Desktop.ViewModels
process.Start(); process.Start();
} }
public void Receive(OopsiesWindowRequestedMessage message)
{
WindowService.ShowOopsiesWindow(message.Value);
}
public void Receive(InfoBarUpdateMessage message) public void Receive(InfoBarUpdateMessage message)
{ {
Avalonia.Threading.Dispatcher.UIThread.Post(async () => await ShowInfoBarAsync(message.Value)); Avalonia.Threading.Dispatcher.UIThread.Post(async () => await ShowInfoBarAsync(message.Value));
@@ -151,5 +148,10 @@ namespace Needlework.Net.Desktop.ViewModels
await Task.Delay(vm.Duration); await Task.Delay(vm.Duration);
InfoBarItems.Remove(vm); InfoBarItems.Remove(vm);
} }
public void Receive(OopsiesDialogRequestedMessage message)
{
Avalonia.Threading.Dispatcher.UIThread.Invoke(async () => await DialogService.ShowAsync<OopsiesDialog>(message.Value));
}
} }
} }

View File

@@ -2,12 +2,12 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class OperationViewModel : ObservableObject public partial class OperationViewModel : ObservableObject
{ {
@@ -73,11 +73,20 @@ namespace Needlework.Net.Desktop.ViewModels
{ {
var type = template[i]; var type = template[i];
if (!type.Contains("#")) continue; if (!type.Contains("#")) continue;
if (requestClasses.Where(c => c.Id == type.Replace("#", string.Empty)).Any())
var foundClass = requestClasses.Where(c => c.Id == type.Replace("#", string.Empty));
if (foundClass.Any())
{ {
AvaloniaList<PropertyClassViewModel> classes = [.. requestClasses]; if (foundClass.First().PropertyEnums.Any())
classes.Remove(rootClass); {
template[i] = string.Join(string.Empty, CreateTemplate(classes)); template[i] = string.Join(string.Empty, CreateTemplate([.. foundClass]));
}
else
{
AvaloniaList<PropertyClassViewModel> classes = [.. requestClasses];
classes.Remove(rootClass);
template[i] = string.Join(string.Empty, CreateTemplate(classes));
}
} }
else else
{ {

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public abstract partial class PageBase(string displayName, string icon, int index = 0) : ObservableValidator public abstract partial class PageBase(string displayName, string icon, int index = 0) : ObservableValidator

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class ParameterViewModel : ObservableObject public partial class ParameterViewModel : ObservableObject
{ {

View File

@@ -3,15 +3,15 @@ using BlossomiShymae.GrrrLCU;
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 Needlework.Net.Core; using Needlework.Net.Messages;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Models;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class PathOperationViewModel : ObservableObject public partial class PathOperationViewModel : ObservableObject
{ {
@@ -32,14 +32,18 @@ namespace Needlework.Net.Desktop.ViewModels
public PathOperationViewModel(PathOperation pathOperation) public PathOperationViewModel(PathOperation pathOperation)
{ {
Method = pathOperation.Method.ToUpper(); Method = pathOperation.Method.ToUpper();
Color = new SolidColorBrush(GetColor(pathOperation.Method.ToUpper())); Color = new SolidColorBrush(GetColor(Method));
Path = pathOperation.Path; Path = pathOperation.Path;
Operation = new OperationViewModel(pathOperation.Operation); Operation = new OperationViewModel(pathOperation.Operation);
ProcessInfo = GetProcessInfo(); ProcessInfo = GetProcessInfo();
ResponsePath = ProcessInfo != null ? $"https://127.0.0.1:{ProcessInfo.AppPort}{Path}" : null; if (ProcessInfo != null)
ResponseUsername = ProcessInfo != null ? new RiotAuthentication(ProcessInfo.RemotingAuthToken).Username : null; {
ResponsePassword = ProcessInfo != null ? new RiotAuthentication(ProcessInfo.RemotingAuthToken).Password : null; ResponsePath = $"https://127.0.0.1:{ProcessInfo.AppPort}{Path}";
ResponseAuthorization = ProcessInfo != null ? $"Basic {new RiotAuthentication(ProcessInfo.RemotingAuthToken).Value}" : null; var riotAuth = new RiotAuthentication(ProcessInfo.RemotingAuthToken);
ResponseUsername = riotAuth.Username;
ResponsePassword = riotAuth.Password;
ResponseAuthorization = $"Basic {riotAuth.Value}";
}
} }
private ProcessInfo? GetProcessInfo() private ProcessInfo? GetProcessInfo()
@@ -60,7 +64,7 @@ namespace Needlework.Net.Desktop.ViewModels
{ {
IsBusy = true; IsBusy = true;
var method = Method.ToUpper() switch var method = Method switch
{ {
"GET" => HttpMethod.Get, "GET" => HttpMethod.Get,
"POST" => HttpMethod.Post, "POST" => HttpMethod.Post,
@@ -74,33 +78,35 @@ namespace Needlework.Net.Desktop.ViewModels
}; };
var processInfo = Connector.GetProcessInfo(); var processInfo = Connector.GetProcessInfo();
var path = Path; var sb = new StringBuilder(Path);
foreach (var pathParameter in Operation.PathParameters) foreach (var pathParameter in Operation.PathParameters)
{ {
path = path.Replace($"{{{pathParameter.Name}}}", pathParameter.Value); sb.Replace($"{{{pathParameter.Name}}}", pathParameter.Value);
} }
var query = ""; var firstQueryAdded = false;
foreach (var queryParameter in Operation.QueryParameters) foreach (var queryParameter in Operation.QueryParameters)
{ {
if (query.Length != 0 && !string.IsNullOrWhiteSpace(queryParameter.Value)) if (!string.IsNullOrWhiteSpace(queryParameter.Value))
query += $"&{queryParameter.Name}={Uri.EscapeDataString(queryParameter.Value)}"; {
else if (query.Length == 0 && !string.IsNullOrWhiteSpace(queryParameter.Value)) sb.Append(firstQueryAdded ? '&' : '?');
query += $"?{queryParameter.Name}={Uri.EscapeDataString(queryParameter.Value)}"; firstQueryAdded = true;
sb.Append($"{queryParameter.Name}={Uri.EscapeDataString(queryParameter.Value)}");
}
} }
var uri = $"{path}{query}"; var uri = sb.ToString();
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "EndpointRequestEditor").Response; var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "EndpointRequestEditor").Response;
var content = new StringContent(Regex.Replace(requestBody, @"\s+", ""), new System.Net.Http.Headers.MediaTypeHeaderValue("application/json")); var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
var response = await Connector.SendAsync(method, $"{uri}", content) ?? throw new Exception("Response is null."); var response = await Connector.SendAsync(method, uri, content);
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken); var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
var responseBody = await response.Content.ReadAsStringAsync(); var responseBytes = await response.Content.ReadAsByteArrayAsync();
responseBody = !string.IsNullOrEmpty(responseBody) ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), App.JsonSerializerOptions) : string.Empty; var responseBody = responseBytes.Length > 0 ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBytes), App.JsonSerializerOptions) : string.Empty;
if (responseBody.Length >= App.MaxCharacters) if (responseBody.Length >= App.MaxCharacters)
{ {
WeakReferenceMessenger.Default.Send(new OopsiesWindowRequestedMessage(responseBody)); WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(responseBody));
WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(string.Empty, "EndpointResponseEditor"))); WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(string.Empty, "EndpointResponseEditor")));
} }
else WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(responseBody, "EndpointResponseEditor"))); else WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(responseBody, "EndpointResponseEditor")));

View File

@@ -5,7 +5,7 @@ using Microsoft.OpenApi.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public class PropertyClassViewModel : ObservableObject public class PropertyClassViewModel : ObservableObject
{ {

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public class PropertyEnumViewModel public class PropertyEnumViewModel
{ {

View File

@@ -1,4 +1,4 @@
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public class PropertyFieldViewModel public class PropertyFieldViewModel
{ {

View File

@@ -2,8 +2,7 @@
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 Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using Needlework.Net.Desktop.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -13,30 +12,28 @@ using System.Text.Json;
using System.Threading; using System.Threading;
using Websocket.Client; using Websocket.Client;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.ViewModels
{ {
public partial class WebsocketViewModel : PageBase public partial class WebsocketViewModel : PageBase
{ {
[NotifyPropertyChangedFor(nameof(FilteredEventLog))] public ObservableCollection<EventViewModel> EventLog { get; } = [];
[ObservableProperty] private ObservableCollection<string> _eventLog = []; public SemaphoreSlim EventLogLock { get; } = new(1, 1);
[NotifyPropertyChangedFor(nameof(FilteredEventLog))] [NotifyPropertyChangedFor(nameof(FilteredEventLog))]
[ObservableProperty] private string _search = string.Empty; [ObservableProperty] private string _search = string.Empty;
[ObservableProperty] private bool _isAttach = true; [ObservableProperty] private bool _isAttach = true;
[ObservableProperty] private bool _isTail = false; [ObservableProperty] private bool _isTail = false;
[ObservableProperty] private string? _selectedEventLog = null; [ObservableProperty] private EventViewModel? _selectedEventLog = null;
private Dictionary<string, EventMessage> _events = []; private Dictionary<string, EventMessage> _events = [];
public WebsocketClient? Client { get; set; } public WebsocketClient? Client { get; set; }
public WindowService WindowService { get; } public IReadOnlyList<EventViewModel> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? EventLog : [.. EventLog.Where(x => x.Key.Contains(Search, StringComparison.InvariantCultureIgnoreCase))];
public List<string> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? [.. EventLog] : [.. EventLog.Where(x => x.ToLower().Contains(Search.ToLower()))]; public WebsocketViewModel() : base("Event Viewer", "plug", -100)
public WebsocketViewModel(WindowService windowService) : base("Event Viewer", "plug", -100)
{ {
WindowService = windowService; EventLog.CollectionChanged += (s, e) => OnPropertyChanged(nameof(FilteredEventLog));
var thread = new Thread(InitializeWebsocket) { IsBackground = true }; var thread = new Thread(InitializeWebsocket) { IsBackground = true };
thread.Start(); thread.Start();
} }
@@ -62,21 +59,22 @@ namespace Needlework.Net.Desktop.ViewModels
} }
} }
partial void OnSelectedEventLogChanged(EventViewModel? value)
{
if (value == null) return;
if (_events.TryGetValue(value.Key, out var message))
{
var text = JsonSerializer.Serialize(message, App.JsonSerializerOptions);
if (text.Length >= App.MaxCharacters) WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(text));
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(text), nameof(WebsocketViewModel));
}
}
[RelayCommand] [RelayCommand]
private void Clear() private void Clear()
{ {
EventLog = []; _events.Clear();
} EventLog.Clear();
partial void OnSelectedEventLogChanged(string? value)
{
if (value == null) return;
if (_events.TryGetValue(value, out var message))
{
var text = JsonSerializer.Serialize(message, App.JsonSerializerOptions);
if (text.Length >= App.MaxCharacters) WindowService.ShowOopsiesWindow(text);
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(text), nameof(WebsocketViewModel));
}
} }
private void OnReconnection(ReconnectionInfo info) private void OnReconnection(ReconnectionInfo info)
@@ -94,30 +92,34 @@ namespace Needlework.Net.Desktop.ViewModels
private void OnMessage(EventMessage message) private void OnMessage(EventMessage message)
{ {
Avalonia.Threading.Dispatcher.UIThread.Invoke(() => Avalonia.Threading.Dispatcher.UIThread.Invoke(async () =>
{ {
if (!IsAttach) return; if (!IsAttach) return;
var line = $"{DateTime.Now:HH:mm:ss.fff} {message.Data?.EventType.ToUpper()} {message.Data?.Uri}"; var line = new EventViewModel(message.Data!);
var log = EventLog.ToList();
Trace.WriteLine($"Message: {line}");
if (log.Count < 1000)
{
log.Add(line);
_events[line] = message;
}
else
{
var key = $"{log[0]}";
log.RemoveAt(0);
_events.Remove(key);
log.Add(line); await EventLogLock.WaitAsync();
_events[line] = message; try
} {
if (EventLog.Count < 1000)
{
EventLog.Add(line);
_events[line.Key] = message;
}
else
{
var _event = EventLog[0];
EventLog.RemoveAt(0);
_events.Remove(_event.Key);
EventLog = []; // This is a hack needed to update for ListBox EventLog.Add(line);
EventLog = new ObservableCollection<string>(log); _events[line.Key] = message;
}
}
finally
{
EventLogLock.Release();
}
}); });
} }
} }

View File

@@ -0,0 +1,167 @@
<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"
xmlns:controls="using:Needlework.Net.Controls"
xmlns:i="https://github.com/projektanker/icons.avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Views.AboutView"
x:DataType="vm:AboutViewModel">
<UserControl.Styles>
<Style Selector="Button">
<Setter Property="Theme" Value="{StaticResource TransparentButton}"/>
<Setter Property="Command" Value="{Binding OpenUrlCommand}"/>
</Style>
<Style Selector="i|Icon">
<Setter Property="FontSize" Value="20" />
</Style>
</UserControl.Styles>
<Grid Margin="8"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<StackPanel Spacing="8">
<Grid HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal">
<controls:Card Margin="8">
<Image Source="/Assets/Users/blossomishymae.png"
RenderOptions.BitmapInterpolationMode="MediumQuality"
Width="200"
Height="200"/>
</controls:Card>
<StackPanel Margin="8 0 0 0">
<controls:Card Width="400" Margin="8">
<StackPanel Orientation="Horizontal">
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
Margin="0 0 8 0">Blossomi Shymae</TextBlock>
<Button CommandParameter="https://github.com/BlossomiShymae">
<i:Icon Value="fa-github"/>
</Button>
</StackPanel>
</controls:Card>
<controls:Card Width="400" Margin="8">
<StackPanel >
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">About</TextBlock>
<TextBlock TextWrapping="Wrap">
Needlework.Net is the .NET rewrite of Needlework. This tool was made to help others with LCU development. Feel free to ask any questions
or help contribute to the project! Made with love. 💜
</TextBlock>
</StackPanel>
</controls:Card>
</StackPanel>
</StackPanel>
</Grid>
<Border Width="800">
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Thanks to the friends and people who made this tool possible...</TextBlock>
</Border>
<WrapPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal"
Margin="8">
<controls:Card>
<Image Source="/Assets/Users/dysolix.png"
RenderOptions.BitmapInterpolationMode="MediumQuality"
Width="100"
Height="100"/>
</controls:Card>
<StackPanel Margin="2 0 0 0">
<controls:Card Width="250" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}"
Margin="0 0 8 0">dysolix</TextBlock>
<Button CommandParameter="https://github.com/dysolix">
<i:Icon Value="fa-github"/>
</Button>
</StackPanel>
</controls:Card>
<controls:Card Width="250" Margin="2">
<StackPanel >
<TextBlock TextWrapping="Wrap">
For providing and hosting an auto-generated OpenAPI document of the LCU.
</TextBlock>
</StackPanel>
</controls:Card>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="8">
<controls:Card>
<Image Source="/Assets/Users/ray.png"
RenderOptions.BitmapInterpolationMode="MediumQuality"
Width="100"
Height="100"/>
</controls:Card>
<StackPanel Margin="2 0 0 0">
<controls:Card Width="250" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}"
Margin="0 0 8 0">Ray</TextBlock>
<Button CommandParameter="https://github.com/Hi-Ray">
<i:Icon Value="fa-github"/>
</Button>
</StackPanel>
</controls:Card>
<controls:Card Width="250" Margin="2">
<StackPanel >
<TextBlock TextWrapping="Wrap">
For guidance, advice, or providing help via HextechDocs.
</TextBlock>
</StackPanel>
</controls:Card>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="8">
<controls:Card>
<Image Source="/Assets/Users/dubble.png"
RenderOptions.BitmapInterpolationMode="MediumQuality"
Width="100"
Height="100"/>
</controls:Card>
<StackPanel Margin="4 0 0 0">
<controls:Card Width="250" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}"
Margin="0 0 8 0">dubble</TextBlock>
<Button CommandParameter="https://github.com/cuppachino">
<i:Icon Value="fa-github"/>
</Button>
</StackPanel>
</controls:Card>
<controls:Card Width="250" Margin="2">
<StackPanel >
<TextBlock TextWrapping="Wrap">
For encouraging me to publish Needlework. This project may never have seen the light of day without him.
</TextBlock>
</StackPanel>
</controls:Card>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="8">
<controls:Card>
<Image Source="/Assets/Users/community.png"
RenderOptions.BitmapInterpolationMode="MediumQuality"
Width="100"
Height="100"/>
</controls:Card>
<StackPanel Margin="4 0 0 0">
<controls:Card Width="250" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}"
Width="250"
TextWrapping="Wrap">Third Party Developer Community</TextBlock>
</StackPanel>
</controls:Card>
<controls:Card Width="250" Margin="2">
<StackPanel >
<TextBlock TextWrapping="Wrap">
For providing numerous documentation on the LCU.
</TextBlock>
</StackPanel>
</controls:Card>
</StackPanel>
</StackPanel>
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -1,6 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views; namespace Needlework.Net.Views;
public partial class AboutView : UserControl public partial class AboutView : UserControl
{ {

View File

@@ -3,10 +3,12 @@
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.Desktop.ViewModels" xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:controls="using:Needlework.Net.Desktop.Controls" xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
xmlns:vm="using:Needlework.Net.ViewModels"
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.Desktop.Views.ConsoleView" x:Class="Needlework.Net.Views.ConsoleView"
x:DataType="vm:ConsoleViewModel"> x:DataType="vm:ConsoleViewModel">
<controls:BusyArea IsBusy="{Binding IsBusy}" <controls:BusyArea IsBusy="{Binding IsBusy}"
BusyText="Loading..."> BusyText="Loading...">

View File

@@ -4,12 +4,12 @@ using Avalonia.Controls.Primitives;
using Avalonia.Styling; using Avalonia.Styling;
using AvaloniaEdit; using AvaloniaEdit;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Extensions; using Needlework.Net.Extensions;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using Needlework.Net.Desktop.ViewModels; using Needlework.Net.ViewModels;
using TextMateSharp.Grammars; using TextMateSharp.Grammars;
namespace Needlework.Net.Desktop.Views; namespace Needlework.Net.Views;
public partial class ConsoleView : UserControl, IRecipient<ResponseUpdatedMessage>, IRecipient<ContentRequestMessage> public partial class ConsoleView : UserControl, IRecipient<ResponseUpdatedMessage>, IRecipient<ContentRequestMessage>
{ {

View File

@@ -3,11 +3,11 @@
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:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:vm="using:Needlework.Net.Desktop.ViewModels" xmlns:vm="using:Needlework.Net.ViewModels"
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit" xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:controls="using:Needlework.Net.Desktop.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.Desktop.Views.EndpointView" x:Class="Needlework.Net.Views.EndpointView"
x:DataType="vm:EndpointViewModel"> x:DataType="vm:EndpointViewModel">
<UserControl.Styles> <UserControl.Styles>
<Style Selector="DataGrid"> <Style Selector="DataGrid">
@@ -55,11 +55,11 @@
<Grid <Grid
RowDefinitions="*" RowDefinitions="*"
ColumnDefinitions="auto,*"> ColumnDefinitions="auto,*">
<Button <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Flat" TextAlignment="Center"
Margin="0 0 8 0" Margin="0 0 8 0"
Content="{Binding Method}" Text="{Binding Method}"
Background="{Binding Color}" Background="{Binding Color}"
FontSize="8" FontSize="8"
Width="50" Width="50"

View File

@@ -4,12 +4,12 @@ using Avalonia.Controls.Primitives;
using Avalonia.Styling; using Avalonia.Styling;
using AvaloniaEdit; using AvaloniaEdit;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Extensions; using Needlework.Net.Extensions;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using Needlework.Net.Desktop.ViewModels; using Needlework.Net.ViewModels;
using TextMateSharp.Grammars; using TextMateSharp.Grammars;
namespace Needlework.Net.Desktop.Views; namespace Needlework.Net.Views;
public partial class EndpointView : UserControl, IRecipient<EditorUpdateMessage>, IRecipient<ContentRequestMessage> public partial class EndpointView : UserControl, IRecipient<EditorUpdateMessage>, IRecipient<ContentRequestMessage>
{ {

View File

@@ -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.Desktop.ViewModels" xmlns:vm="using:Needlework.Net.ViewModels"
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit" xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
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.Desktop.Views.EndpointsContainerView" x:Class="Needlework.Net.Views.EndpointsContainerView"
x:DataType="vm:EndpointsContainerViewModel"> x:DataType="vm:EndpointsContainerViewModel">
<Grid RowDefinitions="auto,*" <Grid RowDefinitions="auto,*"
ColumnDefinitions="*" ColumnDefinitions="*"

View File

@@ -1,6 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views; namespace Needlework.Net.Views;
public partial class EndpointsContainerView : UserControl public partial class EndpointsContainerView : UserControl
{ {

View File

@@ -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.Desktop.ViewModels" xmlns:vm="using:Needlework.Net.ViewModels"
xmlns:controls="using:Needlework.Net.Desktop.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" Name="EndpointsControl"
x:Class="Needlework.Net.Desktop.Views.EndpointsView" x:Class="Needlework.Net.Views.EndpointsView"
x:DataType="vm:EndpointsViewModel"> x:DataType="vm:EndpointsViewModel">
<controls:BusyArea IsBusy="{Binding IsBusy}" <controls:BusyArea IsBusy="{Binding IsBusy}"
BusyText="Loading..."> BusyText="Loading...">

View File

@@ -1,6 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views namespace Needlework.Net.Views
{ {
public partial class EndpointsView : UserControl public partial class EndpointsView : UserControl
{ {

View File

@@ -2,10 +2,10 @@
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.Desktop.ViewModels" xmlns:vm="using:Needlework.Net.ViewModels"
xmlns:controls="using:Needlework.Net.Desktop.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.Desktop.Views.HomeView" x:Class="Needlework.Net.Views.HomeView"
x:DataType="vm:HomeViewModel"> x:DataType="vm:HomeViewModel">
<!-- TOP LEVEL --> <!-- TOP LEVEL -->
<ScrollViewer> <ScrollViewer>

View File

@@ -1,6 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views namespace Needlework.Net.Views
{ {
public partial class HomeView : UserControl public partial class HomeView : UserControl
{ {

View File

@@ -7,14 +7,14 @@
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives" xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:i="https://github.com/projektanker/icons.avalonia" xmlns:i="https://github.com/projektanker/icons.avalonia"
xmlns:vm="using:Needlework.Net.Desktop.ViewModels" xmlns:vm="using:Needlework.Net.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Desktop.Views.MainWindow" x:Class="Needlework.Net.Views.MainWindow"
x:DataType="vm:MainWindowViewModel" x:DataType="vm:MainWindowViewModel"
Title="Needlework.Net" Title="Needlework.Net"
Icon="/Assets/app.ico" Icon="/Assets/app.ico"
Width="1280" Width="1280"
Height="720"> Height="720">
<Grid RowDefinitions="auto,*"> <Grid RowDefinitions="auto,*">
<Grid ColumnDefinitions="auto,auto,*,auto" <Grid ColumnDefinitions="auto,auto,*,auto"
Background="Transparent" Background="Transparent"
@@ -83,6 +83,7 @@
<DataTemplate> <DataTemplate>
<Border Margin="4"> <Border Margin="4">
<ui:InfoBar <ui:InfoBar
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
Title="{Binding Title}" Title="{Binding Title}"
IsOpen="{Binding IsOpen}" IsOpen="{Binding IsOpen}"
Severity="{Binding Severity}" Severity="{Binding Severity}"

View File

@@ -1,6 +1,6 @@
using FluentAvalonia.UI.Windowing; using FluentAvalonia.UI.Windowing;
namespace Needlework.Net.Desktop.Views; namespace Needlework.Net.Views;
public partial class MainWindow : AppWindow public partial class MainWindow : AppWindow
{ {

View File

@@ -0,0 +1,65 @@
using FluentAvalonia.UI.Controls;
using Needlework.Net.Services;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace Needlework.Net.Views
{
public class OopsiesDialog : IDialog, IDisposable
{
private bool _isDisposing;
private string? _text;
private ContentDialog _dialog;
public OopsiesDialog()
{
_dialog = new ContentDialog
{
PrimaryButtonText = "Open",
CloseButtonText = "Cancel",
Title = "Oopsies",
Content = "This response is too large to handle for performance reasons.\nIt can be viewed in an external editor or viewer.",
IsPrimaryButtonEnabled = true,
IsSecondaryButtonEnabled = false,
DefaultButton = ContentDialogButton.Primary
};
_dialog.PrimaryButtonClick += OnPrimaryButtonClick;
}
public async Task<ContentDialogResult> ShowAsync(object data)
{
_text = (string)data;
var result = await _dialog.ShowAsync();
return result;
}
private void OnPrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
var temp = Path.GetTempFileName().Replace(".tmp", ".json");
File.WriteAllText(temp, _text);
Process.Start("explorer", "\"" + temp + "\"");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposing)
{
if (disposing)
{
_text = null;
_dialog.PrimaryButtonClick -= OnPrimaryButtonClick;
}
_isDisposing = true;
}
}
}
}

View File

@@ -3,9 +3,9 @@
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.Desktop.ViewModels" xmlns:vm="using:Needlework.Net.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Desktop.Views.WebsocketView" x:Class="Needlework.Net.Views.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"
@@ -42,7 +42,26 @@
Name="EventViewer" Name="EventViewer"
Margin="0 8 0 0" Margin="0 8 0 0"
ItemsSource="{Binding FilteredEventLog}" ItemsSource="{Binding FilteredEventLog}"
SelectedItem="{Binding SelectedEventLog}"/> SelectedItem="{Binding SelectedEventLog}"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<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>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid> </Grid>
</Border> </Border>
<GridSplitter Grid.Row="1" ResizeDirection="Rows" Background="Gray"/> <GridSplitter Grid.Row="1" ResizeDirection="Rows" Background="Gray"/>

View File

@@ -4,16 +4,19 @@ using Avalonia.Controls.Primitives;
using Avalonia.Styling; using Avalonia.Styling;
using AvaloniaEdit; using AvaloniaEdit;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Extensions; using Needlework.Net.Extensions;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Messages;
using Needlework.Net.Desktop.ViewModels; using Needlework.Net.ViewModels;
using System;
using TextMateSharp.Grammars; using TextMateSharp.Grammars;
namespace Needlework.Net.Desktop.Views; namespace Needlework.Net.Views;
public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMessage> public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMessage>
{ {
private TextEditor? _responseEditor; private TextEditor? _responseEditor;
public WebsocketViewModel? _viewModel;
private ListBox? _viewer;
public WebsocketView() public WebsocketView()
{ {
@@ -29,9 +32,9 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
var vm = (WebsocketViewModel)DataContext!; _viewModel = (WebsocketViewModel)DataContext!;
var viewer = this.FindControl<ListBox>("EventViewer"); _viewer = this.FindControl<ListBox>("EventViewer");
viewer!.PropertyChanged += (s, e) => { if (vm.IsTail) viewer.ScrollIntoView(vm.EventLog.Count - 1); }; _viewModel.EventLog.CollectionChanged += EventLog_CollectionChanged; ;
_responseEditor = this.FindControl<TextEditor>("ResponseEditor"); _responseEditor = this.FindControl<TextEditor>("ResponseEditor");
_responseEditor?.ApplyJsonEditorSettings(); _responseEditor?.ApplyJsonEditorSettings();
@@ -41,6 +44,26 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
OnBaseThemeChanged(Application.Current!.ActualThemeVariant); OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
} }
private void EventLog_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Avalonia.Threading.Dispatcher.UIThread.Post(async () =>
{
if (_viewModel!.IsTail)
{
await _viewModel.EventLogLock.WaitAsync();
try
{
_viewer!.ScrollIntoView(_viewModel.EventLog.Count - 1);
}
catch (InvalidOperationException) { }
finally
{
_viewModel.EventLogLock.Release();
}
}
});
}
private void OnBaseThemeChanged(ThemeVariant currentTheme) private void OnBaseThemeChanged(ThemeVariant currentTheme)
{ {

View File

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 221 KiB

View File

@@ -3,7 +3,7 @@
<!-- This manifest is used on Windows only. <!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls. Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests --> For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="Needlework.Net.Desktop.Desktop"/> <assemblyIdentity version="1.0.0.0" name="Needlework.Net"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 370 KiB