265 lines
9.1 KiB
C#
265 lines
9.1 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using DeclarativeProperty;
|
|
using DynamicData;
|
|
using DynamicData.Binding;
|
|
using FileTime.App.Core.Extensions;
|
|
using FileTime.App.Core.Models;
|
|
using FileTime.App.Core.Models.Enums;
|
|
using FileTime.App.Core.Services;
|
|
using FileTime.Core.Enums;
|
|
using FileTime.Core.Models;
|
|
using FileTime.Core.Models.Extensions;
|
|
using FileTime.Core.Services;
|
|
using FileTime.Core.Timeline;
|
|
using InitableService;
|
|
using MvvmGen;
|
|
using ObservableComputations;
|
|
using IContainer = FileTime.Core.Models.IContainer;
|
|
using static System.DeferTools;
|
|
|
|
namespace FileTime.App.Core.ViewModels;
|
|
|
|
[ViewModel]
|
|
public partial class TabViewModel : ITabViewModel
|
|
{
|
|
private readonly IServiceProvider _serviceProvider;
|
|
private readonly IAppState _appState;
|
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
|
private readonly IRefreshSmoothnessCalculator _refreshSmoothnessCalculator;
|
|
private readonly ObservableCollection<FullName> _markedItems = new();
|
|
private readonly List<IDisposable> _disposables = new();
|
|
private bool _disposed;
|
|
private OcConsumer? _currentItemsConsumer;
|
|
private OcConsumer? _selectedsChildrenConsumer;
|
|
private OcConsumer? _parentsChildrenConsumer;
|
|
|
|
public ITab? Tab { get; private set; }
|
|
public int TabNumber { get; private set; }
|
|
|
|
public IDeclarativeProperty<bool> IsSelected { get; }
|
|
|
|
public IDeclarativeProperty<IContainer?> CurrentLocation { get; private set; }
|
|
public IDeclarativeProperty<IItemViewModel?> CurrentSelectedItem { get; private set; }
|
|
public IDeclarativeProperty<IContainerViewModel?> CurrentSelectedItemAsContainer { get; private set; }
|
|
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> CurrentItems { get; private set; }
|
|
public IDeclarativeProperty<ObservableCollection<FullName>> MarkedItems { get; }
|
|
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> SelectedsChildren { get; private set; }
|
|
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> ParentsChildren { get; private set; }
|
|
|
|
|
|
public TabViewModel(
|
|
IServiceProvider serviceProvider,
|
|
IAppState appState,
|
|
ITimelessContentProvider timelessContentProvider,
|
|
IRefreshSmoothnessCalculator refreshSmoothnessCalculator)
|
|
{
|
|
_serviceProvider = serviceProvider;
|
|
_appState = appState;
|
|
|
|
MarkedItems = _markedItems.Watch();
|
|
IsSelected = _appState.SelectedTab.Map(s => s == this);
|
|
_timelessContentProvider = timelessContentProvider;
|
|
_refreshSmoothnessCalculator = refreshSmoothnessCalculator;
|
|
}
|
|
|
|
public void Init(ITab tab, int tabNumber)
|
|
{
|
|
Tab = tab;
|
|
TabNumber = tabNumber;
|
|
|
|
tab.AddToDisposables(_disposables);
|
|
|
|
CurrentLocation = tab.CurrentLocation;
|
|
|
|
CurrentItems =
|
|
tab.CurrentItems
|
|
.Map((items, _) =>
|
|
Task.FromResult<ObservableCollection<IItemViewModel>?>(
|
|
items?.Selecting<IItem, IItemViewModel>(
|
|
i => MapItemToViewModel(i, ItemViewModelType.Main)
|
|
)
|
|
)
|
|
);
|
|
|
|
using var _ = Defer(
|
|
() => CurrentItems.Subscribe(c => UpdateConsumer(c, ref _currentItemsConsumer))
|
|
);
|
|
|
|
CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest(
|
|
tab.CurrentSelectedItem,
|
|
CurrentItems.Watch<ObservableCollection<IItemViewModel>, IItemViewModel>(),
|
|
(currentSelectedItem, currentItems) =>
|
|
Task.FromResult(currentItems?.FirstOrDefault(i => i.BaseItem?.FullName?.Path == currentSelectedItem?.Path.Path)),
|
|
item =>
|
|
{
|
|
if (item?.BaseItem is not { } baseItem)
|
|
return;
|
|
|
|
tab.SetSelectedItem(new AbsolutePath(_timelessContentProvider, baseItem));
|
|
}
|
|
);
|
|
|
|
CurrentSelectedItem.Subscribe((v) =>
|
|
{
|
|
_refreshSmoothnessCalculator.RegisterChange();
|
|
_refreshSmoothnessCalculator.RecalculateSmoothness();
|
|
});
|
|
|
|
CurrentSelectedItemAsContainer = CurrentSelectedItem.Map(i => i as IContainerViewModel);
|
|
|
|
SelectedsChildren = CurrentSelectedItem
|
|
.Debounce(_ => _refreshSmoothnessCalculator.RefreshDelay, resetTimer: true)
|
|
.DistinctUntilChanged()
|
|
.Map(item =>
|
|
{
|
|
if (item is not IContainerViewModel {Container: { } container})
|
|
return (ObservableCollection<IItemViewModel>?) null;
|
|
|
|
var items = container
|
|
.Items
|
|
.Selecting(i => MapItem(i))
|
|
.Ordering(i => i.Type)
|
|
.ThenOrdering(i => i.Name)
|
|
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild));
|
|
|
|
return items;
|
|
});
|
|
using var __ = Defer(() =>
|
|
SelectedsChildren.Subscribe(c => UpdateConsumer(c, ref _selectedsChildrenConsumer))
|
|
);
|
|
|
|
ParentsChildren = CurrentLocation.Map(async (item, _) =>
|
|
{
|
|
if (item is null || item.Parent is null) return (ObservableCollection<IItemViewModel>?) null;
|
|
var parent = (IContainer) await item.Parent.ResolveAsync();
|
|
|
|
var items = parent.Items
|
|
.Selecting(i => MapItem(i))
|
|
.Ordering(i => i.Type)
|
|
.ThenOrdering(i => i.Name)
|
|
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.Parent));
|
|
|
|
return items;
|
|
});
|
|
using var ___ = Defer(() =>
|
|
ParentsChildren.Subscribe(c => UpdateConsumer(c, ref _parentsChildrenConsumer))
|
|
);
|
|
|
|
tab.CurrentLocation.Subscribe(_ => _markedItems.Clear()).AddToDisposables(_disposables);
|
|
}
|
|
|
|
|
|
static void UpdateConsumer<T>(ObservableCollection<T>? collection, ref OcConsumer? consumer)
|
|
{
|
|
if (collection is not IComputing computing) return;
|
|
|
|
consumer?.Dispose();
|
|
consumer = new OcConsumer();
|
|
computing.For(consumer);
|
|
}
|
|
|
|
private static SortExpressionComparer<IItemViewModel> SortItems()
|
|
//TODO: Order
|
|
=> SortExpressionComparer<IItemViewModel>
|
|
.Ascending(i => i.BaseItem?.Type ?? AbsolutePathType.Unknown)
|
|
.ThenByAscending(i => i.DisplayNameText?.ToLower() ?? "");
|
|
|
|
private static IItem MapItem(AbsolutePath item)
|
|
{
|
|
var t = Task.Run(async () => await MapItemAsync(item));
|
|
t.Wait();
|
|
return t.Result;
|
|
}
|
|
|
|
private static async Task<IItem> MapItemAsync(AbsolutePath item)
|
|
=> await item.ResolveAsync(forceResolve: true,
|
|
itemInitializationSettings: new ItemInitializationSettings {SkipChildInitialization = true});
|
|
|
|
private IItemViewModel MapItemToViewModel(IItem item, ItemViewModelType type)
|
|
{
|
|
if (item is IContainer container)
|
|
{
|
|
var containerViewModel = _serviceProvider
|
|
.GetInitableResolver<IContainer, ITabViewModel, ItemViewModelType>(container, this, type)
|
|
.GetRequiredService<IContainerViewModel>();
|
|
|
|
return containerViewModel;
|
|
}
|
|
else if (item is IElement element)
|
|
{
|
|
var fileExtension = element.GetExtension<FileExtension>();
|
|
|
|
if (fileExtension is not null)
|
|
{
|
|
var fileViewModel = _serviceProvider
|
|
.GetInitableResolver<IElement, FileExtension, ITabViewModel, ItemViewModelType>(
|
|
element, fileExtension, this, type)
|
|
.GetRequiredService<IFileViewModel>();
|
|
|
|
return fileViewModel;
|
|
}
|
|
else
|
|
{
|
|
var elementViewModel = _serviceProvider
|
|
.GetInitableResolver<IElement, ITabViewModel, ItemViewModelType>(element, this, type)
|
|
.GetRequiredService<IElementViewModel>();
|
|
|
|
return elementViewModel;
|
|
}
|
|
}
|
|
|
|
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
|
|
}
|
|
|
|
public void AddMarkedItem(FullName fullName) => _markedItems.Add(fullName);
|
|
|
|
public void RemoveMarkedItem(FullName fullName)
|
|
{
|
|
var itemsToRemove = _markedItems.Where(i => i.Path == fullName.Path).ToList();
|
|
|
|
_markedItems.RemoveMany(itemsToRemove);
|
|
}
|
|
|
|
public void ToggleMarkedItem(FullName fullName)
|
|
{
|
|
if (_markedItems.Any(i => i.Path == fullName.Path))
|
|
{
|
|
RemoveMarkedItem(fullName);
|
|
}
|
|
else
|
|
{
|
|
AddMarkedItem(fullName);
|
|
}
|
|
}
|
|
|
|
public void ClearMarkedItems()
|
|
=> _markedItems.Clear();
|
|
|
|
~TabViewModel() => Dispose(false);
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (!_disposed && disposing)
|
|
{
|
|
foreach (var disposable in _disposables)
|
|
{
|
|
try
|
|
{
|
|
disposable.Dispose();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
_disposed = true;
|
|
}
|
|
} |