Top navigation, search delete

This commit is contained in:
2023-07-28 17:10:50 +02:00
parent ee42e38e45
commit ed6864e127
27 changed files with 320 additions and 149 deletions

View File

@@ -1,8 +1,10 @@
using System.Collections.ObjectModel;
namespace FileTime.Core.ContentAccess;
public interface IContentProviderRegistry
{
IEnumerable<IContentProvider> ContentProviders { get; }
ReadOnlyObservableCollection<IContentProvider> ContentProviders { get; }
void AddContentProvider(IContentProvider contentProvider);
void RemoveContentProvider(IContentProvider contentProvider);
}

View File

@@ -0,0 +1,6 @@
namespace FileTime.Core.ContentAccess;
public interface IRootContentProvider : IContentProvider
{
}

View File

@@ -4,25 +4,29 @@ public record FullName(string Path)
{
public FullName? GetParent()
{
if (Path.Length == 0) return null;
var pathParts = Path.TrimEnd(Constants.SeparatorChar).Split(Constants.SeparatorChar);
return pathParts.Length switch
{
> 1 => CreateSafe(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))),
_ => null
_ => CreateSafe("")
};
}
public static FullName? CreateSafe(string? path)
{
if (string.IsNullOrWhiteSpace(path))
if (path is null)
return null;
if (string.IsNullOrWhiteSpace(path))
return new FullName("");
return new(path);
}
public string GetName()
=> Path.Split(Constants.SeparatorChar).Last();
public FullName GetChild(string childName)
public FullName GetChild(string childName)
=> new(Path + Constants.SeparatorChar + childName);
public override string ToString() => Path;

View File

@@ -1,11 +1,7 @@
namespace FileTime.Core.Models;
public readonly struct ItemInitializationSettings
public sealed class ItemInitializationSettings
{
public readonly bool SkipChildInitialization;
public ItemInitializationSettings(bool skipChildInitialization)
{
SkipChildInitialization = skipChildInitialization;
}
public bool SkipChildInitialization { get; init; }
public AbsolutePath? Parent { get; init; }
}

View File

@@ -52,7 +52,10 @@ public class DeleteCommand : CommandBase, IExecutableCommand
new DeleteStrategy()
);
var parents = ItemsToDelete.Select(i => i.GetParent()).OfType<FullName>().Distinct();
var parents = ItemsToDelete
.Select(i => i.GetParent())
.OfType<FullName>()
.Distinct();
foreach (var parent in parents)
{
await _commandSchedulerNotifier.RefreshContainer(parent);
@@ -68,18 +71,26 @@ public class DeleteCommand : CommandBase, IExecutableCommand
foreach (var itemToDeleteName in itemsToDelete)
{
var itemToDelete = await _timelessContentProvider.GetItemByFullNameAsync(itemToDeleteName, currentTime);
IItemDeleter itemDeleter;
IItemDeleter? itemDeleter = null;
if (itemDeleters.ContainsKey(itemToDelete.Provider.Name))
if (itemDeleters.TryGetValue(itemToDelete.Provider.Name, out var deleter))
{
itemDeleter = itemDeleters[itemToDelete.Provider.Name];
itemDeleter = deleter;
}
else
{
itemDeleter = _contentAccessorFactory.GetItemDeleter(itemToDelete.Provider);
itemDeleters.Add(itemToDelete.Provider.Name, itemDeleter);
try
{
itemDeleter = _contentAccessorFactory.GetItemDeleter(itemToDelete.Provider);
itemDeleters.Add(itemToDelete.Provider.Name, itemDeleter);
}
catch
{
}
}
if (itemDeleter is null) continue;
if (itemToDelete is IContainer container)
{
await TraverseTree(
@@ -95,7 +106,7 @@ public class DeleteCommand : CommandBase, IExecutableCommand
}
}
await itemDeleter.DeleteAsync(itemToDelete.Provider, itemToDelete.FullName!);
await deleteStrategy.DeleteItem(itemToDelete, itemDeleter);
}
}
}

View File

@@ -5,8 +5,6 @@ namespace FileTime.Core.Command.Delete;
public class DeleteStrategy : IDeleteStrategy
{
public async Task DeleteItem(IItem item, IItemDeleter deleter)
{
await deleter.DeleteAsync(item.Provider, item.FullName!);
}
public async Task DeleteItem(IItem item, IItemDeleter deleter)
=> await deleter.DeleteAsync(item.Provider, item.FullName!);
}

