ItemsCollection on Container
This commit is contained in:
@@ -195,8 +195,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
|||||||
var resolvedOnlyItem = await _timelessContentProvider.GetItemByFullNameAsync(itemsToDelete[0], PointInTime.Present);
|
var resolvedOnlyItem = await _timelessContentProvider.GetItemByFullNameAsync(itemsToDelete[0], PointInTime.Present);
|
||||||
|
|
||||||
if (resolvedOnlyItem is IContainer {AllowRecursiveDeletion: true} onlyContainer
|
if (resolvedOnlyItem is IContainer {AllowRecursiveDeletion: true} onlyContainer
|
||||||
&& await onlyContainer.Items.GetItemsAsync() is { } children
|
&& onlyContainer.ItemsCollection.Any())
|
||||||
&& children.Any())
|
|
||||||
{
|
{
|
||||||
questionText = $"The container '{onlyContainer.DisplayName}' is not empty. Proceed with delete?";
|
questionText = $"The container '{onlyContainer.DisplayName}' is not empty. Proceed with delete?";
|
||||||
}
|
}
|
||||||
@@ -206,7 +205,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemsToDelete?.Count == 0) return;
|
if (itemsToDelete.Count == 0) return;
|
||||||
|
|
||||||
if (questionText is { })
|
if (questionText is { })
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ public class SearchTask : ISearchTask
|
|||||||
|
|
||||||
private async Task TraverseTree(IContainer container)
|
private async Task TraverseTree(IContainer container)
|
||||||
{
|
{
|
||||||
var items = (await container.Items.GetItemsAsync())?.ToList();
|
var items = container.ItemsCollection.ToList();
|
||||||
if (items is null) return;
|
|
||||||
|
|
||||||
var childContainers = new List<IContainer>();
|
var childContainers = new List<IContainer>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
using System.Reactive.Linq;
|
|
||||||
using DynamicData;
|
|
||||||
using FileTime.Core.Models;
|
|
||||||
|
|
||||||
namespace FileTime.Core.Extensions;
|
|
||||||
|
|
||||||
public static class DynamicDataExtensions
|
|
||||||
{
|
|
||||||
private class DisposableContext<TParam, TTaskResult>
|
|
||||||
{
|
|
||||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
|
||||||
private readonly Func<TParam, TTaskResult> _transformResult;
|
|
||||||
private readonly TaskCompletionSource<TTaskResult?> _taskCompletionSource;
|
|
||||||
private bool _isFinished;
|
|
||||||
public IDisposable? Disposable { get; set; }
|
|
||||||
|
|
||||||
public DisposableContext(Func<TParam, TTaskResult> transformResult,
|
|
||||||
TaskCompletionSource<TTaskResult?> taskCompletionSource, IDisposable? disposable = null)
|
|
||||||
{
|
|
||||||
_transformResult = transformResult;
|
|
||||||
_taskCompletionSource = taskCompletionSource;
|
|
||||||
Disposable = disposable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNext(TParam param)
|
|
||||||
{
|
|
||||||
if (IsFinished()) return;
|
|
||||||
Disposable?.Dispose();
|
|
||||||
var result = _transformResult(param);
|
|
||||||
_taskCompletionSource.SetResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnError(Exception ex)
|
|
||||||
{
|
|
||||||
if (IsFinished()) return;
|
|
||||||
Disposable?.Dispose();
|
|
||||||
_taskCompletionSource.SetException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted()
|
|
||||||
{
|
|
||||||
if (IsFinished()) return;
|
|
||||||
Disposable?.Dispose();
|
|
||||||
_taskCompletionSource.SetResult(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsFinished()
|
|
||||||
{
|
|
||||||
_semaphore.Wait();
|
|
||||||
var finished = _isFinished;
|
|
||||||
_isFinished = true;
|
|
||||||
_semaphore.Release();
|
|
||||||
|
|
||||||
return finished;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<IEnumerable<AbsolutePath>?> GetItemsAsync(
|
|
||||||
this IObservable<IObservable<IChangeSet<AbsolutePath, string>>?> stream)
|
|
||||||
=> await GetItemsAsync(stream
|
|
||||||
.Select(s =>
|
|
||||||
s is null
|
|
||||||
? new SourceList<AbsolutePath>().Connect().StartWithEmpty().ToCollection()
|
|
||||||
: s.StartWithEmpty().ToCollection())
|
|
||||||
.Switch());
|
|
||||||
|
|
||||||
public static async Task<IEnumerable<AbsolutePath>?> GetItemsAsync(
|
|
||||||
this IObservable<IChangeSet<AbsolutePath, string>> stream)
|
|
||||||
=> await GetItemsAsync(stream.StartWithEmpty().ToCollection());
|
|
||||||
|
|
||||||
public static Task<IEnumerable<AbsolutePath>?> GetItemsAsync(
|
|
||||||
this IObservable<IReadOnlyCollection<AbsolutePath>> stream)
|
|
||||||
{
|
|
||||||
var taskCompletionSource = new TaskCompletionSource<IEnumerable<AbsolutePath>?>();
|
|
||||||
var context = new DisposableContext<IReadOnlyCollection<AbsolutePath>, IEnumerable<AbsolutePath>?>(r => r,
|
|
||||||
taskCompletionSource);
|
|
||||||
|
|
||||||
context.Disposable = stream
|
|
||||||
.Subscribe(
|
|
||||||
context.OnNext,
|
|
||||||
context.OnError,
|
|
||||||
context.OnCompleted
|
|
||||||
);
|
|
||||||
|
|
||||||
return taskCompletionSource.Task;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
|
|
||||||
namespace FileTime.Core.Models;
|
namespace FileTime.Core.Models;
|
||||||
@@ -5,6 +6,7 @@ namespace FileTime.Core.Models;
|
|||||||
public interface IContainer : IItem
|
public interface IContainer : IItem
|
||||||
{
|
{
|
||||||
IObservable<IChangeSet<AbsolutePath, string>> Items { get; }
|
IObservable<IChangeSet<AbsolutePath, string>> Items { get; }
|
||||||
|
ReadOnlyObservableCollection<AbsolutePath> ItemsCollection { get; }
|
||||||
IObservable<bool> IsLoading { get; }
|
IObservable<bool> IsLoading { get; }
|
||||||
bool AllowRecursiveDeletion { get; }
|
bool AllowRecursiveDeletion { get; }
|
||||||
}
|
}
|
||||||
@@ -111,13 +111,12 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
|
|
||||||
if (item is IContainer container)
|
if (item is IContainer container)
|
||||||
{
|
{
|
||||||
if (!((await resolvedTarget.Items.GetItemsAsync())?.Any(i => i.Path.GetName() == item.Name) ?? false))
|
if (resolvedTarget.ItemsCollection.All(i => i.Path.GetName() != item.Name))
|
||||||
{
|
{
|
||||||
await copyOperation.CreateContainerAsync(resolvedTarget, container.Name, container.PointInTime);
|
await copyOperation.CreateContainerAsync(resolvedTarget, container.Name, container.PointInTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = await container.Items.GetItemsAsync();
|
var children = container.ItemsCollection;
|
||||||
if (children is null) continue;
|
|
||||||
|
|
||||||
await TraverseTree(currentTime, children.Select(c => c.Path).ToList(), target.GetChild(item.Name, AbsolutePathType.Container), transportMode, copyOperation);
|
await TraverseTree(currentTime, children.Select(c => c.Path).ToList(), target.GetChild(item.Name, AbsolutePathType.Container), transportMode, copyOperation);
|
||||||
await copyOperation.ContainerCopyDoneAsync(new AbsolutePath(_timelessContentProvider, container));
|
await copyOperation.ContainerCopyDoneAsync(new AbsolutePath(_timelessContentProvider, container));
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ public abstract class CreateItemBase : CommandBase, IExecutableCommand, IInitabl
|
|||||||
var parent = await ResolveParentAsync();
|
var parent = await ResolveParentAsync();
|
||||||
if (parent is not IContainer parentContainer) return CanCommandRun.False;
|
if (parent is not IContainer parentContainer) return CanCommandRun.False;
|
||||||
|
|
||||||
var items = await parentContainer.Items.GetItemsAsync().AwaitWithTimeout(10, Enumerable.Empty<AbsolutePath>());
|
var items = parentContainer.ItemsCollection;
|
||||||
if (items is null) return CanCommandRun.Forcable;
|
|
||||||
|
|
||||||
var existingItem = items.FirstOrDefault(i => i.Path.GetName() == NewItemName);
|
var existingItem = items.FirstOrDefault(i => i.Path.GetName() == NewItemName);
|
||||||
|
|
||||||
return existingItem switch
|
return existingItem switch
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class DeleteCommand : CommandBase, IExecutableCommand
|
|||||||
{
|
{
|
||||||
await TraverseTree(
|
await TraverseTree(
|
||||||
currentTime,
|
currentTime,
|
||||||
(await container.Items.GetItemsAsync())?.Select(i => i.Path) ?? Enumerable.Empty<FullName>(),
|
container.ItemsCollection.Select(i => i.Path),
|
||||||
itemDeleters,
|
itemDeleters,
|
||||||
deleteStrategy
|
deleteStrategy
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public static class Helper
|
|||||||
{
|
{
|
||||||
public static async Task<string?> GetNewNameAsync(IContainer resolvedTarget, string name, TransportMode transportMode)
|
public static async Task<string?> GetNewNameAsync(IContainer resolvedTarget, string name, TransportMode transportMode)
|
||||||
{
|
{
|
||||||
var items = (await resolvedTarget.Items.GetItemsAsync() ?? throw new NullReferenceException()).ToList();
|
var items = resolvedTarget.ItemsCollection.ToList();
|
||||||
var newName = name;
|
var newName = name;
|
||||||
var targetNameExists = items.Any(i => i.Path.GetName() == newName);
|
var targetNameExists = items.Any(i => i.Path.GetName() == newName);
|
||||||
if (transportMode == TransportMode.Merge)
|
if (transportMode == TransportMode.Merge)
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ public class StreamCopyCommandHandler : ICommandHandler
|
|||||||
{
|
{
|
||||||
var parent = (IContainer?) (await targetPath.GetParent()!.ResolveAsync())!;
|
var parent = (IContainer?) (await targetPath.GetParent()!.ResolveAsync())!;
|
||||||
var elementName = targetPath.Path;
|
var elementName = targetPath.Path;
|
||||||
var parentChildren = await parent.Items.GetItemsAsync();
|
var parentChildren = parent.ItemsCollection.ToList();
|
||||||
if (parentChildren!.All(e => e.Path.GetName() != elementName.GetName()))
|
if (parentChildren.All(e => e.Path.GetName() != elementName.GetName()))
|
||||||
{
|
{
|
||||||
var itemCreator = _contentAccessorFactory.GetItemCreator(parent.Provider);
|
var itemCreator = _contentAccessorFactory.GetItemCreator(parent.Provider);
|
||||||
await itemCreator.CreateElementAsync(parent.Provider, elementName);
|
await itemCreator.CreateElementAsync(parent.Provider, elementName);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -13,6 +14,7 @@ public abstract class ContentProviderBase : IContentProvider
|
|||||||
private readonly IObservable<IChangeSet<AbsolutePath, string>> _items;
|
private readonly IObservable<IChangeSet<AbsolutePath, string>> _items;
|
||||||
|
|
||||||
protected SourceCache<AbsolutePath, string> Items { get; } = new(p => p.Path.Path);
|
protected SourceCache<AbsolutePath, string> Items { get; } = new(p => p.Path.Path);
|
||||||
|
public ReadOnlyObservableCollection<AbsolutePath> ItemsCollection { get; }
|
||||||
protected ExtensionCollection Extensions { get; }
|
protected ExtensionCollection Extensions { get; }
|
||||||
|
|
||||||
IObservable<IChangeSet<AbsolutePath, string>> IContainer.Items => _items;
|
IObservable<IChangeSet<AbsolutePath, string>> IContainer.Items => _items;
|
||||||
@@ -61,6 +63,8 @@ public abstract class ContentProviderBase : IContentProvider
|
|||||||
Extensions = new ExtensionCollection();
|
Extensions = new ExtensionCollection();
|
||||||
_extensions = Extensions.AsReadOnly();
|
_extensions = Extensions.AsReadOnly();
|
||||||
_items = Items.Connect().StartWithEmpty();
|
_items = Items.Connect().StartWithEmpty();
|
||||||
|
_items.Bind(out var items).Subscribe();
|
||||||
|
ItemsCollection = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task OnEnter() => Task.CompletedTask;
|
public virtual Task OnEnter() => Task.CompletedTask;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -26,11 +27,19 @@ public record Container(
|
|||||||
ReadOnlyExtensionCollection Extensions,
|
ReadOnlyExtensionCollection Extensions,
|
||||||
IObservable<IChangeSet<AbsolutePath, string>> Items) : IContainer
|
IObservable<IChangeSet<AbsolutePath, string>> Items) : IContainer
|
||||||
{
|
{
|
||||||
|
private readonly Lazy<ReadOnlyObservableCollection<AbsolutePath>> _itemsCollectionLazy =
|
||||||
|
new (() =>
|
||||||
|
{
|
||||||
|
Items.Bind(out var items).Subscribe();
|
||||||
|
return items;
|
||||||
|
});
|
||||||
private readonly CancellationTokenSource _loadingCancellationTokenSource = new();
|
private readonly CancellationTokenSource _loadingCancellationTokenSource = new();
|
||||||
public CancellationToken LoadingCancellationToken => _loadingCancellationTokenSource.Token;
|
public CancellationToken LoadingCancellationToken => _loadingCancellationTokenSource.Token;
|
||||||
public BehaviorSubject<bool> IsLoading { get; } = new(false);
|
public BehaviorSubject<bool> IsLoading { get; } = new(false);
|
||||||
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
|
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
|
||||||
public AbsolutePathType Type => AbsolutePathType.Container;
|
public AbsolutePathType Type => AbsolutePathType.Container;
|
||||||
|
|
||||||
|
public ReadOnlyObservableCollection<AbsolutePath> ItemsCollection => _itemsCollectionLazy.Value;
|
||||||
|
|
||||||
public void CancelLoading() => _loadingCancellationTokenSource.Cancel();
|
public void CancelLoading() => _loadingCancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user