From a01c3a69b44091c7e7a8b6b705a4e2c0bdc9b3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Mon, 27 Feb 2023 08:41:19 +0100 Subject: [PATCH] Search WIP --- .../ToolUserCommandHandlerService.cs | 23 +++++--- .../ViewModels/TabViewModel.cs | 2 - .../ISearchManager.cs | 3 +- .../ISearchTask.cs | 9 +++ .../FileTime.App.Search.csproj | 1 + .../SearchContentProvider.cs | 54 ++++++++---------- .../FileTime.App.Search/SearchManager.cs | 16 ++++-- .../FileTime.App.Search/SearchTask.cs | 12 ++-- src/AppCommon/FileTime.App.Search/Startup.cs | 7 ++- .../Extensions/DynamicDataExtensions.cs | 19 ++++++- .../Models/IContainer.cs | 2 +- .../ContentProviderBase.cs | 6 +- src/Core/FileTime.Core.Models/Container.cs | 2 +- src/Core/FileTime.Core.Services/Tab.cs | 3 +- .../Services/RootDriveInfoService.cs | 55 ++++++++----------- .../LocalContentProvider.cs | 14 ++--- 16 files changed, 128 insertions(+), 100 deletions(-) create mode 100644 src/AppCommon/FileTime.App.Search.Abstractions/ISearchTask.cs diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs index 6662bc2..353573e 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs @@ -4,6 +4,7 @@ using FileTime.App.Core.ViewModels; using FileTime.App.Search; using FileTime.Core.Interactions; using FileTime.Core.Models; +using FileTime.Core.Timeline; namespace FileTime.App.Core.Services.UserCommandHandler; @@ -13,6 +14,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase private readonly IUserCommunicationService _userCommunicationService; private readonly ISearchManager _searchManager; private readonly IItemNameConverterService _itemNameConverterService; + private readonly ITimelessContentProvider _timelessContentProvider; + private readonly IUserCommandHandlerService _userCommandHandlerService; private IContainer? _currentLocation; private IItemViewModel? _currentSelectedItem; @@ -21,12 +24,16 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase ISystemClipboardService systemClipboardService, IUserCommunicationService userCommunicationService, ISearchManager searchManager, - IItemNameConverterService itemNameConverterService) : base(appState) + IItemNameConverterService itemNameConverterService, + ITimelessContentProvider timelessContentProvider, + IUserCommandHandlerService userCommandHandlerService) : base(appState) { _systemClipboardService = systemClipboardService; _userCommunicationService = userCommunicationService; _searchManager = searchManager; _itemNameConverterService = itemNameConverterService; + _timelessContentProvider = timelessContentProvider; + _userCommandHandlerService = userCommandHandlerService; SaveCurrentLocation(l => _currentLocation = l); SaveCurrentSelectedItem(i => _currentSelectedItem = i); @@ -40,8 +47,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase private async Task Search(SearchCommand searchCommand) { - if(_currentLocation is null) return; - + if (_currentLocation is null) return; + var searchQuery = searchCommand.SearchText; if (string.IsNullOrEmpty(searchQuery)) { @@ -59,10 +66,10 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase searchQuery = containerNameInput.Value; } } - + //TODO proper error message - if(string.IsNullOrWhiteSpace(searchQuery)) return; - + if (string.IsNullOrWhiteSpace(searchQuery)) return; + var searchMatcher = searchCommand.SearchType switch { SearchType.NameContains => new NameContainsMatcher(_itemNameConverterService, searchQuery), @@ -70,7 +77,9 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase _ => throw new ArgumentOutOfRangeException() }; - await _searchManager.StartSearchAsync(searchMatcher, _currentLocation); + var searchTask = await _searchManager.StartSearchAsync(searchMatcher, _currentLocation); + var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SearchContainer)); + await _userCommandHandlerService.HandleCommandAsync(openContainerCommand); } private async Task CopyNativePath() diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs index ba545b4..10b5966 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs @@ -134,7 +134,6 @@ public partial class TabViewModel : ITabViewModel .OfType() .Where(c => c?.Container is not null) .Select(c => c.Container!.Items) - .Switch() .Select(i => i ?.TransformAsync(MapItem) @@ -165,7 +164,6 @@ public partial class TabViewModel : ITabViewModel .Select(p => Observable.FromAsync(async () => (IContainer)await p!.ResolveAsync())) .Switch() .Select(p => p.Items) - .Switch() .Select(items => items ?.TransformAsync(MapItem) diff --git a/src/AppCommon/FileTime.App.Search.Abstractions/ISearchManager.cs b/src/AppCommon/FileTime.App.Search.Abstractions/ISearchManager.cs index 5bd898b..75a54dd 100644 --- a/src/AppCommon/FileTime.App.Search.Abstractions/ISearchManager.cs +++ b/src/AppCommon/FileTime.App.Search.Abstractions/ISearchManager.cs @@ -4,5 +4,6 @@ namespace FileTime.App.Search; public interface ISearchManager { - Task StartSearchAsync(ISearchMatcher matcher, IContainer searchIn); + Task StartSearchAsync(ISearchMatcher matcher, IContainer searchIn); + IReadOnlyList SearchTasks { get; } } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Search.Abstractions/ISearchTask.cs b/src/AppCommon/FileTime.App.Search.Abstractions/ISearchTask.cs new file mode 100644 index 0000000..66cebef --- /dev/null +++ b/src/AppCommon/FileTime.App.Search.Abstractions/ISearchTask.cs @@ -0,0 +1,9 @@ +using FileTime.Core.Models; + +namespace FileTime.App.Search; + +public interface ISearchTask +{ + IContainer SearchContainer { get; } + Task StartAsync(); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Search/FileTime.App.Search.csproj b/src/AppCommon/FileTime.App.Search/FileTime.App.Search.csproj index 93ccce2..7c507dd 100644 --- a/src/AppCommon/FileTime.App.Search/FileTime.App.Search.csproj +++ b/src/AppCommon/FileTime.App.Search/FileTime.App.Search.csproj @@ -8,6 +8,7 @@ + diff --git a/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs b/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs index 0b885a9..eff35b6 100644 --- a/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs +++ b/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs @@ -1,4 +1,3 @@ -using DynamicData; using FileTime.Core.ContentAccess; using FileTime.Core.Enums; using FileTime.Core.Models; @@ -6,39 +5,34 @@ using FileTime.Core.Timeline; namespace FileTime.App.Search; -public class SearchContentProvider : ISearchContentProvider +public class SearchContentProvider : ContentProviderBase, ISearchContentProvider { - public string Name { get; } - public string DisplayName { get; } - public FullName? FullName { get; } - public NativePath? NativePath { get; } - public AbsolutePath? Parent { get; } - public bool IsHidden { get; } - public bool IsExists { get; } - public DateTime? CreatedAt { get; } - public SupportsDelete CanDelete { get; } - public bool CanRename { get; } - public IContentProvider Provider { get; } - public string? Attributes { get; } - public AbsolutePathType Type { get; } - public PointInTime PointInTime { get; } - public IObservable> Exceptions { get; } - public ReadOnlyExtensionCollection Extensions { get; } - public IObservable>?> Items { get; } - public IObservable IsLoading { get; } - public bool AllowRecursiveDeletion { get; } - public Task OnEnter() => throw new NotImplementedException(); + private readonly ISearchManager _searchManager; + public const string ContentProviderName = "search"; - public bool SupportsContentStreams { get; } - public Task GetItemByFullNameAsync(FullName fullName, PointInTime pointInTime, bool forceResolve = false, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default) => throw new NotImplementedException(); + public SearchContentProvider(ISearchManager searchManager) : base(ContentProviderName) + { + _searchManager = searchManager; + } - public Task GetItemByNativePathAsync(NativePath nativePath, PointInTime pointInTime, bool forceResolve = false, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default) => throw new NotImplementedException(); + public override Task GetItemByNativePathAsync( + NativePath nativePath, + PointInTime pointInTime, + bool forceResolve = false, + AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, + ItemInitializationSettings itemInitializationSettings = default + ) => + Task.FromResult((IItem)_searchManager.SearchTasks + .First(searchTask => searchTask.SearchContainer.NativePath == nativePath).SearchContainer); - public NativePath GetNativePath(FullName fullName) => throw new NotImplementedException(); + public override NativePath GetNativePath(FullName fullName) => new(fullName.Path); - public Task GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + public override Task GetContentAsync( + IElement element, + int? maxLength = null, + CancellationToken cancellationToken = default + ) + => Task.FromResult(null as byte[]); - public bool CanHandlePath(NativePath path) => throw new NotImplementedException(); - - public bool CanHandlePath(FullName path) => throw new NotImplementedException(); + public override bool CanHandlePath(NativePath path) => path.Path.StartsWith(ContentProviderName); } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Search/SearchManager.cs b/src/AppCommon/FileTime.App.Search/SearchManager.cs index 1e76d6d..e217cd2 100644 --- a/src/AppCommon/FileTime.App.Search/SearchManager.cs +++ b/src/AppCommon/FileTime.App.Search/SearchManager.cs @@ -1,19 +1,25 @@ using FileTime.Core.Models; +using Microsoft.Extensions.DependencyInjection; namespace FileTime.App.Search; public class SearchManager : ISearchManager { - private readonly ISearchContentProvider _searchContainerProvider; + private readonly IServiceProvider _serviceProvider; + private ISearchContentProvider? _searchContainerProvider; private readonly List _searchTasks = new(); + + public IReadOnlyList SearchTasks { get; } - public SearchManager(ISearchContentProvider searchContainerProvider) + public SearchManager(IServiceProvider serviceProvider) { - _searchContainerProvider = searchContainerProvider; + _serviceProvider = serviceProvider; + SearchTasks = _searchTasks.AsReadOnly(); } - public async Task StartSearchAsync(ISearchMatcher matcher, IContainer searchIn) + public async Task StartSearchAsync(ISearchMatcher matcher, IContainer searchIn) { + _searchContainerProvider ??= _serviceProvider.GetRequiredService(); var searchTask = new SearchTask( searchIn, _searchContainerProvider, @@ -23,5 +29,7 @@ public class SearchManager : ISearchManager _searchTasks.Add(searchTask); await searchTask.StartAsync(); + + return searchTask; } } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Search/SearchTask.cs b/src/AppCommon/FileTime.App.Search/SearchTask.cs index f6ec555..5885c3d 100644 --- a/src/AppCommon/FileTime.App.Search/SearchTask.cs +++ b/src/AppCommon/FileTime.App.Search/SearchTask.cs @@ -8,7 +8,7 @@ using FileTime.Core.Timeline; namespace FileTime.App.Search; -public class SearchTask +public class SearchTask : ISearchTask { private readonly IContainer _baseContainer; private readonly ISearchMatcher _matcher; @@ -17,6 +17,9 @@ public class SearchTask private readonly SourceCache _items = new(p => p.Path.Path); private readonly SemaphoreSlim _searchingLock = new(1, 1); private bool _isSearching; + private static int _searchId = 1; + + public IContainer SearchContainer => _container; public SearchTask( IContainer baseContainer, @@ -24,13 +27,14 @@ public class SearchTask ISearchMatcher matcher ) { + var randomId = $"{SearchContentProvider.ContentProviderName}/{_searchId++}_{baseContainer.Name}"; _baseContainer = baseContainer; _matcher = matcher; _container = new Container( baseContainer.Name, baseContainer.DisplayName, - new FullName(""), - new NativePath(""), + new FullName(randomId), + new NativePath(randomId), null, false, true, @@ -43,7 +47,7 @@ public class SearchTask PointInTime.Present, _exceptions.Connect(), new ReadOnlyExtensionCollection(new ExtensionCollection()), - Observable.Return(_items.Connect()) + _items.Connect().StartWithEmpty() ); } diff --git a/src/AppCommon/FileTime.App.Search/Startup.cs b/src/AppCommon/FileTime.App.Search/Startup.cs index 23f438a..2b75630 100644 --- a/src/AppCommon/FileTime.App.Search/Startup.cs +++ b/src/AppCommon/FileTime.App.Search/Startup.cs @@ -1,4 +1,6 @@ +using FileTime.Core.ContentAccess; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace FileTime.App.Search; @@ -6,8 +8,9 @@ public static class Startup { public static IServiceCollection AddSearch(this IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); + services.TryAddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + services.TryAddSingleton(); return services; } diff --git a/src/Core/FileTime.Core.Abstraction/Extensions/DynamicDataExtensions.cs b/src/Core/FileTime.Core.Abstraction/Extensions/DynamicDataExtensions.cs index 08c6057..1225349 100644 --- a/src/Core/FileTime.Core.Abstraction/Extensions/DynamicDataExtensions.cs +++ b/src/Core/FileTime.Core.Abstraction/Extensions/DynamicDataExtensions.cs @@ -8,8 +8,10 @@ public static class DynamicDataExtensions { private class DisposableContext { + private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly Func _transformResult; private readonly TaskCompletionSource _taskCompletionSource; + private bool _isFinished; public IDisposable? Disposable { get; set; } public DisposableContext(Func transformResult, @@ -22,6 +24,7 @@ public static class DynamicDataExtensions public void OnNext(TParam param) { + if (IsFinished()) return; Disposable?.Dispose(); var result = _transformResult(param); _taskCompletionSource.SetResult(result); @@ -29,15 +32,27 @@ public static class DynamicDataExtensions 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?> GetItemsAsync( @@ -46,12 +61,12 @@ public static class DynamicDataExtensions .Select(s => s is null ? new SourceList().Connect().StartWithEmpty().ToCollection() - : s.ToCollection()) + : s.StartWithEmpty().ToCollection()) .Switch()); public static async Task?> GetItemsAsync( this IObservable> stream) - => await GetItemsAsync(stream.ToCollection()); + => await GetItemsAsync(stream.StartWithEmpty().ToCollection()); public static Task?> GetItemsAsync( this IObservable> stream) diff --git a/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs b/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs index 84f5c90..a295f3a 100644 --- a/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs +++ b/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs @@ -4,7 +4,7 @@ namespace FileTime.Core.Models; public interface IContainer : IItem { - IObservable>?> Items { get; } + IObservable> Items { get; } IObservable IsLoading { get; } bool AllowRecursiveDeletion { get; } } \ No newline at end of file diff --git a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs index 9b5d2d7..a2ed227 100644 --- a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs +++ b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs @@ -10,11 +10,12 @@ namespace FileTime.Core.ContentAccess; public abstract class ContentProviderBase : IContentProvider { private readonly ReadOnlyExtensionCollection _extensions; + private readonly IObservable> _items; - protected BehaviorSubject>?> Items { get; } = new(null); + protected SourceCache Items { get; } = new(p => p.Path.Path); protected ExtensionCollection Extensions { get; } - IObservable>?> IContainer.Items => Items; + IObservable> IContainer.Items => _items; public string Name { get; } @@ -59,6 +60,7 @@ public abstract class ContentProviderBase : IContentProvider FullName = FullName.CreateSafe(name); Extensions = new ExtensionCollection(); _extensions = Extensions.AsReadOnly(); + _items = Items.Connect().StartWithEmpty(); } public virtual Task OnEnter() => Task.CompletedTask; diff --git a/src/Core/FileTime.Core.Models/Container.cs b/src/Core/FileTime.Core.Models/Container.cs index 32d47f2..82853b5 100644 --- a/src/Core/FileTime.Core.Models/Container.cs +++ b/src/Core/FileTime.Core.Models/Container.cs @@ -24,7 +24,7 @@ public record Container( PointInTime PointInTime, IObservable> Exceptions, ReadOnlyExtensionCollection Extensions, - IObservable>?> Items) : IContainer + IObservable> Items) : IContainer { private readonly CancellationTokenSource _loadingCancellationTokenSource = new(); public CancellationToken LoadingCancellationToken => _loadingCancellationTokenSource.Token; diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs index e8588e7..b8b0e7b 100644 --- a/src/Core/FileTime.Core.Services/Tab.cs +++ b/src/Core/FileTime.Core.Services/Tab.cs @@ -52,8 +52,7 @@ public class Tab : ITab CurrentLocation .Where(c => c is not null) .Select(c => c!.Items) - .Switch() - .Select(items => items?.TransformAsync(MapItem)), + .Select(items => items.TransformAsync(MapItem)), _itemFilters.Connect().StartWithEmpty().ToCollection(), (items, filters) => items diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RootDriveInfoService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RootDriveInfoService.cs index d184be5..5ac647a 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RootDriveInfoService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RootDriveInfoService.cs @@ -14,45 +14,35 @@ namespace FileTime.GuiApp.Services; public class RootDriveInfoService : IStartupHandler { - private readonly SourceList _rootDrives = new(); + private readonly List _rootDrives = new(); public RootDriveInfoService( IGuiAppState guiAppState, - ILocalContentProvider localContentProvider, - ITimelessContentProvider timelessContentProvider) + ILocalContentProvider localContentProvider) { InitRootDrives(); - var localContentProviderAsList = new SourceCache(i => i.Path.Path); - localContentProviderAsList.AddOrUpdate(new AbsolutePath(timelessContentProvider, localContentProvider)); - var localContentProviderStream = localContentProviderAsList.Connect(); - - var rootDriveInfos = Observable.CombineLatest( - localContentProvider.Items, - _rootDrives.Connect().StartWithEmpty().ToCollection(), - (items, drives) => + var rootDriveInfos = localContentProvider.Items.Transform( + i => + { + var rootDrive = _rootDrives.FirstOrDefault(d => { - return items is null - ? Observable.Empty>() - : items! - .Or(new[] { localContentProviderStream }) - .Transform(i => (Path: i, Drive: drives.FirstOrDefault(d => - { - var containerPath = localContentProvider.GetNativePath(i.Path).Path; - var drivePath = d.Name.TrimEnd(Path.DirectorySeparatorChar); - return containerPath == drivePath - || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && containerPath == "/" && - d.Name == "/"); - }))) - .Filter(t => t.Drive is not null); - } - ) - .Switch() - .TransformAsync(async t => (Item: await t.Path.ResolveAsyncSafe(), Drive: t.Drive!)) - .Filter(t => t.Item is IContainer) - .Transform(t => (Container: (IContainer)t.Item!, t.Drive)) - .Transform(t => new RootDriveInfo(t.Drive, t.Container)) - .Sort(SortExpressionComparer.Ascending(d => d.Name)); + var containerPath = localContentProvider.GetNativePath(i.Path).Path; + var drivePath = d.Name.TrimEnd(Path.DirectorySeparatorChar); + return containerPath == drivePath + || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && containerPath == "/" && + d.Name == "/"); + }); + + return (Path: i, Drive: rootDrive); + } + ) + .Filter(t => t.Drive is not null) + .TransformAsync(async t => (Item: await t.Path.ResolveAsyncSafe(), Drive: t.Drive!)) + .Filter(t => t.Item is IContainer) + .Transform(t => (Container: (IContainer) t.Item!, t.Drive)) + .Transform(t => new RootDriveInfo(t.Drive, t.Container)) + .Sort(SortExpressionComparer.Ascending(d => d.Name)); guiAppState.RootDriveInfos = rootDriveInfos.ToBindedCollection(); @@ -68,6 +58,7 @@ public class RootDriveInfoService : IStartupHandler && d.DriveFormat != "tracefs" && !d.RootDirectory.FullName.StartsWith("/snap/")); + _rootDrives.Clear(); _rootDrives.AddRange(drives); } } diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index ea12b84..af7f966 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -1,5 +1,4 @@ using System.Reactive.Linq; -using System.Reactive.Subjects; using System.Runtime.InteropServices; using DynamicData; using FileTime.Core.ContentAccess; @@ -13,7 +12,6 @@ namespace FileTime.Providers.Local; public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider { private readonly ITimelessContentProvider _timelessContentProvider; - private readonly SourceCache _rootDirectories = new(i => i.Path.Path); private readonly bool _isCaseInsensitive; public LocalContentProvider(ITimelessContentProvider timelessContentProvider) : base("local") @@ -24,8 +22,6 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo SupportsContentStreams = true; RefreshRootDirectories(); - - Items.OnNext(_rootDirectories.Connect()); } public override Task OnEnter() @@ -41,7 +37,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo ? new DirectoryInfo("/").GetDirectories() : Environment.GetLogicalDrives().Select(d => new DirectoryInfo(d)); - _rootDirectories.Edit(actions => + Items.Edit(actions => { actions.Clear(); actions.AddOrUpdate(rootDirectories.Select(d => DirectoryToAbsolutePath(d, PointInTime.Present))); @@ -50,7 +46,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo public override bool CanHandlePath(NativePath path) { - var rootDrive = _rootDirectories + var rootDrive = Items .Items .FirstOrDefault(r => path.Path.StartsWith( @@ -173,7 +169,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo pointInTime, exceptions.Connect(), new ExtensionCollection().AsReadOnly(), - Observable.Return>?>(null) + new SourceCache(a => a.Path.Path).Connect() ); } @@ -228,9 +224,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo pointInTime, exceptions.Connect(), new ExtensionCollection().AsReadOnly(), - //Observable.FromAsync(async () => await Task.Run(InitChildrenHelper) - //Observable.Return(InitChildren()) - Observable.Return(children.Connect()) + children.Connect().StartWithEmpty() ); Task.Run(() => LoadChildren(container, directoryInfo, children, pointInTime, exceptions));