View File

@@ -32,7 +32,7 @@ public abstract class ContentProviderBase : IContentProvider
public IContentProvider Provider => this;
public AbsolutePath? Parent => null;
public AbsolutePath? Parent { get; }
public DateTime? CreatedAt => null;
@@ -46,14 +46,15 @@ public abstract class ContentProviderBase : IContentProvider
public bool AllowRecursiveDeletion => false;
public AbsolutePathType Type => AbsolutePathType.Container;
public PointInTime PointInTime { get; } = PointInTime.Eternal;
public PointInTime PointInTime => PointInTime.Eternal;
public ObservableCollection<Exception> Exceptions { get; } = new();
ReadOnlyExtensionCollection IItem.Extensions => _extensions;
protected ContentProviderBase(string name)
protected ContentProviderBase(string name, ITimelessContentProvider timelessContentProvider)
{
Parent = new AbsolutePath(timelessContentProvider, PointInTime.Eternal, new FullName(""), AbsolutePathType.Container);
DisplayName = Name = name;
FullName = FullName.CreateSafe(name);
Extensions = new ExtensionCollection();

View File

@@ -1,21 +1,64 @@
using System.Collections.ObjectModel;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.ContentAccess;
public class ContentProviderRegistry : IContentProviderRegistry
{
private readonly object _lock = new();
private readonly IServiceProvider _serviceProvider;
private readonly Lazy<IList<IContentProvider>> _defaultContentProviders;
private readonly List<IContentProvider> _additionalContentProviders = new();
private readonly ObservableCollection<IContentProvider> _contentProviders = new();
private readonly ReadOnlyObservableCollection<IContentProvider> _contentProvidersReadOnly;
private bool _initialized;
public ContentProviderRegistry(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_defaultContentProviders = new Lazy<IList<IContentProvider>>(() => serviceProvider.GetServices<IContentProvider>().ToList());
_contentProvidersReadOnly = new ReadOnlyObservableCollection<IContentProvider>(_contentProviders);
}
public IEnumerable<IContentProvider> ContentProviders => _defaultContentProviders.Value.Concat(_additionalContentProviders);
public ReadOnlyObservableCollection<IContentProvider> ContentProviders
{
get
{
InitializeContentProviderListIfNeeded();
return _contentProvidersReadOnly;
}
}
public void AddContentProvider(IContentProvider contentProvider) => _additionalContentProviders.Add(contentProvider);
public void RemoveContentProvider(IContentProvider contentProvider) => _additionalContentProviders.Remove(contentProvider);
private void InitializeContentProviderListIfNeeded()
{
lock (_lock)
{
if (!_initialized)
{
foreach (var contentProvider in _serviceProvider.GetServices<IContentProvider>())
{
_contentProviders.Add(contentProvider);
}
_initialized = true;
}
}
}
public void AddContentProvider(IContentProvider contentProvider)
{
InitializeContentProviderListIfNeeded();
lock (_lock)
{
_contentProviders.Add(contentProvider);
}
}
public void RemoveContentProvider(IContentProvider contentProvider)
{
InitializeContentProviderListIfNeeded();
lock (_lock)
{
_contentProviders.Remove(contentProvider);
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using ObservableComputations;
namespace FileTime.Core.ContentAccess;
public class RootContentProvider : IRootContentProvider
{
private readonly ITimelessContentProvider _timelessContentProvider;
public string Name => "root";
public string DisplayName => "Root";
public FullName? FullName => null;
public NativePath? NativePath => null;
public AbsolutePath? Parent => null;
public bool IsHidden => false;
public bool IsExists => true;
public DateTime? CreatedAt => null;
public SupportsDelete CanDelete => SupportsDelete.False;
public bool CanRename => false;
public IContentProvider Provider => this;
public string? Attributes => null;
public AbsolutePathType Type => AbsolutePathType.Container;
public PointInTime PointInTime => PointInTime.Eternal;
public ObservableCollection<Exception> Exceptions { get; } = new();
public ReadOnlyExtensionCollection Extensions { get; } = new(new ExtensionCollection());
public ObservableCollection<AbsolutePath> Items { get; }
public IObservable<bool> IsLoading => Observable.Return(false);
public bool? IsLoaded => true;
public Task WaitForLoaded(CancellationToken token = default) => Task.CompletedTask;
public bool AllowRecursiveDeletion => false;
public Task OnEnter() => Task.CompletedTask;
public bool SupportsContentStreams => false;
public RootContentProvider(
IContentProviderRegistry contentProviderRegistry,
ITimelessContentProvider timelessContentProvider)
{
_timelessContentProvider = timelessContentProvider;
Items = contentProviderRegistry
.ContentProviders
.Selecting<IContentProvider, AbsolutePath>(c =>
new AbsolutePath(timelessContentProvider, c)
);
}
public async Task<IItem> GetItemByFullNameAsync(
FullName fullName,
PointInTime pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default)
=> await _timelessContentProvider.GetItemByFullNameAsync(
fullName,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings);
public async Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default)
=> await _timelessContentProvider.GetItemByNativePathAsync(
nativePath,
pointInTime
) ?? throw new FileNotFoundException();
public NativePath GetNativePath(FullName fullName) => throw new NotImplementedException();
public FullName GetFullName(NativePath nativePath) => throw new NotImplementedException();
public Task<byte[]?> GetContentAsync(
IElement element,
int? maxLength = null,
CancellationToken cancellationToken = default) => throw new NotImplementedException();
public bool CanHandlePath(NativePath path) => throw new NotImplementedException();
public bool CanHandlePath(FullName path) => throw new NotImplementedException();
}

View File

@@ -140,7 +140,7 @@ public class Tab : ITab
{
var itemNameToSelect = LastDeepestSelectedPath.Path
.Split(Constants.SeparatorChar)
.Skip(parentPath.Split(Constants.SeparatorChar).Length)
.Skip(parentPath == "" ? 0 : parentPath.Split(Constants.SeparatorChar).Length)
.FirstOrDefault();
var itemToSelect = items.FirstOrDefault(i => i.FullName?.GetName() == itemNameToSelect);

View File

@@ -2,18 +2,24 @@ using System.Reactive.Subjects;
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.Timeline;
public class TimelessContentProvider : ITimelessContentProvider
{
private readonly IContentProviderRegistry _contentProviderRegistry;
private readonly Lazy<IRootContentProvider> _rootContentProvider;
public BehaviorSubject<PointInTime> CurrentPointInTime { get; } = new(PointInTime.Present);
public TimelessContentProvider(IContentProviderRegistry contentProviderRegistry)
public TimelessContentProvider(
IContentProviderRegistry contentProviderRegistry,
IServiceProvider serviceProvider
)
{
_contentProviderRegistry = contentProviderRegistry;
_rootContentProvider = new Lazy<IRootContentProvider>(serviceProvider.GetRequiredService<IRootContentProvider>);
}
public async Task<IItem> GetItemByFullNameAsync(FullName fullName, PointInTime? pointInTime,
@@ -23,6 +29,8 @@ public class TimelessContentProvider : ITimelessContentProvider
{
//TODO time modifications
var contentProviderName = fullName.Path.Split(Constants.SeparatorChar).FirstOrDefault();
if (contentProviderName == "") return _rootContentProvider.Value;
var contentProvider = _contentProviderRegistry.ContentProviders.FirstOrDefault(p => p.Name == contentProviderName);
if (contentProvider is null)
@@ -37,7 +45,7 @@ public class TimelessContentProvider : ITimelessContentProvider
{
foreach (var contentProvider in _contentProviderRegistry.ContentProviders)
{
if(!contentProvider.CanHandlePath(nativePath)) continue;
if (!contentProvider.CanHandlePath(nativePath)) continue;
return await contentProvider.GetItemByNativePathAsync(nativePath, pointInTime ?? PointInTime.Present);
}
@@ -49,7 +57,7 @@ public class TimelessContentProvider : ITimelessContentProvider
{
foreach (var contentProvider in _contentProviderRegistry.ContentProviders)
{
if(!contentProvider.CanHandlePath(nativePath)) continue;
if (!contentProvider.CanHandlePath(nativePath)) continue;
return contentProvider.GetFullName(nativePath);
}
@@ -61,7 +69,7 @@ public class TimelessContentProvider : ITimelessContentProvider
{
foreach (var contentProvider in _contentProviderRegistry.ContentProviders)
{
if(!contentProvider.CanHandlePath(fullName)) continue;
if (!contentProvider.CanHandlePath(fullName)) continue;
return contentProvider.GetNativePath(fullName);
}