Compare commits

..

33 Commits

Author SHA1 Message Date
BlossomiShymae 309ac8e8f0 Merge branch 'main' into release 2024-08-23 22:53:40 -05:00
BlossomiShymae 375285067d Bump version 2024-08-23 22:53:05 -05:00
BlossomiShymae 3ec277bdd3 Update GrrrLCU 2024-08-23 22:50:58 -05:00
BlossomiShymae c097890588 Fix issues where messages failed to register 2024-08-23 21:21:01 -05:00
BlossomiShymae 3352740733 Add busy area for sending request in endpoint 2024-08-23 20:26:08 -05:00
BlossomiShymae 48751efc28 Fix endpoints not retaining state 2024-08-23 20:03:18 -05:00
BlossomiShymae b6f713c675 Update GrrrLCU 2024-08-23 19:39:49 -05:00
BlossomiShymae 59619764c2 Improve endpoint loading times by using lazy loading 2024-08-22 20:15:13 -05:00
BlossomiShymae de6f9f64dd Fix use of GrrrLCU 2024-08-22 19:39:27 -05:00
BlossomiShymae 4eae0bd913 Update GrrrLCU 2024-08-22 19:26:11 -05:00
BlossomiShymae 943234464e Merge branch 'main' into release 2024-08-20 06:09:29 -05:00
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 8681cd0b39 Merge branch 'main' into release 2024-08-19 05:15:51 -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 e2bfac458d Merge branch 'main' into release 2024-08-17 16:26:58 -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 9ba36a5d7e Merge branch 'main' into release 2024-08-16 15:05:02 -05:00
BlossomiShymae 9c0bbc2be0 Merge branch 'main' into release 2024-08-16 01:30:48 -05:00
BlossomiShymae 0d077e351d Merge branch 'main' into release 2024-08-15 17:32:45 -05:00
BlossomiShymae 1f20c5a286 Merge branch 'main' into release 2024-08-15 17:20:14 -05:00
BlossomiShymae 41e76c5d10 Merge branch 'main' into release 2024-08-13 02:46:50 -05:00
BlossomiShymae 38ebed976d Merge branch 'main' into release 2024-08-13 02:43:55 -05:00
BlossomiShymae 6875fbaa66 Merge branch 'main' into release 2024-08-13 02:41:11 -05:00
BlossomiShymae 0ddcfb47a6 Add update checker 2024-08-13 02:37:46 -05:00
94 changed files with 715 additions and 610 deletions
+3 -3
View File
@@ -20,14 +20,14 @@ jobs:
fetch-depth: 0
ref: release
- name: Build
run: dotnet build Needlework.Net.Desktop -c Release
run: dotnet build Needlework.Net -c Release
- 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
id: version
shell: powershell
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="VERSION=$ver"
$ver=$ver -replace '\s',''
@@ -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);
}
}
@@ -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>
@@ -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");
}
}
@@ -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>
@@ -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)
{
}
}
@@ -1,9 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Core;
namespace Needlework.Net.Desktop.Messages
{
public class DataRequestMessage : RequestMessage<LcuSchemaHandler>
{
}
}
@@ -1,8 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
{
public class OopsiesWindowCanceledMessage(object? data) : ValueChangedMessage<object?>(data)
{
}
}
@@ -1,8 +0,0 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
{
public class OopsiesWindowRequestedMessage(string text) : ValueChangedMessage<string>(text)
{
}
}
@@ -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();
}
}
}
-28
View File
@@ -1,28 +0,0 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using System;
using System.ComponentModel;
namespace Needlework.Net.Desktop
{
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null) return new TextBlock { Text = "data was null" };
var name = param.GetType().FullName!
.Replace("ViewModels", "Views")
.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null) return (Control)Activator.CreateInstance(type)!;
else return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is INotifyPropertyChanged;
}
}
}
@@ -1,9 +0,0 @@
namespace Needlework.Net.Desktop.ViewModels
{
public class AboutViewModel : PageBase
{
public AboutViewModel() : base("About", "info-circle")
{
}
}
}
@@ -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));
}
}
}
@@ -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>
@@ -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>
@@ -1,13 +0,0 @@
using FluentAvalonia.UI.Windowing;
namespace Needlework.Net.Desktop.Views;
public partial class OopsiesWindow : AppWindow
{
public OopsiesWindow()
{
InitializeComponent();
TitleBar.ExtendsContentIntoTitleBar = true;
}
}
+1 -13
View File
@@ -3,11 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
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}"
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}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Needlework.Net", "Needlework.Net\Needlework.Net.csproj", "{7388B579-2DC0-46D6-957A-6683D0FCF5D3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -18,17 +14,9 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
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.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.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
EndGlobal
@@ -1,8 +1,8 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Needlework.Net.Desktop.App"
xmlns:local="using:Needlework.Net.Desktop"
xmlns:converters="using:Needlework.Net.Desktop.Converters"
x:Class="Needlework.Net.App"
xmlns:local="using:Needlework.Net"
xmlns:converters="using:Needlework.Net.Converters"
xmlns:sty="using:FluentAvalonia.Styling"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
RequestedThemeVariant="Dark">
@@ -3,12 +3,12 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
using Needlework.Net.Desktop.ViewModels;
using Needlework.Net.Desktop.Views;
using Needlework.Net.ViewModels;
using Needlework.Net.Views;
using System;
using System.Text.Json;
namespace Needlework.Net.Desktop;
namespace Needlework.Net;
public partial class App(IServiceProvider serviceProvider) : Application
{

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 938 B

After

Width:  |  Height:  |  Size: 938 B

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

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

@@ -2,9 +2,9 @@
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:controls="using:Needlework.Net.Desktop.Controls"
xmlns:controls="using:Needlework.Net.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Desktop.Controls.BusyArea">
x:Class="Needlework.Net.Controls.BusyArea">
<UserControl.Styles>
<Style Selector="controls|BusyArea">
<Setter Property="Template">
@@ -1,7 +1,7 @@
using Avalonia;
using Avalonia.Controls;
namespace Needlework.Net.Desktop.Controls;
namespace Needlework.Net.Controls;
public partial class BusyArea : UserControl
{
@@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Needlework.Net.Desktop.Controls">
xmlns:controls="using:Needlework.Net.Controls">
<Design.PreviewWith>
<controls:Card />
</Design.PreviewWith>
@@ -1,6 +1,6 @@
using Avalonia.Controls;
namespace Needlework.Net.Desktop.Controls;
namespace Needlework.Net.Controls;
public class Card : ContentControl
{
@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Needlework.Net.Desktop.Converters
namespace Needlework.Net.Converters
{
public class EnumerableBoolConverter : IValueConverter
{
@@ -2,7 +2,7 @@
using System;
using System.Globalization;
namespace Needlework.Net.Desktop.Converters
namespace Needlework.Net.Converters
{
public class NullBoolConverter : IValueConverter
{
@@ -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;
}
}
}
@@ -3,7 +3,7 @@ using AvaloniaEdit;
using AvaloniaEdit.Highlighting;
using AvaloniaEdit.Indentation.CSharp;
namespace Needlework.Net.Desktop.Extensions
namespace Needlework.Net.Extensions
{
public static class TextEditorExtensions
{
@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
namespace Needlework.Net.Messages
{
public class ContentRequestMessage : RequestMessage<string>
{
@@ -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)
{
}
}
@@ -0,0 +1,9 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Needlework.Net.Models;
namespace Needlework.Net.Messages
{
public class DataRequestMessage : RequestMessage<OpenApiDocumentWrapper>
{
}
}
@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
namespace Needlework.Net.Messages
{
public class EditorUpdateMessage(EditorUpdate editorUpdate) : ValueChangedMessage<EditorUpdate>(editorUpdate)
{
@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.OpenApi.Models;
namespace Needlework.Net.Desktop.Messages
namespace Needlework.Net.Messages
{
public class HostDocumentRequestMessage : RequestMessage<OpenApiDocument>
{
@@ -1,7 +1,7 @@
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)
{
@@ -0,0 +1,8 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Messages
{
public class OopsiesDialogRequestedMessage(string text) : ValueChangedMessage<string>(text)
{
}
}
@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Needlework.Net.Desktop.Messages
namespace Needlework.Net.Messages
{
public class ResponseUpdatedMessage(string data) : ValueChangedMessage<string>(data)
{
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace Needlework.Net.Desktop
namespace Needlework.Net.Models
{
public class GithubRelease
{
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
namespace Needlework.Net.Core;
namespace Needlework.Net.Models;
public class LcuSchemaHandler
public class OpenApiDocumentWrapper
{
internal OpenApiDocument OpenApiDocument { get; }
@@ -12,7 +13,7 @@ public class LcuSchemaHandler
public List<string> Paths => [.. OpenApiDocument.Paths.Keys];
public LcuSchemaHandler(OpenApiDocument openApiDocument)
public OpenApiDocumentWrapper(OpenApiDocument openApiDocument)
{
OpenApiDocument = openApiDocument;
var plugins = new SortedDictionary<string, List<PathOperation>>();
@@ -68,5 +69,3 @@ public class LcuSchemaHandler
Plugins = plugins;
}
}
public record PathOperation(string Method, string Path, OpenApiOperation Operation);
+5
View File
@@ -0,0 +1,5 @@
using Microsoft.OpenApi.Models;
namespace Needlework.Net.Models;
public record PathOperation(string Method, string Path, OpenApiOperation Operation);
@@ -1,7 +1,9 @@
using Microsoft.OpenApi.Models;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
namespace Needlework.Net.Core;
namespace Needlework.Net.Models;
public static class Resources
{
@@ -11,7 +11,7 @@
<AvaloniaXamlIlDebuggerLaunch>False</AvaloniaXamlIlDebuggerLaunch>
<ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyName>NeedleworkDotNet</AssemblyName>
<AssemblyVersion>0.5.0.0</AssemblyVersion>
<AssemblyVersion>0.7.0.0</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions>
</PropertyGroup>
@@ -27,12 +27,14 @@
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
<PackageReference Include="BlossomiShymae.GrrrLCU" Version="0.9.0" />
<PackageReference Include="BlossomiShymae.GrrrLCU" Version="0.13.1" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" 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.FontAwesome" Version="9.4.0" />
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.62" />
@@ -42,10 +44,6 @@
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Needlework.Net.Core\Needlework.Net.Core.csproj" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\AboutView.axaml" />
</ItemGroup>
@@ -57,12 +55,10 @@
<Compile Update="Views\EndpointView.axaml.cs">
<DependentUpon>EndpointView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\OopsiesWindow.axaml.cs">
<DependentUpon>OopsiesWindow.axaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="Assets\Users\" />
<Folder Include="Utilities\" />
</ItemGroup>
</Project>
@@ -1,13 +1,14 @@
using Avalonia;
using Microsoft.Extensions.DependencyInjection;
using Needlework.Net.Desktop.Services;
using Needlework.Net.Desktop.ViewModels;
using Needlework.Net.Extensions;
using Needlework.Net.Services;
using Needlework.Net.ViewModels;
using Projektanker.Icons.Avalonia;
using Projektanker.Icons.Avalonia.FontAwesome;
using System;
using System.Linq;
using System.IO;
namespace Needlework.Net.Desktop;
namespace Needlework.Net;
class Program
{
@@ -15,8 +16,13 @@ class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += Program_UnhandledException;
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
@@ -35,17 +41,17 @@ class Program
var builder = new ServiceCollection();
builder.AddSingleton<MainWindowViewModel>();
builder.AddSingleton<WindowService>();
// Dynamically add ViewModels
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.AddSingleton<DialogService>();
builder.AddSingletonsFromAssemblies<PageBase>();
builder.AddHttpClient();
var services = builder.BuildServiceProvider();
return services;
}
private static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
File.WriteAllText($"errorlog-{DateTime.Now:HHmmssfff}", e.ExceptionObject.ToString());
}
}
+20
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;
}
}
}
+10
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);
}
}
+40
View File
@@ -0,0 +1,40 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace Needlework.Net
{
public class ViewLocator : IDataTemplate
{
private readonly Dictionary<object, Control> _controlCache = [];
public Control Build(object? data)
{
var fullName = data?.GetType().FullName;
if (fullName is null)
{
return new TextBlock { Text = "Data is null or has no name." };
}
var name = fullName.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type is null)
{
return new TextBlock { Text = $"No View For {name}." };
}
if (!_controlCache.TryGetValue(data!, out var res))
{
res ??= (Control)Activator.CreateInstance(type)!;
_controlCache[data!] = res;
}
res.DataContext = data;
return res;
}
public bool Match(object? data) => data is INotifyPropertyChanged;
}
}
@@ -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();
}
}
}
@@ -3,14 +3,13 @@ using BlossomiShymae.GrrrLCU;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.Services;
using Needlework.Net.Messages;
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class ConsoleViewModel : PageBase, IRecipient<DataReadyMessage>
{
@@ -26,12 +25,8 @@ namespace Needlework.Net.Desktop.ViewModels
[ObservableProperty] private string? _responseStatus = null;
[ObservableProperty] private string? _responseAuthorization = null;
public WindowService WindowService { get; }
public ConsoleViewModel(WindowService windowService) : base("Console", "terminal", -200)
public ConsoleViewModel() : base("Console", "terminal", -200)
{
WindowService = windowService;
WeakReferenceMessenger.Default.Register<DataReadyMessage>(this);
}
@@ -56,17 +51,18 @@ namespace Needlework.Net.Desktop.ViewModels
_ => throw new Exception("Method is not selected."),
};
var processInfo = Connector.GetProcessInfo();
var processInfo = ProcessFinder.Get();
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "ConsoleRequestEditor").Response;
var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
var response = await Connector.SendAsync(method, RequestPath, content);
var client = Connector.GetLcuHttpClientInstance();
var response = await client.SendAsync(new(method, RequestPath) { Content = content });
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
var responseBody = await response.Content.ReadAsByteArrayAsync();
var body = responseBody.Length > 0 ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), App.JsonSerializerOptions) : string.Empty;
if (body.Length >= App.MaxCharacters)
{
WindowService.ShowOopsiesWindow(body);
WeakReferenceMessenger.Default.Send(new OopsiesDialogRequestedMessage(body));
WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(string.Empty), nameof(ConsoleViewModel));
}
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(body), nameof(ConsoleViewModel));
@@ -1,12 +1,11 @@
using Avalonia.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Messages;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class EndpointViewModel : ObservableObject
{
@@ -2,7 +2,7 @@
using CommunityToolkit.Mvvm.Input;
using System.Net.Http;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class EndpointsContainerViewModel : PageBase
{
@@ -2,12 +2,12 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Messages;
using System;
using System.Linq;
using System.Net.Http;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class EndpointsViewModel : ObservableObject, IRecipient<DataReadyMessage>
{
@@ -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;
}
}
}
@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Input;
using System.Diagnostics;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class HomeViewModel : PageBase
{
@@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls;
using System;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class InfoBarViewModel : ObservableObject
{
@@ -4,9 +4,10 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using FluentAvalonia.UI.Controls;
using Microsoft.OpenApi.Models;
using Needlework.Net.Core;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.Services;
using Needlework.Net.Messages;
using Needlework.Net.Models;
using Needlework.Net.Services;
using Needlework.Net.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -18,9 +19,10 @@ using System.Reflection;
using System.Threading;
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; }
[NotifyPropertyChangedFor(nameof(CurrentPage))]
@@ -31,15 +33,15 @@ namespace Needlework.Net.Desktop.ViewModels
[ObservableProperty] private bool _isUpdateShown = false;
public HttpClient HttpClient { get; }
public WindowService WindowService { get; }
public LcuSchemaHandler? LcuSchemaHandler { get; set; }
public DialogService DialogService { get; }
public OpenApiDocumentWrapper? OpenApiDocumentWrapper { get; set; }
public OpenApiDocument? HostDocument { get; set; }
[ObservableProperty] private bool _isBusy = true;
[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
.OrderBy(p => p.Index)
@@ -53,7 +55,7 @@ namespace Needlework.Net.Desktop.ViewModels
SelectedMenuItem = MenuItems[0];
HttpClient = httpClient;
WindowService = windowService;
DialogService = dialogService;
WeakReferenceMessenger.Default.RegisterAll(this);
@@ -105,8 +107,8 @@ namespace Needlework.Net.Desktop.ViewModels
{
var document = await Resources.GetOpenApiDocumentAsync(HttpClient);
HostDocument = document;
var handler = new LcuSchemaHandler(document);
LcuSchemaHandler = handler;
var handler = new OpenApiDocumentWrapper(document);
OpenApiDocumentWrapper = handler;
WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler));
IsBusy = false;
@@ -114,7 +116,7 @@ namespace Needlework.Net.Desktop.ViewModels
public void Receive(DataRequestMessage message)
{
message.Reply(LcuSchemaHandler!);
message.Reply(OpenApiDocumentWrapper!);
}
public void Receive(HostDocumentRequestMessage message)
@@ -135,11 +137,6 @@ namespace Needlework.Net.Desktop.ViewModels
process.Start();
}
public void Receive(OopsiesWindowRequestedMessage message)
{
WindowService.ShowOopsiesWindow(message.Value);
}
public void Receive(InfoBarUpdateMessage message)
{
Avalonia.Threading.Dispatcher.UIThread.Post(async () => await ShowInfoBarAsync(message.Value));
@@ -151,5 +148,10 @@ namespace Needlework.Net.Desktop.ViewModels
await Task.Delay(vm.Duration);
InfoBarItems.Remove(vm);
}
public void Receive(OopsiesDialogRequestedMessage message)
{
Avalonia.Threading.Dispatcher.UIThread.Invoke(async () => await DialogService.ShowAsync<OopsiesDialog>(message.Value));
}
}
}
@@ -2,12 +2,12 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.OpenApi.Models;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Messages;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class OperationViewModel : ObservableObject
{
@@ -73,12 +73,21 @@ namespace Needlework.Net.Desktop.ViewModels
{
var type = template[i];
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())
{
if (foundClass.First().PropertyEnums.Any())
{
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
{
template[i] = GetRequestDefaultValue(type);
@@ -1,6 +1,6 @@
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
@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class ParameterViewModel : ObservableObject
{
@@ -3,15 +3,15 @@ using BlossomiShymae.GrrrLCU;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Core;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Messages;
using Needlework.Net.Models;
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class PathOperationViewModel : ObservableObject
{
@@ -19,15 +19,12 @@ namespace Needlework.Net.Desktop.ViewModels
public SolidColorBrush Color { get; }
public string Path { get; }
public OperationViewModel Operation { get; }
public ProcessInfo? ProcessInfo { get; }
[ObservableProperty] private bool _isBusy;
[ObservableProperty] private string? _responsePath;
[ObservableProperty] private string? _responseStatus;
[ObservableProperty] private string? _responseAuthentication;
[ObservableProperty] private string? _responseUsername;
[ObservableProperty] private string? _responsePassword;
[ObservableProperty] private string? _responseAuthorization;
[ObservableProperty] private Lazy<ResponseViewModel> _response;
public PathOperationViewModel(PathOperation pathOperation)
{
@@ -35,26 +32,7 @@ namespace Needlework.Net.Desktop.ViewModels
Color = new SolidColorBrush(GetColor(Method));
Path = pathOperation.Path;
Operation = new OperationViewModel(pathOperation.Operation);
ProcessInfo = GetProcessInfo();
if (ProcessInfo != null)
{
ResponsePath = $"https://127.0.0.1:{ProcessInfo.AppPort}{Path}";
var riotAuth = new RiotAuthentication(ProcessInfo.RemotingAuthToken);
ResponseUsername = riotAuth.Username;
ResponsePassword = riotAuth.Password;
ResponseAuthorization = $"Basic {riotAuth.Value}";
}
}
private ProcessInfo? GetProcessInfo()
{
try
{
var processInfo = Connector.GetProcessInfo();
return processInfo;
}
catch (Exception) { }
return null;
Response = new(() => new ResponseViewModel(pathOperation.Path));
}
[RelayCommand]
@@ -77,7 +55,7 @@ namespace Needlework.Net.Desktop.ViewModels
_ => throw new Exception("Method is missing.")
};
var processInfo = Connector.GetProcessInfo();
var processInfo = ProcessFinder.Get();
var sb = new StringBuilder(Path);
foreach (var pathParameter in Operation.PathParameters)
{
@@ -99,23 +77,24 @@ namespace Needlework.Net.Desktop.ViewModels
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "EndpointRequestEditor").Response;
var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
var response = await Connector.SendAsync(method, uri, content);
var client = Connector.GetLcuHttpClientInstance();
var response = await client.SendAsync(new(method, uri) { Content = content });
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
var responseBytes = await response.Content.ReadAsByteArrayAsync();
var responseBody = responseBytes.Length > 0 ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBytes), App.JsonSerializerOptions) : string.Empty;
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")));
}
else WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(responseBody, "EndpointResponseEditor")));
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode}";
ResponsePath = $"https://127.0.0.1:{processInfo.AppPort}{uri}";
ResponseAuthentication = $"Basic {riotAuthentication.Value}";
ResponseUsername = riotAuthentication.Username;
ResponsePassword = riotAuthentication.Password;
Response.Value.Status = $"{(int)response.StatusCode} {response.StatusCode}";
Response.Value.Path = $"https://127.0.0.1:{processInfo.AppPort}{uri}";
Response.Value.Authentication = Response.Value.Authorization = $"Basic {riotAuthentication.Value}";
Response.Value.Username = riotAuthentication.Username;
Response.Value.Password = riotAuthentication.Password;
}
catch (Exception ex)
{
@@ -5,7 +5,7 @@ using Microsoft.OpenApi.Models;
using System.Collections.Generic;
using System.Linq;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public class PropertyClassViewModel : ObservableObject
{
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public class PropertyEnumViewModel
{
@@ -1,4 +1,4 @@
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public class PropertyFieldViewModel
{
@@ -0,0 +1,35 @@
using BlossomiShymae.GrrrLCU;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Needlework.Net.ViewModels
{
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)
{
Path = path;
var processInfo = GetProcessInfo();
if (processInfo != null)
{
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
Path = $"https://127.0.0.1:{processInfo.AppPort}{path}";
Username = riotAuthentication.Username;
Password = riotAuthentication.Password;
Authorization = $"Basic {riotAuthentication.RawValue}";
}
}
private static ProcessInfo? GetProcessInfo()
{
if (ProcessFinder.IsActive()) return ProcessFinder.Get();
return null;
}
}
}
@@ -2,8 +2,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.Services;
using Needlework.Net.Messages;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -13,28 +12,27 @@ using System.Text.Json;
using System.Threading;
using Websocket.Client;
namespace Needlework.Net.Desktop.ViewModels
namespace Needlework.Net.ViewModels
{
public partial class WebsocketViewModel : PageBase
{
public ObservableCollection<string> EventLog { get; } = [];
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 string? _selectedEventLog = null;
[ObservableProperty] private EventViewModel? _selectedEventLog = null;
private Dictionary<string, EventMessage> _events = [];
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 IReadOnlyList<string> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? EventLog : [.. EventLog.Where(x => x.Contains(Search, StringComparison.InvariantCultureIgnoreCase))];
public WebsocketViewModel(WindowService windowService) : base("Event Viewer", "plug", -100)
public WebsocketViewModel() : base("Event Viewer", "plug", -100)
{
WindowService = windowService;
EventLog.CollectionChanged += (s, e) => OnPropertyChanged(nameof(FilteredEventLog));
var thread = new Thread(InitializeWebsocket) { IsBackground = true };
thread.Start();
@@ -61,6 +59,17 @@ 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]
private void Clear()
{
@@ -68,17 +77,6 @@ namespace Needlework.Net.Desktop.ViewModels
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)
{
Trace.WriteLine($"-- Reconnection --\nType{info.Type}");
@@ -94,25 +92,33 @@ namespace Needlework.Net.Desktop.ViewModels
private void OnMessage(EventMessage message)
{
Avalonia.Threading.Dispatcher.UIThread.Invoke(() =>
Avalonia.Threading.Dispatcher.UIThread.Invoke(async () =>
{
if (!IsAttach) return;
var line = $"{DateTime.Now:HH:mm:ss.fff} {message.Data?.EventType.ToUpper()} {message.Data?.Uri}";
Trace.WriteLine($"Message: {line}");
var line = new EventViewModel(message.Data!);
await EventLogLock.WaitAsync();
try
{
if (EventLog.Count < 1000)
{
EventLog.Add(line);
_events[line] = message;
_events[line.Key] = message;
}
else
{
var key = EventLog[0];
var _event = EventLog[0];
EventLog.RemoveAt(0);
_events.Remove(key);
_events.Remove(_event.Key);
EventLog.Add(line);
_events[line] = message;
_events[line.Key] = message;
}
}
finally
{
EventLogLock.Release();
}
});
}
+167
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>
@@ -1,6 +1,6 @@
using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views;
namespace Needlework.Net.Views;
public partial class AboutView : UserControl
{
@@ -3,10 +3,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
xmlns:controls="using:Needlework.Net.Desktop.Controls"
xmlns:ui="using:FluentAvalonia.UI.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"
x:Class="Needlework.Net.Desktop.Views.ConsoleView"
x:Class="Needlework.Net.Views.ConsoleView"
x:DataType="vm:ConsoleViewModel">
<controls:BusyArea IsBusy="{Binding IsBusy}"
BusyText="Loading...">
@@ -1,15 +1,14 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Styling;
using AvaloniaEdit;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Extensions;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.ViewModels;
using Needlework.Net.Extensions;
using Needlework.Net.Messages;
using Needlework.Net.ViewModels;
using TextMateSharp.Grammars;
namespace Needlework.Net.Desktop.Views;
namespace Needlework.Net.Views;
public partial class ConsoleView : UserControl, IRecipient<ResponseUpdatedMessage>, IRecipient<ContentRequestMessage>
{
@@ -31,9 +30,9 @@ public partial class ConsoleView : UserControl, IRecipient<ResponseUpdatedMessag
message.Reply(_requestEditor!.Text);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnApplyTemplate(e);
base.OnAttachedToVisualTree(e);
_responseEditor = this.FindControl<TextEditor>("ResponseEditor");
_requestEditor = this.FindControl<TextEditor>("RequestEditor");
@@ -3,11 +3,11 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
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:controls="using:Needlework.Net.Desktop.Controls"
xmlns:controls="using:Needlework.Net.Controls"
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">
<UserControl.Styles>
<Style Selector="DataGrid">
@@ -95,7 +95,7 @@
<TextBox Grid.Row="0"
Grid.Column="1"
FontSize="12"
Text="{Binding SelectedPathOperation.ResponsePath}"
Text="{Binding SelectedPathOperation.Response.Value.Path}"
IsReadOnly="True"/>
<StackPanel Grid.Row="0"
Grid.Column="2"
@@ -189,7 +189,7 @@
Grid.Column="1"
Margin="0 0 0 8"
IsReadOnly="True"
Text="{Binding SelectedPathOperation.ResponseUsername}" />
Text="{Binding SelectedPathOperation.Response.Value.Username}" />
<TextBlock FontSize="12"
Grid.Row="1"
Grid.Column="0"
@@ -201,7 +201,7 @@
Grid.Column="1"
Margin="0 0 0 8"
IsReadOnly="True"
Text="{Binding SelectedPathOperation.ResponsePassword}"/>
Text="{Binding SelectedPathOperation.Response.Value.Password}"/>
<TextBlock FontSize="12"
Grid.Row="2"
Grid.Column="0"
@@ -212,7 +212,7 @@
Grid.Row="2"
Grid.Column="1"
IsReadOnly="True"
Text="{Binding SelectedPathOperation.ResponseAuthorization}"/>
Text="{Binding SelectedPathOperation.Response.Value.Authorization}"/>
</Grid>
</TabItem>
<TabItem Header="Schemas">
@@ -304,10 +304,12 @@
FontSize="10"
Padding="12 4 12 4"
Classes="Flat"
Content="{Binding SelectedPathOperation.ResponseStatus}"/>
Content="{Binding SelectedPathOperation.Response.Value.Status}"/>
</StackPanel>
<Grid Grid.Row="1" Grid.Column="4">
<controls:BusyArea BusyText="Loading..."
IsBusy="{Binding SelectedPathOperation.IsBusy}">
<TabControl>
<TabItem Header="Preview">
<avalonEdit:TextEditor
@@ -320,6 +322,7 @@
FontSize="12"/>
</TabItem>
</TabControl>
</controls:BusyArea>
</Grid>
</Grid>
</UserControl>
@@ -1,15 +1,14 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Styling;
using AvaloniaEdit;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Extensions;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.ViewModels;
using Needlework.Net.Extensions;
using Needlework.Net.Messages;
using Needlework.Net.ViewModels;
using TextMateSharp.Grammars;
namespace Needlework.Net.Desktop.Views;
namespace Needlework.Net.Views;
public partial class EndpointView : UserControl, IRecipient<EditorUpdateMessage>, IRecipient<ContentRequestMessage>
{
@@ -21,9 +20,9 @@ public partial class EndpointView : UserControl, IRecipient<EditorUpdateMessage>
InitializeComponent();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnApplyTemplate(e);
base.OnAttachedToVisualTree(e);
var vm = (EndpointViewModel)DataContext!;
_requestEditor = this.FindControl<TextEditor>("EndpointRequestEditor");
@@ -2,11 +2,11 @@
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:vm="using:Needlework.Net.ViewModels"
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.Desktop.Views.EndpointsContainerView"
x:Class="Needlework.Net.Views.EndpointsContainerView"
x:DataType="vm:EndpointsContainerViewModel">
<Grid RowDefinitions="auto,*"
ColumnDefinitions="*"
@@ -1,6 +1,6 @@
using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views;
namespace Needlework.Net.Views;
public partial class EndpointsContainerView : UserControl
{
@@ -2,11 +2,11 @@
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"
xmlns:vm="using:Needlework.Net.ViewModels"
xmlns:controls="using:Needlework.Net.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
Name="EndpointsControl"
x:Class="Needlework.Net.Desktop.Views.EndpointsView"
x:Class="Needlework.Net.Views.EndpointsView"
x:DataType="vm:EndpointsViewModel">
<controls:BusyArea IsBusy="{Binding IsBusy}"
BusyText="Loading...">
@@ -1,6 +1,6 @@
using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views
namespace Needlework.Net.Views
{
public partial class EndpointsView : UserControl
{
@@ -2,10 +2,10 @@
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"
xmlns:vm="using:Needlework.Net.ViewModels"
xmlns:controls="using:Needlework.Net.Controls"
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">
<!-- TOP LEVEL -->
<ScrollViewer>
@@ -1,6 +1,6 @@
using Avalonia.Controls;
namespace Needlework.Net.Desktop.Views
namespace Needlework.Net.Views
{
public partial class HomeView : UserControl
{
@@ -7,9 +7,9 @@
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.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"
x:Class="Needlework.Net.Desktop.Views.MainWindow"
x:Class="Needlework.Net.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Title="Needlework.Net"
Icon="/Assets/app.ico"
@@ -1,6 +1,6 @@
using FluentAvalonia.UI.Windowing;
namespace Needlework.Net.Desktop.Views;
namespace Needlework.Net.Views;
public partial class MainWindow : AppWindow
{
+65
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;
}
}
}
}
@@ -3,9 +3,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
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"
x:Class="Needlework.Net.Desktop.Views.WebsocketView"
x:Class="Needlework.Net.Views.WebsocketView"
x:DataType="vm:WebsocketViewModel">
<Grid RowDefinitions="*,auto,*" Margin="16">
<Border Grid.Row="0"
@@ -42,7 +42,26 @@
Name="EventViewer"
Margin="0 8 0 0"
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>
</Border>
<GridSplitter Grid.Row="1" ResizeDirection="Rows" Background="Gray"/>
@@ -4,16 +4,19 @@ using Avalonia.Controls.Primitives;
using Avalonia.Styling;
using AvaloniaEdit;
using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Extensions;
using Needlework.Net.Desktop.Messages;
using Needlework.Net.Desktop.ViewModels;
using Needlework.Net.Extensions;
using Needlework.Net.Messages;
using Needlework.Net.ViewModels;
using System;
using TextMateSharp.Grammars;
namespace Needlework.Net.Desktop.Views;
namespace Needlework.Net.Views;
public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMessage>
{
private TextEditor? _responseEditor;
public WebsocketViewModel? _viewModel;
private ListBox? _viewer;
public WebsocketView()
{
@@ -29,9 +32,9 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
{
base.OnApplyTemplate(e);
var vm = (WebsocketViewModel)DataContext!;
var viewer = this.FindControl<ListBox>("EventViewer");
vm.EventLog.CollectionChanged += (s, e) => { if (vm.IsTail) viewer!.ScrollIntoView(vm.EventLog.Count - 1); };
_viewModel = (WebsocketViewModel)DataContext!;
_viewer = this.FindControl<ListBox>("EventViewer");
_viewModel.EventLog.CollectionChanged += EventLog_CollectionChanged; ;
_responseEditor = this.FindControl<TextEditor>("ResponseEditor");
_responseEditor?.ApplyJsonEditorSettings();
@@ -41,6 +44,26 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
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)
{

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 221 KiB

@@ -3,7 +3,7 @@
<!-- This manifest is used on Windows only.
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 -->
<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">
<application>
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 370 KiB