Refactor: items with DynamicData

This commit is contained in:
2022-04-22 09:09:14 +02:00
parent da3ccf4317
commit 76c6e30154
31 changed files with 281 additions and 141 deletions

View File

@@ -1,15 +1,19 @@
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DynamicData;
using FileTime.App.Core.Extensions;
using FileTime.App.Core.Models;
using FileTime.App.Core.Services;
using FileTime.Core.Models;
using FileTime.Core.Services;
using FileTime.Tools.Extensions;
using Microsoft.Extensions.DependencyInjection;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
{
public class TabViewModel : ITabViewModel, IDisposable
[ViewModel]
public partial class TabViewModel : ITabViewModel, IDisposable
{
private readonly IServiceProvider _serviceProvider;
private readonly IItemNameConverterService _itemNameConverterService;
@@ -26,10 +30,22 @@ namespace FileTime.App.Core.ViewModels
public IObservable<IContainer?> CurrentLocation { get; private set; } = null!;
public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!;
public IObservable<IReadOnlyList<IItemViewModel>> CurrentItems { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; private set; } = null!;
public IObservable<IEnumerable<FullName>> MarkedItems { get; }
public IObservable<IReadOnlyList<IItemViewModel>?> SelectedsChildren { get; private set; } = null!;
public IObservable<IReadOnlyList<IItemViewModel>?> ParentsChildren { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
public IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; private set; } = null!;
[Property]
private BindedCollection<IItemViewModel>? _currentItemsCollection;
[Property]
private BindedCollection<IItemViewModel>? _parentsChildrenCollection;
[Property]
private BindedCollection<IItemViewModel>? _selectedsChildrenCollection;
public TabViewModel(
IServiceProvider serviceProvider,
@@ -53,100 +69,125 @@ namespace FileTime.App.Core.ViewModels
CurrentLocation = tab.CurrentLocation.AsObservable();
CurrentItems = tab.CurrentItems
.Select(items =>
items == null
? new List<IItemViewModel>()
: items.Select(MapItemToViewModel).ToList())
.Select(items => items?.Transform(MapItemToViewModel))
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(new List<IItemViewModel>())
.Publish(null)
.RefCount();
CurrentSelectedItem =
Observable.CombineLatest(
CurrentItems,
tab.CurrentSelectedItem,
(currentItems, currentSelectedItemPath) => currentItems.FirstOrDefault(i => i.BaseItem?.FullName == currentSelectedItemPath?.Path)
(currentItems, currentSelectedItemPath) =>
currentItems == null
? Observable.Return((IItemViewModel?)null)
: currentItems
.ToCollection()
.Select(items => items.FirstOrDefault(i => i.BaseItem?.FullName == currentSelectedItemPath?.Path))
)
.Switch()
.Publish(null)
.RefCount();
var currentSelectedItemThrottled = CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)).Publish(null).RefCount();
SelectedsChildren = Observable.Merge(
currentSelectedItemThrottled
.WhereNotNull()
.OfType<IContainerViewModel>()
.Where(c => c?.Container is not null)
.Select(c => c.Container!.Items)
.Switch()
.Select(items => Observable.FromAsync(async () => await Map(items)))
.Switch()
.Select(items => items?.Select(MapItemToViewModel).ToList()),
currentSelectedItemThrottled
.Where(c => c is null || c is not IContainerViewModel)
.Select(_ => (IReadOnlyList<IItemViewModel>?)null)
)
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
SelectedsChildren = InitSelectedsChildren();
ParentsChildren = InitParentsChildren();
var parentThrottled = CurrentLocation
.Select(l => l?.Parent)
.DistinctUntilChanged()
CurrentItemsCollectionObservable = CurrentItems
.Select(c => c != null ? c.ToCollection() : Observable.Return((IReadOnlyCollection<IItemViewModel>?)null))
.Switch()
.Publish(null)
.RefCount();
ParentsChildren = Observable.Merge(
parentThrottled
.Where(p => p is not null)
.Select(p => Observable.FromAsync(async () => (IContainer)await p!.ResolveAsync()))
.Switch()
.Select(p => p.Items)
.Switch()
.Select(items => Observable.FromAsync(async () => await Map(items)))
.Switch()
.Select(items => items?.Select(MapItemToViewModel).ToList()),
parentThrottled
.Where(p => p is null)
.Select(_ => (IReadOnlyList<IItemViewModel>?)null)
)
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
CurrentItems.Subscribe(children =>
{
CurrentItemsCollection?.Dispose();
CurrentItemsCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
ParentsChildren.Subscribe(children =>
{
ParentsChildrenCollection?.Dispose();
ParentsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
SelectedsChildren.Subscribe(children =>
{
SelectedsChildrenCollection?.Dispose();
SelectedsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
tab.CurrentLocation.Subscribe((_) => _markedItems.OnNext(Enumerable.Empty<FullName>()));
static async Task<List<IItem>?> Map(IEnumerable<IAbsolutePath>? items)
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitSelectedsChildren()
{
if (items == null) return null;
var currentSelectedItemThrottled = CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)).Publish(null).RefCount();
return Observable.Merge(
currentSelectedItemThrottled
.WhereNotNull()
.OfType<IContainerViewModel>()
.Where(c => c?.Container is not null)
.Select(c => c.Container!.Items)
.Switch()
.Select(i => i?.TransformAsync(MapItem).Transform(MapItemToViewModel)),
currentSelectedItemThrottled
.Where(c => c is null || c is not IContainerViewModel)
.Select(_ => (IObservable<IChangeSet<IItemViewModel>>?)null)
)
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
}
return await items
.ToAsyncEnumerable()
.SelectAwait(async i => await i.ResolveAsync(forceResolve: true, itemInitializationSettings: new ItemInitializationSettings(true)))
.ToListAsync();
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitParentsChildren()
{
var parentThrottled = CurrentLocation
.Select(l => l?.Parent)
.DistinctUntilChanged()
.Publish(null)
.RefCount();
return Observable.Merge(
parentThrottled
.Where(p => p is not null)
.Select(p => Observable.FromAsync(async () => (IContainer)await p!.ResolveAsync()))
.Switch()
.Select(p => p.Items)
.Switch()
.Select(items => items?.TransformAsync(MapItem).Transform(MapItemToViewModel)),
parentThrottled
.Where(p => p is null)
.Select(_ => (IObservable<IChangeSet<IItemViewModel>>?)null)
)
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
}
}
private IItemViewModel MapItemToViewModel(IItem item, int index)
private static async Task<IItem> MapItem(IAbsolutePath item)
=> await item.ResolveAsync(forceResolve: true, itemInitializationSettings: new ItemInitializationSettings(true));
private IItemViewModel MapItemToViewModel(IItem item)
{
if (item is IContainer container)
{
var containerViewModel = _serviceProvider.GetInitableResolver<IContainer, ITabViewModel, int>(container, this, index).GetRequiredService<IContainerViewModel>();
var containerViewModel = _serviceProvider.GetInitableResolver<IContainer, ITabViewModel>(container, this).GetRequiredService<IContainerViewModel>();
return containerViewModel;
}
else if (item is IFileElement fileElement)
{
var fileViewModel = _serviceProvider.GetInitableResolver<IFileElement, ITabViewModel, int>(fileElement, this, index).GetRequiredService<IFileViewModel>();
var fileViewModel = _serviceProvider.GetInitableResolver<IFileElement, ITabViewModel>(fileElement, this).GetRequiredService<IFileViewModel>();
fileViewModel.Size = fileElement.Size;
return fileViewModel;
}
else if (item is IElement element)
{
var elementViewModel = _serviceProvider.GetInitableResolver<IElement, ITabViewModel, int>(element, this, index).GetRequiredService<IElementViewModel>();
var elementViewModel = _serviceProvider.GetInitableResolver<IElement, ITabViewModel>(element, this).GetRequiredService<IElementViewModel>();
return elementViewModel;
}