From d39d0774bc8b3805a101163688255db3def48ae8 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 2 Feb 2023 23:15:21 +0100 Subject: [PATCH] feat: add file saving, loading from top menu --- src/PacketLogger/ViewModels/DockFactory.cs | 66 +++++++-- .../ViewModels/MainWindowViewModel.cs | 128 +++++++++++++++++- src/PacketLogger/Views/MainWindow.axaml | 16 +-- 3 files changed, 183 insertions(+), 27 deletions(-) diff --git a/src/PacketLogger/ViewModels/DockFactory.cs b/src/PacketLogger/ViewModels/DockFactory.cs index b2fa60f..ca473ff 100644 --- a/src/PacketLogger/ViewModels/DockFactory.cs +++ b/src/PacketLogger/ViewModels/DockFactory.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Reactive; using Dock.Avalonia.Controls; using Dock.Model.Controls; using Dock.Model.Core; @@ -27,6 +28,57 @@ public class DockFactory : Factory, IDisposable private readonly NostaleProcesses _processes = new(); private readonly CommsInjector _injector; + private IRootDock? _rootDock; + private IDocumentDock? _documentDock; + + /// + /// Initializes a new instance of the class. + /// + /// The communications injector. + /// The repository. + public DockFactory(CommsInjector injector, StatefulRepository repository) + { + _repository = repository; + _injector = injector; + } + + /// + /// Gets the document dock. + /// + public IDocumentDock DocumentDock + => _documentDock ?? throw new InvalidOperationException("Dock factory is not initialized"); + + /// + /// Creaate and load a document. + /// + /// The function to use to load the document. + public void CreateLoadedDocument(Func> load) + { + if (_documentDock is null) + { + return; + } + + var document = new PacketLogDocumentViewModel(_injector, _repository, _processes) + { Id = $"New tab", Title = $"New tab" }; + + var observable = load(document); + observable.Subscribe + ( + x => + { + if (!document.Loaded) + { + return; + } + + AddDockable(_documentDock, document); + SetActiveDockable(document); + SetFocusedDockable(_documentDock, document); + } + ); + } + /// public override IDocumentDock CreateDocumentDock() { @@ -52,20 +104,6 @@ public class DockFactory : Factory, IDisposable return documentDock; } - private IRootDock? _rootDock; - private IDocumentDock? _documentDock; - - /// - /// Initializes a new instance of the class. - /// - /// The communications injector. - /// The repository. - public DockFactory(CommsInjector injector, StatefulRepository repository) - { - _repository = repository; - _injector = injector; - } - /// public override IRootDock CreateLayout() { diff --git a/src/PacketLogger/ViewModels/MainWindowViewModel.cs b/src/PacketLogger/ViewModels/MainWindowViewModel.cs index 4feb4be..4a0dd8d 100644 --- a/src/PacketLogger/ViewModels/MainWindowViewModel.cs +++ b/src/PacketLogger/ViewModels/MainWindowViewModel.cs @@ -7,23 +7,34 @@ using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Reflection; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Dock.Model.Controls; using Dock.Model.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NosSmooth.Comms.Local.Extensions; using NosSmooth.Core.Extensions; +using NosSmooth.PacketSerializer.Abstractions.Attributes; using NosSmooth.PacketSerializer.Extensions; using NosSmooth.PacketSerializer.Packets; using PacketLogger.Models; using PacketLogger.Models.Packets; +using PacketLogger.Views; +using ReactiveUI; namespace PacketLogger.ViewModels; /// public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged { - private readonly IFactory? _factory; + private readonly DockFactory _factory; /// /// Initializes a new instance of the class. @@ -41,6 +52,7 @@ public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged .BuildServiceProvider(); var packetTypes = services.GetRequiredService(); + var result = packetTypes.AddDefaultPackets(); if (!result.IsSuccess) { @@ -48,20 +60,126 @@ public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged } _factory = services.GetRequiredService(); - - Layout = _factory?.CreateLayout(); + Layout = _factory.CreateLayout(); if (Layout is { }) { - _factory?.InitLayout(Layout); + _factory.InitLayout(Layout); if (Layout is { } root) { root.Navigate.Execute("Home"); } } + + SaveAll = ReactiveCommand.CreateFromTask + ( + async () => + { + if (Layout?.FocusedDockable is PacketLogDocumentViewModel activeDocument && activeDocument.Loaded + && activeDocument.LogViewModel is not null) + { + var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime) + ?.MainWindow; + var result = await new SaveFileDialog() + { + DefaultExtension = ".log", + InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName + }.ShowAsync(mainWindow!); + + if (result is null) + { + return; + } + + using var file = File.OpenWrite(result); + using var streamWriter = new StreamWriter(file); + foreach (var packet in activeDocument.LogViewModel.Provider.Packets.Items) + { + await streamWriter.WriteLineAsync + ( + $"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}" + ); + } + } + } + ); + + SaveFiltered = ReactiveCommand.CreateFromTask + ( + async () => + { + if (Layout?.FocusedDockable is PacketLogDocumentViewModel activeDocument && activeDocument.Loaded + && activeDocument.LogViewModel is not null) + { + var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime) + ?.MainWindow; + var result = await new SaveFileDialog() + { + DefaultExtension = ".log", + }.ShowAsync(mainWindow!); + + if (result is null) + { + return; + } + + using var file = File.OpenWrite(result); + using var streamWriter = new StreamWriter(file); + foreach (var packet in activeDocument.LogViewModel.FilteredPackets) + { + await streamWriter.WriteLineAsync + ( + $"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}" + ); + } + } + } + ); + + OpenFile = ReactiveCommand.Create + (() => _factory.CreateLoadedDocument(doc => doc.OpenFile.Execute(Unit.Default))); + + OpenEmpty = ReactiveCommand.Create + (() => _factory.CreateLoadedDocument(doc => doc.OpenDummy.Execute(Unit.Default))); + + NewTab = ReactiveCommand.Create + (() => _factory.DocumentDock.CreateDocument?.Execute(null)); + + QuitApplication = ReactiveCommand.Create + (() => (Application.Current?.ApplicationLifetime as IControlledApplicationLifetime)?.Shutdown()); } /// /// Gets or sets the layout. /// public IRootDock? Layout { get; set; } -} + + /// + /// Gets a command that quits the application. + /// + public ReactiveCommand QuitApplication { get; } + + /// + /// Gets a command that saves all packets. + /// + public ReactiveCommand SaveAll { get; } + + /// + /// Gets a command that saves filtered packets. + /// + public ReactiveCommand SaveFiltered { get; } + + /// + /// Gets the comamnd that opens a file. + /// + public ReactiveCommand OpenFile { get; } + + /// + /// Gets the command that opens empty logger. + /// + public ReactiveCommand OpenEmpty { get; } + + /// + /// Gets the command that opens a new tab. + /// + public ReactiveCommand NewTab { get; } +} \ No newline at end of file diff --git a/src/PacketLogger/Views/MainWindow.axaml b/src/PacketLogger/Views/MainWindow.axaml index 91facc4..8732fdc 100644 --- a/src/PacketLogger/Views/MainWindow.axaml +++ b/src/PacketLogger/Views/MainWindow.axaml @@ -21,18 +21,18 @@ - + + + - - + + - + - - - - + + -- 2.49.0