From 4a152f4e95d9a24e48682da4b7ab0939467e0a59 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 14 Aug 2023 20:42:51 +0200 Subject: [PATCH] UI - Fixed possible crash when loading profiles with broken icons Debugger - Added routing debugger --- .../Screens/Debugger/DebugViewModel.cs | 5 +- .../Tabs/Routing/RoutingDebugView.axaml | 28 +++++ .../Tabs/Routing/RoutingDebugView.axaml.cs | 26 +++++ .../Tabs/Routing/RoutingDebugViewModel.cs | 108 ++++++++++++++++++ .../SidebarProfileConfigurationViewModel.cs | 10 +- 5 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml create mode 100644 src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugViewModel.cs diff --git a/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs index 44f80c8d7..87cc92d7d 100644 --- a/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs +++ b/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs @@ -5,6 +5,7 @@ using Artemis.UI.Screens.Debugger.DataModel; using Artemis.UI.Screens.Debugger.Logs; using Artemis.UI.Screens.Debugger.Performance; using Artemis.UI.Screens.Debugger.Render; +using Artemis.UI.Screens.Debugger.Routing; using Artemis.UI.Services.Interfaces; using Artemis.UI.Shared; using ReactiveUI; @@ -15,9 +16,9 @@ public class DebugViewModel : ActivatableViewModelBase, IScreen { private ViewModelBase _selectedItem; - public DebugViewModel(IDebugService debugService, RenderDebugViewModel render, DataModelDebugViewModel dataModel, PerformanceDebugViewModel performance, LogsDebugViewModel logs) + public DebugViewModel(IDebugService debugService, RenderDebugViewModel render, DataModelDebugViewModel dataModel, PerformanceDebugViewModel performance, RoutingDebugViewModel routing, LogsDebugViewModel logs) { - Items = new ObservableCollection {render, dataModel, performance, logs}; + Items = new ObservableCollection {render, dataModel, performance, routing, logs}; _selectedItem = render; this.WhenActivated(d => Disposable.Create(debugService.ClearDebugger).DisposeWith(d)); diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml new file mode 100644 index 000000000..e89fddd28 --- /dev/null +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml @@ -0,0 +1,28 @@ + + + + + + + + + + + Navigation logs + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml.cs new file mode 100644 index 000000000..05c2474ea --- /dev/null +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml.cs @@ -0,0 +1,26 @@ +using Avalonia.Controls; +using Avalonia.ReactiveUI; +using Avalonia.Threading; + +namespace Artemis.UI.Screens.Debugger.Routing; + +public partial class RoutingDebugView : ReactiveUserControl +{ + public RoutingDebugView() + { + InitializeComponent(); + } + + protected override void OnInitialized() + { + base.OnInitialized(); + Dispatcher.UIThread.Post(() => LogsScrollViewer.ScrollToEnd(), DispatcherPriority.ApplicationIdle); + } + + private void Control_OnSizeChanged(object? sender, SizeChangedEventArgs e) + { + if (!(LogsScrollViewer.Extent.Height - LogsScrollViewer.Offset.Y - LogsScrollViewer.Bounds.Bottom <= 60)) + return; + Dispatcher.UIThread.Post(() => LogsScrollViewer.ScrollToEnd(), DispatcherPriority.Normal); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugViewModel.cs new file mode 100644 index 000000000..63fc7cf10 --- /dev/null +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugViewModel.cs @@ -0,0 +1,108 @@ +using System; +using System.IO; +using System.Reactive; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Artemis.Core; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Routing; +using Avalonia.Controls.Documents; +using Avalonia.Media; +using Avalonia.Threading; +using ReactiveUI; +using Serilog.Events; +using Serilog.Formatting.Display; + +namespace Artemis.UI.Screens.Debugger.Routing; + +public class RoutingDebugViewModel : ActivatableViewModelBase +{ + private readonly IRouter _router; + private const int MAX_ENTRIES = 1000; + private readonly MessageTemplateTextFormatter _formatter; + private string? _route; + + public RoutingDebugViewModel(IRouter router) + { + _router = router; + DisplayName = "Routing"; + Navigate = ReactiveCommand.CreateFromTask(ExecuteNavigate, this.WhenAnyValue(vm => vm.Route).Select(r => !string.IsNullOrWhiteSpace(r))); + + _formatter = new MessageTemplateTextFormatter( + "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}" + ); + + foreach (LogEvent logEvent in LogStore.Events) + AddLogEvent(logEvent); + + this.WhenActivated(disp => + { + LogStore.EventAdded += OnLogEventAdded; + Disposable.Create(() => LogStore.EventAdded -= OnLogEventAdded).DisposeWith(disp); + + _router.CurrentPath.Subscribe(p => Route = p).DisposeWith(disp); + }); + } + + public InlineCollection Lines { get; } = new(); + public ReactiveCommand Navigate { get; } + + public string? Route + { + get => _route; + set => RaiseAndSetIfChanged(ref _route, value); + } + + private void OnLogEventAdded(object? sender, LogEventEventArgs e) + { + Dispatcher.UIThread.Post(() => AddLogEvent(e.LogEvent)); + } + + private void AddLogEvent(LogEvent? logEvent) + { + if (logEvent is null) + return; + if (!logEvent.Properties.TryGetValue("SourceContext", out LogEventPropertyValue? sourceContext) || sourceContext.ToString() != "\"Artemis.UI.Shared.Routing.Navigation\"") + return; + + using StringWriter writer = new(); + _formatter.Format(logEvent, writer); + string line = writer.ToString(); + + Lines.Add(new Run(line.TrimEnd('\r', '\n') + '\n') + { + Foreground = logEvent.Level switch + { + LogEventLevel.Verbose => new SolidColorBrush(Colors.White), + LogEventLevel.Debug => new SolidColorBrush(Color.FromRgb(216, 216, 216)), + LogEventLevel.Information => new SolidColorBrush(Color.FromRgb(93, 201, 255)), + LogEventLevel.Warning => new SolidColorBrush(Color.FromRgb(255, 177, 53)), + LogEventLevel.Error => new SolidColorBrush(Color.FromRgb(255, 63, 63)), + LogEventLevel.Fatal => new SolidColorBrush(Colors.Red), + _ => throw new ArgumentOutOfRangeException() + } + }); + LimitLines(); + } + + private void LimitLines() + { + if (Lines.Count > MAX_ENTRIES) + Lines.RemoveRange(0, Lines.Count - MAX_ENTRIES); + } + + private async Task ExecuteNavigate(CancellationToken arg) + { + try + { + if (Route != null) + await _router.Navigate(Route.Trim()); + } + catch (Exception) + { + // ignored + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs index 98749cb51..4fa5728a5 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs @@ -42,7 +42,15 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase .Select(p => p == null) .ToProperty(this, vm => vm.IsDisabled) .DisposeWith(d)); - _profileService.LoadProfileConfigurationIcon(ProfileConfiguration); + + try + { + _profileService.LoadProfileConfigurationIcon(ProfileConfiguration); + } + catch (Exception) + { + // ignored, too bad but don't crash over corrupt icons + } } public ProfileConfiguration ProfileConfiguration { get; }