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

@@ -15,32 +15,17 @@ public class TabPersistenceService : ITabPersistenceService
private readonly IAppState _appState;
private readonly ILogger<TabPersistenceService> _logger;
private class PersistenceRoot
//TODO: make this a configuration maybe?
private readonly List<string> _contentProvidersNotToRestore = new()
{
public TabStates? TabStates { get; set; }
}
"search"
};
private class TabStates
{
public List<TabState>? Tabs { get; set; }
public int? ActiveTabNumber { get; set; }
}
private record PersistenceRoot(TabStates? TabStates);
private class TabState
{
public string? Path { get; set; }
public int Number { get; set; }
private record TabStates(List<TabState>? Tabs, int? ActiveTabNumber);
public TabState()
{
}
public TabState(FullName path, int number)
{
Path = path.Path;
Number = number;
}
}
private record TabState(string? Path, int Number);
private readonly string _settingsPath;
private readonly JsonSerializerOptions _jsonOptions;
@@ -149,7 +134,7 @@ public class TabPersistenceService : ITabPersistenceService
}
catch
{
path = path.GetParent();
path = path?.GetParent();
if (path == null)
{
throw new Exception($"Could not find an initializable path along {tab.Path}");
@@ -159,6 +144,8 @@ public class TabPersistenceService : ITabPersistenceService
if (container == null) continue;
if (_contentProvidersNotToRestore.Contains(container.Provider.Name)) continue;
var tabToLoad = await _serviceProvider.GetAsyncInitableResolver(container)
.GetRequiredServiceAsync<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tabToLoad, tab.Number)
@@ -181,7 +168,15 @@ public class TabPersistenceService : ITabPersistenceService
if (_appState.Tabs.Count == 0) return false;
var tabToActivate = _appState.Tabs.FirstOrDefault(t => t.TabNumber == tabStates.ActiveTabNumber);
var optimalTabs = _appState
.Tabs
.TakeWhile(t => t.TabNumber <= tabStates.ActiveTabNumber)
.Reverse();
var suboptimalTabs = _appState
.Tabs
.SkipWhile(t => t.TabNumber <= tabStates.ActiveTabNumber);
var tabToActivate = optimalTabs.Concat(suboptimalTabs).FirstOrDefault();
if (tabToActivate is not null) _appState.SetSelectedTab(tabToActivate);
return true;
@@ -189,10 +184,8 @@ public class TabPersistenceService : ITabPersistenceService
public void SaveStates(CancellationToken token = default)
{
var state = new PersistenceRoot
{
TabStates = SerializeTabStates()
};
var state = new PersistenceRoot(SerializeTabStates());
var settingsDirectory = new DirectoryInfo(string.Join(Path.DirectorySeparatorChar,
_settingsPath.Split(Path.DirectorySeparatorChar)[0..^1]));
if (!settingsDirectory.Exists) settingsDirectory.Create();
@@ -207,16 +200,15 @@ public class TabPersistenceService : ITabPersistenceService
{
var currentLocation = tab.CurrentLocation.Value;
if (currentLocation is null) continue;
tabStates.Add(new TabState(currentLocation.FullName!, tab.TabNumber));
tabStates.Add(new TabState(currentLocation.FullName!.Path, tab.TabNumber));
}
return new TabStates
{
Tabs = tabStates,
ActiveTabNumber = _appState.CurrentSelectedTab?.TabNumber
};
return new TabStates(
tabStates,
_appState.CurrentSelectedTab?.TabNumber
);
}
public async Task InitAsync()
public async Task InitAsync()
=> await LoadStatesAsync();
}

View File

@@ -10,7 +10,6 @@ using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels;
using FileTime.Core.Command;
using FileTime.Core.Command.CreateContainer;
using FileTime.Core.Command.CreateElement;
using FileTime.Core.Command.Move;
using FileTime.Core.Extensions;
using FileTime.Core.Interactions;

View File

