~ruther/NosTale-PacketLogger

d39d0774bc8b3805a101163688255db3def48ae8 — Rutherther 2 years ago 04181d4
feat: add file saving, loading from top menu
M src/PacketLogger/ViewModels/DockFactory.cs => src/PacketLogger/ViewModels/DockFactory.cs +52 -14
@@ 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;

    /// <summary>
    /// Initializes a new instance of the <see cref="DockFactory"/> class.
    /// </summary>
    /// <param name="injector">The communications injector.</param>
    /// <param name="repository">The repository.</param>
    public DockFactory(CommsInjector injector, StatefulRepository repository)
    {
        _repository = repository;
        _injector = injector;
    }

    /// <summary>
    /// Gets the document dock.
    /// </summary>
    public IDocumentDock DocumentDock
        => _documentDock ?? throw new InvalidOperationException("Dock factory is not initialized");

    /// <summary>
    /// Creaate and load a document.
    /// </summary>
    /// <param name="load">The function to use to load the document.</param>
    public void CreateLoadedDocument(Func<PacketLogDocumentViewModel, IObservable<Unit>> 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);
            }
        );
    }

    /// <inheritdoc />
    public override IDocumentDock CreateDocumentDock()
    {


@@ 52,20 104,6 @@ public class DockFactory : Factory, IDisposable
        return documentDock;
    }

    private IRootDock? _rootDock;
    private IDocumentDock? _documentDock;

    /// <summary>
    /// Initializes a new instance of the <see cref="DockFactory"/> class.
    /// </summary>
    /// <param name="injector">The communications injector.</param>
    /// <param name="repository">The repository.</param>
    public DockFactory(CommsInjector injector, StatefulRepository repository)
    {
        _repository = repository;
        _injector = injector;
    }

    /// <inheritdoc />
    public override IRootDock CreateLayout()
    {

M src/PacketLogger/ViewModels/MainWindowViewModel.cs => src/PacketLogger/ViewModels/MainWindowViewModel.cs +123 -5
@@ 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;

/// <inheritdoc />
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
    private readonly IFactory? _factory;
    private readonly DockFactory _factory;

    /// <summary>
    /// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.


@@ 41,6 52,7 @@ public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
            .BuildServiceProvider();

        var packetTypes = services.GetRequiredService<IPacketTypesRepository>();

        var result = packetTypes.AddDefaultPackets();
        if (!result.IsSuccess)
        {


@@ 48,20 60,126 @@ public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
        }

        _factory = services.GetRequiredService<DockFactory>();

        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());
    }

    /// <summary>
    /// Gets or sets the layout.
    /// </summary>
    public IRootDock? Layout { get; set; }
}

    /// <summary>
    /// Gets a command that quits the application.
    /// </summary>
    public ReactiveCommand<Unit, Unit> QuitApplication { get; }

    /// <summary>
    /// Gets a command that saves all packets.
    /// </summary>
    public ReactiveCommand<Unit, Unit> SaveAll { get; }

    /// <summary>
    /// Gets a command that saves filtered packets.
    /// </summary>
    public ReactiveCommand<Unit, Unit> SaveFiltered { get; }

    /// <summary>
    /// Gets the comamnd that opens a file.
    /// </summary>
    public ReactiveCommand<Unit, Unit> OpenFile { get; }

    /// <summary>
    /// Gets the command that opens empty logger.
    /// </summary>
    public ReactiveCommand<Unit, Unit> OpenEmpty { get; }

    /// <summary>
    /// Gets the command that opens a new tab.
    /// </summary>
    public ReactiveCommand<Unit, Unit> NewTab { get; }
}
\ No newline at end of file

M src/PacketLogger/Views/MainWindow.axaml => src/PacketLogger/Views/MainWindow.axaml +8 -8
@@ 21,18 21,18 @@
    <Grid RowDefinitions="Auto,*" ColumnDefinitions="Auto, *">
        <Menu Grid.Row="0" Grid.Column="0">
            <MenuItem Header="_File">
                <MenuItem Header="_Open File..." />
                <MenuItem Header="_New Tab" Command="{Binding NewTab}"></MenuItem>
                <MenuItem Header="_Open Empty" Command="{Binding OpenEmpty}" />
                <MenuItem Header="_Open File..." Command="{Binding OpenFile}" />
                <Separator />
                <MenuItem Header="_Save Filtered As..." />
                <MenuItem Header="Save All As..." />
                <MenuItem Header="_Save Filtered As..." Command="{Binding SaveFiltered}" />
                <MenuItem Header="Save All As..." Command="{Binding SaveAll}" />
                <Separator />
                <MenuItem Header="Exit" />
                <MenuItem Header="Exit" Command="{Binding QuitApplication}" />
            </MenuItem>
            <MenuItem Header="_Tools">
                <MenuItem Header="_Packet Sender">
                    <MenuItem Header="_Test" />
                </MenuItem>
                <MenuItem Header="_Packet Analyzer"></MenuItem>
                <MenuItem Header="_Packet Sender"/>
                <MenuItem Header="_Packet Analyzer"/>
            </MenuItem>
        </Menu>
        

Do not follow this link