From 5a88911ca7ee0189a13c4ba148627948186ab8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Fri, 18 Feb 2022 23:28:33 +0100 Subject: [PATCH] Command refactor, fixes, improvements --- src/Core/FileTime.Core/Command/CommandBase.cs | 53 +++++ .../Command/Copy/CalculateOperation.cs | 45 ++++ .../FileTime.Core/Command/Copy/CopyCommand.cs | 119 ++++++++++ .../Command/{ => Copy}/CopyCommandContext.cs | 2 +- .../Command/Copy/CopyOperation.cs | 44 ++++ .../Command/Copy/ICopyOperation.cs | 11 + .../Command/Copy/SimulateOperation.cs | 39 ++++ src/Core/FileTime.Core/Command/CopyCommand.cs | 211 ------------------ .../CreateContainerCommand.cs | 3 +- .../CreateElementCommand.cs | 2 +- .../Command/{ => Delete}/DeleteCommand.cs | 7 +- .../Command/{ => Move}/MoveCommand.cs | 2 +- .../Command/{ => Rename}/RenameCommand.cs | 4 +- .../StreamCopyCommandHandler.cs | 1 + src/Core/FileTime.Core/Components/Tab.cs | 112 ++++++---- src/Core/FileTime.Core/Models/IContainer.cs | 2 +- src/Core/FileTime.Core/Models/IItem.cs | 1 + .../FileTime.Core/Models/VirtualContainer.cs | 1 + .../Providers/AbstractContainer.cs | 4 +- .../FileTime.Core/Providers/TopContainer.cs | 1 + .../FileTime.Core/Timeline/TimeContainer.cs | 1 + .../FileTime.Core/Timeline/TimeElement.cs | 1 + .../FileTime.Core/Timeline/TimeProvider.cs | 1 + src/GuiApp/FileTime.Avalonia/App.axaml.cs | 4 +- .../Application/TabContainer.cs | 17 ++ .../Services/CommandHandlerService.cs | 8 +- .../Services/StatePersistenceService.cs | 10 +- .../ViewModels/ContainerViewModel.cs | 10 +- .../ViewModels/MainPageViewModel.cs | 2 +- .../Views/MainWindow.axaml.cs | 2 +- .../LocalContentProvider.cs | 3 +- .../FileTime.Providers.Local/LocalFile.cs | 1 + .../FileTime.Providers.Local/LocalFolder.cs | 7 +- .../SftpContentProvider.cs | 1 + .../FileTime.Providers.Sftp/SftpFile.cs | 2 + .../FileTime.Providers.Sftp/SftpFolder.cs | 1 + .../FileTime.Providers.Sftp/SftpServer.cs | 1 + .../SmbContentProvider.cs | 1 + .../FileTime.Providers.Smb/SmbFile.cs | 2 + .../FileTime.Providers.Smb/SmbFolder.cs | 3 +- .../FileTime.Providers.Smb/SmbServer.cs | 5 +- .../FileTime.Providers.Smb/SmbShare.cs | 1 + 42 files changed, 456 insertions(+), 292 deletions(-) create mode 100644 src/Core/FileTime.Core/Command/CommandBase.cs create mode 100644 src/Core/FileTime.Core/Command/Copy/CalculateOperation.cs create mode 100644 src/Core/FileTime.Core/Command/Copy/CopyCommand.cs rename src/Core/FileTime.Core/Command/{ => Copy}/CopyCommandContext.cs (89%) create mode 100644 src/Core/FileTime.Core/Command/Copy/CopyOperation.cs create mode 100644 src/Core/FileTime.Core/Command/Copy/ICopyOperation.cs create mode 100644 src/Core/FileTime.Core/Command/Copy/SimulateOperation.cs delete mode 100644 src/Core/FileTime.Core/Command/CopyCommand.cs rename src/Core/FileTime.Core/Command/{ => CreateContainer}/CreateContainerCommand.cs (97%) rename src/Core/FileTime.Core/Command/{ => CreateElement}/CreateElementCommand.cs (97%) rename src/Core/FileTime.Core/Command/{ => Delete}/DeleteCommand.cs (93%) rename src/Core/FileTime.Core/Command/{ => Move}/MoveCommand.cs (96%) rename src/Core/FileTime.Core/Command/{ => Rename}/RenameCommand.cs (89%) diff --git a/src/Core/FileTime.Core/Command/CommandBase.cs b/src/Core/FileTime.Core/Command/CommandBase.cs new file mode 100644 index 0000000..8cd8c9e --- /dev/null +++ b/src/Core/FileTime.Core/Command/CommandBase.cs @@ -0,0 +1,53 @@ +using AsyncEvent; +using FileTime.Core.Models; +using FileTime.Core.Timeline; + +namespace FileTime.Core.Command +{ + public abstract class CommandBase : ICommand + { + private readonly List _canRunMessages = new(); + public Dictionary> OperationStatuses { get; set; } = new(); + protected OperationProgress? CurrentOperationProgress { get; set; } + public virtual string DisplayLabel { get; protected set; } + public virtual IReadOnlyList CanRunMessages { get; protected set; } + public virtual int Progress { get; protected set; } + public virtual int CurrentProgress { get; protected set; } + public virtual AsyncEventHandler ProgressChanged { get; } = new AsyncEventHandler(); + + public abstract Task CanRun(PointInTime startPoint); + public abstract Task SimulateCommand(PointInTime startPoint); + + protected CommandBase() + { + CanRunMessages = _canRunMessages.AsReadOnly(); + DisplayLabel = ""; + } + + public async Task UpdateProgress() + { + var total = 0L; + var current = 0L; + + foreach (var folder in OperationStatuses.Values) + { + foreach (var item in folder) + { + current += item.Progress; + total += item.TotalCount; + } + } + + Progress = total == 0 ? 0 : (int)(current * 100 / total); + if (CurrentOperationProgress == null) + { + CurrentProgress = 0; + } + else + { + CurrentProgress = CurrentOperationProgress.TotalCount == 0 ? 0 : (int)(CurrentOperationProgress.Progress * 100 / CurrentOperationProgress.TotalCount); + } + await ProgressChanged.InvokeAsync(this, AsyncEventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/src/Core/FileTime.Core/Command/Copy/CalculateOperation.cs b/src/Core/FileTime.Core/Command/Copy/CalculateOperation.cs new file mode 100644 index 0000000..94325a4 --- /dev/null +++ b/src/Core/FileTime.Core/Command/Copy/CalculateOperation.cs @@ -0,0 +1,45 @@ +using System.Collections.ObjectModel; +using FileTime.Core.Models; + +namespace FileTime.Core.Command.Copy +{ + public class CalculateOperation : ICopyOperation + { + private readonly Dictionary> _operationStatuses; + public IReadOnlyDictionary> OperationStatuses { get; } + + public CalculateOperation() + { + _operationStatuses = new Dictionary>(); + OperationStatuses = new ReadOnlyDictionary>(_operationStatuses); + } + + public Task ContainerCopyDoneAsync(AbsolutePath path) + { + return Task.CompletedTask; + } + + public async Task CopyAsync(AbsolutePath from, AbsolutePath to, OperationProgress? operation, CopyCommandContext context) + { + var parentPath = to.GetParent(); + List operationsByFolder; + if (_operationStatuses.ContainsKey(parentPath)) + { + operationsByFolder = _operationStatuses[parentPath]; + } + else + { + operationsByFolder = new List(); + _operationStatuses.Add(parentPath, operationsByFolder); + } + + var resolvedFrom = await from.ResolveAsync(); + operationsByFolder.Add(new OperationProgress(from.Path, resolvedFrom is IElement element ? await element.GetElementSize() : 0L)); + } + + public Task CreateContainerAsync(IContainer target, string name) + { + return Task.CompletedTask; + } + } +} \ 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 new file mode 100644 index 0000000..027eda0 --- /dev/null +++ b/src/Core/FileTime.Core/Command/Copy/CopyCommand.cs @@ -0,0 +1,119 @@ +using FileTime.Core.Interactions; +using FileTime.Core.Models; +using FileTime.Core.Timeline; + +namespace FileTime.Core.Command.Copy +{ + public class CopyCommand : CommandBase, ITransportationCommand + { + public IList Sources { get; } = new List(); + + public AbsolutePath? Target { get; set; } + + public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge; + internal TimeRunner? TimeRunner { get; private set; } + + public bool TargetIsContainer => true; + public List Inputs { get; } = new(); + public List? InputResults { get; set; } + + public CopyCommand() + { + DisplayLabel = "Copy"; + } + + public override async Task SimulateCommand(PointInTime startPoint) + { + if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); + if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); + if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); + + var simulateOperation = new SimulateOperation(); + + await TraverseTree(Sources, Target, TransportMode.Value, simulateOperation); + + return startPoint.WithDifferences(simulateOperation.NewDiffs); + } + + public async Task Execute(Func copy, TimeRunner timeRunner) + { + if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); + if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); + if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); + + TimeRunner = timeRunner; + + await CalculateProgress(); + + var copyOperation = new CopyOperation(copy, this); + + await TraverseTree(Sources, Target, TransportMode.Value, copyOperation); + await TimeRunner.RefreshContainer.InvokeAsync(this, Target); + } + + private async Task CalculateProgress() + { + if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); + if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); + if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); + + var calculateOperation = new CalculateOperation(); + await TraverseTree(Sources, Target, TransportMode.Value, calculateOperation); + OperationStatuses = new Dictionary>(calculateOperation.OperationStatuses); + } + + private async Task TraverseTree( + IEnumerable sources, + AbsolutePath target, + TransportMode transportMode, + ICopyOperation copyOperation) + { + var resolvedTarget = (IContainer?)await target.ResolveAsync(); + + foreach (var source in sources) + { + var item = await source.ResolveAsync(); + + if (item is IContainer container) + { + var targetContainer = target.GetChild(item.Name, AbsolutePathType.Container); + if (resolvedTarget != null) + { + await resolvedTarget.RefreshAsync(); + + if (!await resolvedTarget.IsExistsAsync(item.Name)) + { + await copyOperation.CreateContainerAsync(resolvedTarget, container.Name); + } + } + + await TraverseTree((await container.GetItems())!.Select(i => new AbsolutePath(i)), targetContainer, transportMode, copyOperation); + await copyOperation.ContainerCopyDoneAsync(new AbsolutePath(container)); + } + else if (item is IElement element) + { + var newElementName = await Helper.CommandHelper.GetNewNameAsync(resolvedTarget, element.Name, transportMode); + if (newElementName == null) continue; + + OperationProgress? operation = null; + var newElementPath = target.GetChild(newElementName, AbsolutePathType.Element); + + if (OperationStatuses.TryGetValue(target, out var targetPathOperations)) + { + var path = new AbsolutePath(element).Path; + operation = targetPathOperations.Find(o => o.Key == path); + } + CurrentOperationProgress = operation; + + await copyOperation.CopyAsync(new AbsolutePath(element), newElementPath, operation, new CopyCommandContext(UpdateProgress)); + } + } + } + + public override Task CanRun(PointInTime startPoint) + { + //TODO: implement + return Task.FromResult(CanCommandRun.True); + } + } +} \ No newline at end of file diff --git a/src/Core/FileTime.Core/Command/CopyCommandContext.cs b/src/Core/FileTime.Core/Command/Copy/CopyCommandContext.cs similarity index 89% rename from src/Core/FileTime.Core/Command/CopyCommandContext.cs rename to src/Core/FileTime.Core/Command/Copy/CopyCommandContext.cs index 3896e48..5dda9fb 100644 --- a/src/Core/FileTime.Core/Command/CopyCommandContext.cs +++ b/src/Core/FileTime.Core/Command/Copy/CopyCommandContext.cs @@ -1,4 +1,4 @@ -namespace FileTime.Core.Command +namespace FileTime.Core.Command.Copy { public class CopyCommandContext { diff --git a/src/Core/FileTime.Core/Command/Copy/CopyOperation.cs b/src/Core/FileTime.Core/Command/Copy/CopyOperation.cs new file mode 100644 index 0000000..3b2406c --- /dev/null +++ b/src/Core/FileTime.Core/Command/Copy/CopyOperation.cs @@ -0,0 +1,44 @@ +using FileTime.Core.Models; + +namespace FileTime.Core.Command.Copy +{ + public class CopyOperation : ICopyOperation + { + private readonly Func _copy; + private readonly CopyCommand _copyCommand; + + public CopyOperation(Func copy, CopyCommand copyCommand) + { + _copy = copy; + _copyCommand = copyCommand; + } + + public async Task CopyAsync(AbsolutePath from, AbsolutePath to, OperationProgress? operation, CopyCommandContext context) + { + await _copy(from, to, operation, context); + if (operation != null) + { + operation.Progress = operation.TotalCount; + } + await _copyCommand.UpdateProgress(); + } + + public async Task CreateContainerAsync(IContainer target, string name) => await target.CreateContainerAsync(name); + + public async Task ContainerCopyDoneAsync(AbsolutePath path) + { + if (_copyCommand.OperationStatuses.ContainsKey(path)) + { + foreach (var item in _copyCommand.OperationStatuses[path]) + { + item.Progress = item.TotalCount; + } + } + + if (_copyCommand.TimeRunner != null) + { + await _copyCommand.TimeRunner.RefreshContainer.InvokeAsync(this, path); + } + } + } +} \ No newline at end of file diff --git a/src/Core/FileTime.Core/Command/Copy/ICopyOperation.cs b/src/Core/FileTime.Core/Command/Copy/ICopyOperation.cs new file mode 100644 index 0000000..3677ce3 --- /dev/null +++ b/src/Core/FileTime.Core/Command/Copy/ICopyOperation.cs @@ -0,0 +1,11 @@ +using FileTime.Core.Models; + +namespace FileTime.Core.Command.Copy +{ + public interface ICopyOperation + { + Task ContainerCopyDoneAsync(AbsolutePath path); + Task CopyAsync(AbsolutePath from, AbsolutePath to, OperationProgress? operation, CopyCommandContext context); + Task CreateContainerAsync(IContainer target, string name); + } +} \ No newline at end of file diff --git a/src/Core/FileTime.Core/Command/Copy/SimulateOperation.cs b/src/Core/FileTime.Core/Command/Copy/SimulateOperation.cs new file mode 100644 index 0000000..994d192 --- /dev/null +++ b/src/Core/FileTime.Core/Command/Copy/SimulateOperation.cs @@ -0,0 +1,39 @@ +using FileTime.Core.Models; +using FileTime.Core.Timeline; + +namespace FileTime.Core.Command.Copy +{ + public class SimulateOperation : ICopyOperation + { + private readonly List _newDiffs; + public IReadOnlyList NewDiffs { get; } + + public SimulateOperation() + { + _newDiffs = new List(); + NewDiffs = _newDiffs.AsReadOnly(); + } + public Task ContainerCopyDoneAsync(AbsolutePath path) + { + return Task.CompletedTask; + } + + public Task CopyAsync(AbsolutePath from, AbsolutePath to, OperationProgress? operation, CopyCommandContext context) + { + _newDiffs.Add(new Difference(DifferenceActionType.Create, to)); + return Task.CompletedTask; + } + + public Task CreateContainerAsync(IContainer target, string name) + { + var newContainerDiff = new Difference( + DifferenceActionType.Create, + AbsolutePath.FromParentAndChildName(target, name, AbsolutePathType.Container) + ); + + _newDiffs.Add(newContainerDiff); + + return Task.FromResult((IContainer)null!); + } + } +} \ No newline at end of file diff --git a/src/Core/FileTime.Core/Command/CopyCommand.cs b/src/Core/FileTime.Core/Command/CopyCommand.cs deleted file mode 100644 index 81aa1e8..0000000 --- a/src/Core/FileTime.Core/Command/CopyCommand.cs +++ /dev/null @@ -1,211 +0,0 @@ -using AsyncEvent; -using FileTime.Core.Interactions; -using FileTime.Core.Models; -using FileTime.Core.Timeline; - -namespace FileTime.Core.Command -{ - public class CopyCommand : ITransportationCommand - { - private Func? _copyOperation; - private Dictionary> _operationStatuses = new(); - private Func>? _createContainer; - private Func? _containerCopyDone; - private OperationProgress? _currentOperationProgress; - - public IList Sources { get; } = new List(); - - public AbsolutePath? Target { get; set; } - - public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge; - - public int Progress { get; private set; } - public int CurrentProgress { get; private set; } - - public AsyncEventHandler ProgressChanged { get; } = new(); - - public string DisplayLabel { get; } = "Copy"; - public IReadOnlyList CanRunMessages { get; } = new List().AsReadOnly(); - public bool TargetIsContainer => true; - public List Inputs { get; } = new(); - public List? InputResults { get; set; } - - private async Task UpdateProgress() - { - var total = 0L; - var current = 0L; - - foreach (var folder in _operationStatuses.Values) - { - foreach (var item in folder) - { - current += item.Progress; - total += item.TotalCount; - } - } - - Progress = (int)(current * 100 / total); - if (_currentOperationProgress == null) - { - CurrentProgress = 0; - } - else - { - CurrentProgress = (int)(_currentOperationProgress.Progress * 100 / _currentOperationProgress.TotalCount); - } - await ProgressChanged.InvokeAsync(this, AsyncEventArgs.Empty); - } - - public async Task SimulateCommand(PointInTime startPoint) - { - if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); - if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); - if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); - - var newDiffs = new List(); - - _copyOperation = (_, to, _, _) => - { - newDiffs.Add(new Difference(DifferenceActionType.Create, to)); - return Task.CompletedTask; - }; - - _createContainer = (IContainer target, string name) => - { - var newContainerDiff = new Difference( - DifferenceActionType.Create, - AbsolutePath.FromParentAndChildName(target, name, AbsolutePathType.Container) - ); - - newDiffs.Add(newContainerDiff); - - return Task.FromResult((IContainer)null!); - }; - - await TraverseTree(Sources, Target, TransportMode.Value); - - return startPoint.WithDifferences(newDiffs); - } - - public async Task Execute(Func copy, TimeRunner timeRunner) - { - if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); - if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); - if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); - - await CalculateProgress(); - - _copyOperation = async (from, to, operation, context) => - { - await copy(from, to, operation, context); - if (operation != null) - { - operation.Progress = operation.TotalCount; - } - await UpdateProgress(); - }; - - _createContainer = async (IContainer target, string name) => await target.CreateContainerAsync(name); - _containerCopyDone = async (path) => - { - foreach (var item in _operationStatuses[path]) - { - item.Progress = item.TotalCount; - } - - if (timeRunner != null) - { - await timeRunner.RefreshContainer.InvokeAsync(this, path); - } - }; - - await TraverseTree(Sources, Target, TransportMode.Value); - } - - private async Task CalculateProgress() - { - if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); - if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); - if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); - - var operationStatuses = new Dictionary>(); - - _copyOperation = async (from, to, _, _) => - { - var parentPath = to.GetParent(); - List operationsByFolder; - if (operationStatuses.ContainsKey(parentPath)) - { - operationsByFolder = operationStatuses[parentPath]; - } - else - { - var resolvedFrom = await from.ResolveAsync(); - operationsByFolder = new List(); - operationStatuses.Add(parentPath, operationsByFolder); - operationsByFolder.Add(new OperationProgress(from.Path, resolvedFrom is IElement element ? await element.GetElementSize() : 0L)); - } - }; - - await TraverseTree(Sources, Target, TransportMode.Value); - _operationStatuses = operationStatuses; - } - - private async Task TraverseTree( - IEnumerable sources, - AbsolutePath target, - TransportMode transportMode) - { - if (_copyOperation == null) throw new ArgumentException("No copy operation were given."); - if (_createContainer == null) throw new ArgumentException("No container creation function were given."); - - var resolvedTarget = (IContainer?)await target.ResolveAsync(); - - foreach (var source in sources) - { - var item = await source.ResolveAsync(); - - if (item is IContainer container) - { - var targetContainer = target.GetChild(item.Name, AbsolutePathType.Container); - if (_createContainer != null - && resolvedTarget != null - && !await resolvedTarget.IsExistsAsync(item.Name)) - { - await _createContainer.Invoke(resolvedTarget, container.Name); - } - - var childDirectories = (await container.GetContainers())!.Select(d => new AbsolutePath(d)); - var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(f)); - - await TraverseTree(childDirectories.Concat(childFiles), targetContainer, transportMode); - if (_containerCopyDone != null) await _containerCopyDone.Invoke(new AbsolutePath(container)); - } - else if (item is IElement element) - { - var targetName = await Helper.CommandHelper.GetNewNameAsync(resolvedTarget, element.Name, transportMode); - if (targetName == null) continue; - - OperationProgress? operation = null; - var targetFolderPath = new AbsolutePath(target); - var targetElementPath = target.GetChild(targetName, AbsolutePathType.Element); - - if (_operationStatuses.TryGetValue(targetFolderPath, out var targetPathOperations)) - { - var path = new AbsolutePath(element).Path; - operation = targetPathOperations.Find(o => o.Key == path); - } - _currentOperationProgress = operation; - - if (_copyOperation != null) await _copyOperation.Invoke(new AbsolutePath(element), targetElementPath, operation, new CopyCommandContext(UpdateProgress)); - } - } - } - - public Task CanRun(PointInTime startPoint) - { - //TODO: implement - return Task.FromResult(CanCommandRun.True); - } - } -} \ No newline at end of file diff --git a/src/Core/FileTime.Core/Command/CreateContainerCommand.cs b/src/Core/FileTime.Core/Command/CreateContainer/CreateContainerCommand.cs similarity index 97% rename from src/Core/FileTime.Core/Command/CreateContainerCommand.cs rename to src/Core/FileTime.Core/Command/CreateContainer/CreateContainerCommand.cs index c54bf7b..c272ed9 100644 --- a/src/Core/FileTime.Core/Command/CreateContainerCommand.cs +++ b/src/Core/FileTime.Core/Command/CreateContainer/CreateContainerCommand.cs @@ -2,7 +2,7 @@ using AsyncEvent; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Core.Command +namespace FileTime.Core.Command.CreateContainer { public class CreateContainerCommand : IExecutableCommand { @@ -16,7 +16,6 @@ namespace FileTime.Core.Command public string DisplayLabel { get; } public IReadOnlyList CanRunMessages { get; } = new List().AsReadOnly(); - public CreateContainerCommand(AbsolutePath container, string newContainerName) { Container = container; diff --git a/src/Core/FileTime.Core/Command/CreateElementCommand.cs b/src/Core/FileTime.Core/Command/CreateElement/CreateElementCommand.cs similarity index 97% rename from src/Core/FileTime.Core/Command/CreateElementCommand.cs rename to src/Core/FileTime.Core/Command/CreateElement/CreateElementCommand.cs index 8873439..d83059e 100644 --- a/src/Core/FileTime.Core/Command/CreateElementCommand.cs +++ b/src/Core/FileTime.Core/Command/CreateElement/CreateElementCommand.cs @@ -2,7 +2,7 @@ using AsyncEvent; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Core.Command +namespace FileTime.Core.Command.CreateElement { public class CreateElementCommand : IExecutableCommand { diff --git a/src/Core/FileTime.Core/Command/DeleteCommand.cs b/src/Core/FileTime.Core/Command/Delete/DeleteCommand.cs similarity index 93% rename from src/Core/FileTime.Core/Command/DeleteCommand.cs rename to src/Core/FileTime.Core/Command/Delete/DeleteCommand.cs index 9fa4cae..0229739 100644 --- a/src/Core/FileTime.Core/Command/DeleteCommand.cs +++ b/src/Core/FileTime.Core/Command/Delete/DeleteCommand.cs @@ -2,7 +2,7 @@ using AsyncEvent; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Core.Command +namespace FileTime.Core.Command.Delete { public class DeleteCommand : IExecutableCommand { @@ -64,6 +64,11 @@ namespace FileTime.Core.Command { await TraverseTree((await item.ResolveAsync())!); } + + foreach(var updatedParent in ItemsToDelete.Select(i => i.GetParent()).Distinct()) + { + await timeRunner.RefreshContainer.InvokeAsync(this, updatedParent); + } } private async Task TraverseTree(IItem item) diff --git a/src/Core/FileTime.Core/Command/MoveCommand.cs b/src/Core/FileTime.Core/Command/Move/MoveCommand.cs similarity index 96% rename from src/Core/FileTime.Core/Command/MoveCommand.cs rename to src/Core/FileTime.Core/Command/Move/MoveCommand.cs index fe9a537..194c7d2 100644 --- a/src/Core/FileTime.Core/Command/MoveCommand.cs +++ b/src/Core/FileTime.Core/Command/Move/MoveCommand.cs @@ -3,7 +3,7 @@ using FileTime.Core.Interactions; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Core.Command +namespace FileTime.Core.Command.Move { public class MoveCommand : ITransportationCommand { diff --git a/src/Core/FileTime.Core/Command/RenameCommand.cs b/src/Core/FileTime.Core/Command/Rename/RenameCommand.cs similarity index 89% rename from src/Core/FileTime.Core/Command/RenameCommand.cs rename to src/Core/FileTime.Core/Command/Rename/RenameCommand.cs index 744bd7e..a408737 100644 --- a/src/Core/FileTime.Core/Command/RenameCommand.cs +++ b/src/Core/FileTime.Core/Command/Rename/RenameCommand.cs @@ -2,7 +2,7 @@ using AsyncEvent; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Core.Command +namespace FileTime.Core.Command.Rename { public class RenameCommand : IExecutableCommand { @@ -27,7 +27,7 @@ namespace FileTime.Core.Command if (itemToRename != null) { await itemToRename.Rename(Target); - if (timeRunner.RefreshContainer != null) await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!)); + await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!)); } } diff --git a/src/Core/FileTime.Core/CommandHandlers/StreamCopyCommandHandler.cs b/src/Core/FileTime.Core/CommandHandlers/StreamCopyCommandHandler.cs index 3af426c..0db78ef 100644 --- a/src/Core/FileTime.Core/CommandHandlers/StreamCopyCommandHandler.cs +++ b/src/Core/FileTime.Core/CommandHandlers/StreamCopyCommandHandler.cs @@ -1,4 +1,5 @@ using FileTime.Core.Command; +using FileTime.Core.Command.Copy; using FileTime.Core.Models; using FileTime.Core.Timeline; diff --git a/src/Core/FileTime.Core/Components/Tab.cs b/src/Core/FileTime.Core/Components/Tab.cs index f53f4ac..d76f3bf 100644 --- a/src/Core/FileTime.Core/Components/Tab.cs +++ b/src/Core/FileTime.Core/Components/Tab.cs @@ -61,42 +61,51 @@ namespace FileTime.Core.Components { if (_currentlySelecting) return false; - IItem? itemToSelect = null; - if (value != null) + try { - itemToSelect = (await _currentLocation.GetItems(token))?.FirstOrDefault(i => - i.FullName == null && value?.FullName == null - ? i.Name == value?.Name - : i.FullName == value?.FullName); - if (itemToSelect == null) throw new IndexOutOfRangeException($"Provided item ({value.FullName ?? "unknwon"}) does not exists in the current container ({_currentLocation.FullName ?? "unknwon"})."); - } + _currentlySelecting = true; - CancellationToken newToken; - lock (_guardSetCurrentSelectedItemCTS) + IItem? itemToSelect = null; + if (value != null) + { + itemToSelect = (await _currentLocation.GetItems(token))?.FirstOrDefault(i => + i.FullName == null && value?.FullName == null + ? i.Name == value?.Name + : i.FullName == value?.FullName); + if (itemToSelect == null) throw new IndexOutOfRangeException($"Provided item ({value.FullName ?? "unknwon"}) does not exists in the current container ({_currentLocation.FullName ?? "unknwon"})."); + } + + CancellationToken newToken; + lock (_guardSetCurrentSelectedItemCTS) + { + if (token.IsCancellationRequested) return false; + _setCurrentSelectedItemCTS?.Cancel(); + if (token.IsCancellationRequested) + { + _setCurrentSelectedItemCTS = new CancellationTokenSource(); + newToken = _setCurrentSelectedItemCTS.Token; + } + else + { + _setCurrentSelectedItemCTS = new CancellationTokenSource(); + newToken = CancellationTokenSource.CreateLinkedTokenSource(_setCurrentSelectedItemCTS.Token, token).Token; + } + } + + _currentSelectedItem = itemToSelect; + _lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName); + + var newCurrentSelectedIndex = await GetItemIndex(itemToSelect, CancellationToken.None); + CurrentSelectedIndex = newCurrentSelectedIndex; + + await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty, newToken); + + return !newToken.IsCancellationRequested; + } + finally { - if (token.IsCancellationRequested) return false; - _setCurrentSelectedItemCTS?.Cancel(); - if (token.IsCancellationRequested) - { - _setCurrentSelectedItemCTS = new CancellationTokenSource(); - newToken = _setCurrentSelectedItemCTS.Token; - } - else - { - _setCurrentSelectedItemCTS = new CancellationTokenSource(); - newToken = CancellationTokenSource.CreateLinkedTokenSource(_setCurrentSelectedItemCTS.Token, token).Token; - } + _currentlySelecting = false; } - - _currentSelectedItem = itemToSelect; - _lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName); - - var newCurrentSelectedIndex = await GetItemIndex(itemToSelect, CancellationToken.None); - CurrentSelectedIndex = newCurrentSelectedIndex; - - await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty, newToken); - - return !newToken.IsCancellationRequested; } public async Task GetItemByLastPath(IContainer? container = null) { @@ -211,30 +220,35 @@ namespace FileTime.Core.Components { if (token.IsCancellationRequested) return; - _currentlySelecting = true; - if (AutoRefresh && currentLocation != null) - { - await currentLocation.RefreshAsync(token); - if (token.IsCancellationRequested) return; - } - IItem? newSelectedItem = null; - foreach (var item in currentPossibleItems) + try { - if (currentLocationItems.FirstOrDefault(i => i.Name == item.Name) is var possibleNewSelectedItem - && possibleNewSelectedItem is not null) + _currentlySelecting = true; + if (AutoRefresh && currentLocation != null) { - newSelectedItem = possibleNewSelectedItem; - break; + await currentLocation.RefreshAsync(token); + if (token.IsCancellationRequested) return; + } + + foreach (var item in currentPossibleItems) + { + if (currentLocationItems.FirstOrDefault(i => i.Name == item.Name) is var possibleNewSelectedItem + && possibleNewSelectedItem is not null) + { + newSelectedItem = possibleNewSelectedItem; + break; + } + } + + if (newSelectedItem != null) + { + newSelectedItem = (await (await GetCurrentLocation(token)).GetItems(token))?.FirstOrDefault(i => i.Name == newSelectedItem.Name); } } - - if (newSelectedItem != null) + finally { - newSelectedItem = (await (await GetCurrentLocation(token)).GetItems(token))?.FirstOrDefault(i => i.Name == newSelectedItem.Name); + _currentlySelecting = false; } - - _currentlySelecting = false; await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null), token: token); } else diff --git a/src/Core/FileTime.Core/Models/IContainer.cs b/src/Core/FileTime.Core/Models/IContainer.cs index 33396a2..7cd19de 100644 --- a/src/Core/FileTime.Core/Models/IContainer.cs +++ b/src/Core/FileTime.Core/Models/IContainer.cs @@ -10,7 +10,7 @@ namespace FileTime.Core.Models Task?> GetElements(CancellationToken token = default); Task RefreshAsync(CancellationToken token = default); - async Task GetByPath(string path, bool acceptDeepestMatch = false) + public async Task GetByPath(string path, bool acceptDeepestMatch = false) { if (path == null) return this; var paths = path.Split(Constants.SeparatorChar); diff --git a/src/Core/FileTime.Core/Models/IItem.cs b/src/Core/FileTime.Core/Models/IItem.cs index d7fa444..a8cf7f6 100644 --- a/src/Core/FileTime.Core/Models/IItem.cs +++ b/src/Core/FileTime.Core/Models/IItem.cs @@ -9,6 +9,7 @@ namespace FileTime.Core.Models string? NativePath { get; } bool IsHidden { get; } bool IsDestroyed { get; } + bool IsExists { get; } SupportsDelete CanDelete { get; } bool CanRename { get; } IContentProvider Provider { get; } diff --git a/src/Core/FileTime.Core/Models/VirtualContainer.cs b/src/Core/FileTime.Core/Models/VirtualContainer.cs index 082f1c7..3403eb5 100644 --- a/src/Core/FileTime.Core/Models/VirtualContainer.cs +++ b/src/Core/FileTime.Core/Models/VirtualContainer.cs @@ -37,6 +37,7 @@ namespace FileTime.Core.Models public AsyncEventHandler Refreshed { get; } public bool IsDestroyed => BaseContainer.IsDestroyed; + public bool IsExists => BaseContainer.IsExists; private void RefreshAddBase(Func handler) { diff --git a/src/Core/FileTime.Core/Providers/AbstractContainer.cs b/src/Core/FileTime.Core/Providers/AbstractContainer.cs index ede3481..4aac1da 100644 --- a/src/Core/FileTime.Core/Providers/AbstractContainer.cs +++ b/src/Core/FileTime.Core/Providers/AbstractContainer.cs @@ -37,12 +37,14 @@ namespace FileTime.Core.Providers IContentProvider IItem.Provider => Provider; + public abstract bool IsExists { get; } + protected AbstractContainer(TProvider provider, IContainer parent, string name) { _parent = parent; Provider = provider; Name = name; - FullName = (parent?.FullName ?? Name) + Constants.SeparatorChar + Name; + FullName = parent.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name; Exceptions = _exceptions.AsReadOnly(); } diff --git a/src/Core/FileTime.Core/Providers/TopContainer.cs b/src/Core/FileTime.Core/Providers/TopContainer.cs index c722135..269ce0a 100644 --- a/src/Core/FileTime.Core/Providers/TopContainer.cs +++ b/src/Core/FileTime.Core/Providers/TopContainer.cs @@ -33,6 +33,7 @@ namespace FileTime.Core.Providers public bool SupportsDirectoryLevelSoftDelete => false; public bool IsDestroyed => false; + public bool IsExists => true; public TopContainer(IEnumerable contentProviders) { diff --git a/src/Core/FileTime.Core/Timeline/TimeContainer.cs b/src/Core/FileTime.Core/Timeline/TimeContainer.cs index cd4e6b8..a91e664 100644 --- a/src/Core/FileTime.Core/Timeline/TimeContainer.cs +++ b/src/Core/FileTime.Core/Timeline/TimeContainer.cs @@ -33,6 +33,7 @@ namespace FileTime.Core.Timeline //FIXME: currently this can be different of the real items NativePath, should be fixed public string? NativePath => FullName; + public bool IsExists => true; public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime) { diff --git a/src/Core/FileTime.Core/Timeline/TimeElement.cs b/src/Core/FileTime.Core/Timeline/TimeElement.cs index 2771fbe..e5224e5 100644 --- a/src/Core/FileTime.Core/Timeline/TimeElement.cs +++ b/src/Core/FileTime.Core/Timeline/TimeElement.cs @@ -35,6 +35,7 @@ namespace FileTime.Core.Timeline public IContentProvider VirtualProvider { get; } public bool IsDestroyed { get; private set; } + public bool IsExists => true; public Task Delete(bool hardDelete = false) => Task.CompletedTask; diff --git a/src/Core/FileTime.Core/Timeline/TimeProvider.cs b/src/Core/FileTime.Core/Timeline/TimeProvider.cs index 0dc41c9..a0e416d 100644 --- a/src/Core/FileTime.Core/Timeline/TimeProvider.cs +++ b/src/Core/FileTime.Core/Timeline/TimeProvider.cs @@ -33,6 +33,7 @@ namespace FileTime.Core.Timeline public bool SupportsContentStreams => false; public string Protocol => "time2://"; + public bool IsExists => true; public TimeProvider(PointInTime pointInTime) { diff --git a/src/GuiApp/FileTime.Avalonia/App.axaml.cs b/src/GuiApp/FileTime.Avalonia/App.axaml.cs index 21db923..452024d 100644 --- a/src/GuiApp/FileTime.Avalonia/App.axaml.cs +++ b/src/GuiApp/FileTime.Avalonia/App.axaml.cs @@ -24,8 +24,8 @@ namespace FileTime.Avalonia .BuildServiceProvider() .InitSerilog(); - var _logger = ServiceProvider.GetService>(); - _logger?.LogInformation("App initialization completed."); + var logger = ServiceProvider.GetService>(); + logger?.LogInformation("App initialization completed."); } public override void Initialize() diff --git a/src/GuiApp/FileTime.Avalonia/Application/TabContainer.cs b/src/GuiApp/FileTime.Avalonia/Application/TabContainer.cs index 87f485c..dc20fe2 100644 --- a/src/GuiApp/FileTime.Avalonia/Application/TabContainer.cs +++ b/src/GuiApp/FileTime.Avalonia/Application/TabContainer.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using FileTime.App.Core.Tab; using System.Threading; +using FileTime.Core.Timeline; namespace FileTime.Avalonia.Application { @@ -18,6 +19,7 @@ namespace FileTime.Avalonia.Application [Inject(typeof(ItemNameConverterService))] [Inject(typeof(LocalContentProvider))] [Inject(typeof(Tab))] + [Inject(typeof(TimeRunner), propertyName: "_timeRunner")] public partial class TabContainer : INewItemProcessor { private bool _updateFromCode; @@ -77,6 +79,21 @@ namespace FileTime.Avalonia.Application partial void OnInitialize() { _tabState = new TabState(Tab); + _timeRunner.RefreshContainer.Add(TimeRunnerContainerRefreshed); + + } + + private async Task TimeRunnerContainerRefreshed(object? sender, AbsolutePath container, CancellationToken token = default) + { + var currentLocation = await Tab.GetCurrentLocation(); + if (currentLocation != null) + { + var currentLocationPath = new AbsolutePath(currentLocation); + if (currentLocationPath == container) + { + await currentLocation.RefreshAsync(); + } + } } public async Task Init(int tabNumber) diff --git a/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs b/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs index 52b16e3..7211981 100644 --- a/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs +++ b/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs @@ -12,6 +12,12 @@ using FileTime.Avalonia.Misc; using FileTime.Avalonia.Models; using FileTime.Avalonia.ViewModels; using FileTime.Core.Command; +using FileTime.Core.Command.Copy; +using FileTime.Core.Command.CreateContainer; +using FileTime.Core.Command.CreateElement; +using FileTime.Core.Command.Delete; +using FileTime.Core.Command.Move; +using FileTime.Core.Command.Rename; using FileTime.Core.Components; using FileTime.Core.Interactions; using FileTime.Core.Models; @@ -229,7 +235,7 @@ namespace FileTime.Avalonia.Services var newTab = new Tab(); await newTab.Init(newContainer); - tabContainer = new TabContainer(newTab, _localContentProvider, _itemNameConverterService); + tabContainer = new TabContainer(_timeRunner, newTab, _localContentProvider, _itemNameConverterService); await tabContainer.Init(number); var i = 0; diff --git a/src/GuiApp/FileTime.Avalonia/Services/StatePersistenceService.cs b/src/GuiApp/FileTime.Avalonia/Services/StatePersistenceService.cs index a27a9de..66ecd74 100644 --- a/src/GuiApp/FileTime.Avalonia/Services/StatePersistenceService.cs +++ b/src/GuiApp/FileTime.Avalonia/Services/StatePersistenceService.cs @@ -1,6 +1,4 @@ using System.Linq; -using System.Net; -using System.Text; using System.Collections.Generic; using System.IO; using System.Text.Json; @@ -13,6 +11,7 @@ using FileTime.Core.Providers; using FileTime.Providers.Local; using FileTime.Core.Models; using Microsoft.Extensions.Logging; +using FileTime.Core.Timeline; namespace FileTime.Avalonia.Services { @@ -25,13 +24,15 @@ namespace FileTime.Avalonia.Services private readonly IEnumerable _contentProviders; private readonly LocalContentProvider _localContentProvider; private readonly ILogger _logger; + private readonly TimeRunner _timeRunner; public StatePersistenceService( AppState appState, ItemNameConverterService itemNameConverterService, IEnumerable contentProviders, LocalContentProvider localContentProvider, - ILogger logger) + ILogger logger, + TimeRunner timeRunner) { _appState = appState; _itemNameConverterService = itemNameConverterService; @@ -45,6 +46,7 @@ namespace FileTime.Avalonia.Services PropertyNameCaseInsensitive = true, WriteIndented = true }; + this._timeRunner = timeRunner; } public async Task LoadStatesAsync() @@ -151,7 +153,7 @@ namespace FileTime.Avalonia.Services } } - var newTabContainer = new TabContainer(newTab, _localContentProvider, _itemNameConverterService); + var newTabContainer = new TabContainer(_timeRunner, newTab, _localContentProvider, _itemNameConverterService); await newTabContainer.Init(tab.Number); _appState.Tabs.Add(newTabContainer); } diff --git a/src/GuiApp/FileTime.Avalonia/ViewModels/ContainerViewModel.cs b/src/GuiApp/FileTime.Avalonia/ViewModels/ContainerViewModel.cs index be6be80..62be724 100644 --- a/src/GuiApp/FileTime.Avalonia/ViewModels/ContainerViewModel.cs +++ b/src/GuiApp/FileTime.Avalonia/ViewModels/ContainerViewModel.cs @@ -154,10 +154,10 @@ namespace FileTime.Avalonia.ViewModels { _isRefreshing = true; - List newContainers = new List(); - List newElements = new List(); + List newContainers = new(); + List newElements = new(); - if (await _container.GetContainers() is IReadOnlyList containers) + if (await _container.GetContainers(token) is IReadOnlyList containers) { foreach (var container in containers) { @@ -165,7 +165,7 @@ namespace FileTime.Avalonia.ViewModels } } - if (await _container.GetElements() is IReadOnlyList elements) + if (await _container.GetElements(token) is IReadOnlyList elements) { foreach (var element in elements) { @@ -216,7 +216,7 @@ namespace FileTime.Avalonia.ViewModels _exceptions.Add(e); } - await _newItemProcessor.UpdateMarkedItems(this); + await _newItemProcessor.UpdateMarkedItems(this, CancellationToken.None); _isRefreshing = false; } diff --git a/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs b/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs index 9ed8f38..17eef68 100644 --- a/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs +++ b/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs @@ -84,7 +84,7 @@ namespace FileTime.Avalonia.ViewModels var tab = new Tab(); await tab.Init(LocalContentProvider); - var tabContainer = new TabContainer(tab, LocalContentProvider, ItemNameConverterService); + var tabContainer = new TabContainer(_timeRunner, tab, LocalContentProvider, ItemNameConverterService); await tabContainer.Init(1); tabContainer.IsSelected = true; AppState.Tabs.Add(tabContainer); diff --git a/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs b/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs index 7949132..77e9489 100644 --- a/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs +++ b/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs @@ -87,7 +87,7 @@ namespace FileTime.Avalonia.Views private void InputText_AttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper && inputElementWrapper == ViewModel!.AppState.Inputs.First()) + if (sender is TextBox inputText && inputText.IsVisible && inputText.DataContext is InputElementWrapper inputElementWrapper && inputElementWrapper == ViewModel!.AppState.Inputs[0]) { inputText.Focus(); } diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index 6514829..3882108 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -38,6 +38,7 @@ namespace FileTime.Providers.Local public bool IsDestroyed => false; public bool SupportsContentStreams => true; + public bool IsExists => true; public LocalContentProvider(ILogger logger) { @@ -49,8 +50,6 @@ namespace FileTime.Providers.Local ? new DirectoryInfo("/").GetDirectories() : Environment.GetLogicalDrives().Select(d => new DirectoryInfo(d)); - FullName = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "" : null; - _rootContainers = rootDirectories.Select(d => new LocalFolder(d, this, this)).OrderBy(d => d.Name).ToList().AsReadOnly(); _items = _rootContainers.Cast().ToList().AsReadOnly(); } diff --git a/src/Providers/FileTime.Providers.Local/LocalFile.cs b/src/Providers/FileTime.Providers.Local/LocalFile.cs index b7165f9..7dd0ade 100644 --- a/src/Providers/FileTime.Providers.Local/LocalFile.cs +++ b/src/Providers/FileTime.Providers.Local/LocalFile.cs @@ -15,6 +15,7 @@ namespace FileTime.Providers.Local public string FullName { get; } public string? NativePath => File.FullName; + public bool IsExists => File.Exists; public IContentProvider Provider { get; } diff --git a/src/Providers/FileTime.Providers.Local/LocalFolder.cs b/src/Providers/FileTime.Providers.Local/LocalFolder.cs index ee57801..d19d667 100644 --- a/src/Providers/FileTime.Providers.Local/LocalFolder.cs +++ b/src/Providers/FileTime.Providers.Local/LocalFolder.cs @@ -5,13 +5,14 @@ using FileTime.Providers.Local.Interop; namespace FileTime.Providers.Local { - public class LocalFolder : AbstractContainer + public class LocalFolder : AbstractContainer, IContainer { public DirectoryInfo Directory { get; } public string Attributes => GetAttributes(); public DateTime CreatedAt => Directory.CreationTime; + public override bool IsExists => Directory.Exists; public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer parent) : base(contentProvider, parent, directory.Name.TrimEnd(Path.DirectorySeparatorChar)) @@ -52,7 +53,7 @@ namespace FileTime.Providers.Local return Task.FromResult(Enumerable.Empty()); } - public async Task GetByPath(string path, bool acceptDeepestMatch = false) + async Task IContainer.GetByPath(string path, bool acceptDeepestMatch) { var paths = path.Split(Constants.SeparatorChar); @@ -87,7 +88,7 @@ namespace FileTime.Providers.Local return (await GetElements())!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!; } - public override async Task IsExistsAsync(string name) => (await GetItems())?.Any(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false; + public override async Task IsExistsAsync(string name) => (await GetItems())?.Any(i => i.IsExists && Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false; public override Task Delete(bool hardDelete = false) { diff --git a/src/Providers/FileTime.Providers.Sftp/SftpContentProvider.cs b/src/Providers/FileTime.Providers.Sftp/SftpContentProvider.cs index d095f5c..81e7fe7 100644 --- a/src/Providers/FileTime.Providers.Sftp/SftpContentProvider.cs +++ b/src/Providers/FileTime.Providers.Sftp/SftpContentProvider.cs @@ -43,6 +43,7 @@ namespace FileTime.Providers.Sftp public IContentProvider Provider => this; public string Protocol => "sftp://"; + public bool IsExists => true; public SftpContentProvider(IInputInterface inputInterface, ILogger logger) { diff --git a/src/Providers/FileTime.Providers.Sftp/SftpFile.cs b/src/Providers/FileTime.Providers.Sftp/SftpFile.cs index af5fe6f..d30feed 100644 --- a/src/Providers/FileTime.Providers.Sftp/SftpFile.cs +++ b/src/Providers/FileTime.Providers.Sftp/SftpFile.cs @@ -23,6 +23,8 @@ namespace FileTime.Providers.Sftp public IContentProvider Provider => throw new NotImplementedException(); + public bool IsExists => throw new NotImplementedException(); + public Task Delete(bool hardDelete = false) { throw new NotImplementedException(); diff --git a/src/Providers/FileTime.Providers.Sftp/SftpFolder.cs b/src/Providers/FileTime.Providers.Sftp/SftpFolder.cs index a4f7bf1..371888d 100644 --- a/src/Providers/FileTime.Providers.Sftp/SftpFolder.cs +++ b/src/Providers/FileTime.Providers.Sftp/SftpFolder.cs @@ -6,6 +6,7 @@ namespace FileTime.Providers.Sftp public class SftpFolder : AbstractContainer { private readonly SftpServer _server; + public override bool IsExists => true; public SftpFolder(SftpContentProvider provider, SftpServer server, IContainer parent, string path) : base(provider, parent, path) { diff --git a/src/Providers/FileTime.Providers.Sftp/SftpServer.cs b/src/Providers/FileTime.Providers.Sftp/SftpServer.cs index a009c5e..fde945c 100644 --- a/src/Providers/FileTime.Providers.Sftp/SftpServer.cs +++ b/src/Providers/FileTime.Providers.Sftp/SftpServer.cs @@ -18,6 +18,7 @@ namespace FileTime.Providers.Sftp public string? Username { get; private set; } public string? Password { get; private set; } + public override bool IsExists => true; public SftpServer(string name, SftpContentProvider sftpContentProvider, IInputInterface inputInterface, string? username = null, string? password = null) : base(sftpContentProvider, sftpContentProvider, name) diff --git a/src/Providers/FileTime.Providers.Smb/SmbContentProvider.cs b/src/Providers/FileTime.Providers.Smb/SmbContentProvider.cs index 0fcbdab..d76ed81 100644 --- a/src/Providers/FileTime.Providers.Smb/SmbContentProvider.cs +++ b/src/Providers/FileTime.Providers.Smb/SmbContentProvider.cs @@ -42,6 +42,7 @@ namespace FileTime.Providers.Smb public bool IsDestroyed => false; public bool SupportsContentStreams => true; + public bool IsExists => true; public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger logger) { diff --git a/src/Providers/FileTime.Providers.Smb/SmbFile.cs b/src/Providers/FileTime.Providers.Smb/SmbFile.cs index ed89dd8..48638a8 100644 --- a/src/Providers/FileTime.Providers.Smb/SmbFile.cs +++ b/src/Providers/FileTime.Providers.Smb/SmbFile.cs @@ -24,6 +24,8 @@ namespace FileTime.Providers.Smb public IContentProvider Provider { get; } public bool IsDestroyed { get; private set; } + //TODO: implement + public bool IsExists => true; public SmbFile(string name, SmbContentProvider provider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext) { diff --git a/src/Providers/FileTime.Providers.Smb/SmbFolder.cs b/src/Providers/FileTime.Providers.Smb/SmbFolder.cs index 21f5cb2..39c15aa 100644 --- a/src/Providers/FileTime.Providers.Smb/SmbFolder.cs +++ b/src/Providers/FileTime.Providers.Smb/SmbFolder.cs @@ -8,8 +8,9 @@ namespace FileTime.Providers.Smb { private readonly SmbClientContext _smbClientContext; - public SmbShare SmbShare { get; } + //TODO: implement + public override bool IsExists => true; public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext) : base(contentProvider, parent, name) diff --git a/src/Providers/FileTime.Providers.Smb/SmbServer.cs b/src/Providers/FileTime.Providers.Smb/SmbServer.cs index e4ef80d..3eae02f 100644 --- a/src/Providers/FileTime.Providers.Smb/SmbServer.cs +++ b/src/Providers/FileTime.Providers.Smb/SmbServer.cs @@ -8,7 +8,7 @@ using SMBLibrary.Client; namespace FileTime.Providers.Smb { - public class SmbServer : AbstractContainer + public class SmbServer : AbstractContainer, IContainer { internal const int MAXRETRIES = 5; @@ -21,6 +21,7 @@ namespace FileTime.Providers.Smb public string? Username { get; private set; } public string? Password { get; private set; } + public override bool IsExists => true; public SmbServer(string name, SmbContentProvider contentProvider, IInputInterface inputInterface, string? username = null, string? password = null) : base(contentProvider, contentProvider, name) @@ -52,7 +53,7 @@ namespace FileTime.Providers.Smb return Task.CompletedTask; } - public async Task GetByPath(string path, bool acceptDeepestMatch = false) + async Task IContainer.GetByPath(string path, bool acceptDeepestMatch) { var paths = path.Split(Constants.SeparatorChar); diff --git a/src/Providers/FileTime.Providers.Smb/SmbShare.cs b/src/Providers/FileTime.Providers.Smb/SmbShare.cs index 046f6d3..4cae6fe 100644 --- a/src/Providers/FileTime.Providers.Smb/SmbShare.cs +++ b/src/Providers/FileTime.Providers.Smb/SmbShare.cs @@ -8,6 +8,7 @@ namespace FileTime.Providers.Smb public class SmbShare : AbstractContainer { private readonly SmbClientContext _smbClientContext; + public override bool IsExists => true; public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext) : base(contentProvider, parent, name)