@@ -15,7 +15,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
{
private readonly ISystemClipboardService _systemClipboardService;
private readonly IUserCommunicationService _userCommunicationService;
private readonly ISearchManager _searchManager;
private readonly ISearchContentProvider _searchContentProvider;
private readonly IItemNameConverterService _itemNameConverterService;
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IUserCommandHandlerService _userCommandHandlerService;
@@ -28,7 +28,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
IAppState appState,
ISystemClipboardService systemClipboardService,
IUserCommunicationService userCommunicationService,
ISearchManager searchManager,
ISearchContentProvider searchContentProvider,
IItemNameConverterService itemNameConverterService,
ITimelessContentProvider timelessContentProvider,
IUserCommandHandlerService userCommandHandlerService,
@@ -36,7 +36,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
{
_systemClipboardService = systemClipboardService;
_userCommunicationService = userCommunicationService;
_searchManager = searchManager;
_searchContentProvider = searchContentProvider;
_itemNameConverterService = itemNameConverterService;
_timelessContentProvider = timelessContentProvider;
_userCommandHandlerService = userCommandHandlerService;
@@ -113,7 +113,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
_ => throw new ArgumentOutOfRangeException()
};
var searchTask = await _searchManager.StartSearchAsync(searchMatcher, _currentLocation.Value);
var searchTask = await _searchContentProvider.StartSearchAsync(searchMatcher, _currentLocation.Value);
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SearchContainer));
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
}

View File

@@ -52,7 +52,7 @@ public abstract partial class ItemViewModel : IItemViewModel
var displayName = itemViewModelType switch
{
ItemViewModelType.Main => _appState.RapidTravelText.Map(s => (IReadOnlyList<ItemNamePart>) _itemNameConverterService.GetDisplayName(item.DisplayName, s)),
_ => new DeclarativeProperty<IReadOnlyList<ItemNamePart>>(new List<ItemNamePart> {new (item.DisplayName)}),
_ => new DeclarativeProperty<IReadOnlyList<ItemNamePart>>(new List<ItemNamePart> {new(item.DisplayName)}),
};
BaseItem = item;
@@ -103,6 +103,6 @@ public abstract partial class ItemViewModel : IItemViewModel
var deepestPath = _parentTab.Tab.LastDeepestSelectedPath;
var commonPath = FullName.CreateSafe(PathHelper.GetCommonPath(ownFullName.Path, deepestPath.Path));
return commonPath.Path == ownFullName.Path;
return commonPath is not null && commonPath.Path == ownFullName.Path;
}
}

View File

@@ -198,7 +198,7 @@ public partial class TabViewModel : ITabViewModel
private static async Task<IItem> MapItemAsync(AbsolutePath item)
=> await item.ResolveAsync(forceResolve: true,
itemInitializationSettings: new ItemInitializationSettings(true));
itemInitializationSettings: new ItemInitializationSettings {SkipChildInitialization = true});
private IItemViewModel MapItemToViewModel(IItem item, ItemViewModelType type)
{

View File

@@ -31,6 +31,7 @@ public static class DependencyInjection
serviceCollection.TryAddSingleton<ICommandRunner, CommandRunner>();
serviceCollection.TryAddSingleton<IContentAccessorFactory, ContentAccessorFactory>();
serviceCollection.TryAddSingleton<IContentProviderRegistry, ContentProviderRegistry>();
serviceCollection.TryAddSingleton<IRootContentProvider, RootContentProvider>();
//TODO: check local/remote context
serviceCollection.TryAddSingleton<ILocalCommandExecutor, LocalCommandExecutor>();
serviceCollection.TryAddSingleton<ICommandSchedulerNotifier, LocalCommandSchedulerNotifier>();

View File

@@ -1,8 +1,10 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
namespace FileTime.App.Search;
public interface ISearchContentProvider : IContentProvider
{
Task<ISearchTask> StartSearchAsync(ISearchMatcher matcher, IContainer searchIn);
void RemoveSearch(FullName searchFullName);
}

View File

@@ -1,9 +0,0 @@
using FileTime.Core.Models;
namespace FileTime.App.Search;
public interface ISearchManager
{
Task<ISearchTask> StartSearchAsync(ISearchMatcher matcher, IContainer searchIn);
IReadOnlyList<ISearchTask> SearchTasks { get; }
}

View File

@@ -7,12 +7,14 @@ namespace FileTime.App.Search;
public class SearchContentProvider : ContentProviderBase, ISearchContentProvider
{
private readonly ISearchManager _searchManager;
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly List<SearchTask> _searchTasks = new();
public const string ContentProviderName = "search";
public SearchContentProvider(ISearchManager searchManager) : base(ContentProviderName)
public SearchContentProvider(ITimelessContentProvider timelessContentProvider)
: base(ContentProviderName, timelessContentProvider)
{
_searchManager = searchManager;
_timelessContentProvider = timelessContentProvider;
}
public override Task<IItem> GetItemByNativePathAsync(
@@ -21,9 +23,12 @@ public class SearchContentProvider : ContentProviderBase, ISearchContentProvider
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default
) =>
Task.FromResult((IItem) _searchManager.SearchTasks
)
{
if (nativePath.Path == ContentProviderName) return Task.FromResult((IItem) this);
return Task.FromResult((IItem) _searchTasks
.First(searchTask => searchTask.SearchContainer.NativePath == nativePath).SearchContainer);
}
public override NativePath GetNativePath(FullName fullName) => new(fullName.Path);
public override FullName GetFullName(NativePath nativePath) => new(nativePath.Path);
@@ -36,4 +41,34 @@ public class SearchContentProvider : ContentProviderBase, ISearchContentProvider
=> Task.FromResult(null as byte[]);
public override bool CanHandlePath(NativePath path) => path.Path.StartsWith(ContentProviderName);
public async Task<ISearchTask> StartSearchAsync(ISearchMatcher matcher, IContainer searchIn)
{
var searchTask = new SearchTask(
searchIn,
this,
_timelessContentProvider,
matcher,
new AbsolutePath(_timelessContentProvider, this)
);
_searchTasks.Add(searchTask);
await searchTask.StartAsync();
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SearchContainer));
return searchTask;
}
public void RemoveSearch(FullName searchFullName)
{
var searchTask = _searchTasks.FirstOrDefault(t => t.SearchContainer.FullName == searchFullName);
if (searchTask is null) return;
_searchTasks.Remove(searchTask);
var searchItem = Items.FirstOrDefault(c => c.Path == searchTask.SearchContainer.FullName);
if (searchItem is not null)
{
Items.Remove(searchItem);
}
}
}

