Merge pull request #2 from AoshiW/perf

bug fix in WebsocketView(Model), optimization and others
This commit is contained in:
Blossomi Shymae
2024-08-16 14:49:34 -05:00
committed by GitHub
10 changed files with 66 additions and 54 deletions

View File

@@ -12,7 +12,7 @@
<ApplicationIcon>app.ico</ApplicationIcon> <ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyName>NeedleworkDotNet</AssemblyName> <AssemblyName>NeedleworkDotNet</AssemblyName>
<AssemblyVersion>0.4.2.0</AssemblyVersion> <AssemblyVersion>0.4.2.0</AssemblyVersion>
<FileVersion>0.4.2.0</FileVersion> <FileVersion>$(AssemblyVersion)</FileVersion>
<AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions> <AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions>
</PropertyGroup> </PropertyGroup>

View File

@@ -15,10 +15,10 @@ namespace Needlework.Net.Desktop.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;
@@ -59,11 +59,11 @@ 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(requestBody, 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); WindowService.ShowOopsiesWindow(body);
@@ -93,7 +93,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

@@ -2,6 +2,8 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Desktop.Messages;
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Needlework.Net.Desktop.ViewModels namespace Needlework.Net.Desktop.ViewModels
@@ -12,11 +14,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 +31,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

@@ -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

@@ -63,7 +63,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 +84,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 () =>
{ {

View File

@@ -7,6 +7,7 @@ using Needlework.Net.Core;
using Needlework.Net.Desktop.Messages; using Needlework.Net.Desktop.Messages;
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.Threading.Tasks; using System.Threading.Tasks;
@@ -31,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()
@@ -59,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,
@@ -73,30 +78,32 @@ 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(requestBody, 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>(responseBody), App.JsonSerializerOptions) : string.Empty;
if (responseBody.Length >= App.MaxCharacters) if (responseBody.Length >= App.MaxCharacters)
{ {
WeakReferenceMessenger.Default.Send(new OopsiesWindowRequestedMessage(responseBody)); WeakReferenceMessenger.Default.Send(new OopsiesWindowRequestedMessage(responseBody));

View File

@@ -17,8 +17,7 @@ namespace Needlework.Net.Desktop.ViewModels
{ {
public partial class WebsocketViewModel : PageBase public partial class WebsocketViewModel : PageBase
{ {
[NotifyPropertyChangedFor(nameof(FilteredEventLog))] public ObservableCollection<string> EventLog { get; } = [];
[ObservableProperty] private ObservableCollection<string> _eventLog = [];
[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;
@@ -31,12 +30,12 @@ namespace Needlework.Net.Desktop.ViewModels
public WindowService WindowService { get; } public WindowService WindowService { get; }
public List<string> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? [.. EventLog] : [.. EventLog.Where(x => x.ToLower().Contains(Search.ToLower()))]; 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(WindowService windowService) : base("Event Viewer", "plug", -100)
{ {
WindowService = windowService; 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();
} }
@@ -65,7 +64,8 @@ namespace Needlework.Net.Desktop.ViewModels
[RelayCommand] [RelayCommand]
private void Clear() private void Clear()
{ {
EventLog = []; _events.Clear();
EventLog.Clear();
} }
partial void OnSelectedEventLogChanged(string? value) partial void OnSelectedEventLogChanged(string? value)
@@ -99,25 +99,21 @@ namespace Needlework.Net.Desktop.ViewModels
if (!IsAttach) return; if (!IsAttach) return;
var line = $"{DateTime.Now:HH:mm:ss.fff} {message.Data?.EventType.ToUpper()} {message.Data?.Uri}"; var line = $"{DateTime.Now:HH:mm:ss.fff} {message.Data?.EventType.ToUpper()} {message.Data?.Uri}";
var log = EventLog.ToList();
Trace.WriteLine($"Message: {line}"); Trace.WriteLine($"Message: {line}");
if (log.Count < 1000) if (EventLog.Count < 1000)
{ {
log.Add(line); EventLog.Add(line);
_events[line] = message; _events[line] = message;
} }
else else
{ {
var key = $"{log[0]}"; var key = EventLog[0];
log.RemoveAt(0); EventLog.RemoveAt(0);
_events.Remove(key); _events.Remove(key);
log.Add(line); EventLog.Add(line);
_events[line] = message; _events[line] = message;
} }
EventLog = []; // This is a hack needed to update for ListBox
EventLog = new ObservableCollection<string>(log);
}); });
} }
} }

View File

@@ -55,6 +55,7 @@
<Grid <Grid
RowDefinitions="*" RowDefinitions="*"
ColumnDefinitions="auto,*"> ColumnDefinitions="auto,*">
<!-- TODO change button to sometnig else or disabled "highlighting"-->
<Button <Button
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Flat" Classes="Flat"

View File

@@ -82,6 +82,7 @@
<ItemsRepeater.ItemTemplate> <ItemsRepeater.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border Margin="4"> <Border Margin="4">
<!-- TODO add some background to InfoBar (currently it is (probably) transparent and you can see other things under it) -->
<ui:InfoBar <ui:InfoBar
Title="{Binding Title}" Title="{Binding Title}"
IsOpen="{Binding IsOpen}" IsOpen="{Binding IsOpen}"

View File

@@ -31,7 +31,7 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
var vm = (WebsocketViewModel)DataContext!; var vm = (WebsocketViewModel)DataContext!;
var viewer = this.FindControl<ListBox>("EventViewer"); var viewer = this.FindControl<ListBox>("EventViewer");
viewer!.PropertyChanged += (s, e) => { if (vm.IsTail) viewer.ScrollIntoView(vm.EventLog.Count - 1); }; vm.EventLog.CollectionChanged += (s, e) => { if (vm.IsTail) viewer!.ScrollIntoView(vm.EventLog.Count - 1); };
_responseEditor = this.FindControl<TextEditor>("ResponseEditor"); _responseEditor = this.FindControl<TextEditor>("ResponseEditor");
_responseEditor?.ApplyJsonEditorSettings(); _responseEditor?.ApplyJsonEditorSettings();