// // LogTabViewModel.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia; using Avalonia.Collections; using DynamicData; using DynamicData.Binding; using PacketLogger.Models; using PacketLogger.Models.Filters; using PacketLogger.Models.Packets; using ReactiveUI; using Reloaded.Memory.Kernel32; namespace PacketLogger.ViewModels; /// public class LogTabViewModel : ViewModelBase, IDisposable { private readonly ReadOnlyObservableCollection _packets; private readonly IDisposable _cleanUp; private bool _logReceived = true; private bool _logSent = true; /// /// Initializes a new instance of the class. /// /// The packet provider. public LogTabViewModel(IPacketProvider packetProvider) { Provider = packetProvider; var dynamicFilter = this.WhenValueChanged(@this => @this.CurrentFilter) .Select ( filter => { return (Func)((pi) => { if (filter is null) { return true; } return filter.Match(pi); }); } ); var packetsSubscription = Provider.Packets.Connect() .Filter(dynamicFilter) .Sort(new PacketComparer()) .Bind(out _packets) .ObserveOn(RxApp.MainThreadScheduler) .DisposeMany() .Subscribe(); var scrollSubscription = FilteredPackets.ObserveCollectionChanges() .ObserveOn(RxApp.MainThreadScheduler) .Do ( change => { if (Scroll && change.EventArgs.NewItems is not null) { RxApp.MainThreadScheduler.Schedule(DateTimeOffset.Now.AddMilliseconds(100), () => SelectedPacket = FilteredPackets[^1]); } } ) .Subscribe(); _cleanUp = new CompositeDisposable(scrollSubscription, packetsSubscription); CopyPackets = ReactiveCommand.CreateFromObservable ( list => Observable.StartAsync ( async () => { var clipboardString = string.Join ('\n', list.OfType().Select(x => x.PacketString)); await Application.Current!.Clipboard!.SetTextAsync(clipboardString); } ) ); TogglePane = ReactiveCommand.Create ( _ => PaneOpen = !PaneOpen ); Clear = ReactiveCommand.Create ( () => Provider.Clear() ); SendFilter = new(); RecvFilter = new(); SendFilter.PropertyChanged += (s, e) => { if (e.PropertyName == "Whitelist") { CreateSendRecv(); } }; RecvFilter.PropertyChanged += (s, e) => { if (e.PropertyName == "Whitelist") { CreateSendRecv(); } }; SendFilter.Filters.CollectionChanged += (s, e) => { CreateSendRecv(); }; RecvFilter.Filters.CollectionChanged += (s, e) => { CreateSendRecv(); }; } /// /// Gets the send filter model. /// public LogFilterTabViewModel SendFilter { get; } /// /// Gets the receive filter model. /// public LogFilterTabViewModel RecvFilter { get; } /// /// Gets the currently applied filter. /// public IFilter? CurrentFilter { get; private set; } /// /// Gets the filtered packets. /// public ReadOnlyObservableCollection FilteredPackets => _packets; /// /// Gets packet provider. /// public IPacketProvider Provider { get; } /// /// Gets whether the pane is open. /// public bool PaneOpen { get; private set; } = true; /// /// Gets the toggle pane command. /// public ReactiveCommand TogglePane { get; } /// /// Gets command to copy packets. /// public ReactiveCommand CopyPackets { get; } /// /// Gets the command for clearing. /// public ReactiveCommand Clear { get; } /// /// Gets or sets whether to log received packets. /// public bool LogReceived { get => _logReceived; set { Provider.LogReceived = value; _logReceived = value; } } /// /// Gets or sets whether to log sent packets. /// public bool LogSent { get => _logSent; set { Provider.LogSent = value; _logSent = value; } } /// /// Gets or sets whether to scroll to teh bottom of the grid. /// public bool Scroll { get; set; } = true; /// /// Gets or sets the currently selected packet. /// public object? SelectedPacket { get; set; } /// /// Gets empty string. /// public string Empty { get; } = string.Empty; private void CreateSendRecv() { IFilter recvFilter = CreateCompound(RecvFilter); IFilter sendFilter = CreateCompound(SendFilter); CurrentFilter = new SendRecvFilter(sendFilter, recvFilter); } private IFilter CreateCompound(LogFilterTabViewModel logFilterTab) { List filters = new List(); foreach (var filter in logFilterTab.Filters) { filters.Add(FilterCreator.BuildFilter(filter.Type, filter.Value)); } return new CompoundFilter(!logFilterTab.Whitelist, filters.ToArray()); } /// public void Dispose() { TogglePane.Dispose(); _cleanUp.Dispose(); } }