View File

@@ -0,0 +1,13 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
namespace FileTime.App.Search;
public class SearchItemDeleter : IItemDeleter<ISearchContentProvider>
{
public Task DeleteAsync(ISearchContentProvider contentProvider, FullName fullName)
{
contentProvider.RemoveSearch(fullName);
return Task.CompletedTask;
}
}

View File

@@ -1,35 +0,0 @@
using FileTime.Core.Models;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.App.Search;
public class SearchManager : ISearchManager
{
private readonly IServiceProvider _serviceProvider;
private ISearchContentProvider? _searchContainerProvider;
private readonly List<SearchTask> _searchTasks = new();
public IReadOnlyList<ISearchTask> SearchTasks { get; }
public SearchManager(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
SearchTasks = _searchTasks.AsReadOnly();
}
public async Task<ISearchTask> StartSearchAsync(ISearchMatcher matcher, IContainer searchIn)
{
_searchContainerProvider ??= _serviceProvider.GetRequiredService<ISearchContentProvider>();
var searchTask = new SearchTask(
searchIn,
_searchContainerProvider,
matcher
);
_searchTasks.Add(searchTask);
await searchTask.StartAsync();
return searchTask;
}
}

View File

@@ -10,6 +10,7 @@ namespace FileTime.App.Search;
public class SearchTask : ISearchTask
{
private readonly IContainer _baseContainer;
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly ISearchMatcher _matcher;
private readonly Container _container;
private readonly ObservableCollection<Exception> _exceptions = new();
@@ -22,19 +23,22 @@ public class SearchTask : ISearchTask
public SearchTask(
IContainer baseContainer,
IContentProvider contentProvider,
ISearchMatcher matcher
ISearchContentProvider contentProvider,
ITimelessContentProvider timelessContentProvider,
ISearchMatcher matcher,
AbsolutePath parent
)
{
var randomId = $"{SearchContentProvider.ContentProviderName}/{_searchId++}_{baseContainer.Name}";
_baseContainer = baseContainer;
_timelessContentProvider = timelessContentProvider;
_matcher = matcher;
_container = new Container(
baseContainer.Name,
baseContainer.DisplayName,
new FullName(randomId),
new NativePath(randomId),
null,
parent,
false,
true,
null,
@@ -85,7 +89,11 @@ public class SearchTask : ISearchTask
foreach (var itemPath in items)
{
var item = await itemPath.ResolveAsync();
var item = await itemPath.ResolveAsync(
itemInitializationSettings: new ItemInitializationSettings
{
Parent = new AbsolutePath(_timelessContentProvider, _container)
});
if (await _matcher.IsItemMatchAsync(item))
{
_items.Add(itemPath);

View File

@@ -10,7 +10,8 @@ public static class Startup
{
services.TryAddSingleton<ISearchContentProvider, SearchContentProvider>();
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<ISearchContentProvider>());
services.TryAddSingleton<ISearchManager, SearchManager>();
services.TryAddSingleton<IItemDeleter<ISearchContentProvider>, SearchItemDeleter>();
services.TryAddSingleton<IItemDeleter<SearchContentProvider>>(sp => sp.GetRequiredService<IItemDeleter<ISearchContentProvider>>());
return services;
}

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);
}

View File

@@ -14,7 +14,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly bool _isCaseInsensitive;
public LocalContentProvider(ITimelessContentProvider timelessContentProvider) : base(LocalContentProviderConstants.ContentProviderId)
public LocalContentProvider(ITimelessContentProvider timelessContentProvider)
: base(LocalContentProviderConstants.ContentProviderId, timelessContentProvider)
{
_timelessContentProvider = timelessContentProvider;
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
@@ -66,13 +67,13 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
PointInTime pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default)
ItemInitializationSettings? itemInitializationSettings = null)
{
var path = nativePath.Path;
Exception? innerException;
try
{
if ((path?.Length ?? 0) == 0)
if (path.Length == 0)
{
return Task.FromResult((IItem) this);
}
@@ -81,7 +82,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
return Task.FromResult((IItem) DirectoryToContainer(
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
pointInTime,
!itemInitializationSettings.SkipChildInitialization)
itemInitializationSettings)
);
}
else if (File.Exists(path))
@@ -193,18 +194,21 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
}
private Container DirectoryToContainer(DirectoryInfo directoryInfo, PointInTime pointInTime,
bool initializeChildren = true)
ItemInitializationSettings? initializationSettings = null)
{
initializationSettings ??= new();
var fullName = GetFullName(directoryInfo.FullName);
var parentFullName = fullName.GetParent();
var parent =
parentFullName is null
initializationSettings.Parent
?? (parentFullName is null
? null
: new AbsolutePath(
_timelessContentProvider,
pointInTime,
parentFullName,
AbsolutePathType.Container);
AbsolutePathType.Container));
var exceptions = new ObservableCollection<Exception>();
var children = new ObservableCollection<AbsolutePath>();
@@ -229,7 +233,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
children
);
if (initializeChildren)
if (!initializationSettings.SkipChildInitialization)
{
Task.Run(async () => await LoadChildren(container, directoryInfo, children, pointInTime, exceptions));
}

