New reactive core WIP

This commit is contained in:
2023-07-21 22:15:48 +02:00
parent 342fc0d047
commit b61c204e49
61 changed files with 1605 additions and 413 deletions

View File

@@ -11,12 +11,15 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ObservableComputations" Version="2.3.0" />
<PackageReference Include="System.Interactive.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\Library\Defer\Defer.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.Core.Models\FileTime.Core.Models.csproj" />

View File

@@ -1,11 +1,16 @@
using System.Reactive.Linq;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using DeclarativeProperty;
using DynamicData;
using DynamicData.Alias;
using DynamicData.Binding;
using FileTime.App.Core.Services;
using FileTime.Core.Helper;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using ObservableComputations;
using static System.DeferTools;
namespace FileTime.Core.Services;
@@ -13,27 +18,46 @@ public class Tab : ITab
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly ITabEvents _tabEvents;
private readonly BehaviorSubject<IContainer?> _currentLocation = new(null);
private readonly IRefreshSmoothnessCalculator _refreshSmoothnessCalculator;
private readonly DeclarativeProperty<IContainer?> _currentLocation = new(null);
private readonly BehaviorSubject<IContainer?> _currentLocationForced = new(null);
private readonly BehaviorSubject<AbsolutePath?> _currentSelectedItem = new(null);
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new(null);
private readonly SourceList<ItemFilter> _itemFilters = new();
private AbsolutePath? _currentSelectedItemCached;
private PointInTime _currentPointInTime;
private OcConsumer? _currentItemsConsumer;
private CancellationTokenSource? _setCurrentLocationCancellationTokenSource;
private CancellationTokenSource? _setCurrentItemCancellationTokenSource;
public IObservable<IContainer?> CurrentLocation { get; }
public IObservable<IObservable<IChangeSet<IItem, string>>?> CurrentItems { get; }
public IObservable<AbsolutePath?> CurrentSelectedItem { get; }
public IDeclarativeProperty<IContainer?> CurrentLocation { get; }
public IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; }
public IDeclarativeProperty<AbsolutePath?> CurrentSelectedItem { get; }
public FullName? LastDeepestSelectedPath { get; private set; }
public Tab(ITimelessContentProvider timelessContentProvider, ITabEvents tabEvents)
public Tab(
ITimelessContentProvider timelessContentProvider,
ITabEvents tabEvents,
IRefreshSmoothnessCalculator refreshSmoothnessCalculator)
{
_timelessContentProvider = timelessContentProvider;
_tabEvents = tabEvents;
_refreshSmoothnessCalculator = refreshSmoothnessCalculator;
_currentPointInTime = null!;
_timelessContentProvider.CurrentPointInTime.Subscribe(p => _currentPointInTime = p);
CurrentLocation = _currentLocation
CurrentLocation = _currentLocation;
CurrentLocation.Subscribe((c, _) =>
{
if (_currentSelectedItemCached is not null)
{
LastDeepestSelectedPath = FullName.CreateSafe(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, _currentSelectedItemCached.Path.Path));
}
return Task.CompletedTask;
});
/*CurrentLocation = _currentLocation
.DistinctUntilChanged()
.Merge(_currentLocationForced)
.Do(_ =>
@@ -44,14 +68,43 @@ public class Tab : ITab
}
})
.Publish(null)
.RefCount();
.RefCount();*/
CurrentItems =
CurrentItems = CurrentLocation.Map((container, _) =>
{
var items = container is null
? (ObservableCollection<IItem>?) null
: container.Items.Selecting<AbsolutePath, IItem>(i => MapItem(i));
return Task.FromResult(items);
}
) /*.Watch<ObservableCollection<IItem>, IItem>()*/;
/*using var _ = Defer(() =>
CurrentItems.Subscribe(c => UpdateConsumer(c, ref _currentItemsConsumer))
);*/
/*CurrentItems.RegisterTrigger(
(sender, items) =>
{
if (items is null)
return null;
items.CollectionChanged += Handler;
return Disposable.Create(() => items.CollectionChanged -= Handler);
void Handler(object? o, NotifyCollectionChangedEventArgs e)
{
var t = Task.Run(async () => await sender.ReFireAsync());
t.Wait();
}
});*/
/*CurrentItems =
Observable.Merge(
Observable.CombineLatest(
CurrentLocation
.Where(c => c is not null)
.Select(c => c!.Items)
.Select(c => c!.ItemsCollection)
.Select(items => items.TransformAsync(MapItem)),
_itemFilters.Connect().StartWithEmpty().ToCollection(),
(items, filters) =>
@@ -66,9 +119,26 @@ public class Tab : ITab
.Select(_ => (IObservable<IChangeSet<IItem, string>>?) null)
)
.Publish(null)
.RefCount();
.RefCount();*/
CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest(
CurrentItems.Watch<ObservableCollection<IItem>, IItem>(),
_currentRequestItem.DistinctUntilChanged(),
(items, selected) =>
{
if (selected != null && (items?.Any(i => i.FullName == selected.Path) ?? true)) return Task.FromResult<AbsolutePath?>(selected);
if (items == null || items.Count == 0) return Task.FromResult<AbsolutePath?>(null);
CurrentSelectedItem =
return Task.FromResult(GetSelectedItemByItems(items));
}).DistinctUntilChanged();
CurrentSelectedItem.Subscribe((v) =>
{
_refreshSmoothnessCalculator.RegisterChange();
_refreshSmoothnessCalculator.RecalculateSmoothness();
});
/*CurrentSelectedItem =
Observable.CombineLatest(
CurrentItems
.Select(c =>
@@ -88,27 +158,40 @@ public class Tab : ITab
)
.DistinctUntilChanged()
.Publish(null)
.RefCount();
.RefCount();*/
CurrentSelectedItem.Subscribe(s =>
CurrentSelectedItem.Subscribe(async (s, _) =>
{
_currentSelectedItemCached = s;
_currentSelectedItem.OnNext(s);
await _currentRequestItem.SetValue(s);
});
}
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 IItem MapItem(AbsolutePath item)
{
var t = Task.Run(async () => await item.ResolveAsync(true));
t.Wait();
return t.Result;
}
private static SortExpressionComparer<IItem> SortItems()
//TODO: Order
=> SortExpressionComparer<IItem>
.Ascending(i => i.Type)
.ThenByAscending(i => i.DisplayName.ToLower());
private async Task<IItem> MapItem(AbsolutePath item) => await item.ResolveAsync(true);
public void Init(IContainer currentLocation)
{
_currentLocation.OnNext(currentLocation);
}
public async Task InitAsync(IContainer currentLocation)
=> await _currentLocation.SetValue(currentLocation);
private AbsolutePath? GetSelectedItemByItems(IReadOnlyCollection<IItem> items)
{
@@ -140,9 +223,11 @@ public class Tab : ITab
return newSelectedItem;
}
public void SetCurrentLocation(IContainer newLocation)
public async Task SetCurrentLocation(IContainer newLocation)
{
_currentLocation.OnNext(newLocation);
_setCurrentLocationCancellationTokenSource?.Cancel();
_setCurrentLocationCancellationTokenSource = new CancellationTokenSource();
await _currentLocation.SetValue(newLocation, _setCurrentLocationCancellationTokenSource.Token);
if (newLocation.FullName != null)
{
@@ -150,9 +235,11 @@ public class Tab : ITab
}
}
public void ForceSetCurrentLocation(IContainer newLocation)
public async Task ForceSetCurrentLocation(IContainer newLocation)
{
_currentLocationForced.OnNext(newLocation);
_setCurrentLocationCancellationTokenSource?.Cancel();
_setCurrentLocationCancellationTokenSource = new CancellationTokenSource();
await _currentLocation.SetValue(newLocation, _setCurrentLocationCancellationTokenSource.Token);
if (newLocation.FullName != null)
{
@@ -160,7 +247,12 @@ public class Tab : ITab
}
}
public void SetSelectedItem(AbsolutePath newSelectedItem) => _currentSelectedItem.OnNext(newSelectedItem);
public async Task SetSelectedItem(AbsolutePath newSelectedItem)
{
_setCurrentItemCancellationTokenSource?.Cancel();
_setCurrentItemCancellationTokenSource = new CancellationTokenSource();
await _currentRequestItem.SetValue(newSelectedItem, _setCurrentItemCancellationTokenSource.Token);
}
public void AddItemFilter(ItemFilter filter) => _itemFilters.Add(filter);
public void RemoveItemFilter(ItemFilter filter) => _itemFilters.Remove(filter);
@@ -178,13 +270,12 @@ public class Tab : ITab
await _currentSelectedItemCached.TimelessProvider.GetItemByFullNameAsync(_currentSelectedItemCached.Path, _currentPointInTime);
if (resolvedSelectedItem is not IContainer resolvedContainer) return;
SetCurrentLocation(resolvedContainer);
await SetCurrentLocation(resolvedContainer);
}
public void Dispose()
{
_currentLocation.Dispose();
_currentSelectedItem.Dispose();
_itemFilters.Dispose();
}
}