diff --git a/src/AppCommon/FileTime.App.Search/SearchTask.cs b/src/AppCommon/FileTime.App.Search/SearchTask.cs index f6af12f..17a1be5 100644 --- a/src/AppCommon/FileTime.App.Search/SearchTask.cs +++ b/src/AppCommon/FileTime.App.Search/SearchTask.cs @@ -62,12 +62,12 @@ public class SearchTask : ISearchTask { try { - _container.IsLoading.OnNext(true); + _container.StartLoading(); await TraverseTree(_baseContainer); } finally { - _container.IsLoading.OnNext(false); + _container.StopLoading(); await _searchingLock.WaitAsync(); _isSearching = false; diff --git a/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs b/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs index 4487085..e9ad1c9 100644 --- a/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs +++ b/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs @@ -8,5 +8,7 @@ public interface IContainer : IItem IObservable> Items { get; } ReadOnlyObservableCollection ItemsCollection { get; } IObservable IsLoading { get; } + bool? IsLoaded { get; } + Task WaitForLoaded(CancellationToken token = default); bool AllowRecursiveDeletion { get; } } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs b/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs index 3c077f6..dc825d4 100644 --- a/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs +++ b/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs @@ -110,7 +110,7 @@ public class CopyCommand : CommandBase, ITransportationCommand { var total = data.Sum(d => d.TotalProgress); if (total == 0) return 0; - return (int) (data.Sum(d => d.Progress) * 100 / total); + return (int)(data.Sum(d => d.Progress) * 100 / total); }) .Subscribe(SetTotalProgress); @@ -140,10 +140,9 @@ public class CopyCommand : CommandBase, ITransportationCommand TransportMode transportMode, ICopyStrategy copyOperation) { - var resolvedTarget = ((IContainer) await target.ResolveAsync()) ?? throw new Exception(); - foreach (var source in sources) { + var resolvedTarget = (IContainer)await target.ResolveAsync() ?? throw new Exception(); var item = await _timelessContentProvider.GetItemByFullNameAsync(source, currentTime); if (item is IContainer container) diff --git a/src/Core/FileTime.Core.Command/Helper.cs b/src/Core/FileTime.Core.Command/Helper.cs index adffe9b..78ecbe7 100644 --- a/src/Core/FileTime.Core.Command/Helper.cs +++ b/src/Core/FileTime.Core.Command/Helper.cs @@ -6,6 +6,7 @@ public static class Helper { public static async Task GetNewNameAsync(IContainer resolvedTarget, string name, TransportMode transportMode) { + await resolvedTarget.WaitForLoaded(); var items = resolvedTarget.ItemsCollection.ToList(); var newName = name; var targetNameExists = items.Any(i => i.Path.GetName() == newName); diff --git a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs index 3762304..1307e53 100644 --- a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs +++ b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs @@ -44,9 +44,11 @@ public abstract class ContentProviderBase : IContentProvider public string? Attributes => null; protected BehaviorSubject IsLoading { get; } = new(false); - public bool AllowRecursiveDeletion => false; - IObservable IContainer.IsLoading => IsLoading.AsObservable(); + public bool? IsLoaded => true; + public Task WaitForLoaded(CancellationToken token = default) => Task.CompletedTask; + + public bool AllowRecursiveDeletion => false; public AbsolutePathType Type => AbsolutePathType.Container; public PointInTime PointInTime { get; } = PointInTime.Eternal; diff --git a/src/Core/FileTime.Core.Models/Container.cs b/src/Core/FileTime.Core.Models/Container.cs index 8751d15..e89a279 100644 --- a/src/Core/FileTime.Core.Models/Container.cs +++ b/src/Core/FileTime.Core.Models/Container.cs @@ -27,19 +27,42 @@ public record Container( ReadOnlyExtensionCollection Extensions, IObservable> Items) : IContainer { + private readonly Lazy> _itemsCollectionLazy = - new (() => + new(() => { Items.Bind(out var items).Subscribe(); return items; }); private readonly CancellationTokenSource _loadingCancellationTokenSource = new(); + private readonly BehaviorSubject _isLoading = new(false); + public CancellationToken LoadingCancellationToken => _loadingCancellationTokenSource.Token; - public BehaviorSubject IsLoading { get; } = new(false); - IObservable IContainer.IsLoading => IsLoading.AsObservable(); + public IObservable IsLoading => _isLoading.AsObservable(); + public bool? IsLoaded { get; private set; } public AbsolutePathType Type => AbsolutePathType.Container; public ReadOnlyObservableCollection ItemsCollection => _itemsCollectionLazy.Value; - public void CancelLoading() => _loadingCancellationTokenSource.Cancel(); + public async Task WaitForLoaded(CancellationToken token = default) + { + while (IsLoaded != true) await Task.Delay(1, token); + } + + public void StartLoading() + { + _isLoading.OnNext(true); + IsLoaded = false; + } + public void StopLoading() + { + _isLoading.OnNext(false); + IsLoaded = true; + } + public void CancelLoading() + { + _loadingCancellationTokenSource.Cancel(); + _isLoading.OnNext(false); + IsLoaded = true; + } } \ No newline at end of file diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index 1b5fb48..d9bd09e 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -71,11 +71,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo { if ((path?.Length ?? 0) == 0) { - return Task.FromResult((IItem) this); + return Task.FromResult((IItem)this); } else if (Directory.Exists(path)) { - return Task.FromResult((IItem) DirectoryToContainer( + return Task.FromResult((IItem)DirectoryToContainer( new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar), pointInTime, !itemInitializationSettings.SkipChildInitialization) @@ -83,7 +83,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo } else if (File.Exists(path)) { - return Task.FromResult((IItem) FileToElement(new FileInfo(path), pointInTime)); + return Task.FromResult((IItem)FileToElement(new FileInfo(path), pointInTime)); } var type = forceResolvePathType switch @@ -115,10 +115,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo return forceResolvePathType switch { AbsolutePathType.Container => Task.FromResult( - (IItem) CreateEmptyContainer( + (IItem)CreateEmptyContainer( nativePath, pointInTime, - new List() {innerException} + new List() { innerException } ) ), AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)), @@ -229,28 +229,6 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo Task.Run(() => LoadChildren(container, directoryInfo, children, pointInTime, exceptions)); return container; - - IObservable>? InitChildren() - { - if (!initializeChildren) return null; - - try - { - var items = GetItemsByContainer(directoryInfo, pointInTime); - var result = new SourceCache(i => i.Path.Path); - - if (items.Count == 0) return (IObservable>?) result.Connect().StartWithEmpty(); - - result.AddOrUpdate(items); - return (IObservable>?) result.Connect(); - } - catch (Exception e) - { - exceptions.Add(e); - } - - return null; - } } private void LoadChildren(Container container, @@ -259,16 +237,16 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo PointInTime pointInTime, SourceList exceptions) { - var lockobj = new object(); + var lockObj = new object(); var loadingIndicatorCancellation = new CancellationTokenSource(); Task.Run(DelayedLoadingIndicator); LoadChildren(); - lock (lockobj) + lock (lockObj) { loadingIndicatorCancellation.Cancel(); - container.IsLoading.OnNext(false); + container.StopLoading(); } void LoadChildren() @@ -306,10 +284,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo { } - lock (lockobj) + lock (lockObj) { if (token.IsCancellationRequested) return; - container.IsLoading.OnNext(true); + container.StartLoading(); } } } @@ -397,7 +375,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo var finalSize = size switch { > int.MaxValue => int.MaxValue, - _ => (int) size + _ => (int)size }; var buffer = new byte[finalSize]; var realSize = await reader.ReadAsync(buffer.AsMemory(0, finalSize), cancellationToken);