View File

@@ -9,22 +9,22 @@ namespace FileTime.Providers.LocalAdmin;
//TODO: this should be a RemoteContentProvider if there will be one
public class AdminContentProvider : RemoteContentProvider, IAdminContentProvider
{
public AdminContentProvider() : base("local", "localAdmin")
public AdminContentProvider(ITimelessContentProvider timelessContentProvider) : base(timelessContentProvider, "local", "localAdmin")
{
}
public override Task<IItem> GetItemByNativePathAsync(NativePath nativePath, PointInTime pointInTime, bool forceResolve = false, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default)
public override Task<IItem> GetItemByNativePathAsync(NativePath nativePath, PointInTime pointInTime, bool forceResolve = false, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default)
=> throw new NotImplementedException();
public override NativePath GetNativePath(FullName fullName)
public override NativePath GetNativePath(FullName fullName)
=> throw new NotImplementedException();
public override FullName GetFullName(NativePath nativePath)
public override FullName GetFullName(NativePath nativePath)
=> throw new NotImplementedException();
public override Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
public override Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public override bool CanHandlePath(NativePath path)
public override bool CanHandlePath(NativePath path)
=> throw new NotImplementedException();
}

View File

@@ -7,7 +7,11 @@ namespace FileTime.Providers.Remote;
public class RemoteContentProvider : ContentProviderBase, IRemoteContentProvider
{
public RemoteContentProvider(string remoteName, string name = "remote") : base(name)
public RemoteContentProvider(
ITimelessContentProvider timelessContentProvider,
string remoteName,
string name = "remote")
: base(name, timelessContentProvider)
{
}